any_pdf_fixer

HarfBuzz-powered complex-script text shaping for Dart's pdf package.

any_pdf_fixer is for text that cannot be rendered correctly by simple Unicode-codepoint-to-glyph mapping: Bengali, Devanagari, Tamil, Telugu, Malayalam, Kannada, Gujarati, Gurmukhi, Arabic, Hebrew, Thai, Khmer, Myanmar, Sinhala, Tibetan, and other scripts supported by HarfBuzz and by the font you provide.

Why

Complex scripts need OpenType shaping. Conjunct substitution, mark positioning, matra reordering, ligatures, and contextual forms depend on GSUB/GPOS data inside the font.

This package:

  • shapes Unicode with HarfBuzz;
  • renders the shaped glyph outlines into package:pdf;
  • supports font fallback;
  • preserves line breaks and wraps text;
  • can add an invisible Unicode layer for PDF search/copy;
  • does not convert text into legacy ANSI encodings;
  • does not bundle or redistribute fonts.

Supported platforms

Platform Support
Android
iOS
Linux
macOS
Windows
Web ❌ currently

The native backend uses harfbuzz_ffi. Web is intentionally not advertised until a WASM/native shaping backend is available.

Installation

dependencies:
  any_pdf_fixer: ^0.1.0
  pdf: ^3.13.0

The package requires Dart 3.13 or newer.

Basic usage

final bytes = await File('/path/to/NotoSansBengali.ttf').readAsBytes();
final font = AnyPdfFont.fromBytes(bytes, name: 'Bengali');

final document = pw.Document();

document.addPage(
  pw.Page(
    build: (_) => ShapedText(
      'আমি বাংলায় লিখছি।',
      style: AnyPdfTextStyle(
        font: font,
        fontSize: 18,
      ),
    ),
  ),
);

final pdfBytes = await document.save();
font.dispose();

Use a Unicode OpenType TTF/OTF whose license permits your intended use and whose glyph coverage includes the text you render.

Mixed scripts and fallback fonts

final style = AnyPdfTextStyle(
  font: latinFont,
  fallbackFonts: [
    bengaliFont,
    devanagariFont,
    tamilFont,
    arabicFont,
  ],
  fontSize: 16,
);

final widget = ShapedText(
  'Invoice ১২৩ • हिन्दी • தமிழ் • العربية',
  style: style,
);

The renderer selects a font per Unicode run and lets HarfBuzz infer shaping properties.

Searchable/copyable text

preserveUnicodeText defaults to true.

The visible text is drawn from shaped glyph outlines. A second invisible Unicode text layer is emitted with the matching source text and font. This is designed to retain useful PDF search/copy semantics while avoiding the visual corruption caused by unshaped PDF text.

AnyPdfTextStyle(
  font: font,
  preserveUnicodeText: true,
)

Set it to false when you only need visual output or when PDF size/text extraction behavior matters more than selection.

Font diagnostics

final diagnostics = AnyPdfFontDiagnostics(font);

print(diagnostics.report('বাংলা'));

Current limitations

  • Web is not supported by the native HarfBuzz backend.
  • Mixed bidirectional paragraphs use script-run direction handling, not a complete Unicode Bidirectional Algorithm implementation yet. Pure RTL runs work; very complex nested LTR/RTL text should be tested.
  • Glyphs are rendered as PDF vector paths. This favors correctness and independence from pdf's text shaper, but can create larger files than native subset-text rendering.
  • Color emoji/color-font painting is not implemented.
  • Vertical text is not implemented.
  • The invisible Unicode layer is a pragmatic text-extraction bridge, not a custom CID/ToUnicode shaper.

Resource lifetime

AnyPdfFont owns native HarfBuzz resources. Reuse a font for the whole PDF generation session and dispose it after document.save():

final font = AnyPdfFont.fromBytes(bytes);
try {
  // build + save PDF
} finally {
  font.dispose();
}

Repository

https://github.com/ardevcraft/any_pdf_fixer

License

Apache-2.0. Fonts supplied by applications remain under their own licenses.

Libraries

pdf_text_shaper
HarfBuzz-powered complex-script text shaping for package:pdf.