Win GDI DrawText bad rendering - winapi

Until today I've been using GetTextExtentPoint32 to measure the text to render, prior to rendering using DrawText.
Since I now need to render a multiline text, I must use DrawText with DT_CALCRECT (as GetTextExtentPoint32 ignores newline chars).
However this causes the text to render differently, mostly by reducing character interspacing. For example:
The bottom rendering is after the change.
I've isolated the problem to find that just by calling the first DrawText while using the original output from GetTextExtentPoint32 - the problem remains.
Here's the reduction of the sequence of calls:
::GetTextExtentPoint32(...); // Result is used
::DrawText(... DT_CALCRECT); // Result is unused
BITMAPINFOHEADER bih = { size values from GetTextExtentPoint32 };
hBitMap = ::CreateDIBSection(... (BITMAPINFO*)&bih, ...);
::SelectObject (hdc, hBitMap);
::DrawText(...); // Render the text
Note, it doesn't matter if I remove the DT_CALCRECT in the second line. The call to DrawText before CreateDIBSection seems to be the culprit.
What else should I be trying?

How about font quality and precision flags in LOGFONT during font creation? Did you try to see if it makes a difference?
Also I didn't understand the last sentence. Are you saying that if you remove DrawText (... DT_CALC) or move it after CreateDIBSection the results are different, without bad character spacing?

Related

DirectWrite + Direct2D custom text rendering is hairy

I'm evaluating Direct2D for research purposes, and while I was at it, I decided to render my usual help text with a DirectWrite custom text renderer, which converts the text to a path geometry in order to add outline (as demonstrated in the DWriteHelloWorld sample on MSDN).
However, some letters have weird "hairs" or "horns" on them (picture: stroke width of 3 and 5).
Also tried with other fonts (f.e. Consolas), the effect is the same.
Source code (VS 2015):
https://www.dropbox.com/s/v3204h0ww2cp0yk/FOR_STACKOVERFLOW_12.zip?dl=0
The solution is as easy as I hoped. The "hairs" are actually caused by the line joints which D2D generates. Therefore the solution is to create an ID2D1StrokeStyle object as the following:
ID2D1StrokeStyle* strokestyle = nullptr;
D2D1_STROKE_STYLE_PROPERTIES strokeprops = D2D1::StrokeStyleProperties();
strokeprops.lineJoin = D2D1_LINE_JOIN_ROUND;
d2dfactory->CreateStrokeStyle(strokeprops, NULL, 0, &strokestyle);
// draw
rendertarget->DrawGeometry(transformedgeometry, blackbrush, 3.0f, strokestyle);
With this solution, the text is rendered as expected (perhaps a little more roundish at the joints).
I would suspect the reason for that is that default flattening tolerance in D2D does not work well for the purpose of rendering glyph outlines, at small enough sizes. Normally you'd use bitmap rendering for small sizes and outlines for larger ones, according to GetRecommendedRenderingMode(). Do you have same artifacts if you increase font size let's say 10 times?

Prevent ugly kerning when using DrawText

I use simple GDI DrawText to output blocks of text to a printer.
The font used in the sample is Segoe UI. But you can use Arial or others too. It doesn't matter.
The algorithm for large text blocks is simple. DrawText is called with DT_CALCRECT with a kind of binary search for the length to get the largest possible text to print. Than DrawText is called without DT_CALCRECT to print the block.
Simple one line text column text is written with one call to DrawText with the given coordinates of the rectangle.
The result is real strange and can be seen in this sample PDF.
Just look on the first line after the header. You can see the text "Test, Test" and you can see the strange kerning here perfectly. The kerning os sometimes so bad, that you can't even read the words.
How to get around this? Is it a problem with the used printer? Is it a problem with DrawText?
The distance between some chars in a word seem to be random in some case. Some spacing are wide other to narrow. The letter combination looks strange unreadable and ugly.
I tried different fonts and printers but the problem just varies but it is always present.
I know about ExTextOut and the capabilities to define the distance/kerning between all chars, but frankly I don't want to care about this. I just want that DrawText behaves on the printer like on the screen. The stuff works on the screen perfectly.
Added 2018-08-23 08:49 GMT+2*
To the code (it is a complex printing engine).
1.Fonts to print are created simply with CFont::CreatePointFont, so the LOGFONT structure is cleared to zero and no additional flags are used except point and face.
2.The mapping mode is MM_ANISOTROPIC. To scale what is seen on the screen and what is to be printed I just use the size of a komparable object (textblock) on the printer and the same size on the screen. The real values for the sample printout to the Microsoft PDF Printer are as follows, the real way I calculate them is not of interest:
m_pDC->SetMapMode(MM_ANISOTROPIC);
m_pDC->SetViewportExt(2363,100);
m_pDC->SetWindowExt(355,13);
This has the effect that the height of a line in LPs is 13, the average character width in LPs is 6...

What is the correct way to autosize a Static control?

