How to draw vertical text in Windows GUI? - user-interface

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?

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...

Display of Asian characters (with Unicode): Difference in character spacing when presented in a RichEdit control compared with using ExtTextOut

This picture illustrates my predicament:
All of the characters appear to be the same size, but the space between them is different when presented in a RichEdit control compared with when I use ExtTextOut.
I would like to present the characters the same as in the RichEdit control (ideally), in order to preserve wrap positions.
Can anyone tell me:
a) Which is the more correct representation?
b) Why the RichEdit control displays the text with no gaps between the Asian Characters?
c) Is there any way to make ExtTextOut reproduce the behaviour of the RichEdit control when drawing these characters?
d) Would this be any different if I was working on an Asian version of Windows?
Perhaps I'm being optimistic, but if anyone has any hints to offer, I'd be very interested to hear.
In case it helps:
Here's my text:
快的棕色狐狸跳在懶惰狗1 2 3 4 5 6 7 8 9 0
apologies to Asian readers, this is merely for testing our Unicode implemetation and I don't even know what language the characters are taken from, let alone whether they mean anything
In order to view the effect by pasting these characters into a RichEdit control (eg. Wordpad), you may find you have to swipe them and set the font to 'Arial'.
The rich text that I obtain is:
{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Arial;}}{\colortbl ;\red0\green0\blue0;}\viewkind4\uc1\pard\sa200\sl276\slmult1\lang9\fs22\u24555?\u30340?\u26837?\u33394?\u29392?\u29432?\u36339?\u22312?\u25078?\u24816?\u29399?1 2 3 4 5 6 7 8 9 0\par\pard\'a3 $$ \'80\'80\cf1\lang2057\fs16\par}
It doesn't appear to contain a value for character 'pitch' which was my first thought.
I don't know the answer, but there are several things to suspect:
There are several versions of the rich edit control. Perhaps you're using an older one that doesn't have all the latest typographic improvements.
There are many styles and flags that affect the behavior of a rich editcontrol, so you might want to explore which ones are set and what they do. For example, look at EM_GETEDITSTYLE.
Many Asian fonts come in two versions on Windows. One is optimized for horizontal layout, and the other for vertical layout. That latter usually has the same name, but has # prepended to it. Perhaps you are using the wrong one in the rich edit control.
UPDATE: By messing around with Wordpad, I was able to reproduce the problem with the crowded text in the rich edit control.
Open a new document in Wordpad on Windows 7. Note that the selected font is Calibri.
Paste the sample text into the document.
Text appears correct, but Wordpad changed the font to SimSun.
Select the text and change the font back to Calibri or Arial.
The text will now be overcrowded, very similar to your example. Thus it appears the fundamental problem is with font linking and fallback. ExtTextOut is probably selecting an appropriate font for the script automatically. Your challenge is to figure out how to identify the right font for the script and set that font in the rich edit control.
This will only help with part of your problem, but there is a way to draw text to a DC that will look exactly the same as it does with RichEdit: what's called the windowless RichEdit control. It not exactly easy to use: I wrote a CodeProject article on it a few years back. I used this to solve the problem of a scrollable display of blocks of text, each one of which can be edited by clicking on it: the normal drawing is done with the windowless RichEdit, and the editing by showing a "real" RichEdit control on the top of it.
That would at least get you the text looking the same in both cases, though unfortunately both cases would show too little character spacing.
One further thought: if you could rely on Microsoft Office being installed, you could also try later versions of RichEdit that come with office. There's more about these on Murray Sargent's blog, as well as some interesting articles on font binding that might also help.
ExtTextOut allows you to specify the logical spacing between records. It has the parameter lpDx which is a const pointer to an array of values that indicate the distance between origins of adjacent character cells. The Microsoft API documentation notes that if you don't set it, then it sets it's own default spacing. I would have to say that's why ExtTextOut is working fine.
In particular, when you construct a EMR_EXTTEXTOUTW record in EMF, it populates an EMR_TEXT structure with this DX array - which looking at one of your comments, allowed the RichEdit to insert the EMF with the information contained in the record, whereby if you didn't set a font binding then the RTF record does some matching to work out what font to use.
In terms of the RichEdit control, the following article might be useful:
Use Font Binding in a Rich Edit Control
After character sets are assigned, Rich Edit scans the text around the
insertion point forward and backward to find the nearest fonts that
have been used for the character sets. If no font is found for a
character set, Rich Edit uses the font chosen by the client for that
character set. If the client hasn't specified a font for the character
set, Rich Edit uses the default font for that character set. If the
client wants some other font, the client can always change it, but
this approach will work most of the time. The current default font
choices are based on the following table. Note that the default fonts
are set per-process, and there are separate lists for UI usage and for
non-UI usage.
If you haven't set the characterset, then it further explains that it falls back to ANSI_CHARSET. However, it's most definitely a lot more complicated than that, as that blog article by Murray Sargent (a programmer at Microsoft) shows.

Why doesn't FONTSIGNATURE reflect lfCharSet?

I'm enumerating Windows fonts like this:
LOGFONTW lf = {0};
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfFaceName[0] = L'\0';
lf.lfPitchAndFamily = 0;
::EnumFontFamiliesEx(hdc, &lf,
reinterpret_cast<FONTENUMPROCW>(FontEnumCallback),
reinterpret_cast<LPARAM>(this), 0);
My callback function has this signature:
int CALLBACK FontEnumerator::FontEnumCallback(const ENUMLOGFONTEX *pelf,
const NEWTEXTMETRICEX *pMetrics,
DWORD font_type,
LPARAM context);
For TrueType fonts, I typically get each face name multiple times. For example, for multiple calls, I'll get pelf->elfFullName and pelf->elfLogFont.lfFaceName set as "Arial". Looking more closely at the other fields, I see that each call is for a different script. For example, on the first call pelf->elfScript will be "Western" and pelf->elfLogFont.lfCharSet will be the numeric equivalent of ANSI_CHARSET. On the second call, I get "Hebrew" and HEBREW_CHARSET. Third call "Arabic" and ARABIC_CHARSET. And so on. So far, so good.
But the font signature (pMetrics->ntmFontSig) field for all versions of Arial is identical. In fact, the font signature claims that all of these versions of Arial support Latin-1, Hebrew, Arabic, and others.
I know the character sets of the strings I'm trying to draw, so I'm trying to instantiate an appropriate font based on the font signatures. Because the font signatures always match, I always end up selecting the "Western" font, even when displaying Hebrew or Arabic text. I'm using low level Uniscribe APIs, so I don't get the benefit of Windows font linking, and yet my code seems to work.
Does lfCharSet actually carry any meaning or is it a legacy artifact? Should I just set lfCharSet to DEFAULT_CHARSET and stop worrying about all the script variations of each face?
For my purposes, I only care about TrueType and OpenType fonts.
I think I found the answer. Fonts that get enumerated multiple times are "big" fonts. Big fonts are single fonts that include glyphs for multiple scripts or code pages.
The Unicode portion of the FONTSIGNATURE (fsUsb) represents all the Unicode subranges that the font can handle. This is independent of the character set. If you use the wide character APIs, you can use all the included glyphs in the font, regardless of which character set was specified when you create the font.
The code page portion of the FONTSIGNATURE (fsCsb) represents the code pages that the font can handle. I believe this is only significant when the font is not a "big" font. In that case, the fsUsb masks will be all zeros, and the fsCsb will specify the appropriate character set(s). In those cases, it's important to get the lfCharSet correct in the LOGFONT.
When instantiating a "big" font and using the wide character APIs, it apparently doesn't matter which lfCharSet you specify.

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