#1 2022-04-25 08:58:52

rvk
Member
Registered: 2022-04-14
Posts: 115

Embedding and using font added with AddFontMemResourceEx

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

#2 2022-04-25 12:31:00

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,653
Website

Re: Embedding and using font added with AddFontMemResourceEx

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

#3 2022-04-25 14:00:18

rvk
Member
Registered: 2022-04-14
Posts: 115

Re: Embedding and using font added with AddFontMemResourceEx

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

#4 2022-04-25 14:50:47

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,653
Website

Re: Embedding and using font added with AddFontMemResourceEx

I have just added a new TPdfDocument.AddTrueTypeFont() method.
It should help you in your case.
See https://synopse.info/fossil/info/aedd978136

Offline

#5 2022-04-25 15:21:26

rvk
Member
Registered: 2022-04-14
Posts: 115

Re: Embedding and using font added with AddFontMemResourceEx

ab wrote:

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

#6 2022-07-28 14:46:00

chowette
Member
Registered: 2022-07-26
Posts: 1

Re: Embedding and using font added with AddFontMemResourceEx

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

Board footer

Powered by FluxBB