[Lazarus] PDF generator: please test

Ondrej Pokorny lazarus at kluug.net
Sat Apr 9 10:09:47 CEST 2016


I started testing.

1. Why does TFPFontCacheItem.GetFontData create and load TTFFileInfo 
every time it is called? As a result TTFFileInfo is created and loaded 
every time TextWidth is called - there's no way to go around this. The 
TTFFileInfo should be cached IMO.
Please consider the attached patch.

2. Is gTTFontCache/uFontCacheList really needed? fcl-pdf doesn't use it. 
The user should create such a variable by himself, IMO.

3. WriteText in combination with custom TTF font doesn't print anything. 
There should be an exception when calling WriteText with custom TTF font 
or the call should be redirected to WriteUTF8Text automatically. (See 
demo code below.)

4. I cannot make TFPFontCacheItem.TextWidth work. (I was able to make 
"TextWidth" work, though.) Could you please check the code below to see 
what I am doing wrong? I want to draw a rectangle around text. (I just 
added a procedure to the example project):

procedure TPDFTestApp.TextBox(D: TPDFDocument; APage: integer);
var
   P : TPDFPage;
   FtTitle: integer;
   xFontCache: TFPFontCacheList;
   xFont: TFPFontCacheItem;
   xWidth, xHeight, xDesc: Extended;
const
   cFontSize = 50;
begin
   P := D.Pages[APage];

   FtTitle := D.AddFont('FreeSans.ttf', 'FreeSans', clGreen);

   P.SetFont(FtTitle, cFontSize);
   P.SetColor(clBlack, false);
   P.WriteText(25, 0, 'Sample Text'); // DOESN'T WORK !!!
   P.WriteUTF8Text(25, 20, 'Sample Text');

   xFontCache := TFPFontCacheList.Create;
   try
     xFont := TFPFontCacheItem.Create('fonts\FreeSans.ttf');
     xFontCache.Add(xFont);
     xWidth := xFont.TextWidth('Sample Text', cFontSize) * 25.4 / 72; // 
25.4 / 72 = conversion PDFTomm (?)
     xHeight := xFont.GetFontData.CapHeight * cFontSize * xFontCache.DPI 
/ (72 * xFont.GetFontData.Head.UnitsPerEm) * 25.4 / 72;
     xDesc := xFont.GetFontData.Descender * cFontSize * xFontCache.DPI / 
(72 * xFont.GetFontData.Head.UnitsPerEm) * 25.4 / 72;
   finally
     xFontCache.Free;
   end;

   P.SetColor(clRed, true);
   P.SetColor($37b344, false); // some green color
   P.SetPenStyle(ppsDashDot);
   P.DrawRect(25, 20-xDesc, xWidth, xHeight, 1, False, True);
end;

Ondrej
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20160409/17e59e8b/attachment-0003.html>
-------------- next part --------------
Index: fpttf.pp
===================================================================
--- fpttf.pp	(revision 33453)
+++ fpttf.pp	(working copy)
@@ -43,11 +43,13 @@
     FFamilyName: String;
     FFileName: String;
     FStyleFlags: LongWord;
+    FFileInfo: TTFFileInfo;
     FOwner: TFPFontCacheList; // reference to FontCacheList that owns this instance
     function    GetIsBold: boolean;
     function    GetIsFixedWidth: boolean;
     function    GetIsItalic: boolean;
     function    GetIsRegular: boolean;
+    procedure   SetFileName(const AFileName: String);
     procedure   SetIsBold(AValue: boolean);
     procedure   SetIsFixedWidth(AValue: boolean);
     procedure   SetIsItalic(AValue: boolean);
@@ -54,11 +56,12 @@
     procedure   SetIsRegular(AValue: boolean);
   public
     constructor Create(const AFilename: String);
-    { Returns the actual TTF font file information. Caller needs to free the returned instance. }
+    destructor  Destroy; override;
+    { Returns the actual TTF font file information. }
     function    GetFontData: TTFFileInfo;
     { Result is in pixels }
     function    TextWidth(AStr: string; APointSize: single): single;
-    property    FileName: String read FFileName write FFileName;
+    property    FileName: String read FFileName write SetFileName;
     property    FamilyName: String read FFamilyName write FFamilyName;
     { A bitmasked value describing the full font style }
     property    StyleFlags: LongWord read FStyleFlags write FStyleFlags;
@@ -147,6 +150,14 @@
   Result := (FStyleFlags and FP_FONT_STYLE_REGULAR) <> 0;
 end;
 
+procedure TFPFontCacheItem.SetFileName(const AFileName: String);
+begin
+  if FFileName = AFileName then Exit;
+  FFileName := AFileName;
+  if FFileInfo<>nil then
+    FreeAndNil(FFileInfo);
+end;
+
 procedure TFPFontCacheItem.SetIsBold(AValue: boolean);
 begin
   if AValue then
@@ -192,14 +203,25 @@
   FStyleFlags := FP_FONT_STYLE_REGULAR;
 end;
 
+destructor TFPFontCacheItem.Destroy;
+begin
+  FFileInfo.Free;
+
+  inherited Destroy;
+end;
+
 function TFPFontCacheItem.GetFontData: TTFFileInfo;
 begin
+  if FFileInfo <> nil then
+    Exit(FFileInfo);
+
   if FileName = '' then
     raise ETTF.Create(rsNoFontFileName);
   if FileExists(FileName) then
   begin
-    Result := TTFFileInfo.Create;
-    Result.LoadFromFile(FileName);
+    FFileInfo := TTFFileInfo.Create;
+    FFileInfo.LoadFromFile(FileName);
+    Result := FFileInfo;
   end
   else
     Result := nil;
@@ -262,25 +284,21 @@
     sl.Free;
   {$ENDIF}
 
-  try
-    lWidth := 0;
-    for i := 1 to Length(AStr) do
-    begin
-      c := AStr[i];
-      lGIndex := lFntInfo.GetGlyphIndex(Ord(c));
-      lWidth := lWidth + lFntInfo.GetAdvanceWidth(lGIndex);
-    end;
+  lWidth := 0;
+  for i := 1 to Length(AStr) do
+  begin
+    c := AStr[i];
+    lGIndex := lFntInfo.GetGlyphIndex(Ord(c));
+    lWidth := lWidth + lFntInfo.GetAdvanceWidth(lGIndex);
+  end;
 
-    if APointSize = 0.0 then
-      Result := lWidth
-    else
-    begin
-      { Converting Font Units to Pixels. The formula is:
-        pixels = glyph_units * pointSize * resolution / ( 72 points per inch * THead.UnitsPerEm )  }
-      Result := lWidth * APointSize * FOwner.DPI / (72 * lFntInfo.Head.UnitsPerEm);
-    end;
-  finally
-    lFntInfo.Free;
+  if APointSize = 0.0 then
+    Result := lWidth
+  else
+  begin
+    { Converting Font Units to Pixels. The formula is:
+      pixels = glyph_units * pointSize * resolution / ( 72 points per inch * THead.UnitsPerEm )  }
+    Result := lWidth * APointSize * FOwner.DPI / (72 * lFntInfo.Head.UnitsPerEm);
   end;
 end;
 


More information about the Lazarus mailing list