#pdf

    Core types and operations for in-memory PDF document representation.

    #Overview

    The pdf package provides the fundamental data structures for representing PDF documents in memory. It defines the PdfObject enum for all PDF value types, the Pdf struct for complete documents, and operations for manipulating objects, dictionaries, streams, and object graphs.

    #Core Types

    #PdfObject

    The central enum representing all PDF value types:

    ///|
    pub(all) enum PdfObject {
    Null
    Boolean(Bool)
    Integer(Int)
    Real(Double)
    String(String)
    Name(PdfName)
    Array(Array[PdfObject])
    Dictionary(Array[(String, PdfObject)])
    Stream(Ref[(PdfObject, Stream)])
    Indirect(Int)
    }

    • Null: PDF null.
    • Boolean: true or false.
    • Integer/Real: Numeric values.
    • String: Literal (...) or hex <...> strings. Stored as a MoonBit String, but treated as raw bytes by the parser/serializer.
    • Name: PDF names like /Type, /Page, stored as PdfName (byte sequence, not Unicode text).
    • Array: Ordered collection of objects.
    • Dictionary: Key-value pairs, stored as Array[(String, PdfObject)] so we can represent malformed PDFs with duplicate keys and preserve insertion order; use lookup_immediate (first match) or lookup_immediate_all (all matches).
    • Stream: A stream dictionary plus stream payload (possibly deferred), stored behind a Ref so getstream can materialize bytes in-place.
    • Indirect: Reference to another object by number (n 0 R).

    #Pdf

    The in-memory document representation:

    ///|
    pub(all) struct Pdf {
    major : Int // PDF major version
    minor : Int // PDF minor version
    root : Int // Object number of document catalog
    objects : PdfObjects // All objects in the document
    mut trailerdict : PdfObject
    was_linearized : Bool
    mut saved_encryption : SavedEncryption?
    }

    #Stream

    Stream data can be loaded or deferred:

    ///|
    pub(all) enum Stream {
    Got(Array[Byte]) // Data in memory
    ToGet(ToGet) // Data still on disk
    }

    #Creating Documents

    #Empty Document

    ///|
    let pdf = @pdf.Pdf::empty()
    // Creates PDF 2.0 with no objects

    #Adding Objects

    let pdf = @pdf.Pdf::empty()

    // Add an object and get its number
    let objnum = pdf.addobj(@pdf.PdfObject::Dictionary([
    ("/Type", @pdf.PdfObject::Name("/Page")),
    ]))

    // Add with a specific object number
    pdf.addobj_given_num((42, @pdf.PdfObject::Integer(100)))

    #Object Lookup

    #Basic Lookup

    ///|
    test "lookup_obj returns Null for missing objects" {
    let pdf = @pdf.Pdf::empty()
    assert_true(pdf.lookup_obj(999) is Null)
    }

    #Following Indirect References

    ///|
    test "direct follows indirects" {
    let pdf = @pdf.Pdf::empty()
    let objnum = pdf.addobj(Integer(42))
    let indirect = @pdf.PdfObject::Indirect(objnum)
    guard pdf.direct(indirect) is Integer(n) else { fail("expected Integer") }
    inspect(n, content="42")
    }

    #Dictionary Key Lookup

    ///|
    test "lookup_direct finds keys" {
    let pdf = @pdf.Pdf::empty()
    let dict = @pdf.PdfObject::Dictionary([
    ("/Type", Name(PdfName(b"/Page"))),
    ("/Count", Integer(5)),
    ])
    guard pdf.lookup_direct("/Type", dict) is Some(Name(name)) else {
    fail("expected Name")
    }
    inspect(name.to_string_bytes(), content="/Page")
    assert_true(pdf.lookup_direct("/Missing", dict) is None)
    }

    #Nested Chain Lookup

    For deeply nested dictionaries, use lookup_chain:

    ///|
    test "lookup_chain navigates nested dicts" {
    let pdf = @pdf.Pdf::empty()
    let inner = @pdf.PdfObject::Dictionary([("/Value", Integer(100))])
    let outer = @pdf.PdfObject::Dictionary([("/Inner", inner)])
    guard pdf.lookup_chain(outer, ["/Inner", "/Value"][:]) is Some(Integer(n)) else {
    fail("expected Integer")
    }
    inspect(n, content="100")
    }

    #Dictionary Manipulation

    #Adding Entries

    ///|
    test "add_dict_entry" {
    let dict = @pdf.PdfObject::Dictionary([("/Type", Name(PdfName(b"/Page")))])
    let updated = dict.add_entry("/Count", Integer(1))
    match updated {
    Dictionary(entries) => inspect(entries.length(), content="2")
    _ => fail("expected dictionary")
    }
    }

    #Traits

    #ToPdfNumber

    @pdf.ToPdfNumber is a small helper trait for converting primitive numeric types into PdfObject numeric nodes.

    ///|
    test "ToPdfNumber converts Int/Double to PdfObject numbers" {
    guard @pdf.ToPdfNumber::to_pdf_number(42) is Integer(42) else {
    fail("expected Integer(42)")
    }
    guard @pdf.ToPdfNumber::to_pdf_number(1.5) is Real(r) && r == 1.5 else {
    fail("expected Real(1.5)")
    }
    }

    #Replacing Entries

    ///|
    test "replace_dict_entry" {
    let dict = @pdf.PdfObject::Dictionary([("/Count", Integer(1))])
    let updated = dict.replace_entry("/Count", Integer(5))
    guard updated.lookup_immediate("/Count") is Some(Integer(n)) else {
    fail("expected Integer")
    }
    inspect(n, content="5")
    }

    #Removing Entries

    ///|
    test "remove_dict_entry" {
    let dict = @pdf.PdfObject::Dictionary([
    ("/Type", Name(PdfName(b"/Page"))),
    ("/Count", Integer(1)),
    ])
    let updated = dict.remove_entry("/Count")
    match updated {
    Dictionary(entries) => inspect(entries.length(), content="1")
    _ => fail("expected dictionary")
    }
    }

    #Object Iteration

    #Iterating All Objects

    obj.objiter(fn(objnum) {
    println("Object \{objnum}: \{obj}")
    },
    pdf,
    )

    #Selecting Objects by Predicate

    // Find all page objects

    ///|
    let page_nums = obj.objselect(
    fn(obj) {
    match pdf.lookup_direct("/Type") {
    Some(Name("/Page")) => true
    _ => false
    }
    },
    pdf,
    )

    #Transforming All Objects

    // Apply a transformation to every object
    pdf,
    .objselfmap(fn(obj) {
    // Return transformed object
    obj
    })

    #Stream Operations

    #Getting Stream Data

    match obj {
    Stream(_) => {
    obj.getstream!() // Loads data if deferred
    let bytes = obj.bigarray_of_stream!()
    // Use bytes...
    }
    _ => ()
    }

    #Geometry Operations

    #Parsing Rectangles

    ///|
    test "parse_rectangle" {
    let pdf = @pdf.Pdf::empty()
    let rect = @pdf.PdfObject::PdfArray([
    Real(0.0),
    Real(0.0),
    Real(612.0),
    Real(792.0),
    ])
    let (minx, miny, maxx, maxy) = pdf.parse_rectangle(rect)
    debug_inspect((minx, miny, maxx, maxy), content="(0, 0, 612, 792)")
    }

    #Matrices

    // Parse a matrix from a dictionary

    ///|
    let matrix = pdf.parse_matrix("/Matrix", dict)

    // Create a matrix object

    ///|
    let matrix_obj = @pdf.make_matrix(@pdftransform.TransformMatrix::identity())

    #Reference Management

    #Finding Referenced Objects

    // Find all objects reachable from a starting object

    ///|
    let refs = pdf.objects_referenced([], [], start_obj)

    #Removing Unreferenced Objects

    // Garbage collect unreferenced objects
    pdf.remove_unreferenced!()

    #Document Operations

    #Renumbering Objects

    // Calculate changes to renumber 1..n

    ///|
    let change_table = pdf.changes()

    // Apply renumbering

    ///|
    let renumbered = pdf.renumber(change_table)

    #Deep Copy

    // Create an independent copy

    ///|
    let copy = pdf.deep_copy()

    #Renumbering Multiple PDFs

    // Make object numbers mutually exclusive across documents

    ///|
    let renumbered = @pdf.renumber_pdfs([pdf1, pdf2, pdf3])

    #Name Trees

    PDF name trees are hierarchical structures for mapping names to values:

    // Lookup in a name tree

    ///|
    let value = pdf.nametree_lookup(@pdf.PdfObject::String("key"), tree)

    // Get all entries

    ///|
    let entries = pdf.contents_of_nametree(tree)

    #Character Classification

    ///|
    test "is_delimiter" {
    inspect(is_delimiter('('), content="true") // internal function
    inspect(is_delimiter('/'), content="true")
    inspect(is_delimiter('a'), content="false")
    }

    ///|
    test "is_whitespace" {
    inspect(@pdf.is_whitespace(' '), content="true")
    inspect(@pdf.is_whitespace('\n'), content="true")
    inspect(@pdf.is_whitespace('a'), content="false")
    }

    #Error Handling

    The package uses PdfError for error conditions:

    ///|
    pub(all) suberror PdfError {
    Msg(String)
    }

    Functions that can fail use the raise keyword and should be called with ! or within error handling contexts.

    EncryptionValues

    type EncryptionValues = (
    Encryption
    , String, String, Int, String, String?, String?)

    Snapshot of encryption settings used for re-encryption.

    PdfObjMap

    type PdfObjMap = Map[Int, (
    Ref
    [ObjectData], Int)]

    Mutable linked hash map that maintains the order of insertion, not thread safe.

    Example

    test {
    let map = { 3: "three", 8: "eight", 1: "one" }
    @test.assert_eq(map.get(2), None)
    @test.assert_eq(map.get(3), Some("three"))
    map.set(3, "updated")
    @test.assert_eq(map.get(3), Some("updated"))
    }

    ToPdfNumber

    pub trait ToPdfNumber {
    fn to_pdf_number(Self) -> PdfObject
    }

    Convert primitive numeric types into PDF numeric objects.

    This is intentionally narrow (only Int/Double) so callers don't have to choose between PDF Strings vs Names, etc.
    impl ToPdfNumber for Int

    PdfError

    pub(all) suberror PdfError {
    Msg(String)
    }

    Core error used across the PDF surface.

    DeferredEncryption

    pub(all) struct DeferredEncryption {
    crypt_type :
    Encryption

    file_encryption_key : String?
    obj : Int
    gen : Int
    key : Array[Int]
    keylength : Int
    r : Int
    } derive(
    Debug
    )

    Deferred crypt settings for stream materialization.

    ObjectData

    pub(all) enum ObjectData {
    Parsed(PdfObject)
    ParsedAlreadyDecrypted(PdfObject)
    ToParse
    ToParseFromObjectStream(Map[Int, Array[Int]], Int, Int, (Int, Array[Int]) -> Array[(Int, (
    Ref
    [ObjectData], Int))])
    }

    Object data state during parsing or decryption.

    Pdf

    pub(all) struct Pdf {
    major : Int
    minor : Int
    root : Int
    objects : PdfObjects
    trailerdict : PdfObject
    was_linearized : Bool
    saved_encryption : SavedEncryption?
    }

    In-memory PDF document.

    Pdf::addobj

    fn Pdf::addobj(self : Pdf, obj : PdfObject) -> Int

    Add an object. Returns the number chosen.

    Pdf::addobj_given_num

    fn Pdf::addobj_given_num(self : Pdf, pair : (Int, PdfObject)) -> Unit

    Same as addobj, but pick a number ourselves.

    Pdf::catalog_of_pdf

    fn Pdf::catalog_of_pdf(self : Pdf) -> PdfObject raise

    Return the document catalog.

    Pdf::change_id

    fn Pdf::change_id(self : Pdf, path : String) -> Unit raise

    Replace the /ID entry in the trailer dictionary.

    Pdf::changes

    fn Pdf::changes(self : Pdf) -> Map[Int, Int]

    Calculate the change table required to renumber objects 1..n.

    Pdf::contents_of_nametree

    fn Pdf::contents_of_nametree(self : Pdf, tree : PdfObject) -> Array[(PdfObject, PdfObject)] raise

    Return an ordered list of all (k, v) pairs in a name tree.

    Pdf::deep_copy

    fn Pdf::deep_copy(self : Pdf) -> Pdf

    Create a deep copy of a PDF document.

    Pdf::direct

    fn Pdf::direct(self : Pdf, obj : PdfObject) -> PdfObject

    Make a PDF object direct -- that is, follow any indirect links.

    Pdf::empty

    fn Pdf::empty() -> Pdf

    The empty document (PDF 2.0, no objects, no root, empty trailer dictionary).

    Pdf::generate_id

    fn Pdf::generate_id(self : Pdf, path : String, gettime : () -> Double) -> PdfObject

    Generate an /ID entry for a document.

    Pdf::getnum

    fn Pdf::getnum(self : Pdf, obj : PdfObject) -> Double raise

    Return a float from a PDF number.

    Pdf::indirect_number

    fn Pdf::indirect_number(self : Pdf, key : String, dict : PdfObject) -> Int?

    Return the object number of an indirect dictionary object, if it is indirect.

    Pdf::lookup_chain

    fn Pdf::lookup_chain(self : Pdf, start : PdfObject, keys : ArrayView[String]) -> PdfObject?

    Lookup a key in a nested dictionary chain.

    Pdf::lookup_direct

    fn Pdf::lookup_direct(self : Pdf, key : String, dict : PdfObject) -> PdfObject?

    Lookup the key, resolving indirections at source and destination.

    Pdf::lookup_direct_orelse

    fn Pdf::lookup_direct_orelse(self : Pdf, key : String, alt : String, dict : PdfObject) -> PdfObject?

    Same as lookup_direct, but allow a second alternative key.

    Pdf::lookup_fail

    fn Pdf::lookup_fail(self : Pdf, errtext : String, key : String, dict : PdfObject) -> PdfObject raise

    Lookup an object, failing if not found.

    Pdf::lookup_obj

    fn Pdf::lookup_obj(self : Pdf, objnum : Int) -> PdfObject

    Lookup an object in a document, parsing it if required.

    Pdf::nametree_lookup

    fn Pdf::nametree_lookup(self : Pdf, key : PdfObject, dict : PdfObject) -> PdfObject? raise

    Look something up in a name tree.

    Pdf::objcard

    fn Pdf::objcard(self : Pdf) -> Int

    Return the size of the object map.

    Pdf::objects_referenced

    fn Pdf::objects_referenced(self : Pdf, no_follow_entries : Array[String], no_follow_contains : Array[(String, PdfObject)], pdfobject : PdfObject) -> Array[Int]

    Find the objects reachable from the given object.

    Pdf::objiter

    fn Pdf::objiter(self : Pdf, f : (Int, PdfObject) -> Unit raise?) -> Unit raise?

    Iterate over all objects in a document.

    Pdf::objiter_gen

    fn Pdf::objiter_gen(self : Pdf, f : (Int, Int, PdfObject) -> Unit raise?) -> Unit raise?

    Iterate over all objects in a document with generation numbers.

    Pdf::objiter_inorder

    fn Pdf::objiter_inorder(self : Pdf, f : (Int, PdfObject) -> Unit raise?) -> Unit raise?

    Iterate over all objects in a document, ordered by object number.

    Pdf::objnumbers

    fn Pdf::objnumbers(self : Pdf) -> Array[Int]

    Return the object numbers in ascending order.

    Pdf::objselect

    fn Pdf::objselect(self : Pdf, f : (PdfObject) -> Bool raise?) -> Array[Int] raise?

    Select objects matching a predicate and return their numbers.

    Pdf::objselfmap

    fn Pdf::objselfmap(self : Pdf, f : (PdfObject) -> PdfObject raise?) -> Unit raise?

    Map a function over all objects in a document.

    Pdf::page_reference_numbers

    fn Pdf::page_reference_numbers(self : Pdf) -> Array[Int] raise

    Return the page reference numbers in order.

    Pdf::parse_matrix

    fn Pdf::parse_matrix(self : Pdf, key : String, dict : PdfObject) ->
    TransformMatrix
    raise

    Parse a transform matrix, or return the identity if missing.

    Pdf::parse_rectangle

    fn Pdf::parse_rectangle(self : Pdf, obj : PdfObject) -> (Double, Double, Double, Double) raise

    Parse a PDF rectangle structure into min x, min y, max x, max y.

    Pdf::remove_unreferenced

    fn Pdf::remove_unreferenced(self : Pdf) -> Unit raise

    Remove any unreferenced objects.

    Pdf::removeobj

    fn Pdf::removeobj(self : Pdf, objnum : Int) -> Unit

    Remove the given object.

    Pdf::renumber

    fn Pdf::renumber(self : Pdf, change_table : Map[Int, Int], preserve_order? : Bool) -> Pdf

    Renumber a PDF's objects using a change table.

    Pdf::renumber_object_parsed

    fn Pdf::renumber_object_parsed(self : Pdf, changes : Map[Int, Int], obj : PdfObject, preserve_order? : Bool) -> PdfObject

    Renumber indirect references using a change table.

    Pdf::replace_chain

    fn Pdf::replace_chain(self : Pdf, chain : ArrayView[String], obj : PdfObject) -> Unit raise

    Replace or insert a chain from the trailer dictionary.

    Pdf::transform_quadpoints

    fn Pdf::transform_quadpoints(self : Pdf, transform :
    TransformMatrix
    , qp : PdfObject) -> PdfObject raise

    Transform quadpoints by a matrix.

    Pdf::transform_rect

    fn Pdf::transform_rect(self : Pdf, transform :
    TransformMatrix
    , rect : PdfObject) -> PdfObject raise

    Transform a rectangle by a matrix, returning the bounding rectangle.

    PdfName

    pub(all) struct PdfName(Bytes) derive(Eq, Hash,
    Debug
    )

    PDF Name object stored as raw bytes.

    PDF names are byte sequences (not Unicode text). In PDF syntax they are written like /Type and may use #xx hex escapes to represent arbitrary bytes. Our lexer decodes #xx escapes into the underlying bytes.

    This type keeps the bytes (including the leading / byte) so that:
    • parsing/serialization is byte-faithful,
    • we avoid accidentally applying Unicode/text semantics,
    • comparisons can be made lexicographically by byte.
    impl Compare for PdfName
    impl Show for PdfName

    PdfName::compare

    fn PdfName::compare(self : PdfName, other : PdfName) -> Int

    PdfName::equal

    fn PdfName::equal(PdfName, PdfName) -> Bool

    PdfName::equal_string_bytes

    fn PdfName::equal_string_bytes(self : PdfName, key : String) -> Bool

    Compare a PdfName with a name key written as a String (byte-string).

    This expects key to contain the leading / and to be in the same "bytes-in-String" encoding as PdfName::to_string_bytes.

    PdfName::hash

    fn PdfName::hash(self : PdfName) -> Int

    PdfName::hash_combine

    fn PdfName::hash_combine(PdfName, Hasher) -> Unit

    PdfName::lexical_compare

    fn PdfName::lexical_compare(self : PdfName, other : PdfName) -> Int

    Compare by raw bytes, lexicographically (prefix order).

    Note: MoonBit's default compare for sequences/strings is shortlex (length-first, then element-wise). PDF name ordering (e.g. in name trees) needs byte-wise lexicographic order, so we implement it explicitly.

    PdfName::not_equal

    fn PdfName::not_equal(x : PdfName, y : PdfName) -> Bool

    PdfName::of_bytes

    fn PdfName::of_bytes(bytes : Bytes) -> PdfName

    Build a PdfName from raw bytes.

    bytes should include the leading / byte.

    PdfName::of_string

    fn PdfName::of_string(value : String) -> PdfName

    Build a PdfName from a "bytes-in-String" representation.

    This is mainly for internal construction sites and tests. Each UTF-16 code unit is truncated to its low 8 bits (i.e. treated as a byte).

    PdfName::op_ge

    fn PdfName::op_ge(x : PdfName, y : PdfName) -> Bool

    PdfName::op_gt

    fn PdfName::op_gt(x : PdfName, y : PdfName) -> Bool

    PdfName::op_le

    fn PdfName::op_le(x : PdfName, y : PdfName) -> Bool

    PdfName::op_lt

    fn PdfName::op_lt(x : PdfName, y : PdfName) -> Bool

    PdfName::output

    fn PdfName::output(self : PdfName, logger : &Logger) -> Unit

    PdfName::to_bytes

    fn PdfName::to_bytes(self : PdfName) -> Bytes

    Raw bytes (including the leading /).

    PdfName::to_repr

    PdfName::to_string

    fn PdfName::to_string(self : PdfName) -> String

    PdfName::to_string_bytes

    fn PdfName::to_string_bytes(self : PdfName) -> String

    Convert to the historical "bytes-in-String" representation.

    This maps each byte to a single UTF-16 code unit with the same numeric value (0..255). It is not a Unicode decoding.

    PdfObject

    pub(all) enum PdfObject {
    Null
    Boolean(Bool)
    Integer(Int)
    Real(Double)
    PdfString(String)
    Name(PdfName)
    PdfArray(Array[PdfObject])
    Dictionary(Array[(String, PdfObject)])
    Stream(
    Ref
    [(PdfObject, Stream)])
    Indirect(Int)
    } derive(
    Debug
    )

    Core PDF object tree.

    This is a low-level representation close to PDF syntax. In particular:
    • PdfObject::PdfString is treated as a byte string by the parser/serializer.
    • PdfObject::Dictionary keys are also stored as "bytes-in-String" so we can round-trip weird/malformed PDFs without losing information.

    PdfObject::add_entry

    fn PdfObject::add_entry(self : PdfObject, key : String, value : PdfObject) -> PdfObject raise

    Add a dictionary entry, replacing if already present (also works for streams).

    PdfObject::bigarray_of_stream

    fn PdfObject::bigarray_of_stream(self : PdfObject) -> Array[Byte] raise

    Find the contents of a stream as bytes.

    PdfObject::find_indirect

    fn PdfObject::find_indirect(self : PdfObject, key : String) -> Int? raise

    Find the indirect reference given by the value associated with a key.

    PdfObject::getstream

    fn PdfObject::getstream(self : PdfObject) -> Unit raise

    Get a stream from disk if it hasn't already been got.

    PdfObject::lookup_immediate

    fn PdfObject::lookup_immediate(self : PdfObject, key : String) -> PdfObject?

    Lookup the key without following indirects at either source or destination.

    PdfObject::lookup_immediate_all

    fn PdfObject::lookup_immediate_all(self : PdfObject, key : String) -> Array[PdfObject]

    Lookup all values for a key without following indirects.

    This is useful for handling malformed PDFs where dictionaries may contain duplicate keys.

    PdfObject::remove_entry

    fn PdfObject::remove_entry(self : PdfObject, key : String) -> PdfObject raise

    Remove a dictionary entry (also works for streams).

    PdfObject::replace_entry

    fn PdfObject::replace_entry(self : PdfObject, key : String, value : PdfObject) -> PdfObject raise

    Replace a dictionary entry, raising if it's not present (also works for streams).

    PdfObject::to_string

    fn PdfObject::to_string(self : PdfObject) -> String

    Convert a PDF object to its string representation.

    PdfObject::to_string_including_data

    fn PdfObject::to_string_including_data(self : PdfObject) -> String

    Convert a PDF object to string including stream data.

    PdfObjects

    pub(all) struct PdfObjects {
    max_obj_num : Int
    parse : (Int) -> PdfObject?
    objects : Map[Int, (
    Ref
    [ObjectData], Int)]
    object_stream_ids : Map[Int, Int]
    }

    Container for document objects.

    SavedEncryption

    pub(all) struct SavedEncryption {
    from_get_encryption_values : (
    Encryption
    , String, String, Int, String, String?, String?)
    encrypt_metadata : Bool
    perms : String
    }

    Saved encryption metadata used by recrypt helpers.

    Stream

    pub(all) enum Stream {
    Got(Array[Byte])
    ToGet(ToGet)
    } derive(
    Debug
    )

    Stream payload, either in memory or deferred.

    Stream::to_repr

    ToGet

    pub(all) struct ToGet {
    input :
    Input

    position : Int
    length : Int
    crypt : ToGetCrypt
    } derive(
    Debug
    )

    Stream descriptor when bytes are not yet materialized.

    ToGet::length

    fn ToGet::length(self : ToGet) -> Int

    ToGet::new

    fn ToGet::new(input :
    Input
    , position : Int, length : Int, crypt? : ToGetCrypt) -> ToGet

    ToGet::to_repr

    ToGetCrypt

    pub(all) enum ToGetCrypt {
    NoChange
    ToDecrypt(DeferredEncryption)
    } derive(
    Debug
    )

    Indicates whether a stream needs decryption when materialized.

    input_pdferror

    fn input_pdferror(input :
    Input
    , message : String) -> String

    Build an error string which includes the input source and position.

    is_whitespace

    fn is_whitespace(c : Char) -> Bool

    PDF whitespace predicate.

    make_matrix

    Build a matrix object.

    pdfobjmap_bindings

    fn pdfobjmap_bindings(map : Map[Int, (
    Ref
    [ObjectData], Int)]) -> Array[(Int, (
    Ref
    [ObjectData], Int))]

    pdfobjmap_empty

    fn pdfobjmap_empty() -> Map[Int, (
    Ref
    [ObjectData], Int)]

    pdfobjmap_find

    fn pdfobjmap_find(key : Int, map : Map[Int, (
    Ref
    [ObjectData], Int)]) -> (
    Ref
    [ObjectData], Int) raise

    recurse_array

    fn recurse_array(f : (PdfObject) -> PdfObject, elts : Array[PdfObject]) -> PdfObject

    Recursively rebuild an array.

    renumber_pdfs

    fn renumber_pdfs(pdfs : Array[Pdf]) -> Array[Pdf]

    Renumber a list of PDFs so their object numbers are mutually exclusive.