Page 1 of 2

Formatted Text via RichEdit and TOM - by just me

Posted: 12 Mar 2018, 10:28
by burque505
Foreword
I found this tutorial by just me in the German forum, and thought it would be useful for those who come here.
With just me's kind permission, I offer this translation.
Regards,
burque505
MyRTF.PNG
MyRTF.PNG (29.36 KiB) Viewed 3771 times
As a developer, every now and then you might need to display formatted text in a GUI. AHK does not provide suitable control for this. AHK 1. 1 offers two possibilities for embedding further controls registered in the system:
• ActiveX
• Custom
As a rule, ActiveX controls are COM objects that can only be integrated with a GUI in a way that permits use similar to that of built-in controls.

Custom controls usually behave similar to built-in controls. You can assign variables and subroutines, for example. In principle, all built-in controls can also be used as custom controls. That offers few practical benefits.

For 'simple' output of formatted text, there are two alternatives:
1. HTML -> MSIE-Browser Control (ActiveX)
2. RTF -> RichEdit Control (Custom)
We'll limit ourselves here to the second variant (RTF), and the implementation of custom controls.

RichEdit Control

Like the Edit Control, the RichEdit Control is one of the controls provided by Microsoft via the system, but unlike the 'normal' Edit Control, it has not yet been integrated into AHK..

Some time ago I wrote a RichEdit Class, which allows integrating RichEdit Controls into your own GUIs. The actual reason for this was that I needed a way to print formatted text out of AHK without being dependent on any other application. Nonetheless, I equipped the class with a lot of features and methods only used for editing. This makes the whole package a bit on the bulky side, and it requires a bit of training and practice to use.

While I was developing the class, I kept running across documentation for the Text Object Model (TOM). At first I couldn't get very far with it, and even later I remained a bit leery of getting used to this … 'stuff'. Recently I found this post by teadrinker, motivating me to take a closer look at it. I realized that TOM can make the handling of RichEdit Controls for simple tasks, such as the display of RTF files, much easier. Thus, the following tutorial!

Let's get started!

Step 1:

The RichEdit control is provided and registered by the system library msftedit. dll. From WinXP on, the control class registered is RICHEDIT50W. To be able to use the RichEdit control, the script has to explicitly load this dll, i.e.:

Code: Select all

RE_Dll := DllCall("LoadLibrary", "Str", "Msftedit.dll", "Ptr")
Step 2:

Once you've done that, you can add your RichEdit control to the GUI as a custom control:

Code: Select all

Gui, Add, Custom, ClassRICHEDIT50W vRE hwndHRE
Because AHK doesn't recognize the controls added as being Custom, only the essential styles are set automatically, such as: Eg WS_CHILD, WS_TABSTOP, WS_VISIBLE. Scrollbars can be set with the usual options HScroll / VScroll, because they are not control specific (WS_HSCROLL, WS_VSCROLL). However, even with a height specification such as h400 or r20, AHK can't tell it's a multi-line RichEdit Control. You have to set the appropriate style ES_MULTILINE (0x04) yourself. This applies to all other possible RichEdit styles too, for example, ES_READONLY (0x0800). For a multi-line, read-only RichEdit Control with a width and height of 400 pixels each and a vertical scrollbar, you need:

Code: Select all

Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
Step 3:

Now we have an empty RichEdit Control. To get this to display an existing RTF file, we need the associated TOM object, more specifically the TextDocument object. This can be elicited by the control via the message EM_GETOLEINTERFACE. How to do that is roughly described here. For AHK, it looks like this (without going into the ComObj instructions):
• Here, we send an EM_GETOLEINTERFACE message to the RichEdit control. This returns a pointer to the IRichEditOle interface.

Code: Select all

DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") ; EM_GETOLEINTERFACE
• Now, using ComObjQuery (), we get a pointer to the ITextDocument interface,

Code: Select all

ITextDocument := ComObjQuery(IRichEditOle, IID_ITextDocument)
• which we in turn transform, via ComObject() into a TextDocument object.

Code: Select all

DocObject := ComObject(9, ITextDocument, 1)
Here's an example of a complete function:

Code: Select all

GetTomDoc(HRE) {
   ; TextDocument Objekt für ein RichEdit Control abrufen
   Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
   DocObj := 0
   If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
      DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
      ObjRelease(IRichEditOle)
   }
   Return DocObj
}
Step 4:

Now we've got a RichEdit control and its TextDocument object. This object has the Open method, used to read files into the RichEdit control. The parameter Flags determines what content the file has. For files with RTF text, the flag tomRTF (0x01) is displayed. You can also specify that the file content being read in should a) completely replace the content of the RichEdit control or b) be inserted (tomPasteFile (0x1000).) That's all that's needed to display the file:

Code: Select all

DocObj.Open("MeineDatei.rtf", 0x01, 0)
Putting it all together

Back to work. Start Microsoft WordPad and create a RichText file. Save it with the name MyRTF.rtf in the same folder as the following script:

Code: Select all

