MiniPdf implementation
.NET
Problem or use case
MiniPdf.RegisterFont(string name, byte[] fontData) appends to a process-wide static
list. There is no public or internal way to remove, replace, or clear entries.
MiniPdf is a public static class, so there is no instance to dispose and no scope in
which registrations can be reset.
MiniPdf.cs:110:
private static readonly List<(string Name, byte[] Data)> _registeredFonts = new();
MiniPdf.cs:118:
public static void RegisterFont(string name, byte[] fontData)
{
...
lock (_registeredFonts)
_registeredFonts.Add((name, fontData));
}
A grep over src/ finds no Clear, Remove, Unregister, or replace path, public or
internal. Every conversion reads the full list, at PdfWriter.cs:173 and
PdfWriter.cs:2774, so the per-conversion font-loading cost grows with the number of
registrations for the lifetime of the process.
Consequences for library hosts:
- Registrations cannot be released. A long-running host that registers fonts per request
or per tenant accumulates them; CJK fonts are tens of MB each.
- A font cannot be replaced. Registering the same
name again adds a second entry rather
than overriding the first, and both entries are loaded.
- Font sets cannot be swapped between conversions, for example when different documents
require different font sets.
The XML documentation names Blazor WASM as a target scenario, where a long-lived process
is the normal case.
The CLI is unaffected because it is a one-shot process.
Proposed solution
Either of the following would resolve it; the choice is a design decision for
maintainers:
- Add
MiniPdf.ClearRegisteredFonts(), and possibly
MiniPdf.UnregisterFont(string name).
- Make
RegisterFont key on name, so registering an existing name replaces it. This
changes existing behavior and may not be desirable.
Option 1 is additive and does not change current behavior.
Affected area
MiniPdf library API
MiniPdf version or baseline
main @ 71c44aa6
Example usage
// A long-running host that serves documents needing different font sets.
MiniPdf.RegisterFont("NotoSansTC", tcBytes);
MiniPdf.ConvertToPdf(inputA, outputA);
// Intent: use only the JP font for the next document.
// There is currently no way to express this.
// MiniPdf.ClearRegisteredFonts(); // does not exist
MiniPdf.RegisterFont("NotoSansJP", jpBytes);
MiniPdf.ConvertToPdf(inputB, outputB); // both fonts are still loaded
// Intent: replace the TC font with a different build.
MiniPdf.RegisterFont("NotoSansTC", otherTcBytes); // adds a second entry
Sample files or references
Measured on macOS arm64, main @ 71c44aa6, .NET SDK 9.0.317.
One point worth stating explicitly, so the scope is not overstated: registering the same
font data twice under different names does not duplicate it in the output. Converting
tests/Issue_Files/docx/Issue79_FilledContract.docx with --fonts pointing at a
directory holding two copies of the same TTC produced 579,885 bytes with a single
embedded font, against 579,888 bytes for a single copy. Only slots actually used are
embedded. The cost is in loading and retention, not in output size.
I have not measured process memory growth over a long-running host; the statements above
are from reading the code and from the conversion measurements described.
Alternatives considered
- Restarting the process to release registrations. Not viable for a server or for Blazor
WASM.
- Registering the union of all fonts once at startup. Workable when the set is known and
small, but it loads every font on every conversion and does not allow replacement.
Please close this if the current lifetime is intentional; I could not tell from the code
or history whether it is a deliberate design choice.
MiniPdf implementation
.NET
Problem or use case
MiniPdf.RegisterFont(string name, byte[] fontData)appends to a process-wide staticlist. There is no public or internal way to remove, replace, or clear entries.
MiniPdfis apublic static class, so there is no instance to dispose and no scope inwhich registrations can be reset.
MiniPdf.cs:110:MiniPdf.cs:118:A grep over
src/finds noClear,Remove,Unregister, or replace path, public orinternal. Every conversion reads the full list, at
PdfWriter.cs:173andPdfWriter.cs:2774, so the per-conversion font-loading cost grows with the number ofregistrations for the lifetime of the process.
Consequences for library hosts:
or per tenant accumulates them; CJK fonts are tens of MB each.
nameagain adds a second entry ratherthan overriding the first, and both entries are loaded.
require different font sets.
The XML documentation names Blazor WASM as a target scenario, where a long-lived process
is the normal case.
The CLI is unaffected because it is a one-shot process.
Proposed solution
Either of the following would resolve it; the choice is a design decision for
maintainers:
MiniPdf.ClearRegisteredFonts(), and possiblyMiniPdf.UnregisterFont(string name).RegisterFontkey onname, so registering an existing name replaces it. Thischanges existing behavior and may not be desirable.
Option 1 is additive and does not change current behavior.
Affected area
MiniPdf library API
MiniPdf version or baseline
main@71c44aa6Example usage
Sample files or references
Measured on macOS arm64,
main@71c44aa6, .NET SDK 9.0.317.One point worth stating explicitly, so the scope is not overstated: registering the same
font data twice under different names does not duplicate it in the output. Converting
tests/Issue_Files/docx/Issue79_FilledContract.docxwith--fontspointing at adirectory holding two copies of the same TTC produced 579,885 bytes with a single
embedded font, against 579,888 bytes for a single copy. Only slots actually used are
embedded. The cost is in loading and retention, not in output size.
I have not measured process memory growth over a long-running host; the statements above
are from reading the code and from the conversion measurements described.
Alternatives considered
WASM.
small, but it loads every font on every conversion and does not allow replacement.
Please close this if the current lifetime is intentional; I could not tell from the code
or history whether it is a deliberate design choice.