Home  ›  Guides  ›  Troubleshooting

Troubleshooting

Symptom-first fixes for the classic failures — tofu boxes (a font registered but never loaded), RTL text appearing backwards, oversized files, PDF/A validation errors, parser rejections — each with the check that identifies it and the change that fixes it.

Font Not Rendering (Boxes or Blank)#

Symptom: Non-Latin text shows as empty rectangles or missing glyphs.

Cause: The font for that script is not registered or not loaded.

Fix:

import { registerFonts, loadFontData } from 'pdfnative';

// 1. Register font loaders (lazy — no data loaded yet)
registerFonts({
  th: () => import('pdfnative/fonts/noto-thai-data.js'),
  ar: () => import('pdfnative/fonts/noto-arabic-data.js'),
});

// 2. Load font data when needed
const thaiFont = await loadFontData('th');

// 3. Pass fontEntries to your builder
const pdf = buildDocumentPDFBytes({
  blocks: [{ type: 'paragraph', text: 'สวัสดี' }],
  fontEntries: [{ fontData: thaiFont!, fontRef: '/F3', lang: 'th' }],
});

PDF File Too Large#

Symptom: Output PDF is unexpectedly large (>1 MB for text content).

Causes & fixes:

  1. Enable compression:

    buildPDFBytes(params, { compress: true });
    

    FlateDecode typically reduces size by 50–90%.

  2. Initialize native compression (Node.js):

    import { initNodeCompression } from 'pdfnative';
    await initNodeCompression();
    

    Without this, a stored-block fallback is used (minimal compression).

  3. Large images: JPEG is already compressed (DCTDecode). For PNG, the raw pixels are FlateDecode-compressed. Use JPEG for photos.

  4. Font subsetting: Automatic — only used glyphs are embedded. If you're embedding many scripts, each adds a font subset.

RTL Text Backwards#

Symptom: Arabic or Hebrew text appears in logical order instead of visual (right-to-left) order.

Cause: Font entries must include the correct lang property for BiDi detection.

Fix:

const fontEntries = [
  { fontData: arabicFont, fontRef: '/F3', lang: 'ar' },
  { fontData: hebrewFont, fontRef: '/F4', lang: 'he' },
];

The lang property triggers:

Reversed digits or backwards parentheses in RTL text? Fixed in v1.7.0 with no API change: digit runs now take even embedding levels (UAX #9 rules I1/I2), so a number like 1405 keeps its digit order instead of rendering 5041, and rule L4 mirrors delimiters during reversal, so a logical (X) no longer renders )X(. If you see either symptom, upgrade pdfnative.

PDF/A Validation Fails#

Symptom: veraPDF reports non-conformance.

Common issues:

  1. Missing tagged mode:

    buildPDFBytes(params, { tagged: true }); // PDF/A-2b
    // or
    buildPDFBytes(params, { tagged: 'pdfa1b' }); // PDF/A-1b
    
  2. PDF/A + encryption conflict: ISO 19005-1 §6.3.2 forbids encryption in PDF/A. Use one or the other.

  3. Transparency in PDF/A-1b: Watermarks with opacity < 1.0 are blocked in PDF/A-1b (ISO 19005-1 §6.4). Use PDF/A-2b or remove transparency.

"Document has too many blocks" / Very Large Documents#

Symptom: Generating a multi-thousand-page report throws a "too many blocks" error.

Cause: The document builder caps the number of content blocks as a safety rail. Before v1.3.0 this was a hard-coded 10 000. As of v1.3.0 the default is 100 000 and is configurable.

Fix: raise the limit via the layout option:

buildDocumentPDFBytes(params, { maxBlocks: 500000 });
// also honoured by the streaming builders:
buildDocumentPDFStream(params, { maxBlocks: 500000 }, { chunkSize: 65536 });

For very large outputs, prefer the streaming builders (buildDocumentPDFStream, or buildDocumentPDFStreamTrue for constant memory) so the full binary never has to sit in memory at once.

Parser Fails on External PDF#

Symptom: openPdf() throws on a PDF file not generated by pdfnative.

Common causes:

  1. Encrypted PDF opened without a password: Since v1.6.0 the parser decrypts the Standard Security Handler (RC4 V1-V4, AES-128, AES-256 R6) — but it needs the password. Call openPdf(bytes, { password }). Without one you get PdfPasswordError; with an algorithm the handler does not cover, PdfEncryptionUnsupportedError.
  2. Linearized PDF: The parser follows standard xref/trailer. Linearized hint tables may cause offset issues.
  3. Non-standard formatting: Some PDF generators produce non-compliant output. The parser follows ISO 32000-1 strictly.

Build / Import Issues#

ESM import paths:

// Bare package import — no extension involved
import { buildPDFBytes } from 'pdfnative';

// Font data modules — the .js extension IS required here (ESM subpath),
// and the modules only have named exports, so use a namespace import:
import * as thaiData from 'pdfnative/fonts/noto-thai-data.js';

Browser vs Node.js: The library works in both environments. For compression in Node.js, call initNodeCompression() once at startup.