#NoEnv
SetWorkingDir, %A_ScriptDir%
RE_Dll := DllCall("LoadLibrary", "Str", "Msftedit.dll", "Ptr")

Gui, +hwndhGui
Gui, Margin, 10, 10
Gui, Font, s10, Arial
Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
Gui, Show, , RichEdit

TomDoc := GetTomDoc(hRE)
TomFile := "MyRTF.rtf"
TomDoc.Open(TomFile, 0x01, 0)
Return

GuiClose:
ExitApp

GetTomDoc(HRE) {
   ; Get the document object of the specified RichEdit control
   Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
   DocObj := 0
   If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
      DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
      ObjRelease(IRichEditOle)
   }
   Return DocObj
}
Additional considerations for Win 8+

With Win 8, Microsoft distributed a significantly updated RichEdit control. For one thing, the display of 'embedded objects' like images is greatly simplified. For RTF files created and displayed under Win 8, no further action is required. However, per my tests, the RichEdit control must not be ReadOnly when the file is read. Why? Because you can't just isolate and remove this style, you have to use the EM_SETOPTIONS message after the control has been created and populated:

Code: Select all

DllCall("SendMessage", "Ptr", HRE, "UInt", 0x044D, "Ptr", 0x02, "Ptr", 0x0800) ; EM_SETOPTIONS: ECOOP_OR, ECO_READONLY

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 29 Nov 2018, 17:32
by iPhilip
Hi burque505,

Thank you for posting this tutorial. I found it easy to follow and the example was easy to test. :)
My question is: where did you get the value for the IID_ITextDocument variable?

Thank you again,

- iPhilip

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 29 Nov 2018, 18:53
by burque505
Hi iPhilip,
All I did was translate this from German to English - I'm not sure where @just me got that value. I'll dig around, though.
EDIT: There is some good info here.
And https://stackoverflow.com/questions/179 ... interfaces] here.
Even cooler: If you have an old version of Delphi, you can get these IDs easily. See [url=https://stackoverflow.com/questions/179 ... interfaces] here. [url]
The tool is tlibimp.exe, and is generally in a Delphi bin directory.
I generated files by running

Code: Select all

tlibimp -D. C:\Windows\System32\msftedit.dll
. Looking inside tom_TLB.pas, I got all this near the top:

Code: Select all

  tomMajorVersion = 1;
  tomMinorVersion = 0;

  LIBID_tom: TGUID = '{8CC497C9-A1DF-11CE-8098-00AA0047BE5D}';

  IID_ITextDocument: TGUID = '{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}';
  IID_ITextRange: TGUID = '{8CC497C2-A1DF-11CE-8098-00AA0047BE5D}';
  IID_ITextSelection: TGUID = '{8CC497C1-A1DF-11CE-8098-00AA0047BE5D}';
  IID_ITextFont: TGUID = '{8CC497C3-A1DF-11CE-8098-00AA0047BE5D}';
  IID_ITextPara: TGUID = '{8CC497C4-A1DF-11CE-8098-00AA0047BE5D}';
  IID_ITextStoryRanges: TGUID = '{8CC497C5-A1DF-11CE-8098-00AA0047BE5D}';
  IID_ITextDocument2: TGUID = '{01C25500-4268-11D1-883A-3C8B00C10000}';
  IID_ITextMsgFilter: TGUID = '{A3787420-4267-11D1-883A-3C8B00C10000}';
Now I feel like a kid with a new toy!
Regards,
burque505

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 29 Nov 2018, 19:10
by iPhilip
burque505 wrote:
29 Nov 2018, 18:53

... All I did was translate this from German to English ...

Thank you for your translation skills. :)

- iPhilip

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 29 Nov 2018, 19:15
by burque505
Hi iPhilip, check out the edited post above.
Regards,
burque505

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 29 Nov 2018, 19:24
by jeeswg
Installing Visual Studio Express 2013 for Windows Desktop, created this folder:
C:\Program Files (x86)\Windows Kits\8.1
I searched the .h files for IID_ITextDocument and found:
C:\Program Files (x86)\Windows Kits\8.1\Include\um\TOM.h
C:\Program Files (x86)\Windows Kits\8.1\Include\winrt\windows.ui.text.h
Only TOM.h contained the value, 8CC497C0-A1DF-11ce-8098-00AA0047BE5D. Cheers.

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 29 Nov 2018, 19:31
by iPhilip
Hi burque505,

Great! Thank you. I look forward to playing with that as well. :)

- iPhilip

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 03 Dec 2018, 19:07
by iPhilip
jeeswg wrote:
29 Nov 2018, 19:24
Installing Visual Studio Express 2013 for Windows Desktop, created this folder:
C:\Program Files (x86)\Windows Kits\8.1
I searched the .h files for IID_ITextDocument and found:
C:\Program Files (x86)\Windows Kits\8.1\Include\um\TOM.h
C:\Program Files (x86)\Windows Kits\8.1\Include\winrt\windows.ui.text.h
Only TOM.h contained the value, 8CC497C0-A1DF-11ce-8098-00AA0047BE5D. Cheers.
Thank you jeeswg,