I want to adjust a Static control's size to its content size, so I need to calculate the size of its text content first. I found a way to use GetTextExtentPoint32 to calculate the size, but I need to set the DC's font to the same as the control's font first. Is there a better way to do this? I've set the Static control's font once, I think maybe I don't need to set the DC's font the second time.
What is the best way to calculate the size of a Static control's text content? And is there a better way to autosize the Static control?
It sounds to me like you've already figured out the correct way to do it. Call GetTextExtentPoint32 to figure out the ideal size of the control given the text that it contains, and then resizing the control to the calculated size.
It's a lot of work, but that's what happens when you're working with the raw Win32 API. You don't have a handy wrapper library that abstracts all this for you in a Control.AutoSize() function. You could easily write your own function and re-use it, but the Win32 standard controls do not expose an "auto-size" API.
As far as the font, you will definitely need to make sure that the device context is using the same font as the control, otherwise you'll calculate the wrong size. But you don't have to create a new device context, request a handle the static control's font, and select that into your new DC. Instead, you can just use the static control's DC using the GetDC function and passing in the handle to your static control window. Make sure that if you call GetDC, you always follow up with a call to ReleaseDC when you're finished!
However, do note some caveats of the GetTextExtentPoint32 function that may interfere with the accuracy of the size you calculate:
It ignores clipping.
It does not take into account new lines (\n) or carriage returns (\r\n) when computing the height.
It does not take into account prefix characters (those preceded in the string with ampersand) and used to denote keyboard mnemonics if your static control does not have the SS_NOPREFIX style.
It may not return an accurate result in light of the kerning that may be implemented automatically by some devices.
(This is all mentioned in the linked documentation, but does anyone actually read that?)
Perhaps an easier alternative is to draw the text the same way that the static control is already doing. Unless you have the SS_SIMPLE style set (which uses TextOut or ExtTextOut to draw text as an optimization), static controls draw their text by calling the DrawText function with the appropriate parameters, given the other control styles that are set (reference).
You can do exactly the same thing, and add the DT_CALCRECT flag in your call to the DrawText function, which causes it to determine the width and height of the rectangle required to draw the specified text without actually drawing the text.
Most windows using static text controls are dialogs, where the static control's size is expressed in dialog units (DLU), which account roughly for the size of the font. In this way, dialog controls tend to have sensible sizes.
If you are not using dialogs, you can attempt to fake dialog behavior using MapDialogRect.
Otherwise yes you must use GetTextExtentPoint32.
There is no autosize for static control as far as I know. You are doing it correct.
Use GetWinDowText to get the text of static window
Use GetDC to get the dc for the window
Use WM_GETFONT to get the font for the window and select the font into the dc
Use one of the text size calculation function to calculate the text size
Restore the the original dc font
Release dc
You will always have to select the proper font into the dc to get accurate result. Also I personally prefer DrawText with DT_CALCRECT to calculate the size of a text. Refer http://msdn.microsoft.com/en-us/library/windows/desktop/dd162498%28v=vs.85%29.aspx
With DrawText, you dont have to supply the character count if the text is NULL terminated. Plus you can combine various formatting option to adjust the calculation. For example, an Ampersand(&) character in a static control text underlines the next character. With Drawtext you will be able to calculate the size properly but in GetTextExtentPoint32 there is no provision to specify this.

How to draw vertical text in Windows GUI?

