Sign in Join
Wiki copytemporary

fcl-pdf: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## About
22
3-The fcl-pdf package contains a [[PDF]] generating unit **fppdf** that does not depend on any external libraries.
3+The fcl-pdf package contains a [[PDF]] generating unit **fppdf** that does not depend on any external libraries.
44
5-The PDF generator has the following features:
5+The PDF generator has the following features:
66
7-- Support for basic shapes.
8-- Support for basic line styles.
9-- Dictionary support.
10-- Multi-page PDF.
11-- Image support.
12-- TTF Font support.
13-- Font embedding.
14-- Unicode font support.
15-- Stream Compression.
16-- Image embedding.
17-- Several paper types.
18-- Portrait/Landscape.
19-- Support for multiple units.
20-- Rotation matrix system.
21-- PDF creator information.
22-- Output validates by several PDF validators.
7+ * Support for basic shapes.
8+ * Support for basic line styles.
9+ * Dictionary support.
10+ * Multi-page PDF.
11+ * Image support.
12+ * TTF Font support.
13+ * Font embedding.
14+ * Unicode font support.
15+ * Stream Compression.
16+ * Image embedding.
17+ * Several paper types.
18+ * Portrait/Landscape.
19+ * Support for multiple units.
20+ * Rotation matrix system.
21+ * PDF creator information.
22+ * Output validates by several PDF validators.
2323
24-Contains units:
24+Contains units:
2525
26-- fppdf
27-- fpparsettf
28-- fpttf
29-- fpttfencodings
26+ * fppdf
27+ * fpparsettf
28+ * fpttf
29+ * fpttfencodings
3030
3131## Examples
3232
3333### Official test project
3434
35-Take a look at the "fcl-pdf/examples/testfppdf.lpr" project included with FPC's source code. It was purposely designed to help explain how to use the fcl-pdf package. Each page of that demo is defined in a separate method to help remove clutter, and explain the usage and functionality in smaller chucks of code.
35+Take a look at the "fcl-pdf/examples/testfppdf.lpr" project included with FPC's source code. It was purposely designed to help explain how to use the fcl-pdf package. Each page of that demo is defined in a separate method to help remove clutter, and explain the usage and functionality in smaller chucks of code.
3636
3737### Writing few lines, without embedded fonts
3838
39-Example by forum member **Moritz**, fixed by member **paweld**. Code disables embedding of fonts into PDF.
40-
41-``` pascal
39+Example by forum member **Moritz** , fixed by member **paweld**. Code disables embedding of fonts into PDF.
40+```
4241procedure TMainForm.TestButtonClick(Sender: TObject);
43-var
44- FontID, FontBoldID: Integer;
45- Document: TPDFDocument;
46- Section: TPDFSection;
47- Page: TPDFPage;
48-begin
49- Document := TPDFDocument.Create(nil);
50- Document.FontDirectory := 'C:\Windows\Fonts';
51- Document.StartDocument;
52- FontID := Document.AddFont('arial.ttf', 'Arial');
53- FontBoldID := Document.AddFont('arialbd.ttf', 'Arial Bold');
54-
55- Document.Options := Document.Options + [poPageOriginAtTop, poNoEmbeddedFonts];
56- Section := Document.Sections.AddSection;
57-
58- Page := Document.Pages.AddPage;
59- Section.AddPage(Page);
60-
61- Page.SetFont(FontID, 11);
62- Page.WriteText(20, 20, 'This is normal text');
63-
64- Page.SetFont(FontBoldID, 11);
65- Page.WriteText(20, 30, 'This is bold text');
66-
67- Document.SaveToFile('output.pdf');
68-end;
42+ var
43+ FontID, FontBoldID: Integer;
44+ Document: TPDFDocument;
45+ Section: TPDFSection;
46+ Page: TPDFPage;
47+ begin
48+ Document := TPDFDocument.Create(nil);
49+ Document.FontDirectory := 'C:\Windows\Fonts';
50+ Document.Options := Document.Options + [poPageOriginAtTop, poNoEmbeddedFonts];
51+ Document.StartDocument;
52+ FontID := Document.AddFont('arial.ttf', 'Arial');
53+ FontBoldID := Document.AddFont('arialbd.ttf', 'Arial Bold');
54+
55+ Section := Document.Sections.AddSection;
56+
57+ Page := Document.Pages.AddPage;
58+ Section.AddPage(Page);
59+
60+ Page.SetFont(FontID, 11);
61+ Page.WriteText(20, 20, 'This is normal text');
62+
63+ Page.SetFont(FontBoldID, 11);
64+ Page.WriteText(20, 30, 'This is bold text');
65+
66+ Document.SaveToFile('output.pdf');
67+ end;
6968```
7069
7170### Enumerate available fonts
7271
73-Example by forum member **paweld**.
74-
75-``` pascal
72+Example by forum member **paweld**.
73+```
7674uses
77- fppdf, fpttf;
78-
79-procedure TForm1.FormCreate(Sender: TObject);
80-var
81- g: TFPFontCacheList;
82- i: Integer;
83-begin
84- g := TFPFontCacheList.Create;
85- g.SearchPath.Add('C:\Windows\Fonts');
86- g.BuildFontCache;
87- for i := 0 to g.Count - 1 do
88- Memo1.Lines.Add(Format('File name: %s > Font name: %s > Family: %s',
89- [g.Items[i].FileName, g.Items[i].HumanFriendlyName, g.Items[i].FamilyName]));
90- g.Free;
91-end;
75+ fppdf, fpttf;
76+
77+ procedure TForm1.FormCreate(Sender: TObject);
78+ var
79+ g: TFPFontCacheList;
80+ i: Integer;
81+ begin
82+ g := TFPFontCacheList.Create;
83+ g.SearchPath.Add('C:\Windows\Fonts');
84+ g.BuildFontCache;
85+ for i := 0 to g.Count - 1 do
86+ Memo1.Lines.Add(Format('File name: %s > Font name: %s > Family: %s',
87+ [g.Items[i].FileName, g.Items[i].HumanFriendlyName, g.Items[i].FamilyName]));
88+ g.Free;
89+ end;
9290```
9391
9492### Render picture to PDF
9593
96-Example by forum member **CynicRus** changed by **AlexTP**. Note: on Ubuntu 20.04, default PDF viewer cannot show the picture correctly, it crops some top part of the picture.
97-
98-``` pascal
94+Example by forum member **CynicRus** changed by **AlexTP**. Note: on Ubuntu 20.04, default PDF viewer cannot show the picture correctly, it crops some top part of the picture.
95+```
9996uses
100- fppdf;
97+ fppdf;
98+
99+ procedure WritePictureToPDF(const APictureFilename, APdfFilename: string);
100+ var
101+ PDF: TPDFDocument;
102+ Page: TPDFPage;
103+ Section: TPDFSection;
104+ Paper: TPDFPaper;
105+ Index: Integer;
106+ W, H: Integer;
107+ begin
108+ PDF := TPDFDocument.Create(nil);
109+ try
110+ PDF.StartDocument;
111+ Section := PDF.Sections.AddSection;
112+
113+ Page := PDF.Pages.AddPage;
114+ Page.Orientation := ppoLandscape;
115+ Page.UnitOfMeasure := uomPixels;
116+
117+ Section.AddPage(Page);
118+
119+ Index := PDF.Images.AddFromFile(APictureFilename);
120+ W := PDF.Images[Index].Width;
121+ H := PDF.Images[Index].Height;
122+ paper.W := W;
123+ paper.H := H;
124+ Page.Paper := Paper;
125+ Page.PaperType := ptCustom;
126+ Page.DrawImageRawSize(0, 0, W, H, Index);
127+
128+ PDF.SaveToFile(APdfFilename);
129+ finally
130+ PDF.Free;
131+ end;
132+ end;
133+```
101134
102-procedure WritePictureToPDF(const APictureFilename, APdfFilename: string);
103-var
104- PDF: TPDFDocument;
105- Page: TPDFPage;
106- Section: TPDFSection;
107- Paper: TPDFPaper;
108- Index: Integer;
109- W, H: Integer;
110-begin
111- PDF := TPDFDocument.Create(nil);
112- try
113- PDF.StartDocument;
114- Section := PDF.Sections.AddSection;
135+## PDFs and Fonts
115136
116- Page := PDF.Pages.AddPage;
117- Page.Orientation := ppoLandscape;
118- Page.UnitOfMeasure := uomPixels;
137+**A draft, a work in progress** so criticize, hack away, as you see fit ! dbannon.
119138
120- Section.AddPage(Page);
139+A practical system to produce PDFs will have to cope with text and text means fonts. While the PDF engine is in the fppdf unit, including fpttf will give you access to the gTTFontCache, a tool that loads data about the fonts you will be using. The Cache will tell you the name and path to the actual font file and will give you dimensions of the specific text you are putting into a PDF, essential information as you adjust line spacing and width.
121140
122- Index := PDF.Images.AddFromFile(APictureFilename);
123- W := PDF.Images[Index].Width;
124- H := PDF.Images[Index].Height;
125- paper.W := W;
126- paper.H := H;
127- Page.Paper := Paper;
128- Page.PaperType := ptCustom;
129- Page.DrawImageRawSize(0, 0, W, H, Index);
141+The PDF and Font Cache has had substantial developments, especially for Windows, after the release of of FPC3.2.2 so, probably a good idea to consider FPC-fixes as of mid 2024. But even then, there are some limitations, chief being that it cannot handle the newer font file format, 'collections' typically with an extension of .ttc (True Type Collection) or .otc (Open Type Collection). The cache, when scanning for font files, will ignore .ttc (and, perhaps .otc) but if you offer the PDF engine a .ttc file you will, later, cause an exception. Better to take the Cache's advice, skip .ttc.
130142
131- PDF.SaveToFile(APdfFilename);
132- finally
133- PDF.Free;
134- end;
135-end;
143+There are a set of "standard PDF" fonts, Helvetica, Courier, Times-Roman that are expected to be available in any PDF Viewer and are therefore not needed to be 'embedded' in the document. However, these fonts have problems with some non-Latin or accented characters and, apparently may not be usable at all on some localized systems, particularly Asian system. Better, by far to find appropriate available .ttf or .otf fonts and embed them. To embed fonts is the default option in fpPDF.
144+
145+Options, there is an option, poSubsetFont, who's purpose is unclear to this author that causes the DPF engine to crash when used with .otf fonts. And, interesting, with some Chinese .ttf fonts. Some pundits suggest that there are OpenTypeFonts packaged with .otc extensions, maybe, maybe not. In either case, leave that particular option out, does no apparent good, definite harm.
146+
147+### gTTFontCache
148+
149+As mentioned above, gTTFontCache is brought into your application by including the fpttf unit. It does not need to be created (or freed) but does need a touch of setup and some initialization. Because its global, this only needs be done once for the app's run time, even if called repeatably (as long as the availability of fonts does not change during the run). Your code may look like this -
136150```
151+const HaveReadFonts : boolean = false;
152+ ...
153+ ...
154+ if not HaveReadFonts then begin
155+ {$if defined(CPU32) and defined(LINUX)}
156+ gTTFontCache.SearchPath.Add('/usr/share/fonts/'); // Avoids a problem noted on 32bit linux where
157+ gTTFontCache.BuildFontCache; // libfontconfig returns a nil pointer to font.cfg file
158+ {$else}
159+ {$ifdef WINDOWS}
160+ gTTFontCache.SearchPath.Add('./'); // CHECK DAVO Also look for fonts where binary lives on Windows
161+ {$endif}
162+ gTTFontCache.ReadStandardFonts;
163+ {$endif}
164+ HaveReadFonts := True;
165+ end;
166+```
137167
168+The Font Cache looks for fonts in the appropriate places for each operating system but you can add to that list with the gTTFontCache.SearchPath.add() line. On Linux for example, it also looks in $HOME/.fonts, a great place for a user to add their own, perhaps short term fonts.
169+
170+Its possible (but a bit slow) to iterate over the Cache's internal list once populated by the ReadStandardFonts;, see above. What is really useful is how you can find what you need by declaring a pointer to a Cache Item and using the Cache's Find() command -
171+```
172+var
173+ CachedFont : TFPFontCacheItem;
174+ DescenderH : single;
175+ ...
176+ ...
177+ CachedFont := gTTFontCache.Find('Noto Sans CJK SC', Bold, Italic); // Bold and Italic are booleans
178+ if Assigned(CachedFont) then begin
179+ writeln('Width of ABC in a PDF at 12pt is ', CachedFont.TextWidth('ABC', 12));
180+ writeln('Height of above is ', CachedFont.TextHeight('ABC', 12, DescenderH))
181+ end else writeln('Sorry, don''t seem to have that font.');
182+```
183+
184+That is key information if you are "flowing" text into a PDF, it tells you both the vertical and horizontal information in millimeters of each "word", you keep running totals to to tell you when to start a new line and how much further down the page to put it. You will have to deal with some offset issues if changing font sizes but it is not difficult.
185+
186+### Flowing Text into a PDF
187+
188+You may have a quantity of text that you need to write to a PDF. Maybe that text is 'marked up', has some large fonts, some small, some monospaced etc. Your code must calculate where each line goes and, in particular, how many words you can fit into each line. And, obviously, you need to start a new page when you get to the bottom of the existing one. Here is one approach !
189+
190+I work by breaking the content up into (text)words, I copy the content into a list of records, each record has a word of text, a font type, name, size and bold or italic. Then while writing a line of text, I add up the width of each word I consider until I get to the desired width for the line. The method that calculates the width also calculates the height and I keep a running max-height for that line.
191+
192+When I know how much text I can fit into the line, I then calculate the y point to start writing that text to the PDF.
193+
194+If all your text is the same size, its a bit easier but you still need to work out how many words fit into each line as you process the content. This system will also work for right aligned text, but slightly different calculation. Right and Left justified is a bit harder, you need to calculate how much space would be left at the end of a line and distribute that extra across the line.
195+
196+See <https://github.com/tomboy-notes/tomboy-ng/blob/master/source/kmemo2pdf.pas> for a specific example.
197+
138198## See also
139199
140-- [[Package List]]
200+ * [[Package List]]
141201
142202---
143-*From the Free Pascal wiki, page "fcl-pdf", as it stood on 2023-08-16 after a change by Alextpp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
144-Category: PDF
145-Category: fcl-pdf
203+*From the Free Pascal wiki, page "fcl-pdf", as the Internet Archive kept it on 2025-02-11, last changed on 2024-10-06. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history