How to draw separate texts one by one in MFC? - c++

I am trying to draw two pieces of text one by one in MFC as they would be part of the same text. Right now I am drawing them as they are just one string:
CString text1 = "A text";
CString text2 = "A second text";
CString textToDraw = text1 + text2;
CDC* dc = GetDC(); //assume that this is initialized elsewhere
dc->TextOut(0, 0, textToDraw);
It is simple to draw the both texts as one because I only need to find the position where they should be started to be draw. The problem I am facing is how to compute the new X coordinate at which the second text should be draw (considering that the texts can be chosen at run-time so they do not have a known length):
dc->TextOut(0, 0, text1);
int X;
//how should I compute X...?
dc->TextOut(X, 0, text2);
I appreciate any help received!

You can use DrawText() with DT_CALCRECT flag to calculate the width and height the text would occupy without actually drawing the text. The following may be the answer to your question.
dc->TextOut(100, 100, text1);
RECT rect = { 0, 0, 0, 0 };
dc->DrawText(text1, &rect, DT_CALCRECT);
dc->TextOut(100 + rect.right, 100, text2);

Related

MFC DrawText, vertical, DT_CALCRECT with lf_escapement = 900

I'm working on a MFC project with some GDI drawings.
I use DC.DrawText to draw a vertical text into a DC using a LOGFONT with lfEscapement = 900.
The text is output when i use DT_NOCLIP in the desired vertical formatting.
However to center this text i used a call to DC.DrawText with the DT_CALCRECT argument.
I recognized that, despite the text is indeed drawn vertically, the CRect has a larger width
than height.
My intuition says me that a vertical drawn text should have a larger height than width.
I did not include the calculation for centering the text. The question is just about what i can rely upon when i implement that vertical centering.
Does DC.DrawText with DT_CALCRECT ignore escapement?
void CMFCFontTestDlg::OnPaint()
{
CPaintDC dc(this); // Gerätekontext zum Zeichnen
if (IsIconic())
{
...
}
else
{
CDialogEx::OnPaint();
CRect clTextRect;
CFont myFont;
myFont.CreateFont(12, 0, 900, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("Tahoma"));
CFont* oldFont = dc.SelectObject(&myFont);
dc.DrawText(_T("000000"), clTextRect, DT_CALCRECT);
clTextRect.MoveToXY(100, 100);
dc.DrawText(_T("000000"), clTextRect, DT_NOCLIP);
dc.SelectObject(oldFont);
}
}
I found out about it in the remarks to the DrawTextEx function
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawtextexa.
However, please note that neither the documentation of DrawText does say anything about this nor the documentation of the DT_CALCRECT flag.
This is likely to be overseen if one uses DrawText and not DrawTextEx.
I informed MS about this via "Is this page helpful?" feedback possibility.
Remarks
The DrawTextEx function supports only fonts whose escapement and
orientation are both zero.
The text alignment mode for the device context must include the
TA_LEFT, TA_TOP, and TA_NOUPDATECP flags.
Considering this the solution is to use some trigonometric calculation starting from the size determined for escapement = 0 and then calculate the topleft and bottomright points of the rotated rect.

How to stroke text when drawing it with DrawString function in GDI+?

There's a GDI's StrokePath API that can allow to simulate "stroking" of text using this method. I'll copy it here:
void CCanvas::DrawOutlineText( CDC& dc, const CString& Text )
{
const int RestorePoint = dc.SaveDC();
// Create new font
CFont NewFont;
NewFont.CreatePointFont( 700, TEXT( "Verdana" ), &dc );
// Use this font
dc.SelectObject( &NewFont );
// Brush for pen
LOGBRUSH lBrushForPen = { 0 };
lBrushForPen.lbColor = RGB( 200, 150, 100 );
lBrushForPen.lbHatch = HS_CROSS;
lBrushForPen.lbStyle = BS_SOLID;
// New pen for drawing outline text
CPen OutlinePen;
OutlinePen.CreatePen( PS_GEOMETRIC | PS_SOLID, 2, &lBrushForPen, 0, 0 );
// Use this pen
dc.SelectObject( &OutlinePen );
dc.SetBkMode( TRANSPARENT );
dc.BeginPath();
// This text is not drawn on screen, but instead each action is being
// recorded and stored internally as a path, since we called BeginPath
dc.TextOut( 20, 20, Text );
// Stop path
dc.EndPath();
// Now draw outline text
dc.StrokePath();
dc.RestoreDC( RestorePoint );
}
In my case I'm using DrawString function from GDI+ to draw text.
Does anyone know if there's an alternative to BeginPath, EndPath and StrokePath in GDI+ to simulate text stroking?
EDIT: Following the advice by jschroedl below, I tried the following:
CString str = L"Text";
Graphics grpx(dc.GetSafeHdc());
SolidBrush gdiBrush(Color(0xFF, 0xFF, 0, 0));
StringFormat gdiSF;
gdiSF.SetAlignment(StringAlignmentNear);
gdiSF.SetFormatFlags(StringFormatFlagsNoWrap | StringFormatFlagsNoFitBlackBox |
StringFormatFlagsNoFontFallback | StringFormatFlagsNoClip);
gdiSF.SetHotkeyPrefix(HotkeyPrefixNone);
gdiSF.SetTrimming(StringTrimmingNone);
grpx.SetTextRenderingHint(TextRenderingHintAntiAlias);
grpx.SetPixelOffsetMode(PixelOffsetModeNone);
grpx.SetInterpolationMode(InterpolationModeHighQualityBicubic);
GraphicsPath dd;
FontFamily gdiFF(L"Segoe UI");
const PointF pntF(0, 0);
dd.AddString(str, str.GetLength(), &gdiFF, FontStyleRegular, 58.0f, pntF, &gdiSF);
Pen penFF(Color(0xFF, 0xFF, 0, 0), 1.0f);
grpx.DrawPath(&penFF, &dd);
that produced quite a jagged outline (enlarged screenshot):
Any idea how to make it render with anti-aliasing?
I believe our code creates a GraphicsPath object, calls GraphicsPath::AddString() to get the path for the text and later draws the path with Graphics::DrawPath().
Update:
Based on the blocky text, I experimented and think this looks a bit smoother.
grpx.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
grpx.SetSmoothingMode(SmoothingModeHighQuality);
grpx.SetPixelOffsetMode(PixelOffsetModeHalf);