After installing Visual Studio Community 2017 (Desktop development with C++), I found the above information in C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\TOM.h

Cheers!

- iPhilip

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 24 Dec 2018, 18:43
by iPhilip
sanaeerumey wrote:
24 Dec 2018, 07:52
You can't fixed those error with zero information, remember that :offtopic:
Hi sanaeerumey,

I am confused by your post. What errors are you referring to? Why "zero information"? Why off-topic? Could you elaborate?

- iPhilip

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 24 Dec 2018, 19:06
by gregster
@iPhilip: I think you can ignore this. All three posts of sana seem quite strange and not related to the individual threads. In one, that I have reported now and should get deleted, this user also added a spam link to a quote (not quoting him-/herself, but some other user). Nothing productive so far from this user...

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 24 Dec 2018, 19:28
by iPhilip
@gregster: Thank you. :)

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 25 Feb 2019, 17:23
by robodesign
Hello, burque505!

Very nice tutorial.

How can I set the text color of the entire text in the rich edit?

Thank you.

Best regards, Marius.

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 25 Feb 2019, 19:01
by burque505
Hi Marius, nice to hear from you. Here's a new version (still a work in progress), and a video that I hope will help with your question.
Regards,
burque505
RTF20190225.gif
RTF20190225.gif (271.73 KiB) Viewed 2004 times

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 08:10
by robodesign
Thank you very much .

I have succeded in changing the text colors.

Best regards, Marius.

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 09:06
by burque505
Glad to hear it, Marius, it didn't occur to me till later that you might be using the class, and not the sample - sorry about that!

(I didn't want to say "Just hit Ctrl-A and change the font color from the dialog", which is why I posted the GIF.)

For the benefit of anyone else who might have the same question or issue, the included file Class_RichEditDlgs.ahk is the place to look for things like changing font, color, opening and saving files, and finding and replacing text.

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 09:42
by robodesign
Thank you.

For the past month or more, I've been recoding KeyPress OSD.

I managed to get it much more optimized and with zero width carets and so on, text selections. Even, full support for all hindi / abugida scripts. This version is already available on the official site.

Now, I am rethinking / recoding the alternate typing mode, for which I decided to use a rich edit..... I don't need RTF stuff, just to allow the user to type in any language. I'm halfway done with it already, but still a lot left....

Now I'm struggling to force right alignment. Center works, but not right. I don't know why.

Best regards, Marius.

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 12:08
by burque505
Marius, this is just a thought and maybe way off base (and is definitely off-topic), but if you are looking to use RTL languages, a DotNet WinForm might be easier. Here is a very crude example - I just lost a ton of files, and I mashed this up from what I could find so far. Please let me know if it doesn't work out of the box. The EventHelper dll was compiled on 64-bit Win7.
Regards,
burque505
WinFormRTF-rtl.gif
WinFormRTF-rtl.gif (40.55 KiB) Viewed 1941 times

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 12:26
by robodesign
Thank you very much .

But... I get... Call to non-existing function... for CLR_LoadLibrary("System.Drawing")

I have two questions:
1. How to set font quality to the rich edit field?
2. How to force the field use multiple fonts? ... now, when characters are missing, it falls back to Arial for the entire line...


This RichEdit seems to be what I need and it works with IMEs, and RTL scripts. I managed to overcome the issues I was having with the text alignment.

I also use Fnt_Library. With this one, the font looks good , with desired quality and font substitution works as expected. However, when I use Fnt_SetFont on the richedit, it gets to the default color: black.

Best regards, Marius. ...

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 12:42
by burque505
Hi Marius, the line at top of the script that reads

Code: Select all

drw := Clr_LoadLibrary("System.Drawing")
can be commented out or deleted. It doesn't throw an error on my system, so I didn't notice it. Are you running Windows 10?
By the way, is the Font 3.0 lib the one you mean?
Regards,
burque505

Re: Formatted Text via RichEdit and TOM - by just me

Posted: 26 Feb 2019, 12:50
by robodesign
burque505 wrote:
26 Feb 2019, 12:42
Hi Marius, the line at top of the script that reads

Code: Select all

drw := Clr_LoadLibrary("System.Drawing")
can be commented out or deleted. It doesn't throw an error on my system, so I didn't notice it. Are you running Windows 10?
By the way, is the Font 3.0 lib the one you mean?
Regards,
burque505
I still get the call to non-existent function, for thenext Clr_LoadLibrary...

Yes, i use Windows 10 . And, yes... Font Library 3.0. It is very good .

I need to change RichEdit style, i think: draftmode, custom look, I do not know ... for my issues, I tried:

REstyle := MyRE.GetStyles()
REstyle.Customlook := 1
REstyle.lowercase := 1
MyRE.SetStyles(REstyle)

But, nothing changes... am I doing something wrong?

Best regards, Marius.