#1 2018-06-05 23:32:07

oliver
Member
Registered: 2018-06-05
Posts: 5

Beginner questions about making a text box ((Multiline)TextRect?)

Hello

I'm trying to create a program, that basically allows the user just to fill in the blanks and receive a fully formatted .pdf file. The program I've done is much longer but I made a shortened version to illustrate the biggest problem this far.

Basically I want to create a text box with certain size and position that draws the text of TMemo.text and continues the same text on a new line whenever the length of the input text reaches its set limit. Also the whole text would be justified inside the given area.

I searched the whole forum for examples and I've tried MultilineTextRect and some other alternatives but sadly unsuccessfully. Since I'm a beginner then I would be extremely glad, if anyone could be of any help on my search for the solution.

The code part is here:

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, ShellAPI;

type
  TForm2 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses synPDF;

var filename : string;
    pdf  :TPdfDocumentGDI;
    page :TPdfPage;
    Rect1 :TRect;

procedure TForm2.Button1Click(Sender: TObject);  //Clicking the button saves all the formatted text to example.pdf file in the programs directory and opens the same pdf file automatically

begin
  filename := extractfilepath(paramstr(0))+'example.pdf';
  pdf.Canvas.BeginText;
  Rect1 := TRect.Create(20,20,30,16); // I don't even know, if the rectangle is needed here, because the parameters don't seem to change the outcome of the text in any way.
  pdf.VCLCanvas.Font.Name := 'Times';
  pdf.VCLCanvas.Font.Size := 14;
  pdf.VCLCanvas.TextHeight(Edit1.Text);
  pdf.VCLCanvas.TextWidth(Edit1.Text);
  pdf.VCLCanvas.FrameRect(Rect1);
  pdf.VCLCanvas.TextRect(Rect1, ((page.PageWidth - pdf.VCLCanvas.TextWidth(Edit1.Text)) div 2), 250, Edit1.Text);  // A solution the title can always stay in the middle of the page.

  pdf.VCLCanvas.TextRect(Rect1, 48 , 300, Memo1.Text); // THE BIGGEST QUESTION - This is the text I want to be in a certain box with certain text alignment etc. ~2 cm from the left edge of page.

  pdf.Canvas.EndText;
  try
    pdf.SaveToFile(filename);
  finally pdf.Free;
    Shellexecute(Handle,'open',pchar(filename),'','', SW_Normal);
  end;
end;


procedure TForm2.FormCreate(Sender: TObject); 
begin
  pdf:=TPdfDocumentGDI.Create();
  filename := extractfilepath(paramstr(0))+'test.pdf';
  pdf.ScreenLogPixels   := 72;
  page := pdf.AddPage;
  page.PageLandscape := false;
  pdf.DefaultPaperSize := psA4;
end;

end. 

I thought that maybe one and also a quite simple-minded solution to the problem would be something like this, but I couldn't even finish this ones "else section". Basically the logical operation would draw the text, if it was short enough (until the 48 pixels from the left edge of the paper) or else detect the nearest spacebar before the edge of the box and then the program starts a new line from that place. Also the text would be justified, so the left edge looks straight.