resizing rect but keep text at same pos

I have a rect that i resize with text on it.
The text is drawn on top of that rect with DT_CENTER, I want the text to be able to stay in its same position but only print the parts that would be seen where the rect is ontop.
At the moment my text just prints at its right spot but shows when the rect is not even under the text.
This is set in WM_TIMER i add 10 to i everytime timer called
this it is all drawn in WM_PAINT
This is when the rect slides from left to right:
if (i <= m_sWndSize.cx)
{
m_rcCurrent = { 0, 0, i, m_sWndSize.cy };
m_rcCurrentText = { 0, 0, i, m_sWndSize.cy };
}
Worked it out with what Jonathan Potter said.
With the use of CreateRectRgnIndirect and SelectClipRgn
Thanks

Black border around characters when draw Image to a transparent Bitmap

I have to draw a String on a transparent bitmap at first, then draw A to destination canvas.
However on certain case, there is black border around the characters.
Bitmap* tempImg = new Bitmap(1000, 1000, PixelFormat32bppARGB);
Graphics tempGr(tempImg);
tempGr.Clear(Color(0, 255,255,255));
Gdiplus::SolidBrush* brush = new SolidBrush(Color(255, 255, 0, 0 ));
Gdiplus::FontFamily fontFamily(L"Times New Roman");
Gdiplus::Font* font = new Gdiplus::Font(&fontFamily, 19, FontStyleRegular, UnitPixel);
RectF rec(400, 400, 1000, 10000);
tempGr.DrawString(
L"Merry Chrismas",
-1,
font,
rec,
NULL,
brush
);
Graphics desGr(hdc);
desGr.Clear(Color::Gray);
desGr.DrawImage(tempImg , 0,0, 1000, 1000);
The character draw on desGr have black board for some fontsize.
How can I avoid this problem?
Many thanks!
I think the problem here is that you are drawing the text onto a transparent background.
You could try adding this line after the call to tempGr.Clear...
tempGr.TextRenderingHint = TextRenderingHint.AntiAlias;
ps - sorry not sure the exact syntax in C++ ;)
I just solved this problem in XNA:
Clear background to the same as the foreground color. The only difference is that the background should have Alpha=0, and the foreground with Alpha >> 0
The black border comes from blending of your background and foreground of different colors. Try to clear the background to some contrasting color to fully appreciate the phenomenon.

How to find the width of a String (in pixels) in WIN32

Can you measure the width of a string more exactly in WIN32 than using the GetTextMetrics function and using tmAveCharWidth*strSize?
Try using GetTextExtentPoint32. That uses the current font for the given device context to measure the width and height of the rendered string in logical units. For the default mapping mode, MM_TEXT, 1 logical unit is 1 pixel.
However, if you've changed the mapping mode for the current device context, a logical unit may not be the same as a pixel. You can read about the different mapping modes on MSDN. With the mapping mode, you can convert the dimensions returned to you by GetTextExtentPoint32 to pixels.
I don't know for certain, but it seems that:
HDC hDC = GetDC(NULL);
RECT r = { 0, 0, 0, 0 };
char str[] = "Whatever";
DrawText(hDC, str, strlen(str), &r, DT_CALCRECT);
might work.
Graphics::MeasureString ?
VOID Example_MeasureString(HDC hdc)
{
Graphics graphics(hdc);
// Set up the string.
WCHAR string[] = L"Measure Text";
Font font(L"Arial", 16);
RectF layoutRect(0, 0, 100, 50);
RectF boundRect;
// Measure the string.
graphics.MeasureString(string, 12, &font, layoutRect, &boundRect);
// Draw a rectangle that represents the size of the string.
graphics.DrawRectangle(&Pen(Color(255, 0, 0, 0)), boundRect);
}
Depending on how you are using this, you can use DrawText with DT_CALCRECT specified and it will (its always done it fairly accurately for me) calculate the size of the required rectangle based on the text/font/etc.
For Builder C++ first make new TLabel dynamicly and then change font attributes.Set your TLabel as autosize.Then you can get you TLabel width witch represents your string width in pixels.
int WidthPixels (String font, int size, String text)
{
TLabel* label = new TLabel(Form1); // dynamic TLabel
label->AutoSize = true;
label->Font->Name = font; // your font
label->Font->Size = size; // your font size
label->Caption = text; // your string
return label->Width;
}
int width = WidthPixels("Times New Roman", 19 , "Hey");