MuhammaraJS PDF Library

repository·develop·Indexed 43 days ago

https://github.com/julianhille/muhammarajs

A high-performance library for creating, reading, and modifying PDF files, serving as a maintained successor to HummusJS. It provides native Node.js addons via @muhammara/native and @muhammara/native-with-source, as well as a WebAssembly version via @muhammara/wasm for browser and Worker compatibility. Features include a Recipe API for programmatic PDF manipulation with one-based page numbering.

Tokens
80.5K
Snippets
226
Records
350
Agent score
54%

What's inside MuhammaraJS

  1. Overview of the MuhammaraJS Low-Level API

    develop

    The low-level API provides direct access to the underlying PDF engine. It is designed for advanced manipulation and exposes the following core components:

    • Native PDF Writer and Reader: For creating new PDF documents or parsing existing ones.
    • Page Content Contexts: For interacting with the content of specific pages.
    • Streams: For handling raw data streams within the PDF structure.
    • Forms: For managing PDF form fields and interactive elements.
    • Copying Contexts: For duplicating content or pages from one context to another.
    • Raw PDF Drawing Operators: For low-level control over PDF drawing commands.

    Note for legacy users: This API is a rewritten and improved version of the original HummusJS wiki. When performing operations, always use the documentation provided in the MuhammaraJS navigation for supported writer, reader, stream, form, copying, and drawing operations instead of referring to legacy HummusJS wiki material.

  2. Use @muhammara/wasm for Browser, Worker, and Node WebAssembly environments

    develop

    The @muhammara/wasm package is designed for environments where filesystem access or Node.js streams are unavailable, such as browsers or Web Workers. Unlike the native Node package, it provides a byte-first API.

    Every completed writer or Recipe operation returns an owned Uint8Array. You can use this array directly with Blob, browser storage, or for uploads and downloads.

    import { createMuhammaraWasm } from "@muhammara/wasm";
    
    var muhammara = await createMuhammaraWasm();
    var pdfBytes = muhammara.createBlankPdf(595, 842);
    var pdf = new Blob([pdfBytes], { type: "application/pdf" });
  3. Understand the MuhammaraJS WASM Browser Module structure

    develop

    The browser example is organized into several functional modules that demonstrate different ways to interact with the WebAssembly PDF engine:

    • module-options.mjs: Handles asynchronous ESM loading and explicit .wasm file location via locateFile.
    • low-level.mjs: Provides access to low-level PDF operations including boxes, graphics/text state, paths, clipping, colors, forms, images, metadata, links, annotations, raw streams, and page modification (copying, appending, merging).
    • recipe.mjs: High-level API for creating/editing PDFs using Recipe compositions, including flowed HTML text, tables, shapes, and overlays.
    • workflow.mjs: Orchestrates staged PDF processing and parses the results back.
    • example-worker.mjs: Manages execution across a transferable module Worker boundary to prevent main-thread blocking.
    • lifecycle.mjs: Handles cancellation, structured errors, and object-URL cleanup.
    • app.mjs: Manages the responsive UI and Worker lifecycle.
  4. Manage page context after Recipe.endPage() in v7

    develop

    In v7, calling Recipe.endPage() clears the completed page and its content context. You cannot continue drawing on the previous page without re-establishing context.

    To add content to a new page, call recipe.createPage() or recipe.editPage() after endPage().

    To add content to an existing page: Use recipe.editPage(pageNumber) where pageNumber is one-based.

    recipe.endPage();
    
    // Create another page before adding more content.
    recipe.createPage("letter");
    recipe.text("Next page", 72, 72);
    
    // OR: Modify an existing page
    recipe.endPage();
    recipe.editPage(2);
    recipe.text("More content", 72, 72);
  5. Manage resources and trust boundaries in the browser

    develop

    When using @muhammara/wasm in a browser environment, follow these best practices for performance and security:

    • Use Web Workers: PDF parsing, font loading, and image decoding are CPU and memory-intensive. Run untrusted documents in a terminable Worker rather than the main thread to avoid freezing the UI.
    • Input/Output Types: The library uses Uint8Array, ArrayBuffer slices, Blob, and File for inputs.
    • Memory Management: Explicitly call end() or dispose() for object owners, unregister assets, call disposeAssets(), and revoke object URLs to prevent memory leaks.
    • Unsupported Features: The WASM version does not support PDF 2.0, AES-256 encryption, continuation state files, filesystem paths, Node streams, plugins, or Node EventEmitter hooks.
  6. Choose between @muhammara/native and @muhammara/native-with-source

    develop

    Depending on your environment, you should choose one of the following packages:

    1. @muhammara/native: Use this for standard Node.js environments where prebuilt binaries are available. It is a smaller package.
    2. @muhammara/native-with-source: Use this if a compatible prebuilt binary is unavailable, if you need to compile the addon locally, or if you are rebuilding the addon for Electron.

    Both packages share the same @muhammara/native-core runtime and expose the exact same API.

  7. Understand the security model and vendored dependencies in @muhammara/wasm

    develop

    The @muhammara/wasm package compiles its WebAssembly target from C and C++ source code located in packages/native-with-source/src/deps/. These dependencies are vendored (manually included) and are not managed by npm or other package managers.

    Key Security Note: The WebAssembly build deliberately excludes OpenSSL-backed encryption because browsers lack an OpenSSL runtime. Consequently, OpenSSL-backed encryption is not available in this target.

  8. Manage the PDF Writer lifecycle

    develop

    A PDFWriter can be initialized using createWriter(), createWriterToModify(), or createWriterToContinue().

    Lifecycle Rules:

    • Termination: Once end() or shutdown() is called, the writer is retired. Any subsequent calls to stateful methods (like page creation or writing) will throw Error("PDF writer has ended").
    • Idempotency: Calling end() multiple times is safe; it will return the writer without attempting to finalize again.
    • Cleanup: Ensure all drawing operations are complete and all borrowed contexts (fonts, parsers, file wrappers) are consumed before ending the writer.
    • Resuming: If you need to perform more work after a writer has ended, you must start a new writer or use createWriterToContinue() with a saved continuation state.
  9. Choose the right MuhammaraJS package

    develop

    MuhammaraJS provides different packages depending on your runtime environment and whether you need prebuilt binaries or source compilation.

    • @muhammara/native: Best for most Node.js applications. It provides a small package with prebuilt native binaries.
    • @muhammara/native-with-source: Use this for local native builds, Electron rebuilds, or if your platform does not have a matching prebuilt binary for @muhammara/native.
    • @muhammara/wasm: Use this for Browsers, Web Workers, and other byte-oriented WebAssembly environments where native Node.js addons or filesystem APIs are unavailable.

    Note on API differences: The Native packages work with Node.js paths, streams, and buffers. The Wasm package is byte-first and uses Uint8Array, ArrayBuffer, Blob, and File.