if pdf.VCLCanvas.TextWidth(Memo1.Text) < page.PageWidth-48
  then
  pdf.VCLCanvas.TextRect(Rect2, 48 , 300, Memo1.Text)

  else
  pdf.VCLCanvas.TextRect(Rect2, 48 , 300, wraptext(Memo1.Text, #13#10, ['a'], 10));

The .pdf outcome at the moment is like this. The title is where it should be, but the text goes far over the edge: https://ufile.io/ydqmo
The .zip file with all the Delphi Pascal files can be found on the following link: https://ufile.io/yj59n
Just open Project3.dproj and you see the whole program.

I'm pretty sure that I have more questions coming up but I need to solve this problem first. When I get the thing ready I can upload it here also, so people could have an example.

All the best
Oliver

Last edited by oliver (2018-06-05 23:34:26)

Offline

#2 2018-06-07 07:01:58

Nouba
Member
Registered: 2015-10-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

I hope I understood your problem correctly. With an appropriate resolution of the canvas (only whole numbers can be used here), page margins can be set relatively precisely. The following applies: the higher the resolution, the more precisely it can be positioned.

After you have filled a TRect with appropriate values, you can set the TTextStyle properties of the canvas or use the overloaded method with the TTextStyle argument in TextOut. Unfortunately I only have Delphi 2007 available and therefore depend on the direct API method DrawText.

In your code I have set all four margins of the PDF to 20 mm and output the headline centered. With 2 mm distance the text follows from Memo1.

You can play around with the left and right margins, because the page can take up a little more text than fits into one line in the memo control.

implementation

{$R *.dfm}

uses synPDF;

var
  filename: string;
  pdf: TPdfDocumentGDI;
  page: TPdfPage;
  rect1: TRect;

procedure TForm2.Button1Click(Sender: TObject);
const
  // if you need a finer canvas resolution, use a factor to multiply with
  Factor = 2
  Resolution = 72 * Factor;
  OneMM = Resolution / 25.4; // = 1 mm
begin
  filename := ExtractFilePath(ParamStr(0)) + 'example.pdf';

  pdf := TPdfDocumentGDI.Create();
  try
    pdf.ScreenLogPixels := Resolution;
    page := pdf.AddPage;

    with pdf.VCLCanvas do begin
      Font.Name := 'Times';
      Font.Size := 14 * Factor;

      TextOut((pdf.VCLCanvasSize.cx - TextWidth(Edit1.Text)) div 2,
               Round(20 * OneMM), Edit1.Text);

      with Rect1 do begin
        Left   := Round(20 * OneMM);
        Top    := Round(22 * OneMM + TextHeight(Edit1.Text));
        Right  := pdf.VCLCanvasSize.cx - Round(20 * OneMM);
        Bottom := pdf.VCLCanvasSize.cy - Round(20 * OneMM);
      end;
      Font.Size := 12 * Factor;
      // My Delphi 2007 has no TTextStyle
      DrawText(Handle, PAnsiChar(Memo1.Text), Length(Memo1.Text),
               Rect1, DT_LEFT or DT_WORDBREAK); Text
    end;

    pdf.SaveToFile(filename);
    Shellexecute(Handle, 'open', PChar(filename), '', '', SW_Normal);
  finally
    pdf.Free;
  end;
end;

end.

Last edited by Nouba (2018-06-07 09:37:15)

Offline

#3 2018-06-07 09:15:19

oliver
Member
Registered: 2018-06-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

Thank you very much for your reply. This code works good although I have a small problem with it (maybe it's the compilers fault, I'm using Rad Studio).

The input text of Memo1 doesn't appear correctly in the .pdf file, instead it consists of random symbols and it also displays a error by Acrobat reader. Do you have any idea about that?

IgL4RpZ.jpg

all the best
Oliver

Offline

#4 2018-06-07 12:24:28

Nouba
Member
Registered: 2015-10-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

I'm also a blind fish sometimes. Of course there is already TTextFormat in Delphi 2007, which is preferable to an API call. Since you probably have Unicode in your memo, DrawTextW should at least be used.

      // lText is a local variable of type string
      lText := Memo1.Lines; // because the 2nd arg is var Text: string
      TextRect(Rect1, lText, [tfLeft, tfWordBreak]);

Last edited by Nouba (2018-06-07 12:25:34)

Offline

#5 2018-06-07 15:33:43

oliver
Member
Registered: 2018-06-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

Thanks! I got both of your suggestions to work after some small changes:

The one in your first post:

 DrawText(Handle, AnsiString(lText), Length(lText),
               Rect1, DT_Center or DT_WORDBREAK); Text 

The one in your second post:
     

lText := Memo1.Lines.Text;  // because the 2nd arg is var Text: string
 TextRect(Rect1, lText, [tfBottom, tfWordBreak]); 

My next question is, that is there any way to make the text justified? Values like DT_JUSTIFIED of tfJUSTIFIED sadly don't exist and I'm unsure, that if I used some other libraries (like mORMotReport) to stylize the text the whole thing would work.

Offline

#6 2018-06-07 17:07:50

Nouba
Member
Registered: 2015-10-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

Glad it works.

There is a blog Drawing fully justified text to a canvas that might be able to help you with your request.

Offline

#7 2018-06-08 13:47:18

oliver
Member
Registered: 2018-06-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

Sorry to bother again, but I still cannot get it to work. I managed to get the justified text right but the outcome looks like this:

j6L7F1z.jpg.

It seems as if the drawtext using the justified method somehow doubles the text output on the canvas. One of the texts is completely normal (with two #13#10's before) but the second text (don't know why its appearing) is repeating itself on the first line and some kind of gibberish appears there which I don't want.

I hope I'm not turning to the forums with an OT problem. Personally I am suspicious about the DrawTextJustified line where there is both pdf.VCLCanvas and rect1 in the same brackets and they might be somehow controversial. Maybe it's because there is the programs original rectangle code used but the pdf.VCLCanvas is from another library.

I also created a small "debug" line, which assures me, that the problem must be in that line.

Here is the full code:

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, ShellAPI, System.Types,
  Vcl.ComCtrls;

type
    TForm2 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    //procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses synPDF, JustifiedDrawText;

var
  filename: string;
  pdf: TPdfDocumentGDI;
  tpdf : TPdfDocument;
  page: TPdfPage;


procedure TForm2.Button1Click(Sender: TObject);
      var
      lText: string;
      rect1: TRect;

const
  // if you need a finer canvas resolution, use a factor to multiply with
  Factor = 2;
  Resolution = 72 * Factor;
  OneMM = Resolution / 25.4; // = 1 mm

begin
  filename := ExtractFilePath(ParamStr(0)) + 'example.pdf';

  pdf := TPdfDocumentGDI.Create();
  try
    pdf.ScreenLogPixels := Resolution;
    page := pdf.AddPage;

    with pdf.VCLCanvas do begin
      Font.Name := 'Times';
      Font.Size := 14 * Factor;

      TextOut((pdf.VCLCanvasSize.cx - TextWidth(Edit1.Text)) div 2, Round(20 * OneMM), Edit1.Text);

      with Rect1 do begin
        Left   := Round(20 * OneMM);
        Top    := Round(22 * OneMM + TextHeight(Edit1.Text));
        Right  := pdf.VCLCanvasSize.cx - Round(20 * OneMM);
        Bottom := pdf.VCLCanvasSize.cy - Round(20 * OneMM);
      end;


      Font.Size := 12 * Factor;
      lText := Memo1.Text;

      DrawTextJustified( pdf.VCLCanvas,  #13#10+#13#10+(lText), rect1,  []); // The problem lies somewhere here


//      DrawText(Handle, AnsiString(lText), Length(lText),
//               Rect1, DT_Center or DT_WORDBREAK); Text

     end;

    pdf.SaveToFile(filename);
    Shellexecute(Handle, 'open', PChar(filename), '', '', SW_Normal);


        //Debugger
        Application.Initialize;
        Application.CreateForm(TForm2, Form2);
        AllocConsole;
        writeln(lText);
        Application.Run;

  finally
    pdf.Free;
  end;
end;

end.

all the best
Oliver

Offline

#8 2018-06-10 08:40:04

Nouba
Member
Registered: 2015-10-05
Posts: 5

Re: Beginner questions about making a text box ((Multiline)TextRect?)

Oliver, unfortunately I can't test the code with Delphi 2007 myself. However, it may be that the implementation in the link has minor flaws that lead to the first line being overwritten.

I found a similar approach in the Lazarus forum Canvas Text Justified left AND right. Additionally, you must include VCLCanvas in the procedure. A simple Delphi Implementation of RPosEx can be found here or has to be written by yourself.

ln2n9Bw.png

With Hyphenation in Delphi, what AFAIK currently only works with versions <= 2007, results can certainly be improved.

Last edited by Nouba (2018-06-10 08:48:17)

Offline

Board footer

Powered by FluxBB