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);
Related
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?
Is there a way to achieve something similar to SysLink's LM_GETIDEALHEIGHT for static controls, i.e. getting the size (or at least the height) of the actual text?
I have a multilingual program, and I want to position the controls accordingly, otherwise there's blank space left for some of the languages, which is not great.
You can use DrawTextEx() with the DT_CALCRECT flag (thanks to Jonathan Potter for that addition). Then find the difference between top and bottom of the output RECT object.
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.
For my graphics class, I need to match an OpenGL sample output in a pixel-perfect way.
I figured it would be cool if I could spawn the sample, send it some input, then take a screenshot of the exact OpenGL area, do the same for mine, and then just compare those screenshots. I also figured something like AutoIT would be the easiest way to do something like this.
I know that I can use the screencapture function, but I'm unsure of how to get the exact coordinates and size of the OpenGL area of the window (not the title bar/surrounding window stuff).
If anybody could help me out that would be awesome.
Or if anybody can think of an easier solution than AutoIt, and can point me in the right direction, that'd be great too.
EDIT: I also don't have access to the source code of the sample output program.
AutoIt is a pretty good tool for this job. I think you already found the _ScreenCapture in the help file, it has parameters for: X left, Y top, X right and Y bottom coordinates. However, the _ScreenCapture function stores to a file. I've made a library where you can capture part of the screen, or a window, and save this to memory. Then you can get the pixel colors from the memory and compare them to your existing pixels. You can find it here: http://www.autoitscript.com/forum/topic/63318-get-or-read-pixel-from-memory-udf-pixelgetcolor-au3/
The part of a window which does not include the titlebar and the borders is called the 'client area'. You can get the width and the height of the client area with the WinGetClientSize. Alternatively, you can use ControlGetPos on the OpenGL control to get the X and Y relative to the window, and the width and height of the OpenGL control. Combined with WinGetPos you should be able to calculate the values you need for _ScreenCapture. You should be able to find out a good approach if you use the "AutoIt window info" tool.
Finally, a simple and short solution which gives you little control, but might be just what you need, is the PixelChecksum function. Once you have the coordinates of the OpenGL part, you can use PixelChecksum and get a value corresponding to the pixels of the screen (a checksum of the pixels). You can then compare this value to a pre-recorded value to tell whether the pixels on the screen are exactly the same. Check the Autoit help file of PixelChecksum for an example.
If you want to capture data from an OpenGL buffer, here is how you can do it with legacy OpenGL (version 1.2 or so)
glFinish();
glReadBuffer(GL_FRONT);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
glReadPixels(ox, oy, w, h, mode, GL_UNSIGNED_BYTE, img);
where:
ox and oy are origin x and y
w and h are width and height
mode is the mode, like GL_BGR if you want to save to BMP
img is a pointer to unsigned int * to copy the image to
You can search for glReadPixels on the internet and see the reference for more information.
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?