How to get the automatically calculated font width from a CFont Object? - c++

I'm using a fixed size font ( eg: "Courier New" ). When I initialize the CFont object by calling CFont::CreateFont function, I want to specify only the font height.
CFont Font;
Font.CreateFont( nFontHeight, 0, 0, 0, 0, false, false,
0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
FIXED_PITCH|FF_MODERN, _T("Courier New") );
As per documentation, font width will be calculated automatically. I need that automatically calculated value for some other calculation.
GetLogFont Function is useless as it seems CFont only holds the value we give that is width = 0 and it calculates the value only when it is used for first time. ( Please check the Microsoft documentation )
Another option was to use CDC::GetTextExtent using a single character. But in that case also I could see some minor differences even in the height. For example, when I give -32 as height, GetTextExtent returns 33 for y value.
Is there any way to get the correct calculated width?

First of all, if you only want to specify the font height, you normally want to use CreatePointFont. Second, Windows 95/98/SE/Me are dead and gone -- and with them, essentially all reason to use Microsoft's "text" macros like _T("whatever"). If you want wide characters, ask for them directly:
CFont font;
font.CreatePointFont(nFontHeight, L"Courier New");
Then, as suggested by #MikMik, you can use GetTextMetrics to get the width -- but only after you select the font into a DC (GetTextMetrics gets the data for a font selected into a DC, not just for the raw font -- especially at small font sizes, some things get adjusted to compensate for the resolution of the output device).
Note, however, that even for a fixed-width font, the width of a string is not necessarily char_width * num_chars. At least if I recall correctly, even a fixed-width font can still be kerned, which means the spacing is adjusted based on what pairs of characters occur together. The classic example is a pair like AV. Because the lines where they're next to each other are typically at the same angle (or at least very close to the same) the spacing will be adjusted to move them closer together -- in fact, the top of the "V" will often overlap with the bottom of the "A". The width of a string of characters can vary even though each individual character has the same width as every other.
Offhand, I'm not sure that Courier New does that, but I'm reasonably certain at least a few fixed-width fonts do.

Have you tried CDC::GetTextMetrics()? I've never used it, but it seems to be what you're looking for. You can get the average and maximum character width, which I guess should be the same for Courier New.

Related

Motif How to calculate or retrieve the font pixel size?

I am trying to write code in Motif to change a dialog warning box to resize size it if the box is not wide enough. The width and height is always being set by the calling classes and its not always wide enough for the message being displayed and the end of the line is truncated off. Instead of fixing everywhere to use auto sizing (i.e. width is 0 or not set at all) they want to figure out what the pixel width size is for a character in the dialog. They can then multiple the longest line X pixels width to get the lines length in pixels. Then we would see if the dialog declared width needs to be reset to stop the truncation. Only dialogs that are too short will be changed (dialogs too wide are not to be changed).
However; I can't find any example on how get the character width in pixels anywhere. I remember years ago I was on a project where they created some type of widget, inserted a character into it, and then did a XtGetValues to get the width and height so I think it can be done. So does anyone know how to do this?
That was a long time ago, but if memory serves, Xt doesn't have any specific support for fonts, it relies on plain libx11. You will need to call XQueryFont or XLoadQueryFont to get the XFontStruct describing your font, then grovel through the per_char array to find the extents of individual glyphs.

How to get correct position in the std::string?

I am creating a custom single line edit control, with a custom font in win32 api on windows 7, the font is not a fixed width font, and I need to move caret according to the mouse click, The edit control is not empty and if I know the horizontal position of the mouse click within the window, how do I calculate the number of characters after which I need to move caret to ?
I really am out of ideas, if it was a fixed width font, I would have divided the horizontal mouse click position with average character width, that would have been simpler, doing the same with not a fixed width font, is prone to errors.
Given that it's a single-line control, you probably don't plan on working with immensely long input (at least normally). That being the case, one possibility would be to just store the character positions in an array (or vector, etc.) Then you can use (for example) a binary search in that array to find character positions. Of course, you can do the same even for longer strings--though it can increase storage requirements quite a bit.
This is a familiar problem. You are in essence trying to do hit testing on text and for that you need the location on the screen of each character of the text.
My preferred strategy is to calculate an array of RECT, one for each character of displayed text. The array needs to be updated when text is added or deleted, but it easily handles single or multiple lines. The function GetCharWidth32 retrieves all the widths for a string of text in a particular font selected into a DC. For single line one call is enough, and calculating the array of RECTs is simple. It's not much harder to do multiline.
Handle the mouse down message, loop through the array and find the right character. A brute force search is plenty fast enough.
This method is simple and easily generalises to a range of similar problems.

How to work out width and height of multined string with font?

My program draws text strings into rectangles by working out the width and height of the text, and selecting a smaller font if its too big for the rectangle. But originally i only used single lined text, now i need some multi lined, i used to use GetTextExtentPoint32 but if theres \n in the string, it seems to think of that as a normal character.
DrawText with DT_CALCRECT only returns the height of the text...
Is there a simple way to do this?
Docs for DrawText state that while it only returns the height, it modifies the rectangle you pass it. Are you checking the rectangle, or only the return value? It sounds like you would actually want to pass in a rectangle with a large width (ie. maximum width you want to allow), and DrawText will reduce as necessary. (If you pass in a small width, it will expand it only enough to fit the largest word.)
From MSDN:
If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the rectangle to bound the last line of text. If the largest word is wider than the rectangle, the width is expanded.
You should do roughly this (pseudo code):
size text_dim(0,0);
foreach( line in text.split("\n") )
{
size line_dim = GetTextExtentPoint32(line.start,line.length);
text_dim.y += line_dim.y;
text_dim.x = max(text_dim.x,line_dim.x);
}
return text_dim;

