How to draw text on a canvas with left alignment? - delphi

I want to draw a rectangle on a canvas and I want to fill it with text. I've tried with the below code, but it only succeeded with right alignment.
Can anyone help me? How can I draw text on a canvas with left alignment?
{Header Table}
SetStyleHuruf(FCanvas, fsBold, 12, clWhite, 'Maiandra GD');
Brush.Color := color;
Rectangle(cx+50, cy-50, cx+370, cy - 30);
TextOut(round(cx + 215), cy-50, Name);`

Look at the definition of TextOut:
procedure TextOut(X, Y: Integer; const Text: string); override;
The description in help says:
Writes a string on the canvas, starting at the point (X,Y), ...
So, modify your code to draw the text at an X coordinate that is closer to the left border of the rectangle instead of the right right. For example:
TextOut(round(cx + 215), cy-50, Name);
Outputs the text 5 pixels from the left border of the rectangle
TextOut(round(cx + 55), cy-50, Name);
BTW, assuming cx is an integer, you dont need to use Round()

Instead of textout I would use DrawText function which is explained on the Microsoft site here
The DrawText function draws formatted text in the specified rectangle. It formats the text according to the specified method (expanding tabs, justifying characters, breaking lines, and so forth).
To specify additional formatting options, use the DrawTextEx function.
Here you find a great example how to use it

Related

How set Panel position always above of hole region?

I have a code that creates a hole in Form using the mouse.
var
FormRegion, HoleRegion: HRGN;
begin
FormRegion := CreateRectRgn(0, 0, Form1.Width, Form1.Height);
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form1.Handle, FormRegion, True);
end;
Now i wish put one Panel (that already have a fixed height) always above of hole region (and with same width of hole) to simulate a title bar, something like this:
How can be made?
You did not read carefully my request for additional information, so I leave it to you to adjust however you like.
Anyway, I believe your actual question is concerning the alignment of the panel with the transparent region. You probably do not consider that the region calculations with the form window includes the borders, so you have an offset to the right and downwards.
Since the regions are calculated including the borders of the form, you will need a variable ClientOffset: TPoint to hold the values of width of the left border and the height of the top border (incl. title bar of the form).
var
ClientOffset: TPoint;
To calculate the ClientOffset you can use the predefined ClientOrigin and the forms Left and Top properties.
ClientOffset.X := Form36.ClientOrigin.X - Form36.Left; // Left border width
ClientOffset.Y := Form36.ClientOrigin.Y - Form36.Top; // Top border height (incl. title bar)
Then, either subtract ClientOffset from the panels Left and Top properties, or add ClientOffset to the HoleRegions coordinates. The latter being more correct if you use the mouse (and presumably the forms client coordinates) to define the "hole" region

delphi canvas drawtext does not clip the text

I am using Windows 10, Delphi 7. Trying to make Pdf document with SynPDF using it's canvas directly. I need to draw in the rectangle only that part of the text that corresponds to the length of the rectangle, the rest cut off. I am using DrawText (and DrawTextEx) functions to write text in the given rectangle with alignment (TA_LEFT, TA_RIGHT, TA_CENTER).
The problem: these functions draw the text, but do not take into account the given boundaries - they do not clip(crop) this text.
var
R: TRect;
s: String;
begin
R:= Rect(50, 50, 120, 75);
Canvas.Brush.Color:=clYellow;
Canvas.Rectangle(R);
Canvas.Font.Name:='Arial';
Canvas.Font.Size:=10;
Canvas.Font.Style:=[];
Canvas.Brush.Style:= bsClear;
s:='Sample for text clipping';
DrawText(Canvas.Handle, PChar(s), -1, R, TA_LEFT or
{DT_END_ELLIPSIS or }DT_VCENTER or DT_SINGLELINE);
end;
If I add DT_END_ELLIPSIS it works correctly but adds three dots - I do not need dots. What I am doing wrong? Or I need to use other functions for my task?
Unfortunately, I'm not allowed (by StackOverflow) to add a photo with the result...

Delphi FireMonkey app can not draw simple black rectangle

Just create simple FireMokey HD app, put TImage with align=alclient on the form and trying to draw simple black rect:
procedure TForm8.FormCreate(Sender: TObject);
var
c: TCanvas;
begin
Image.Bitmap := TBitmap.Create(ClientWidth, ClientHeight);
c := Image.Bitmap.Canvas;
c.BeginScene;
try
c.Clear(claWhite);
c.Stroke.Color := claBlack;
c.Stroke.Kind := TBrushKind.bkSolid;
c.DrawRect(
TRectF.Create(7,7,ClientWidth-7,ClientHeight-7),
0,0,
[],
1
);
finally
c.EndScene;
end;
end;
And it doesn't work. Color of the rect is not black, it is kind of gray. There some changes of the color in corners. Did i need to set some other properties or what is wrong here ?
I tried different opacity values (1,100,255,65535), picture doesn't change at all and there is no information in the help what the hell this option means.
Zoomed left-top corner:
Also tried to use polygons as it described in example. Same problem - rounded corners and gray color instead of black (Opacity property of image is 1, all properties as by default):
procedure TForm8.Button2Click(Sender: TObject);
var
p1, p2, p3, p4, p5: TPointF;
MyPolygon: TPolygon;
begin
// sets the points that define the polygon
p1.Create(100, 100);
p2.Create(200, 100);
p3.Create(200, 200);
p4.Create(100, 200);
p5.Create(100, 100);
// creates the polygon
SetLength(MyPolygon, 5);
MyPolygon[0] := p1;
MyPolygon[1] := p2;
MyPolygon[2] := p3;
MyPolygon[3] := p4;
MyPolygon[4] := p5;
Image.Bitmap.Canvas.BeginScene;
// draws the polygon on the canvas
Image.Bitmap.Canvas.DrawPolygon(MyPolygon, 50);
Image.Bitmap.Canvas.EndScene;
// updates the bitmap
// Image.Bitmap.BitmapChanged;
end;
http://roman.yankovsky.me/?p=1018
if Canvas.BeginScene then
try
Canvas.Stroke.Thickness := 1.5;
Canvas.Stroke.Kind := TBrushKind.bkSolid;
Canvas.Fill.Color := TAlphaColorRec.Black;
Canvas.Fill.Kind := TBrushKind.bkSolid;
for I := 1 to 9 do
begin
Canvas.DrawLine(PointF(50 + I * 25 - (Canvas.Stroke.Thickness / 2), 0),
PointF(50 + I * 25 - (Canvas.Stroke.Thickness / 2), ClientHeight), 1);
end;
finally
Canvas.EndScene;
end;
This is easy to fix once you understand the better paradigm of Firemonkey. Firemonkey uses real coordinates, not integer coordinates. You unwittingly told it to draw lines that were centered the boundaries between pixels, so each of your lines were half in one set of pixels and half in another set of pixels.
Specifically, what happened is that your integer coordinates were interpreted as exact center points on a continuous number line. For example, say the point is 7. A line of width 1 centered on the point at 7.0 will extend from 6.5 to 7.5 on the number line. But because the pixels extend from 6.0 to 6.99 and from 7.0 to 7.99 on the number line, each pixel is half black and half white. Automatic antialiasing caused them to be drawn 50% black, which is where the two-pixel wide gray comes from.
When using FMX (now called FMX) you have to switch your thinking from integer coordinates to real coordinates, which is far more sophisticated and powerful.
The easiest solution is to move your integer-based math by 0.5 to the right and 0.5 down. Then a one-pixel wide line at 7.5 will extend from 7.0 to 7.999, which is what you were expecting. To do this, just add 0.5 to all your pixel coordinates, both horizontal and vertical, as you issue drawing commands.
The nice thing is, lines that are 0.8 pixels wide or 1.5 pixels wide will automatically appear thinner or thicker, respectively. Diagonal lines and other curves will appear correct without jagged edges. You can scale complex drawings and they will look perfect at any zoom level. (The math for the half-pixel shift stays the same for all zoom levels. The 0.5 is added after scaling immediately before drawing the line.)
The above is true for all devices: screens, bitmaps, and printers, etc. So the same code to draw on screen can be used to draw to everything else. When drawing text, you can use fractional point sizes for the fonts, so they scale with everything else.

DrawText with DT_CALCRECT - Is there a way to calculate the height of the rect WITHOUT modifying the width (with large strings)?

I have a string that i need to calculate the Rect size (text height) when drawing. My implementation uses the DrawTextW() function with DT_WORDBREAK or DT_CALCRECT flags.
An example of my string:
thisisaverylonglonglonglineoftextthatneedstofitinsideagivenrectwidth
I can see in the MSDN docs, that DrawTextW() method states:
If the largest word is wider than the rectangle, the width is expanded. If the text is less than the width of the rectangle, the width is reduced. If there is only one line of text, DrawText modifies the right side of the rectangle so that it bounds the last character in the line.
however in the MSDN docs, then DrawTextExW() method does not state this.
So i tried to calculate the height using the DrawTextExW() method, however the result is the same as with DrawTextW() function, where it extends the width of the rect to fit the largest line of text.
So how can i correctly calculate the height of the text rect with a given (fixed) width when drawing a large string (with no spaces) where DT_WORDBREAK and DT_CALCRECT are specified?
EDIT:
As a side note, does anyone know how Microsoft Excel does cell text drawing? Is there an API call for this text drawing? This was where my original question stemmed from, however the way it is implemented in Excel is to draw the text and wordbreak/wordwrap on any character (not just a space).
You need to use the DT_WORD_ELLIPSIS flag in uFormat parameter (along with DT_WORDBREAK of course). That will prevent the widening due to long strings with no spaces. It still will not break those long strings though, but your width problem will be solved.
If you also specify DT_MODIFYSTRING, then you could kind of figure out where to break that long string yourself, prior to the final draw.
As for the difference between DrawText(W) and DrawTextEx(W): the latter provides tab formatting, setting margins and returns the actual number of drawn characters. There is no difference in (dimensioning) functionality.

Delphi Printer.Canvas.TextWidth property

I'm trying to set the column width for printing with my Delphi application. Whatever I type for the string doesn't make the width fewer. Actually I don't understand why the property returns a string, it should return width in pixels.
My code is
Printer.Canvas.TextWidth('M');
Edit: i understood it doesn't return a string but what does 'M' mean? what i m trying to do is making a column narrower. my code is located at sudrap.org/paste/text/19688
Edit: i m afraid i couldn t explain the problem clearly, i m sorry. i want it to print like this:
not like this:
Try to check TextRect function. Using this function you can specify the target rectangle where the text should be printed, so you can narrow your column.
uses Graphics;
var
Text: string;
TargetRect: TRect;
begin
Printer.BeginDoc;
Text := 'This is a very long text';
// now I'll specify the rectangle where the text will be printed
// it respects the rectangle, so the text cannot exceed these coordinates
// with the following values you will get the column width set to 50 px
TargetRect := Rect(Margin, Y, Margin + 50, Y + LineHeight);
Printer.Canvas.Font.Size := 11;
Printer.Canvas.Font.Name := 'Arial';
Printer.Canvas.Font.Style := [fsBold];
Printer.Canvas.TextRect(TargetRect, Text);
Printer.EndDoc;
end;
Except this you can get with the TextRect function set of the formatting flags which can help you to specify e.g. text alignment, word wrap etc. For instance if you would like to center the text horizontally in the specified rectangle [100;100], [250;117] you can use the following.
Text := 'Centered text';
TargetRect := Rect(100, 100, 250, 117);
Printer.Canvas.TextRect(TargetRect, Text, [tfCenter]);
Or in your case might be more useful word wrap. Here's an example with rectangle [100;100], [200;134] where the text is automatically wrapped by the TextRect function.
Text := 'This is a very long text';
TargetRect := Rect(100, 100, 200, 134);
Printer.Canvas.TextRect(TargetRect, Text, [tfWordBreak]);
If you use a fixed width font on the canvas, you should get the same result for all single-character strings. If you use a variable width font, each character will return a different width.
Printer.Canvas.Font.Name = 'Courier New';
Printer.Canvas.Font.Size = 13;
ColumnWidth := Printer.Canvas.TextWidth('M');
For different fonts or different font sizes, you will get different results.
I don't see how you're saying it returns text. If it were returning text your code wouldn't even compile, you would be getting errors when you tried to multiply a number by text. You even convert it to a string for display purposes.
Are you being mislead by the fact that with a variable-width font that you'll get different answers for different strings? You can even get different answers for the same letters in a different order. For some fonts "WAM" will produce a different answer than "WMA" because of how the W and A fit together.
Also, you're simply assuming that your labels are narrower than 15 M's. While this is generally the case it's not good programming practice. Instead, you should be asking for the width of each label and using something a bit above the biggest answer.
Finally, your handling of LineHeight is atrocious. Simply add 300 to y if that's what you really want although it should be some multiple of your line height, not a fixed value. You'll get VERY different results from your code off printers with different DPI settings.
Have you even tried stepping through this code with the debugger to see what's going on internally? Your output of the position to the printout suggests you aren't using the debugger.

Resources