Lazarus

Programming => Packages and Libraries => RichMemo => Topic started by: rick2691 on September 12, 2016, 06:03:34 pm

Title: Inserting, Saving and Loading Images
Post by: rick2691 on September 12, 2016, 06:03:34 pm
Quote
I suggest that you create another thread if you are still looking for a solution to save/load the images with files.

It basically needs an interface. Specifically IRichEditOleCallback interface. You pass an instance to the rich edit using EM_SETOLECALLBACK message.

For reading images it needs two functions implemented GetNewStorage and QueryInsertObject. One is to allocate space for the parsed images, while the other is to ask if you want to show the image. The rest of the interface functions can return E_NOTIMPL.

As for writing, I just tested EM_STREAMOUT. It embedded the image in the file successfully.

I have tried to find out information on the functions that you have listed. Documentations are terse. I have found an example, but it is in C code, and my knowledge of C is very limited.

Code: Pascal  [Select][+][-]
  1. private string ReadRTF(IntPtr handle)
  2. {
  3.     string result = String.Empty;
  4.     using (MemoryStream stream = new MemoryStream())
  5.     {
  6.         EDITSTREAM editStream = new EDITSTREAM();
  7.         editStream.pfnCallback = new EditStreamCallback(EditStreamProc);
  8.         editStream.dwCookie = stream;
  9.  
  10.         SendMessage(handle, EM_STREAMOUT, SF_RTF, editStream);
  11.  
  12.         stream.Seek(0, SeekOrigin.Begin);
  13.         using (StreamReader reader = new StreamReader(stream))
  14.         {
  15.             result = reader.ReadToEnd();
  16.         }
  17.     }
  18.     return result;
  19. }
  20.  
  21. private int EditStreamProc(MemoryStream dwCookie, IntPtr pbBuff, int cb, out int pcb)
  22. {
  23.     pcb = cb;
  24.     byte[] buffer = new byte[cb];
  25.     Marshal.Copy(pbBuff, buffer, 0, cb);
  26.     dwCookie.Write(buffer, 0, cb);
  27.     return 0;
  28. }
  29.  
  30. private delegate int EditStreamCallback(MemoryStream dwCookie, IntPtr pbBuff, int cb, out int pcb);
  31.  
  32. [StructLayout(LayoutKind.Sequential)]
  33. private class EDITSTREAM
  34. {
  35.     public MemoryStream dwCookie;
  36.     public int dwError;
  37.     public EditStreamCallback pfnCallback;
  38. }
  39.  
  40. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  41. private static extern IntPtr SendMessage(HandleRef hwnd, uint msg, uint wParam, ref EDITSTREAM lParam);
  42.  
  43. public const int WM_USER = 0x0400;
  44. public const int EM_STREAMOUT = WM_USER + 74;
  45. public const int SF_RTF = 2;
  46.  
  47. //apply the above
  48.  
  49. here's how you can call this:
  50.  
  51. string temp = ReadRTF(richTextBox1.Handle);
  52. Console.WriteLine(temp);
  53. on my test richedit this returns following string:
  54.  
  55. {\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}} \viewkind4\uc1\pard\qc\f0\fs17 test paragraph\par \pard test paragraph\par }
  56.  
  57. static DWORD CALLBACK
  58. MyStreamOutCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
  59. {
  60.         HANDLE pFile = (HANDLE) dwCookie;
  61.         DWORD dwW;
  62.         WriteFile(pFile,pbBuff,cb,&dwW,NULL);
  63.         *pcb = cb;
  64.  
  65.         return 0;
  66. }
  67.  
  68. void SaveTest(HWND hWnd, int nIDDlgItem)
  69. {
  70.         HANDLE hFile = CreateFile("c:\\myfile.rtf",GENERIC_WRITE,0,NULL,
  71.                 CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  72.  
  73.         EDITSTREAM es;
  74.  
  75.         es.dwCookie = (DWORD) &hFile;
  76.         es.pfnCallback = MyStreamOutCallback;
  77.  
  78.         SendDlgItemMessage(hWnd,nIDDlgItem,EM_STREAMOUT,(WPARAM)SF_RTF,(LPARAM)&es);
  79.  
  80.         CloseHandle(hFile);
  81. }
  82.  
  83. The above line might be wrong:
  84.  
  85. es.dwCookie = (DWORD) &hFile;
  86.  
  87. You're passing the address of the file's handle, not the handle itself, which is what your MyStreamOutCallback() function is expecting. Just try
  88.  
  89. es.dwCookie = (DWORD) hFile;
  90.  
  91. -Mike
  92.  
  93. By the way, EM_STREAMIN/EM_STREAMOUT can load or save unicode content to/from a file if the flag SF_UNICODE is set:
  94.  
  95. SendMessageW(richeditWindow, EM_STREAMOUT, (WPARAM) SF_TEXTIZED | SF_UNICODE, (LPARAM) &eds);
  96.  

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: Leledumbo on September 12, 2016, 06:56:44 pm
I have found an example, but it is in C code, and my knowledge of C is very limited.
That's C# btw. Ugh, the code is pure WinAPI, guess I've lost that knowledge long time ago.
Title: Re: Inserting, Saving and Loading Images
Post by: engkin on September 12, 2016, 09:51:01 pm
The example you found does not load images.

Attached with this post a small example. You will notice that btnLoad without btnOLE does not show images.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 01:32:33 pm
engkin,

Thanks. I will look at your example.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 01:51:33 pm
engkin,

Your example work perfectly. I inserted a call into btnLoad that activates btnOLE. It worked, but is there anything wrong with doing that?

I have to say that your code is "way simple and efficient." I am grateful.

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.btnLoadClick(Sender: TObject);
  2. var
  3.   fs: TFileStream;
  4. begin
  5.   btnOLEClick(self);   //*** inserted call ***
  6.   if OpenDialog1.Execute then
  7.   begin
  8.     fs := TFileStream.Create(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
  9.     try
  10.       PageMemo.LoadRichText(fs);
  11.     finally
  12.       fs.Free;
  13.     end;
  14.   end;
  15. end;
  16.  
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 14, 2016, 03:32:04 pm
 :o
I'd never thought that assigning OLE callback is that critical for embedded objects to be loaded. (having this implemented by default in RichEdit control would make more sense, as it seems doing now in Windows 8 and later)

At least that explains the difference why wordpad (http://forum.lazarus.freepascal.org/index.php/topic,33772.0.html) loads images and RM doesn't.

I've added creation of the OLE callback for RichMemo by default in r5159. Thus you shouldn't need to create the object yourself.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 04:18:29 pm
skalogryz,

Thank you much.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 14, 2016, 05:04:03 pm
thanks should go to engkin :)
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 05:34:18 pm
I loaded r5159. This change has also permitted the image to be selected and dragged to another location.

Do you think it might be possible to access its properties to alter width and height?

And, big thanks to engkin!
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 14, 2016, 06:03:14 pm
Do you think it might be possible to access its properties to alter width and height?
Do you mean programmatically? from UI perspective you should be able to click it and resize it.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 06:32:00 pm
Correct. I forgot to mention that it can be clicked and stretched. I want to do it by code so that I can resize it proportionately. Distorted images by hand dragging are not useful to me.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 08:52:39 pm
The OLE allows for dragging a node for dynamically resizing the image. Can that be modified so that a corner node will be proportional? The sides would allow distortion.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 09:29:54 pm
I have noticed a problem. The image isn't always selectable. Additional images cannot be selected.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 14, 2016, 09:38:20 pm
I have noticed a problem. The image isn't always selectable. Additional images cannot be selected.
you might want to see if Wordpad behaves in the same manner. If it does... it might be unfixable.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 14, 2016, 10:01:54 pm
it just occurred to me... I am doing additional inserts. It may mean that the insert code needs a OLE callback.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 15, 2016, 05:31:40 am
Let's give r5163 a try.

In order to resize an object two additional functions were introduced in Win32RichMemo module:
* GetRichEditOLE - the function returns IRichEditOle interface. It is used in the next function
* SetOleObjectSize - the function sets the size of an object at the given cursor position. (If there's no object under the position, i.e. text only, the function returns false). The Size is specified in Points (1/72 of an inch).
For your convenience, there's also
* GetOleObjectSize - the function returns the current size of an object in points.

Here's an example of how to shrink the currently selected object twice:
Code: [Select]
var
  ole : IRichEditOle;
  sz : TSize;
begin
  ole:=GetRichEditOLE(RichMemo1);
  GetOleObjectSize(ole, RichMemo1.SelStart, sz);
  sz.cx:=(sz.cx div 2);
  sz.cy:=(sz.cy div 2);
  SetOleObjectSize(ole, RichMemo1.SelStart, sz);     

it just occurred to me... I am doing additional inserts. It may mean that the insert code needs a OLE callback.
Now flags for an inline object inserted can be controlled from your code.
Win32RichMemo is exposing InlineInsertFlag variable. The flag matches values of dwFlags field in REOBJECT structure. MSDN documentation is here. (https://msdn.microsoft.com/en-us/library/windows/desktop/bb787946(v=vs.85).aspx).

Values are declared at Win32RichMemoProc unit.

If you want to make any image inserted via InsertInline resizable you want to add the following line into your code.
Code: [Select]
uses ... Win32RichMemo, Win32RichMemoProc;


  InsertInlineFlags:=REO_RESIZABLE;
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 15, 2016, 05:42:21 am
WARNING: InsertInlineFlags variable might (and likely will be) removed in future revisions of RichMemo. More likely when "inlines" interfaces will be stable, official and truly cross-platform.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 15, 2016, 02:13:37 pm
Just tried r5163.

When opening a file that was previous to this version, the image can be resized by dragging its grips.

Images inserted by this version have the grips, but dragging a grip will resize its box and not resize the image.

The only code that I have implemented at this point is: InsertInlineFlags:=REO_RESIZABLE;

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.MnuDiskImageClick(Sender: TObject);
  2.   var
  3.     pic : TPicture;
  4.     ip  : TRichMemoInlinePicture;
  5.     sz  : TSize;
  6.   begin
  7.     OpenDialog1.Title:= 'Select Image-File';
  8.     OpenDialog1.Filter:= 'Jpeg (*.jpg)|*.jpg|Ping (*.png)|*.png|Tiff (*.tif)|*.tif';
  9.     OpenDialog1.FileName:= '';
  10.     if OpenDialog1.Execute then
  11.        begin
  12.        pic:=TPicture.Create;
  13.        try pic.LoadFromFile(OpenDialog1.FileName);
  14.            except on e:Exception do
  15.                   begin
  16.                   pic.Free;
  17.                   ShowMessage('unable to read image file: '+OpenDialog1.FileName+#13#10+e.Message);
  18.                   Exit;
  19.                   end;
  20.            end;
  21.        ip:=TRichMemoInlinePicture.Create(pic);
  22.        sz:=Types.Size(round(pic.Width*72/96), round(pic.Height*72/96));  //  Types.Size due to unit conflict
  23.        InsertInlineFlags:=REO_RESIZABLE;      // ***test placement***
  24.        PageMemo.InDelInline(ip, PageMemo.SelStart, 0, sz);
  25.        end;
  26.   end;
  27.  

Does it need to be elsewhere?
And shouldn't the drag-grip to resize still operate?

Rick
 
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 15, 2016, 02:19:09 pm
I also saved the file with the new inserts. When I opened the file the images were not there.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 15, 2016, 03:00:35 pm
I relocated the InsertInlineFlags:=REO_RESIZABLE to the top of my function code (just before the picture.create.

It operated the same way. Drag box resized, but the image did not. The image couldn't be saved with the file.

I then removed the InsertInlineFlags:=REO_RESIZABLE and the image was not selectable, nor could it be saved with the file.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 16, 2016, 01:15:29 pm
I tried a coded resizing for the image to see if it changed the situation.

To do so I had to change the type of IRichEditOle to WIN32RICHMEMOPROC.IRichEditOle due to a naming conflict.

The resizing worked, but with the same results... the frame resized, but the image did not.

It also crashed on closing the program. It went to an Assembler error. CPM is still alive!

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.MnuDiskImageClick(Sender: TObject);
  2. var
  3.   pic : TPicture;
  4.   ip  : TRichMemoInlinePicture;
  5.   ole : WIN32RICHMEMOPROC.IRichEditOle; // IRichEditOle;
  6.   sz  : TSize;
  7.   x   : longint;
  8. begin
  9.   OpenDialog1.Title:= 'Select Image-File';
  10.   OpenDialog1.Filter:= 'Jpg (*.jpg)|*.jpg|Png (*.png)|*.png|Tif (*.tif)|*.tif|Bmp (*.bmp)|*.bmp';
  11.   OpenDialog1.FileName:= '';
  12.   if OpenDialog1.Execute then
  13.      begin
  14.      pic:=TPicture.Create;
  15.      try pic.LoadFromFile(OpenDialog1.FileName);  // load image file
  16.          except on e:Exception do
  17.                 begin
  18.                 pic.Free;
  19.                 ShowMessage('Unknown Image Format: '+OpenDialog1.FileName+#13#10+e.Message);
  20.                 Exit;
  21.                 end;
  22.          end;
  23.  
  24.      InsertInlineFlags:= REO_RESIZABLE; // test placement
  25.  
  26.      ip:=TRichMemoInlinePicture.Create(pic);  // Screen.PixelsPerInch typically = 96
  27.      sz:=Types.Size(round(pic.Width*72/Screen.PixelsPerInch), round(pic.Height*72/Screen.PixelsPerInch));  //  "Types.Size" due to unit conflict
  28.      PageMemo.InDelInline(ip, PageMemo.SelStart, 0, sz);
  29.  
  30.      ole:=GetRichEditOLE(PageMemo);              // test resizing
  31.      GetOleObjectSize(ole, PageMemo.SelStart, sz);
  32.      sz.cx:=round(sz.cx * 1.25);  // magnify
  33.      sz.cy:=round(sz.cy * 1.25);
  34.      SetOleObjectSize(ole, PageMemo.SelStart, sz);
  35.  
  36.      end;
  37. end;  
  38.  

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 16, 2016, 01:35:04 pm
I found the naming conflict. I still had the engkin unit files in my design folder. I deleted them, but it did not change any of the results.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 05:00:24 pm
Is it possible that the image needs an align=alClient setting?

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 19, 2016, 05:18:16 pm
do you mean you want an image to be always at the length of the page?
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 05:41:20 pm
No. Rather that it would fit the container box built by the callback procedure. The box with grips for resizing.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 19, 2016, 06:55:05 pm
No. Rather that it would fit the container box built by the callback procedure. The box with grips for resizing.
Well, you should be able to achieve this by specifying the size of the image you're trying to insert. There's nothing a callback could do about it.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 07:05:41 pm
Please allow me to repeat the problem...

Images that are already in an RTF file (placed by another editor) show fine and are resizable. The same file saved can be opened and are visible and resizable.

But images inserted from a file using InDelInline have the resize box, and it resizes by code and also dragging grips, but the image does not resize with the box.

Code: Pascal  [Select][+][-]
  1.  
  2. type
  3.  
  4.   { TRichMemoInlinePicture }
  5.  
  6.   TRichMemoInlinePicture = class(TRichMemoInline)
  7.     pic: TPicture;
  8.     constructor Create(Apicture: TPicture);
  9.     procedure Draw(Canvas: TCanvas; const ASize: TSize); override;
  10.   end;  
  11.  
  12. implementation
  13.  
  14. {$R *.lfm}
  15.  
  16. { TRichMemoInlinePicture }
  17.  
  18.   constructor TRichMemoInlinePicture.Create(Apicture: TPicture);
  19.   begin
  20.     inherited Create;
  21.     pic:=APicture;
  22.   end;
  23.  
  24.   procedure TRichMemoInlinePicture.Draw(Canvas: TCanvas; const ASize: TSize);
  25.   begin
  26.     inherited Draw(Canvas, ASize);   //** was omited
  27.     Canvas.Draw(0,0, pic.Graphic);
  28.   end;  
  29.  
  30. procedure TCmdForm.MnuDiskImageClick(Sender: TObject);
  31. var
  32.   pic : TPicture;
  33.   ip  : TRichMemoInlinePicture;
  34.   sz  : TSize;
  35.   x   : longint;
  36.  
  37. begin
  38.   OpenDialog1.Title:= 'Select Image-File';
  39.   OpenDialog1.Filter:= 'JPG (*.jpg)|*.jpg|PNG (*.png)|*.png|TIF (*.tif)|*.tif|BMP (*.bmp)|*.bmp';
  40.   OpenDialog1.FileName:= '';
  41.   if OpenDialog1.Execute then
  42.      begin
  43.      pic:=TPicture.Create;
  44.      try pic.LoadFromFile(OpenDialog1.FileName);  // load image file
  45.          except on e:Exception do
  46.                 begin
  47.                 pic.Free;
  48.                 ShowMessage('Unknown Image Format: '+OpenDialog1.FileName+#13#10+e.Message);
  49.                 Exit;
  50.                 end;
  51.          end;
  52.  
  53.      InsertInlineFlags:= REO_RESIZABLE;
  54.      ip:=TRichMemoInlinePicture.Create(pic);
  55.      //  Types.Size ...due to unit conflict
  56.      sz:=Types.Size(round(pic.Width*72/Screen.PixelsPerInch),
  57.                     round(pic.Height*72/Screen.PixelsPerInch));
  58.      PageMemo.InDelInline(ip, PageMemo.SelStart, 0, sz);
  59.  
  60.      (*
  61.      ole:=GetRichEditOLE(PageMemo);                // **test resizing**
  62.      GetOleObjectSize(ole, PageMemo.SelStart, sz);
  63.      sz.cx:=round(sz.cx * 1.5);  // magnify
  64.      sz.cy:=round(sz.cy * 1.5);
  65.      SetOleObjectSize(ole, PageMemo.SelStart, sz);
  66.      *)
  67.  
  68.      end;
  69.  

I have included in the above the global type for the unit, and its implementation settings. It is code that goes back to when we were first testing an inline insert. Maybe it is conflicting with things.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 19, 2016, 07:12:42 pm
try this code:
Code: [Select]
procedure TRichMemoInlinePicture.Draw(Canvas: TCanvas; const ASize: TSize);
  begin
    inherited Draw(Canvas, ASize);   //** was omited
    Canvas.StretchDraw( bounds(0,0, ASize.cx, ASize.cy), pic.Graphic);
    //Canvas.Draw(0,0, pic.Graphic);
  end;
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 07:16:45 pm
I just got here. I am attaching a PNG of the application when doing an inline insert. I will try your code changes.

Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 07:21:26 pm
Yes, the code change made it work... thank you. I never would have tracked it down. I was looking at everything else.

Rick

Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 19, 2016, 07:25:54 pm
Please note that stretch drawing might not always be the desired result.
So, it's up to you how to draw the inserted inline object, you'll always have the size of the rectangle area where the object needs to be drawn at. (No pixels would be drawn outside of the area)
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 08:30:40 pm
Thanks, but I am not sure why I would not want to resize something. That said, I am going to build a function to resize proportionately, which is what I always need.

Rick.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 09:41:54 pm
Unfortunately it isn't over, an inserted image won't save to disk.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 19, 2016, 09:44:05 pm
Unfortunately it isn't over, an inserted image won't save to disk.
Yeah, I know. You mentioned it several times before :)
Working on it.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 19, 2016, 10:00:52 pm
Sorry for the nag... I had only thought that once it had a grip frame it meant that it was built by a callback, and could be saved.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 19, 2016, 10:26:00 pm
Sorry for the nag... I had only thought that once it had a grip frame it meant that it was built by a callback, and could be saved.
Not that easy. Windows needs to recognize the image ... as an image... in order to be able to save it to rtf.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 20, 2016, 01:47:31 pm
I came across this code that is supposed to work for RxRichEdit. I has function calls that are specific to that driver, so it hard for me to sort out what is actually needed. I also think that it defective code... incomplete call formats that omit parameters. But maybe you might can get something out of it.

Code: Pascal  [Select][+][-]
  1. // Insert a image into a TRxRichEdit?
  2. // Author: Thomas Stutz
  3.  
  4.  
  5. uses
  6.   RichEdit;
  7.  
  8. // Stream Callback function
  9. type
  10.   TEditStreamCallBack = function(dwCookie: Longint; pbBuff: PByte;
  11.     cb: Longint; var pcb: Longint): DWORD;
  12.   stdcall;
  13.  
  14.   TEditStream = record
  15.     dwCookie: Longint;
  16.     dwError: Longint;
  17.     pfnCallback: TEditStreamCallBack;
  18.   end;
  19.  
  20. // RichEdit Type
  21. type
  22.   TMyRichEdit = TRxRichEdit;
  23.  
  24. // EditStreamInCallback callback function
  25. function EditStreamInCallback(dwCookie: Longint; pbBuff: PByte;
  26.   cb: Longint; var pcb: Longint): DWORD; stdcall;
  27.   // by P. Below
  28. var
  29.   theStream: TStream;
  30.   dataAvail: LongInt;
  31. begin
  32.   theStream := TStream(dwCookie);
  33.   with theStream do
  34.   begin
  35.     dataAvail := Size - Position;
  36.     Result := 0;
  37.     if dataAvail <= cb then
  38.     begin
  39.       pcb := read(pbBuff^, dataAvail);
  40.       if pcb <> dataAvail then
  41.         Result := UINT(E_FAIL);
  42.     end
  43.     else
  44.     begin
  45.       pcb := read(pbBuff^, cb);
  46.       if pcb <> cb then
  47.         Result := UINT(E_FAIL);
  48.     end;
  49.   end;
  50. end;
  51.  
  52. // Insert Stream into RichEdit
  53. procedure PutRTFSelection(RichEdit: TMyRichEdit; SourceStream: TStream);
  54.   // by P. Below
  55. var
  56.   EditStream: TEditStream;
  57. begin
  58.   with EditStream do
  59.   begin
  60.     dwCookie := Longint(SourceStream);
  61.     dwError := 0;
  62.     pfnCallback := EditStreamInCallBack;  // ***missing parameter calls***
  63.   end;
  64.   RichEdit.Perform(EM_STREAMIN, SF_RTF or SFF_SELECTION, Longint(@EditStream));
  65. end;
  66.  
  67. // Convert Bitmap to RTF Code
  68. function BitmapToRTF(pict: TBitmap): string;
  69. // by D3k
  70. var
  71.   bi, bb, rtf: string;
  72.   bis, bbs: Cardinal;
  73.   achar: ShortString;
  74.   hexpict: string;
  75.   I: Integer;
  76. begin
  77.   GetDIBSizes(pict.Handle, bis, bbs);    // unique to RxRichEdit
  78.   SetLength(bi, bis);
  79.   SetLength(bb, bbs);
  80.   GetDIB(pict.Handle, pict.Palette, PChar(bi)^, PChar(bb)^);  // unique to RxRichEdit
  81.   rtf := '{\rtf1 {\pict\dibitmap ';
  82.   SetLength(hexpict, (Length(bb) + Length(bi)) * 2);
  83.   I := 2;
  84.   for bis := 1 to Length(bi) do
  85.   begin
  86.     achar := Format('%x', [Integer(bi[bis])]);
  87.     if Length(achar) = 1 then
  88.       achar := '0' + achar;
  89.     hexpict[I - 1] := achar[1];
  90.     hexpict[I] := achar[2];
  91.     Inc(I, 2);
  92.   end;
  93.   for bbs := 1 to Length(bb) do
  94.   begin
  95.     achar := Format('%x', [Integer(bb[bbs])]);
  96.     if Length(achar) = 1 then
  97.       achar := '0' + achar;
  98.     hexpict[I - 1] := achar[1];
  99.     hexpict[I] := achar[2];
  100.     Inc(I, 2);
  101.   end;
  102.   rtf := rtf + hexpict + ' }}';
  103.   Result := rtf;
  104. end;
  105.  
  106.  
  107. // Example to insert image from Image1 into RxRichEdit1
  108. procedure TForm1.Button1Click(Sender: TObject);
  109. var
  110.   SS: TStringStream;
  111.   BMP: TBitmap;
  112. begin
  113.   BMP := TBitmap.Create;
  114.   BMP := Image1.Picture.Bitmap;
  115.   SS  := TStringStream.Create(BitmapToRTF(BMP));
  116.   try
  117.     PutRTFSelection(RxRichEdit1, SS);
  118.   finally
  119.     SS.Free;
  120.   end;
  121. end;
  122.  

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: molly on September 20, 2016, 02:05:50 pm
I came across this code that is supposed to work for RxRichEdit. ... I also think that it defective code... .

Code: Pascal  [Select][+][-]
  1.     pfnCallback := EditStreamInCallBack;  // ***missing parameter calls***
  2.  
should read (for FPC)
Code: Pascal  [Select][+][-]
  1.     pfnCallback := @EditStreamInCallBack;  // ***missing parameter calls***
  2.  
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 20, 2016, 02:28:23 pm
I came across this code that is supposed to work for RxRichEdit.
Well... instead of doing direct RTF injections, you could try to use these functions in richmemoutils.pas
  InsertImageFromFile
or
  InsertImageFromFileNoResize

Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 20, 2016, 07:26:18 pm
Looking at the code that I recently posted, I noticed that the function BitmapToRTF is building place holder for the image within the RTF file code.

InDelInline is not building that code. What it inserts is {\pict\wmetafile0}, which means there was noting to insert. Normally it would be something like the following...

{\pict\wmetafile8\picwgoal5550\pichgoal4560
0100090000033afc0100000024fc01000000050000000b0200000000050000000c025a1a222324
fc0100430f2000cc000000ff005401000000005a1a2223000000002800000054010000ff000000

It starts with wmetafile8 instead of wmetafile0, and the numbers and alphabet are what the function BitmapToRTF builds.

When I copied and pasted the full body of the wmetafile8 to replace the wmetafile0 series, it loaded, was resizable, and could be saved.

It seems that all you need is to improvise a similar function to BitmapToRTF.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 20, 2016, 07:50:44 pm
It seems that all you need is to improvise a similar function to BitmapToRTF.
That's another hack, that I'd like to avoid. However, you're free to use it.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 20, 2016, 08:24:57 pm
OK. Everything that I have researched states that RichEdit was never built to do images. To have them you have to "hack" or do a "workaound." If it is done properly it works. Not because of RichEdit, but on account of modern RTF methods.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 20, 2016, 08:42:21 pm
OK. Everything that I have researched states that RichEdit was never built to do images.
Not exactly so.
Windows RichEdit is designed to embed OLE objects.
OLE used to be ...and infact is ... Windows backbone technology, and microsoft used it heavily back in the days (switching to .NET in 2000s).
Thus, RichEdit was designed with OLE objects in mind, even for simple stuff like links of images.

In the most recent versions of RichEdit (i think starting Windows 10), there's a special non OLE API to embed an image. (Which is not applicable for earlier versions of Windows), so the only cross-Windows solution is to use OLE.
Which is done by Inline objects... however, since the documentation on RichEdit internals is quite poor, lots of the development is a guess work.

To have them you have to "hack" or do a "workaound."
That's a common practice that has been in use since Delphi RichEdit days.
Yet again, I'm not going to use it with the official RichMemo inline objects, but anyone else are free to use it, since it works perfectly fine for all the small needs.
Please note that you don't have to use inline objects at all at this point.
Title: Re: Inserting, Saving and Loading Images
Post by: tk on September 20, 2016, 09:09:08 pm
OK. Everything that I have researched states that RichEdit was never built to do images.

That I would not say, but you might try KMemo http://tkweb.eu/en/delphicomp/kmemo.html (http://tkweb.eu/en/delphicomp/kmemo.html), it should be able to handle them fine already (I mean the newest repo download).
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 20, 2016, 09:53:35 pm
Very well, and it is only an argument, but there is a difference between "capacity" and "functionality". Windows built RichEdit with capacity, but not for functionality. The actual production of functionality was being reserved for them. That is how you make money.

To be "cross-platform" with RichMemo you have to develop the functionality. Moreover, it needs to not be dependent on the limitations of RichEdit. It needs to be RTF driven.

Inserting the RTF code for an image is simply complying with RTF rules.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 21, 2016, 06:22:44 pm
I was able to implement the BitmapToRTF code. Images insert and resize. The RTF file saves and opens with the image intact and editable. Another editor was able to load and edit the file as well.

I wouldn't have been able to do it without your refusal to do so. It made me track everything down. Thanks.

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 21, 2016, 06:31:21 pm
In reference to another snippet of code that you introduced on this thread. The following implementation to resize an image by code performs the work. But after do so, there isn't any problem until I close program. Then it raises an exception...

external: SIGSEGV at address 4B4112E4

It does it every time I close the program after having tested the code. It never does if I haven't tested the code.

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.MnuResizeClick(Sender: TObject);
  2. var
  3.   ole: IRichEditOle;
  4.   sz: TSize;
  5. begin
  6.   ole:= GetRichEditOLE(PageMemo);
  7.   GetOleObjectSize(ole, PageMemo.SelStart, sz);
  8.   sz.cx:=round(sz.cx * 1.5);  // magnify
  9.   sz.cy:=round(sz.cy * 1.5);
  10.   SetOleObjectSize(ole, PageMemo.SelStart, sz);
  11. end;
  12.  

It happens as a result of calling SetOleObjectSize(ole, PageMemo.SelStart, sz);

Rick
Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 21, 2016, 06:41:59 pm
Are images inline images or rtf generated images?

If inline, try to change the code to rtf generated and see if it solves the problem
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 21, 2016, 07:13:43 pm
It has always been doing this. I didn't want to bring up until the other issues were settled. I think they had been inline, but I am not using inline. I don't know about RTF files that have images. I'll have to test.

Attached is the assembler message. I scrolled it down a little so you could see what lead into the break. The green arrow is pointing at the break.



Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 21, 2016, 07:27:41 pm
I loaded a file where another editor had inserted the image. It does the same crash.

It did it with Inline before, and with my separate insert now. It is across the board.

Here is a better image of the crash info. The crash point is near the bottom.

Title: Re: Inserting, Saving and Loading Images
Post by: skalogryz on September 21, 2016, 08:02:08 pm
r5196 should fix the issue. However, the interface of SetOleObjectSize / GetOleObjectSize functions changed. Now the first parameter must be TRichMemo object rather than an IRichMemoOle object.
Title: Re: Inserting, Saving and Loading Images
Post by: rick2691 on September 21, 2016, 08:40:54 pm
That fixed it. Thank you much.

Code: Pascal  [Select][+][-]
  1. {
  2.  r5196 should fix the issue. However, the interface of SetOleObjectSize / GetOleObjectSize
  3.  functions changed. Now the first parameter must be TRichMemo object rather than an
  4.  IRichMemoOle object.
  5. }
  6. procedure TCmdForm.MnuResizeClick(Sender: TObject);
  7. var
  8.   //ole: IRichEditOle;
  9.   sz: TSize;
  10. begin
  11.   //ole:= GetRichEditOLE(PageMemo);
  12.   GetOleObjectSize(PageMemo, PageMemo.SelStart, sz);
  13.   sz.cx:=round(sz.cx * 1.25);  // magnify
  14.   sz.cy:=round(sz.cy * 1.25);
  15.   SetOleObjectSize(Pagememo, PageMemo.SelStart, sz);
  16. end;
  17.  

The above is the implementation. I can go on with refining the code.

Rick
TinyPortal © 2005-2018