Painting text lazarus / 39-Graphics ---------------------------------------------------------------------- SaraT · 2024-04-11 16:14 Hello coders I would like to paint text with word wrap like the attached image, rounded rect, background and border color. How can I do this on a PaintBox control? Or please give a better idea. Thanks a lot. ---------------------------------------------------------------------- KodeZwerg · 2024-04-11 16:46 I would create a TBitmap on the fly, WordWrap I would realize by calculating via "theBitmap.Canvas.TextWidth(AText)" and to know where the new line starts use "theBitmap.Canvas.TextHeight(AText)" do not forget to add some space between words, borders, new line etc... Rounded corners are more challenging due the fact that an image is a 2D rectangle, anyway you could start experimenting with "theBitmap.Canvas.LineTo()" to draw "from x to y" a line for all borders with a hole/space between. One thing you should have in mind about the round corners, they are visible as a rectangle (eg background color from bitmap is visible) When the bitmap is prepared, assign it to the control you need it. ---------------------------------------------------------------------- SaraT · 2024-04-11 17:31 Quote from: KodeZwerg on April 11, 2024, 06:46:08 pmI would create a TBitmap on the fly... Thanks for the information, but I posted in the forum because I have no idea of how to paint anything on PaintBox control / canvas. So please, if anybody has a working code and share with us, I would be so thankful. Regards! ---------------------------------------------------------------------- paweld · 2024-04-11 17:52 Here are some examples: https://lazplanet.gitlab.io/2013/06/how-to- draw-text-on-canvas.html ---------------------------------------------------------------------- TRon · 2024-04-11 17:54 TextRect can be used to wrap text automatically. See also developing with graphics for information on graphics and besides that there are plenty enough examples distributed with Lazarus. ---------------------------------------------------------------------- Handoko · 2024-04-11 18:49 Draw the rounded corners That's easy. I done it, OpenGL version. KodeZwerg already explained the basic. But forget to mention, you need have good understanding in trigonometry, to calculate the X-Y positions. The easier solution is, use TCanvas.Arc command: https://lazarus-ccr.sourceforge.io/docs/lcl/graphics/tcanvas.arc.html Word-wrapping the text Just as TRon said, TextRect has word wrap feature: https://lazarus- ccr.sourceforge.io/docs/lcl/graphics/tcanvas.textrect.html Alternatively, we can use TextWidth and TextHeight and do the calculation manually, just as KodeZwerg said. Draw the dash lines You just need to set the Canvas.Pen.Style: https://lazarus-ccr.sourceforge.io/docs/lcl/graphics/tpen.style.html The problem is when all them need to be done together, that will be not so easy. ---------------------------------------------------------------------- TRon · 2024-04-11 19:05 Quote from: Handoko on April 11, 2024, 08:49:27 pmDraw the rounded corners That's easy. I done it, OpenGL version. KodeZwerg already explained the basic. But forget to mention, you need have good understanding in trigonometry, to calculate the X-Y positions. The easier solution is, use TCanvas.Arc command: https://lazarus-ccr.sourceforge.io/docs/lcl/graphics/tcanvas.arc.html Alternatively you could use RoundRect QuoteThe problem is when all them need to be done together, that will be not so easy. Not too difficult either  3 canvas calls in onpaint event should be able to do it (I could be a couple of calls off because of setting the correct penstyle/brush/font). ---------------------------------------------------------------------- Handoko · 2024-04-11 19:20 You're right. It is easier than I thought.  Done: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin  Canvas.Pen.Style   := psDash;  Canvas.Pen.Width   := 3;  Canvas.Pen.Color   := clGreen;  Canvas.Brush.Color := clYellow;  Canvas.RoundRect(50, 50 , 250, 150, 50, 50);end; For the word wrapping part, I will let others to do it. It is midnight here, I am going to sleep now. Good night and have fun! ---------------------------------------------------------------------- KodeZwerg · 2024-04-11 19:42 Quote from: SaraT on April 11, 2024, 07:31:51 pmThanks for the information, but I posted in the forum because I have no idea of how to paint anything on PaintBox control / canvas.You are welcomed, it sounded a bit bit different in your first post. I am no magician that knows your skills. Quote from: SaraT on April 11, 2024, 06:14:03 pmOr please give a better idea.I was replying to that statement. Since others showed how to do the roundings .... here is my way about how to wordwrap and put text on a paintbox control, with easy adjustments you can use it wherever. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure WordWrappedImage(var APaintBox: TPaintBox; const AFont: TFont; const AText: AnsiString; const ABackground: TColor);var  bmp: TBitmap;  sa: TStringArray;  s: string;  i: Integer;  PosY, Padding: Integer;begin  // create a bitmap on the fly  bmp := TBitmap.Create;  try    // set size to paintbox    bmp.SetSize(APaintBox.Width, APaintBox.Height);  // set background color    bmp.Canvas.Brush.Color := ABackground;    // fill image with color    bmp.Canvas.FillRect(bmp.Canvas.ClipRect);    // set font, including color and any set style(s)    bmp.Canvas.Font := AFont;     // space that stay free around text    Padding := 5;    // vertical counter    PosY := Padding;    // temporal string    s := '';    // convert the text to an array of words by splitting them at spaces or lineendings    sa := AText.Split([' ', #10, #13]);    // draw text wordwrapped on image    // this method fails if a word is bigger than the image got space    for i := Low(sa) to High(sa) do    begin        // assign some word to draw for a new line        if s = '' then          s := sa[i]        else          // or begin testing if the collection of words are fitting into the image          begin          // add more words to current line            if (bmp.Canvas.TextWidth(s + ' ' + sa[i]) < (bmp.Width - Padding)) then            s := s + ' ' + sa[i]            else              begin              // draw current line                bmp.Canvas.TextOut(Padding, PosY, s);                Inc(PosY, bmp.Canvas.TextHeight(s));                // check if we are in bounds of image                if ((PosY + bmp.Canvas.TextHeight(s)) > (bmp.Height - Padding)) then                  begin                  s := '';                    break;                  end;              // assign a new word for the next line                s := sa[i];            end;          end;        // if it is just one line, draw it        if (s <> '') then          bmp.Canvas.TextOut(Padding, PosY, s);      end;    // give the prepared image to the paintbox    APaintBox.Canvas.Draw(0, 0, bmp);  finally    bmp.Free;  end;end;In attachment you see my test setup. Here is the code for the button: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin  WordWrappedImage(PaintBox1, Memo1.Font, Memo1.Text, clSkyBlue);end; ---------------------------------------------------------------------- KodeZwerg · 2024-04-12 12:16 Updated my way of doing, included Handokos suggestion and added a X-center option. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure WordWrappedImage(var APaintBox: TPaintBox; const AFont: TFont; const AText: AnsiString; const ABackground, ABorder, ATransparent: TColor; const ACentered: Boolean = False);var  bmp: TBitmap;  sa: TStringArray;  s: string;  i: Integer;  PosY, Padding: Integer;begin  // create a bitmap on the fly  bmp := TBitmap.Create;  try    // set size to paintbox    bmp.SetSize(APaintBox.Width, APaintBox.Height);  // set fake transparent color    bmp.Canvas.Brush.Color := ATransparent;    // fill image with above fake color so we dont have "black" corners    bmp.Canvas.FillRect(bmp.Canvas.ClipRect);     // set real background color and draw rounded corner style like Handoko showed, just adjusted for this example    bmp.Canvas.Brush.Color := ABackground;    bmp.Canvas.Pen.Style   := psDash;    bmp.Canvas.Pen.Width   := 1; // i prefer a small line around, adjust to your needs    bmp.Canvas.Pen.Color   := ABorder;    bmp.Canvas.RoundRect(bmp.Canvas.ClipRect, 15, 15); // adjust the numbers, higher values = bigger corner angle, you can also play with two different values like 15 and 30 to get a different shaped corner     // set font, including color and any set style(s)    bmp.Canvas.Font := AFont;     // space that stay free around text    Padding := 5;    // vertical counter    PosY := Padding;    // temporal string    s := '';    // convert the text to an array of words by splitting them at spaces or lineendings    sa := AText.Split([' ', #10, #13]);    // draw text wordwrapped on image    // this method fails if a word is bigger than the image got space    for i := Low(sa) to High(sa) do    begin        // assign some word to draw for a new line        if s = '' then          s := sa[i]        else          // or begin testing if the collection of words are fitting into the image          begin          // add more words to current line            if (bmp.Canvas.TextWidth(s + ' ' + sa[i]) < (bmp.Width - Padding)) then            s := s + ' ' + sa[i]            else              begin              // draw current line                if ACentered then                bmp.Canvas.TextOut((bmp.Width - bmp.Canvas.TextWidth(s)) div 2, PosY, s)                else                  bmp.Canvas.TextOut(Padding, PosY, s);                Inc(PosY, bmp.Canvas.TextHeight(s));                // check if we are in bounds of image                // everything that not fit anymore will be skipped                if ((PosY + bmp.Canvas.TextHeight(s)) > (bmp.Height - Padding)) then                  break;                // assign the last word that not fitted for the next line                s := sa[i];              end;          end;        // if it is just one line, draw it        if (s <> '') then          if ACentered then            bmp.Canvas.TextOut((bmp.Width - bmp.Canvas.TextWidth(s)) div 2, PosY, s)          else            bmp.Canvas.TextOut(Padding, PosY, s);      end;     // give the prepared image to the paintbox    APaintBox.Canvas.Draw(0, 0, bmp);  finally    bmp.Free;  end;end;Example call: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin  WordWrappedImage(PaintBox1, Memo1.Font, Memo1.Text, clSkyBlue, clBlue, clBtnFace, True);end; ---------------------------------------------------------------------- SaraT · 2024-05-18 06:13 Hi guys!! I would like to paint and bold a word only with the above code. How can I do this? Lets say to bold the first word "Lorem ipsum..." in your example. Please and thanks a lot!