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:
Enable compression:
buildPDFBytes(params, { compress: true });FlateDecode typically reduces size by 50–90%.
Initialize native compression (Node.js):
import { initNodeCompression } from 'pdfnative'; await initNodeCompression();Without this, a stored-block fallback is used (minimal compression).
Large images: JPEG is already compressed (DCTDecode). For PNG, the raw pixels are FlateDecode-compressed. Use JPEG for photos.
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:
- BiDi run detection (
containsRTL()) - Arabic positional shaping (GSUB forms)
- Glyph mirroring for brackets and other paired delimiters (the full 428-pair Unicode BidiMirroring table since v1.7.0, per UAX #9 rule L4)
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:
Missing tagged mode:
buildPDFBytes(params, { tagged: true }); // PDF/A-2b // or buildPDFBytes(params, { tagged: 'pdfa1b' }); // PDF/A-1bPDF/A + encryption conflict: ISO 19005-1 §6.3.2 forbids encryption in PDF/A. Use one or the other.
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:
- 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 getPdfPasswordError; with an algorithm the handler does not cover,PdfEncryptionUnsupportedError. - Linearized PDF: The parser follows standard xref/trailer. Linearized hint tables may cause offset issues.
- 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.