LibPDF Documentation

repository·main·Indexed 23 days ago

https://github.com/libpdf-js/core

A modern, TypeScript-first PDF library for parsing, modifying, and generating PDF documents. It provides a high-level API for common tasks like form filling, digital signatures, and drawing, as well as a low-level API for direct access to PDF structures (PdfDict, PdfArray, PdfStream). Includes fontbox for TrueType font parsing and subsetting. Supports Node.js 20+, Bun, and modern browsers.

Tokens
77K
Snippets
235
Records
402
Agent score
78%

What's inside @libpdf/core

  1. Overview of LibPDF features

    main

    LibPDF is a comprehensive TypeScript library for both parsing and generating PDFs. Key capabilities include:

    • PDF Versions: Full read/write support for PDF 1.0–1.7; read support for PDF 2.0.
    • Security: Full support for encrypted PDFs (RC4, AES-128, AES-256).
    • Forms: Full AcroForms support (read, fill, and flatten).
    • Signatures: Digital signature support (PAdES B-B through B-LTA).
    • Text: Full text extraction with positions and search capabilities.
    • Fonts: Full TTF/OTF font embedding with subsetting.
    • Persistence: Incremental saves that preserve existing signatures.
  2. Understand the licensing for @libpdf/core (fontbox)

    main

    The @libpdf/core package (specifically the fontbox component) is primarily licensed under the Apache License, Version 2.0.

    This license allows you to use, reproduce, modify, and distribute the work. However, if you redistribute the work or derivative works, you must:

    1. Provide a copy of the Apache License to recipients.
    2. Include prominent notices in modified files stating that you changed them.
    3. Retain all original copyright, patent, trademark, and attribution notices.
    4. Include a copy of the NOTICE file (if present) in your distribution.

    Note: This package contains code derived from Apache PDFBox FontBox and includes components subject to the SIL Open Font License, Version 1.1 (such as Lohit fonts, FoglihtenNo07, and Josefin Sans).

  3. Compare libpdf performance with other PDF libraries

    main

    LibPDF is benchmarked against pdf-lib and @cantoo/pdf-lib across several core operations. Key performance advantages include:

    • Loading PDFs: Up to 13.60x faster than pdf-lib.
    • Creating blank PDFs: Up to 4.72x faster than @cantoo/pdf-lib.
    • Drawing elements: Up to 4.37x faster than @cantoo/pdf-lib for drawing rectangles.
    • Saving PDFs: Up to 31.31x faster than @cantoo/pdf-lib for load-and-save operations.
    • Splitting/Extracting: Significantly faster at extracting single pages from large documents and splitting large PDFs into single-page files.
    • Form Handling: Faster at filling and flattening forms, with pdf-lib failing certain flattening benchmarks in this report.
  4. Configure PAdES signature conformance levels

    main

    LibPDF supports several PAdES (PDF Advanced Electronic Signatures) conformance levels via the level option in the pdf.sign() method:

    • B-B (Basic Signature): Simple signing.
    • B-T (With Timestamp): Proves when the document was signed using a timestampAuthority.
    • B-LT (Long-term Validation): Embeds validation data (certificates, CRLs, OCSP responses) so the signature remains valid after the certificate expires.
    • B-LTA (Long-term Archival): The highest durability level; adds a document timestamp covering the embedded validation data for indefinite verification.
    // Example: Signing with a timestamp (B-T)
    import { HttpTimestampAuthority } from "@libpdf/core";
    
    const tsa = new HttpTimestampAuthority("http://timestamp.digicert.com");
    
    await pdf.sign({
      signer,
      level: "B-T",
      timestampAuthority: tsa,
    });
  5. Digital Signature Limitations

    main

    When using LibPDF for digital signatures, be aware of the following:

    • Verification is not implemented: LibPDF can create signatures but cannot verify them.
    • No visible signatures: All signatures are currently invisible (embedded in the PDF metadata/structure) and do not appear visually on the pages.
    • LTV requires network access: Conformance levels like B-LT and B-LTA require the ability to reach OCSP/CRL servers to fetch validation data.
    • Incremental saving is mandatory: To preserve multiple signatures, every signing operation must be saved incrementally.
  6. Benchmark form handling operations

    main

    LibPDF handles PDF form interactions with the following performance characteristics:

    • Reading field values: The fastest form operation.
    • Getting form fields: Slightly slower than reading values.
    • Flattening forms: Slower than reading or retrieving fields.
    • Filling text fields: The most intensive form operation in terms of time per operation.
  7. How LibPDF's API layers work

    main

    LibPDF provides two distinct layers of abstraction depending on your needs:

    1. High-level API: Uses PDF, PDFPage, and PDFForm for common, intuitive tasks like loading, filling forms, and drawing.
    2. Low-level API: Provides direct access to the PDF structure via PdfDict, PdfArray, and PdfStream for full control over the document internals.
  8. Benchmark copying and merging operations

    main

    LibPDF provides high-performance operations for manipulating pages between documents:

    • Copying pages: Performance scales based on the number of pages; copying a single page is significantly faster than copying large batches or entire documents.
    • Duplicating pages: Efficiently duplicates pages within the same document.
    • Merging PDFs: High-speed merging of multiple PDF documents, ranging from small files to large 100-page documents.
  9. Benchmark loading and saving operations

    main

    LibPDF's performance for I/O operations depends heavily on file size and the type of operation:

    Loading

    • Small PDFs (~888B): Extremely fast (tens of thousands of ops/sec).
    • Medium PDFs (~19KB): Significantly slower than small files.
    • Form PDFs (~116KB): Slower than medium files.
    • Heavy PDFs (~2.0MB): The slowest loading category.

    Saving

    • Unmodified Save: The fastest way to save a document (e.g., when no changes were made).
    • Incremental Save: Faster than a full save with modifications, but slower than an unmodified save.
    • Save with Modifications: Slower than incremental or unmodified saves.
    • Heavy PDF Saving: Saving large files (e.g., 2.0MB) is significantly slower than saving small (19KB) files.
  10. How incremental saves work in LibPDF

    main

    LibPDF supports two modes for saving documents: full rewrite and incremental update. Choosing the correct mode is critical for maintaining document integrity and digital signatures.

    Full Rewrite (Default)

    When saving without options, the entire PDF is rebuilt from scratch.

    • Pros: Smaller file size (removes unused objects), clean structure, and removes document history.
    • Cons: Invalidates all existing signatures, breaks certified documents, and loses incremental history.

    Incremental Update

    When using { incremental: true }, changes are appended to the end of the file rather than rewriting it.

    • Pros: Preserves existing signatures, maintains document history, and is faster for small changes.
    • Cons: File size grows with each update because old content remains in the file.
    // Default: full rewrite
    await pdf.save();
    
    // Explicit incremental
    await pdf.save({ incremental: true });
  11. Preserve type information when wrapping LibPDF

    main

    To ensure consumers of your library maintain full type safety, use generics instead of upcasting to base types or unknown.

    Do: Use Generics

    By using generics that extend LibPDF types (like PDFPage), you preserve the exact type information through the call chain, allowing subclasses to work correctly.

    Don't: Lose Type Information

    Avoid functions that return the base PDF type when a more specific subclass is expected, or functions that upcast to unknown.

    import { PDF, PDFPage } from "@libpdf/core";
    
    // Preserves the exact type
    function processPages<T extends PDFPage>(pages: T[]): T[] {
      return pages.filter(p => p.width > 100);
    }
    
    // Works with subclasses too
    async function loadAndProcess(bytes: Uint8Array): Promise<PDF> {
      const pdf = await PDF.load(bytes);
      // Type is preserved through the call chain
      return pdf;
    }