I need to draw a column of vertical text (in Japanese language - it is drawn top-to-bottom instead of left-to-right) in my native C++ Win32 GUI application. I've looked through MSDN and only found how to draw right-to-left text.
How do I output top-to-bottom text except drawing each character separately?
The straight Win32 API has no way to draw (unrotated) vertical text (with an arbitrary font) in that way except 1 character at at time.
You can do more complex text output with GDI+
But that probably isn't what you want either, since the text will be vertical, but the characters will also be rotated.
Similarly, you can use CreateFont with an lfEscapement value of 900 or 2700 to get rotated text, but this will rotate everything. So that doesn't help either.
To do Japanese Top to Bottom drawing, you want the characters to be unrotated, but the placment of each character to advance in Y but not in X. Windows has no API that does this for all fonts. (you can do right-to-left and left-to-right, but not top-to-bottom).
In theory creating a font with an Orientation of 900 and an escapement of 2700 would do what you want, but it appears that if you set the escapement, then the orientation is ignored for most fonts. It's possible that for Japanese fonts, this will work differently. It's worth spending some time to play with. (see the addendum for more information on this)
I think your best bet is a probably a loop drawing one character at a time with ExtTextOut which gives you full control over the placement of each character.
If you use ETO_OPAQUE to draw the first character in a column, and not with all of the others, then you will be permitted to kern the characters vertically if you need to.
Addendum
Roygbiv points to an interesting article that says that fonts whose names begin with an # behave differently then other fonts when you use CreateFont a font with an lfEscapement value of 2700, These special fonts produce upright characters while still advancing down the page. So while there is no way to do what you want for arbitrary fonts, you may be able to get it working using certain fonts.
Options for Displaying Text
Out of curiosity, i wrote a small console app to enum fonts and list the names. My Windows Server 2003 machine has not fonts with names beginning with #. But my Windows 7 machine has a few. All seem to be Chinese fonts though, I see no Japanese fonts in the default Windows 7 Ultimate install.
The correct answer is:
There are three methods to do this:
Using the Edit or RichEdit controls to render your text
Using the Uniscribe API
Using the TextOut function with a font face name that begins with an at sign (#).
Here is an article that discusses some of these approaches.
Fortunately, with Win32 you do not need to write code to rotate characters. To display text vertically on Windows 2000 and Windows XP, enumerate the available fonts as usual, and select a font whose font face name begins with the at sign (#). Then create a LOGFONT structure, setting both the escapement and the orientation to 270 degrees. Calls to TextOut are the same as for horizontal text.
In Win32, use the lfEscapement member of a LOGFONT structure to define the rotation of a font:
LOGFONT LogFont
LogFont.lfEscapement = 900; // 90 degreees rotated text
... // Many more initializations
HFONT newFont = CreateFontIndirect(LogFont);
SelectObject(hdc, newFont);
char tx[255];
strcpy(tx, "vertical text");
TextOut(hdc, x, y, tx, strlen(tx)); // draw a vertical font
For More Information see the online Help of LOGFONT structure and of the CreateFontIndirect Function
HFONT gui_font = CreateFont( -MulDiv( 9, GetDeviceCaps( GetDC( hWnd ), LOGPIXELSY ), 72 ),
0,
900, // here
0,
FW_THIN, 0, 0, 0,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FF_MODERN | FIXED_PITCH,
L"Segoe UI" );
Using lfEscapement (and if necessary lfOrientation) is superior in many ways to making the rectangle minimally wide (for instance: the dutch word 'wij' would have the 'i' and 'j' next to each other, because their combined width is less than the 'w'), or inserting a newline after each character.
The method this library uses sounds slow, but if do want it, it appears source code is provided:
http://www.ucancode.net/faq/CDC-DrawText-Drawing-Vertical-Text.htm
You may also find this discussion useful - http://www.eggheadcafe.com/forumarchives/win32programmergdi/Aug2005/post23542233.asp - apparently you need a vertical font (one beginning with #) and the API will take care of the rest.
As a quick hack type of answer, what happens if you use a standard control (CEdit for instance) and insert a new-line after every character typed?
Just an idea:
Did you try using DrawText or DrawTextEx using a very narrow rectangle that just fits the widest character?

Wordwrap Win32

I'm trying to wordwrap a block of text for display in a window that would otherwise be too long. The font I'm using is not a fixed width font, so GetSystemMetrics will not return accurate values for my text.
Using a static window is not an option here because a static window doesn't tell me one crucial piece of information: The height of the text drawn after wordwrapping.
Any ideas?
Billy3
Check out the Win32 API call DrawtextEx. You'll need to pass the DT_CALCRECT option, telling Windows that you wish for the rectangle to be calculated.
You could use the DrawText() API function with the DT_CALCRECT flag set. You would need to select the correct font for the HDC first.
DrawTextEx() will do everything you want and need if the entire text is to be displayed in a single font.
If you need to mix fonts, you'll have to do the work yourself. In that case, you'll want to look at APIs like GetTextMetrics() (not GetSystemMetrics()) and GetTextExtentExPoint32() to figure out positions and sizes for each run.
If you need to handle complex scripts (e.g., right-to-left languages and scripts where the letters change shape depending on context), you'll need Uniscribe. Caution: Uniscribe is powerful but very low level. It can take a lot of work to wrap it with a higher-level interface. If you need complex script handling, you might be better off using a browser control.
Both DrawText() and DrawTextEx() can accomplish that. The key is to use DT_WORDBREAK in the format parameter.
To draw text within a 100 x 100 rectangle at the top let, one could use either:
RECT textRect;
SetRect(&textRect, 0, 0, 100, 100);
DrawText(hdc,TEXT("THIS IS TEXT THAT WILL PROBABLY WRAP AROUND A 100 BY 100 RECTANGLE. BUT YOU CAN'T BE SURE UNLESS YOU TRY."),-1,&textRect, DT_LEFT | DT_NOCLIP | DT_WORDBREAK);
The DT_LEFT option aligns the sentences to the left. The DT_NOCLIP ensures that text isn't cut out at the bottom.
OR
Use DrawTextEx():
RECT textRect;
SetRect(&textRect, 0, 0, 100,100);
DrawTextEx(hdc,(LPSTR)TEXT("THIS IS TEXT THAT WILL PROBABLY WRAP AROUND A 100 BY 100 RECTANGLE. BUT YOU CAN'T BE SURE UNLESS YOU TRY."), -1, &textRect, DT_LEFT| DT_NOCLIP|DT_WORDBREAK , NULL);

Resources