-
Jan 30th, 2023, 02:25 PM
#1
RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
This is something that has been bothering me for a long time, but it's getting worse now that there are so many different/varied DPI settings out there. I'm hoping one of you have found a solution, or even just some ideas for things I might try to resolve the issue.
I have a RichTextBox that is a certain width (let's say 5000 twips). My application has a manifest marking it as DPI aware:
Code:
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
When I run the program at 100%, 150%, and 225% DPI, the width of the RTB is always reported to be the same 5000 twips, so everything looks good there.
I'm setting the line length to the width of the control less a fudge factor using the EM_SETTARGETDEVICE message:
Code:
SendMessage Me.RichTextBox1.hwnd, EM_SETTARGETDEVICE, Me.Hdc, Me.RichTextBox1.Width - 150
However, when I populate the RTB with identical text across the separate runs at different DPI scaling amounts, the text wraps at different positions:
- At 100% the text actually extends of the right-edge of the control a bit, and then 3 letter "i"s wrap over to a new line.
- At 150% the text all fits within the control, and no "i"s wrap at the end.
- At 225% there is a large amount of white space at the right edge of the control, and 2 letter "i"s wrap over to a new line.
I've tried creating/using different hDC's but I don't know if there's any way to create a "baseline" hDC that would be the same across all devices/DPIs (or something along those lines if that is gibberish)?
WordPad doesn't suffer from this problem - the same text at different DPIs always takes up the same amount of line real-estate and wraps at the same points. AFAIK WordPad uses a RichEdit , so I'm hoping there's some way to get the RTB to perform the same. Anybody have any ideas for things I can try (I'm using Kr00l's RTB in case it makes any difference.)
Thanks in advance for any help.
-
Jan 30th, 2023, 02:33 PM
#2
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
The RichTextBox does not use the same RichEdit control as Wordpad in modern versions of Windows.
InkEdit uses a newer one, so you might try testing with that.
I suspect that the fundamental problem is that font scaling isn't simple and linear, but instead sizing changes in jumps.
-
Jan 30th, 2023, 02:48 PM
#3
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Thanks dilettante...I'm using kr00l's RTB which tries to use Msftedit.dll/RichEdit50W but will fallback to Riched20.dll/RichEdit20W if the first option fails. Tests here show that Msftedit.dll/RichEdit50W is succeeding, but may be WordPad uses something newer.
I've also dug up an EM_SETTYPOGRAPHYOPTIONS message and 2 parameters call TO_ADVANCEDTYPOGRAPHY and TO_ADVANCEDLAYOUT that sounds promising, but I can't find much documentation on them (especially TO_ADVANCEDLAYOUT). I'll play around with them and see if they are of any use.
-
Jan 30th, 2023, 03:17 PM
#4
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by jpbro
WordPad doesn't suffer from this problem - the same text at different DPIs always takes up the same amount of line real-estate and wraps at the same points.
How do you test that?
And you need to close Wordpad, change the DPI, launch Wordpad again to make the right measurement (not just changing the DPI having Wordpad open).
(The other issue is normal and expected, BTW, fonts scale taking steps).
-
Jan 30th, 2023, 03:29 PM
#5
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by Eduardo-
How do you test that?
And you need to close Wordpad, change the DPI, launch Wordpad again to make the right measurement (not just changing the DPI having Wordpad open).
(The other issue is normal and expected, BTW, fonts scale taking steps).
Yeah, that's how I tested it - saved an RTF, closed WordPad, changed the DPI scaling, re-opened the RTF. Word-wrapping was consistent across all tested DPI scaling values.
-
Jan 30th, 2023, 03:39 PM
#6
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by jpbro
Yeah, that's how I tested it - saved an RTF, closed WordPad, changed the DPI scaling, re-opened the RTF. Word-wrapping was consistent across all tested DPI scaling values.
Not here (Windows 11 current version).
I tested just with its own rule and measures changed with DPI.
100% <> 125%, 125% <> 150%
-
Jan 30th, 2023, 03:51 PM
#7
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Ahh, nevermind it must have been a fluke of the document I tested that it was wrapping the same at 100%, 150%, and 225%...just tried a more rigorous test with a line full of the letter "i" and I'm getting different wrap points now...Looks like I'm probably out of luck.
-
Jan 30th, 2023, 04:09 PM
#8
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by jpbro
Ahh, nevermind it must have been a fluke of the document I tested that it was wrapping the same at 100%, 150%, and 225%...just tried a more rigorous test with a line full of the letter "i" and I'm getting different wrap points now...Looks like I'm probably out of luck.
It is normal for DPI awareness. BTW this is one of the things you need to be "aware" of.
The issue is that fonts have hinting, kerning and [whatever] to adapt to the pixels (rasterization).
And characters can't be rasterized and look good multiplying the sizes arbitrarily. Then they are scaled "in steps".
If you want the RTB (or whatever control) to wrap at certain word, then also scale the RTB measuring the text. But... that may work well for one particular text but not for other anyway.
-
Jan 31st, 2023, 11:25 PM
#9
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by Eduardo-
It is normal for DPI awareness. BTW this is one of the things you need to be "aware" of.
The issue is that fonts have hinting, kerning and [whatever] to adapt to the pixels (rasterization).
And characters can't be rasterized and look good multiplying the sizes arbitrarily. Then they are scaled "in steps".
If you want the RTB (or whatever control) to wrap at certain word, then also scale the RTB measuring the text. But... that may work well for one particular text but not for other anyway.
I agree that I have to scale the RTB. I do this in fact - my RTB sits on top of a rendered PDF and the size of the RTB and it's font size perfectly match the size and contents of the underlying page rendering - all except for where the word-wrap points. As such, I can tell that it is scaled "properly" at any DPI.
I think that the problem is that RTB font rendering is kind of dumb compared to say, Word. Word definitely wraps at the same point for any DPI. <Baseless Speculation>I'm guessing it does this by measuring the text extents at a baseline canvas DPI unrelated to the screen DPI. AFAIK PDFs do this too - typically using a 72DPI canvas so that text is always placed the same regardless of the screen DPI when the text is drawn onto the page</BaselessSpeculation>. This does mean that Word's rendering can look a bit wonky - if you zoom into a Word document, you'll see the spacing & anti-aliasing is imprecise, but accurate:
Whereas the RTB renders very precisely, but inaccurately:
So the RTB appears to be working against display pixel measurements, while Word appears to be performing subpixel measurements against a screen-independent canvas. Word's rendering is a bit distorted when rendered on a display, but here we can clearly see the difference between precision and accuracy.
Anyway, I'm thinking that there isn't a solution for this other than rolling my own RTB (I only need a subset of RTF features, so this might be do-able despite being a massive pain in the arse).w
Last edited by jpbro; Jan 31st, 2023 at 11:46 PM.
-
Feb 1st, 2023, 01:03 AM
#10
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
You could try experimenting with DirectWrite.
I cannot help because I know nothing.
-
Feb 1st, 2023, 02:59 AM
#11
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by Eduardo-
I cannot help because I know nothing.
It appears you're not the only one:
-
Feb 1st, 2023, 07:02 AM
#12
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
"RICHEDIT60W" is the newest one, RichEdit 8.0.
But I'm on Windows 10 1809 and Wordpad is still using 50W here.
-
Feb 1st, 2023, 11:46 PM
#13
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by Eduardo-
I cannot help because I know nothing.
?? I think you know a lot >0
I'm trying to learn from you and everyone else on this forum. I also try to teach what I know when I can. IMO everyone still posting here is a smart and interesting person with a unique technical perspective, and I really enjoy exploring interesting problems with whatever remains of the VB6 community.
Anyways, thanks for the link to DirectWrite, I'll give it a look. TBH I'm *almost* all in on RC6, so I'll probably try my hand at a Cairo RTB.
-
Feb 1st, 2023, 11:49 PM
#14
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by fafalone
"RICHEDIT60W" is the newest one, RichEdit 8.0.
But I'm on Windows 10 1809 and Wordpad is still using 50W here.
Thanks @fafalone,,,I'll see if I can get a RICHEDIT60W window working, and see if there's any improvement.
-
Feb 2nd, 2023, 12:13 AM
#15
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by jpbro
?? I think you know a lot >0
I'm trying to learn from you and everyone else on this forum. I also try to teach what I know when I can. IMO everyone still posting here is a smart and interesting person with a unique technical perspective, and I really enjoy exploring interesting problems with whatever remains of the VB6 community.
Anyways, thanks for the link to DirectWrite, I'll give it a look. TBH I'm *almost* all in on RC6, so I'll probably try my hand at a Cairo RTB.
I repeat: I know nothing (or I don't know anything).
Context: about DirectWrite and how to use its API.
-
Feb 3rd, 2023, 02:11 AM
#16
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Gotcha now, I misunderstood what you meant by "I cannot help because I know nothing" as a naked sentence. I was betwixt the real world and the online world, and I interpreted that as a passive-aggressive statement that you thought that I considered your input without value. My mistake, so please accept my apologies.
-
Feb 3rd, 2023, 03:14 AM
#17
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Ambiguous Input + Interpersonal History + Cultural/Language Differences = Misunderstanding, hence my apology.
-
Feb 3rd, 2023, 06:41 PM
#18
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by Eduardo-
You could try experimenting with DirectWrite.
I cannot help because I know nothing.
look for RichEditD2D
https://devblogs.microsoft.com/math-...ndow-controls/
-
Feb 3rd, 2023, 07:03 PM
#19
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Thank you Eduardo and Episcopal, I will look into DirectWrite & RichEditD2D.
-
Feb 3rd, 2023, 10:07 PM
#20
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by jpbro
... will look into DirectWrite & RichEditD2D.
From Episcopals link:
...there’s a new message EM_SWITCHTOD2D (WM_USER + 389) with wparam = lparam = 0
that switches the current window control to D2D...
Seems that's all what it takes (shortly after creating the RTB-hWnd "the old way")
Olaf
Last edited by Schmidt; Feb 3rd, 2023 at 10:12 PM.
-
Feb 3rd, 2023, 11:58 PM
#21
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by Schmidt
From Episcopals link:
...there’s a new message EM_SWITCHTOD2D (WM_USER + 389) with wparam = lparam = 0
that switches the current window control to D2D...
Seems that's all what it takes (shortly after creating the RTB-hWnd "the old way")
Olaf
Thanks Olaf, I did try that as a quick first test against my full app, but it didn't render any differently. My editor is a bit of a wild beast after years of undireected development, so I'll try again this weekend with a minimal test program to play around with. If that works, then I might be able to justify a big refactoring.
I've been ruminating about writing an RC6/Cairo RTB-subset widget for a years now, but other things keep getting in the way. While I have your ear though - would Cairo's font rendering/word-wrapping be consistent across devices with different screen DPIs? I would assume the answer is yes, but I was wrong in my assumptions about the WinRTB behaviour...and you know what they say about assumptions.
-
Feb 4th, 2023, 02:12 AM
#22
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
I was looking at the D2D stuff... the interfaces it uses aren't normal IDL like the rest of the TOM interfaces... how would you make this declarable for VB6-compatible typelibs:
Code:
class ITextServices : public IUnknown
{
public:
//@cmember Generic Send Message interface
virtual HRESULT TxSendMessage(
UINT msg,
WPARAM wparam,
LPARAM lparam,
LRESULT *plresult) = 0;
//@cmember Rendering
virtual HRESULT TxDraw(
DWORD dwDrawAspect,
LONG lindex,
void * pvAspect,
DVTARGETDEVICE * ptd,
HDC hdcDraw,
HDC hicTargetDev,
LPCRECTL lprcBounds,
LPCRECTL lprcWBounds,
LPRECT lprcUpdate,
BOOL (CALLBACK * pfnContinue) (DWORD),
DWORD dwContinue,
LONG lViewId) = 0;
//@cmember Horizontal scrollbar support
virtual HRESULT TxGetHScroll(
LONG *plMin,
LONG *plMax,
LONG *plPos,
LONG *plPage,
BOOL * pfEnabled ) = 0;
//@cmember Horizontal scrollbar support
virtual HRESULT TxGetVScroll(
LONG *plMin,
LONG *plMax,
LONG *plPos,
LONG *plPage,
BOOL * pfEnabled ) = 0;
//@cmember Setcursor
virtual HRESULT OnTxSetCursor(
DWORD dwDrawAspect,
LONG lindex,
void * pvAspect,
DVTARGETDEVICE * ptd,
HDC hdcDraw,
HDC hicTargetDev,
LPCRECT lprcClient,
INT x,
INT y) = 0;
//@cmember Hit-test
virtual HRESULT TxQueryHitPoint(
DWORD dwDrawAspect,
LONG lindex,
void * pvAspect,
DVTARGETDEVICE * ptd,
HDC hdcDraw,
HDC hicTargetDev,
LPCRECT lprcClient,
INT x,
INT y,
DWORD * pHitResult) = 0;
//@cmember Inplace activate notification
virtual HRESULT OnTxInPlaceActivate(LPCRECT prcClient) = 0;
//@cmember Inplace deactivate notification
virtual HRESULT OnTxInPlaceDeactivate() = 0;
//@cmember UI activate notification
virtual HRESULT OnTxUIActivate() = 0;
//@cmember UI deactivate notification
virtual HRESULT OnTxUIDeactivate() = 0;
//@cmember Get text in control
virtual HRESULT TxGetText(BSTR *pbstrText) = 0;
//@cmember Set text in control
virtual HRESULT TxSetText(LPCWSTR pszText) = 0;
//@cmember Get x position of
virtual HRESULT TxGetCurTargetX(LONG *) = 0;
//@cmember Get baseline position
virtual HRESULT TxGetBaseLinePos(LONG *) = 0;
//@cmember Get Size to fit / Natural size
virtual HRESULT TxGetNaturalSize(
DWORD dwAspect,
HDC hdcDraw,
HDC hicTargetDev,
DVTARGETDEVICE *ptd,
DWORD dwMode,
const SIZEL *psizelExtent,
LONG *pwidth,
LONG *pheight) = 0;
//@cmember Drag & drop
virtual HRESULT TxGetDropTarget( IDropTarget **ppDropTarget ) = 0;
//@cmember Bulk bit property change notifications
virtual HRESULT OnTxPropertyBitsChange(DWORD dwMask, DWORD dwBits) = 0;
//@cmember Fetch the cached drawing size (logical not physical)
virtual HRESULT TxGetCachedSize(DWORD *pdwWidth, DWORD *pdwHeight)=0;
};
There's not even a GUID for it... I got some unofficial sites listing IID_ITextServices; should I just go ahead and make a standard def anyway?
(And what is BOOL (CALLBACK * pfnContinue) (DWORD), supposed to be? Is it a pointer or not? I'm making everything 64bit compatible these days so it matters)
Last edited by fafalone; Feb 4th, 2023 at 02:22 AM.
-
Feb 4th, 2023, 06:46 AM
#23
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by jpbro
...did try that as a quick first test against my full app, but it didn't render any differently.
Might be that this really works only with the Office365-RichText-Implementation (and not msftedit32.dll)...
Originally Posted by jpbro
I've been ruminating about writing an RC6/Cairo RTB-subset widget for a years now, but other things keep getting in the way. While I have your ear though - would Cairo's font rendering/word-wrapping be consistent across devices with different screen DPIs? I would assume the answer is yes, but I was wrong in my assumptions about the WinRTB behaviour...and you know what they say about assumptions.
These days (where "Web-Exports" of richtext-formatted snippets are common-place),
I wouldn't bother with MS-RTF anymore...
At my workplace, we've thrown out all MS-RTF-based rich-text-editing -
and now use HTML-based richtext-editors throughout the Desktop-App...
(e.g. CKEditor via "IE11-elevated IE-WebBrowser-Ctl", or on Win10 and higher: TinyMCE with cWebView2).
TinyMCE comes with a free Print-Plugin... (in case that is your "primary output-target" for rich-text -
and not "exporting User-pre-formatted HTML-snippets to customer-websites"; as it is for us).
Olaf
-
Feb 4th, 2023, 07:01 AM
#24
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
DirectWrite examples.
Originally Posted by fafalone
There's not even a GUID for it... I got some unofficial sites listing IID_ITextServices; should I just go ahead and make a standard def anyway?
Use this IID_ITextServices = {8D33F740-CF58-11CE-A89D-00AA006CADC5}. Anyway you should get the IID from a dll dynamicly.
Originally Posted by fafalone
I was looking at the D2D stuff... the interfaces it uses aren't normal IDL like the rest of the TOM interfaces... how would you make this declarable for VB6-compatible typelibs:
It's just C++ abstract class declaration which is an interface.
Originally Posted by fafalone
(And what is BOOL (CALLBACK * pfnContinue) (DWORD), supposed to be? Is it a pointer or not? I'm making everything 64bit compatible these days so it matters)
It's a pointer to an user callback function:
Code:
Function Continue(ByVal l As Long) As Long
As far as i understand from the documentation it isn't used (?)
-
Feb 4th, 2023, 07:27 AM
#25
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
It's not used but in 64bit it matters if it's pointer or a BOOL because that will throw off the stack if it's wrong.
ITextServices GUID could be found in google, but others... I searched and came up empty, so I extracted them from msftedit.dll:
Code:
IID_ITextServices={8D33F740-CF58-11CE-A89D-00AA006CADC5}
IID_ITextServices2={8D33F741-CF58-11CE-A89D-00AA006CADC5}
IID_ITextHost={13E670F4-1A5A-11CF-ABEB-00AA00B65EA1}
IID_ITextHost2={13E670F5-1A5A-11CF-ABEB-00AA00B65EA1}
IID_IRicheditWindowlessAccessibility={983E572D-20CD-460B-9104-83111592DD10}
IID_IRicheditUiaOverrides={F2FB5CC0-B5A9-437F-9BA2-47632082269F}
However, IID_IRichEditUiaInformation for interface IRichEditUiaInformation : public IUnknown is *not* exported from msftedit.dll like the others, there's 0 results online, so it's a complete mystery.
Last edited by fafalone; Feb 4th, 2023 at 07:39 AM.
-
Feb 5th, 2023, 03:21 AM
#26
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
If anyone wanted to play around with the RichEdit D2D stuff, oleexp 5.3 is out with both the ITextServices/ITextHost interfaces and The trick's Direct2D and DirectWrite typelibs integrated; all of these are also in tbShellLib for twinBASIC (I did an initial pass looking for direct2d/directwrite things that needed to be updated for 64bit, and think I got them all, but if I missed them, let me know).
-
Feb 5th, 2023, 05:46 AM
#27
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Originally Posted by fafalone
however, iid_irichedituiainformation for interface irichedituiainformation : Public iunknown is *not* exported from msftedit.dll like the others, there's 0 results online, so it's a complete mystery.
{23969a9d-8546-4032-a1bb-73750cbf3333}
-
Feb 5th, 2023, 06:19 AM
#28
Re: RichTextBox Variable Word-Wrap at Different DPI Scaling Levels
Thanks. Will add it in the next version... not an immediate priority because that interface is for accessibility, which requires IRicheditWindowlessAccessibility, which requires IRawElementProviderWindowlessSite and IRawElementProviderSimple, which are interconnected with a whole large set of interfaces in uiautomationcore.h that will take some time to bring in.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|