RichEdit 8 Zoom Support

In this day and age with touch pinch and expand zooming of screens, zoom is everywhere! So you might wonder how to zoom the contents of a RichEdit control. There are two ways: 1) maintaining the display width and rewrapping the text as necessary to fill the client rectangle, and 2) zooming everything including the display width. The first way can be called rewrapped zoom and is enabled by sending an EM_SETZOOM message or by providing an appropriate display height in the ITextHost::TxGetExtent callback. Rewrapped zoom can be accomplished by RichEdit itself, requiring a minimum of host effort. It’s useful notably in that no panning is needed to read the text. For example, the Amazon Kindle offers rewrapped zoom to allow readers to view text at a larger size without panning.

The second way can be called unrewrapped zoom and has to be implemented in the host since the client RECT zooms along with the contents. This is the typical zoom method used on cell phones, tablets, and in many Windows applications such as OneNote and Word. It requires panning for larger zoom sizes when the text lines no longer fit within the window width. It uses a six-factor 2D transform and the host has to “unzoom” mouse coordinates so that RichEdit hit testing works correctly. For the traditional Windows GDI environment, the hosts sets the zoom factor by calling SetWorldTransform(). For the relatively new D2D environment, the host sets the zoom factor by calling ID2D1RenderTarget::SetTransform(). Both calls pass the same 2×3 transform matrix, although the matrices have different names. The host can also implement rewrapped zoom by unzooming the display width appropriately, although it’s easier to let RichEdit handle rewrapped zoom.

Up through RichEdit 8, both kinds of zoom work in GDI mode. In RichEdit 8, unrewrapped zoom works in D2D mode; earlier versions of RichEdit don’t have a D2D mode. RichEdit 8 doesn’t rerasterize images when the zoom factor changes, so images can become blurry even when they have high native resolution.

EM_SETZOOM provides the zoom numerator in wparam and the zoom denominator in lparam. Alternatively the numerator is given by the height returned by the ITextHost::TxGetClientRect() and the denominator by the vertical size returned by ITextHost::TxGetExtent. The latter is in HIMETRIC units (0.01 mm), and is converted to pixels for the zoom denominator. The zoom factor is incorporated into the horizontal and vertical display device resolution factors when GDI is being used to display text. These factors are set to EMUs per inch (914400/inch) for the D2D/DWrite mode, and hence RichEdit 8 doesn’t offer rewrapped zoom in D2D mode. Hopefully someday!


Comments (4)

  1. Neal says:

    When trying to accomplish unrewrapped zoom is it possible to get the wrapped text to stay static and not change lines. WordPad works fine so I'm assuming that it's possible. However when implementing either ITextServices or using say the .Net WinForms RichTextBox (using RICHEDIT50W & MSFTEDIT.DLL)  the text wrapping changes with some zoom factors but not others. Also some zoom factors appear to incorrectly process tabs. If I have a line beginning with 4 tabs, only three off them get expanded, at other zoom factors all the tabs get expanded. Very odd, I don't think its a bug as WordPad works fine. Any ideas on to how to achieve consistent static wrapping when zooming, would be much appreciated.

    Many Thanks

  2. MurrayS3 says:

    Have you tried using a transform as mentioned in the post? That's the way OneNote does it. Then RichEdit doesn't even know about the zoom factor except when rasterizing images. WordPad's wrap-to-ruler uses RichEdit's GDI mode with wrap-to-target (see EM_SETTARGETDEVICE). This has a simpler zoom method that seems to work correctly for everything. Maybe we can implement something similar for the D2D/DWrite mode, but we haven't figured it out yet.

  3. Neal says:

    Thanks. I've got the beginning of it working now using a Transform.I did previously try to use a Transform but I couldn't get it working (probably some additional issues which are now resolved). So I assumed Transforms were maybe only used for static representations.  I'm now "unzooming" the mouse messages passed through to ITextServices, and then "rezooming" them when they come back through via the ITextHost. I've only got the TxSetCaretPos working so far with the mouse, so hopefully the other callbacks should just be a formality.

    Again, Many Thanks


  4. Stefan says:

    Hi, I've been trying to use SetWorldTransform on our ITextHost Implementation (GDI Win 7), but we cannot get it to work correctly. To use SetWorldTransform you must SetGraphicsMode to GM_ADVANCED. Unfortunately setting this causes all the Ole objects to draw very small.

    We then tried using the SetWindowExt and SetViewportExt API's. But setting these causes table borders to flicker and to not draw at all when zoomed below 30%. Is there a way to get SetWorldTransform to work with ole objects or for SetWindowExt to work nicely with lines. (Note the lines draw fine when using SetWorldTransform)

    Thank you.