WYSIWYG with Qt - font size woes

I am creating a custom Qt widget that mimics an A4 printed page and am having problems getting fonts to render at the correct size. My widget uses QPainter::setViewport and QPainter::setWindow to mimic the A4 page, using units of 10ths of a millimetre which enables me to draw easily. However, attempting to create a font at a specific point size doesn't seem to work and using QFont:setPixelSize isn't accurate. Here is some code:
View::View(QWidget *parent) :
QWidget(parent),
printer(new QPrinter)
{
printer->setPaperSize(QPrinter::A4);
printer->setFullPage(true);
}
void View::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.setWindow(0, 0, 2100, 2970);
painter.setViewport(0, 0, printer->width(), printer->height());
// Draw a rect at x = 1cm, y = 1cm, 6cm wide and 1 inch high
painter.drawRect(100, 100, 600, 254);
// Create a 72pt (1 inch) high font
QFont font("Arial");
font.setPixelSize(254);
painter.setFont(font);
// Draw in the same box
// The font is too large
painter.drawText(QRect(100, 100, 600, 254), tr("Wg\u0102"));
// Ack - the actual font size reported by the metrics is 283 pixels!
const QFontMetrics fontMetrics = painter.fontMetrics();
qDebug() << "Font height = " << fontMetrics.height();
}
So I'm asking for a 254 high font (1 inch, 72 pts) and it's too big and sure enough when I query for the font height via QFontMetrics it is 283 high.
Does anyone else know how to use font sizes in points when using custom mapping modes like this? It must be possible. Note that I cannot see how to convert between logical/device points either (i.e. the Win32 DPtoLP/LPtoDP equivalents.)
EDIT: Well, it turns out that my code was working fine after all. I converted it to work with a printer, printed it out and then printed the same text using various word processors and the results are exactly the same. It seems that asking for a font size doesn't take the descent into account and this seems to be the norm.
QFont may or may not be able to match the exact font that you request. QFontMetrics::height() returns a size of 284 on my system, but QFontInfo::pixelSize() returns a size of 254 which is what was requested. I assume the difference is that height() includes the descent where pixelSize() returns the size in pixels of the matched font, implying that I had a match.
As for drawing, placement, and conversions, you'll need to be careful because the printer device won't be the same as the screen device nor will the resolutions match. To further exacerbate the problem, you can't get the exact printer metrics without calling setup on the print dialog. Device independence gets you close to WYSIWYG, but not always close enough.
In your sample, the paint device is this. Thus, you can get at the logical dpi through the logicalDpiX() and logicalDpiY() functions (as well as their physical equivalents).

vertical text won't be bold in win32 GDI c++

I'm trying to draw vertical text in win32 GDI api.
I call CreateFont() with 2700 for the angle and 900 for bold.
logPxlY = ::GetDeviceCaps (c->_hdc, LOGPIXELSY);
_hfont = ::CreateFont (-::MulDiv (point, logPxlY, 72),
0, angle, weight, 0, FALSE, FALSE, FALSE, 0, 0, 0, 0,
FIXED_PITCH | FF_MODERN, face);
where face is "Courier New", point is 9, angle is 2700 and weight is 900.
The text IS rotated correctly, but it's pretty dang faint compared to normal Courier New rendering. Is this since truetype is polishing the normal text real nice and isn't bothering with wierd rotated text or something?
Thanks for any insight :)
The font mapper can be a bit strange at times. 700 is bold, 900 is supposed to be "black". However, you probably have a Courier New Bold font file, so it can be used directly, whereas you probably do not have a Courier New Black font file, so it'll be synthesized -- probably from the normal Courier New font instead of from Courier New Bold (not sure why that is, but such is life).
If you try running through the possibilities, something like this (MFC code, for the moment, but the idea applies regardless):
CFont fonts[num];
pDC->SetBkMode(TRANSPARENT);
for (int i=0; i<num; i++) {
fonts[i].CreateFont(10, 0, 2700, 0, i*100, 0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, OUT_CHARACTER_PRECIS, PROOF_QUALITY, FF_MODERN, L"Courier New");
pDC->SelectObject(fonts+i);
pDC->TextOut(20+i*14, 20, L"This is some text");
}
What you'll probably see (what I get anyway) looks like this:
The 700 weight font is noticeably bolder than the 800 or 900 weight. This is not unique to Courier New either -- for example, the same code with the font name changed to Times New Roman produces output like this:
That's a bit closer, but the 700 weight still clearly looks darker than the 800 or 900. I won't bore you with screen shots, but the same effect happens when the text is horizontal.
Edit: One other minor detail: though it probably doesn't make any difference, you're currently computing the size of the font a bit incorrectly -- you're using logpixelsy, which makes sense when the font is horizontal. For a vertical font, you should use logpixelsx instead. On a typical screen, those will be identical, but on a printer (for example) they may be substantially different.