You are not logged in.
Is it possible for SynPdf to use fonts which are added with AddFontMemResourceEx from a resource in memory?
For example, fonts loaded with the following snippet in Delphi are not used in TPdfDocumentGDI.
const
C39CodeName = 'Code 3 de 9';
var
hC39FontRes: Cardinal;
function CheckC39: Boolean;
var
ResS1: TResourceStream;
FontCount1: Cardinal;
FontId: Integer;
begin
Result := true;
FontId := Screen.Fonts.IndexOf(C39CodeName);
if FontId > 0 then exit;
if hC39FontRes > 0 then exit;
Result := false;
FontCount1 := 0;
try
ResS1 := TResourceStream.Create(hInstance, 'C39_FONT', 'RT_FONT');
try
if ResS1.Size > 14 then
hC39FontRes := AddFontMemResourceEx(ResS1.Memory, ResS1.Size, nil, @FontCount1);
finally
ResS1.Free;
Result := (FontCount1 = 1);
end;
except
on E: Exception do ; // ShowException(E, 'Error loading C39font');
end;
end;
Using this code:
procedure MakePdf;
var
FileTemp: string;
Doc: TPdfDocumentGDI;
Page: TPdfPage;
begin
FileTemp := 'C:\Temp\Test.pdf';
Doc := TPdfDocumentGDI.Create;
try
Doc.EmbeddedTTF := true;
Doc.EmbeddedTTFIgnore.Text := MSWINDOWS_DEFAULT_FONTS;
Doc.EmbeddedWholeTTF := true;
Doc.Root.PageLayout := plSinglePage;
Doc.NewDoc;
Page := Doc.AddPage;
Doc.VCLCanvas.TextOut(100, 100, 'Test1');
Doc.VCLCanvas.TextOut(200, 200, 'Test2');
Doc.VCLCanvas.TextOut(300, 300, 'Test3');
if CheckC39 then
begin
Doc.VCLCanvas.Font.Name := C39CodeName;
Doc.VCLCanvas.Font.size := 24;
Doc.VCLCanvas.TextOut(400, 400, '*123456789*');
end;
Doc.VCLCanvas.Font.Name := 'Segoe Script';
Doc.VCLCanvas.Font.size := 14;
Doc.VCLCanvas.TextOut(500, 500, 'Hello World');
Doc.SaveToFile(FileTemp);
finally
Doc.Free;
end;
end;
This works fine if I use a printer-based PDF creator.
For SynPdf LucidaSansUnicode is used and embedded.
If it possible to make this work in SynPdf?
Edit: Sidenote: Using AddFontResource() just before TPdfDocumentGDI.Create to include a font from a temp-file does work.
Doing the AddFontMemResourceEx() before the TPdfDocumentGDI.Create also doesn't work.
Last edited by rvk (2022-04-25 09:16:18)
Offline
Is it a true type font?
Could you try to debug and see why TPdfFontTrueType does not include the font, maybe in TPdfCanvas.SetFont?
Is the font available in fTrueTypeFonts[] as generated in TPdfDocument.Create? Is it appearing in the EnumFontsProcW callback?
Offline
Yes, it's a TTF font. It's the Code 3 de 9 (code39.ttf) from here https://grandzebu.net/informatique/codbar-en/code39.htm
The "Code 3 de 9" (of code39) is not in in Doc.fTrueTypeFonts after TPdfDocumentGDI.Create (so also not in EnumFontsProcW).
It's also not in Printer.Fonts.Text but for the Printer it works so for printers it doesn't need to be in there to be selected.
According to the documentation fonts added with AddFontMemResourceEx are always private and not enumerable.
This function allows an application to get a font that is embedded in a document or a webpage. A font that is added by AddFontMemResourceEx is always private to the process that made the call and is not enumerable.
But that doesn't seem to stop the Printer unit from being able to use it.
So it is selectable.
(As a workaround I'm now using AddFontResourceEx with a temporary disk-font (with FR_PRIVATE set but without FR_NOT_ENUM) and that works for now but I would rather have direct memory resource fonts.)
Is there an option to force this font to be accepted (selected) without being in fTrueTypeFonts[] ?
Offline
I have just added a new TPdfDocument.AddTrueTypeFont() method.
It should help you in your case.
See https://synopse.info/fossil/info/aedd978136
Offline
I have just added a new TPdfDocument.AddTrueTypeFont() method.
It should help you in your case.
See https://synopse.info/fossil/info/aedd978136
Yes, that works perfectly.
Thank you very much.
I added some extra lines in mORMotReport.pas because I use that to more closely emulate the printer-code (so I hacked it a bit and maybe in the future I will use more native code).
And it doesn't matter if nonexistent fonts are added here because if they are not used for selecting, they are not used in SynPdf to get embedded.
// rvk
PDF.AddTrueTypeFont('KIX Barcode');
PDF.AddTrueTypeFont('Code EAN13');
PDF.AddTrueTypeFont('Code 3 de 9');
PDF.AddTrueTypeFont('Code 128');
PDF.EmbeddedTTF := true; // could also use ExportPDFEmbeddedTTF
PDF.EmbeddedTTFIgnore.Text := MSWINDOWS_DEFAULT_FONTS; // but these 2 are hidden
PDF.EmbeddedWholeTTF := true; // needed for barcode fonts
PDF.Root.PageLayout := plSinglePage; // always force show whole page
Offline
When using the TGDIPage report generator, we have no access to the pdf object created when the user clicks on "Export pdf".
So the only way to use embedded font is to modify mORMotReoprt.pas to add the call to the PDF.AddTrueTypeFont(...) like rvk did.
Do you think it is possible to either
a) add the AddTrueTypeFont() to the TGDIPage class, record the list of added font and later inject them in the PDF object created when user exports.
b) add a callback OnPdfExport() called when user click on the export button with a reference to the newly created PDF object, to let the programmer a way to add the font to the PDF object before use ?
What solution do you prefer? I can try to submit a patch to solve this.
As a side note, I had to play with TGdiPage.ForceNoAntiAliased := true; for the preview to render my memory loaded Font. I think the GDI+ that is created do not see the memory font. Maybe we need to call GdipPrivateAddMemoryFont() in the TGDIPlusFull instance created in ExpectGDIPlusFull() ? But this is just speculation for now, I don't really know GDI+ internals...
Offline