///|
pub(all) enum PdfObject {
Null
Boolean(Bool)
Integer(Int)
Real(Double)
String(String)
Name(String)
Array(Array[PdfObject])
Dictionary(Array[(String, PdfObject)])
Stream(Ref[(PdfObject, Stream)])
Indirect(Int)
}///|
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?
}///|
pub(all) enum Stream {
Got(@pdfio.MutableBytes) // Data in memory
ToGet(ToGet) // Data still on disk
}///|
let pdf = @pdf.Pdf::empty()
// Creates PDF 2.0 with no objectslet 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)))///|
test "lookup_obj returns Null for missing objects" {
let pdf = @pdf.Pdf::empty()
assert_true(pdf.lookup_obj(999) is Null)
}///|
test "direct follows indirects" {
let pdf = @pdf.Pdf::empty()
let objnum = pdf.addobj(@pdf.PdfObject::Integer(42))
let indirect = @pdf.PdfObject::Indirect(objnum)
guard pdf.direct(indirect) is Integer(n) else { fail("expected Integer") }
inspect(n, content="42")
}///|
test "lookup_direct finds keys" {
let pdf = @pdf.Pdf::empty()
let dict = @pdf.PdfObject::Dictionary([
("/Type", @pdf.PdfObject::Name(@pdf.PdfName::of_string("/Page"))),
("/Count", @pdf.PdfObject::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)
}///|
test "lookup_chain navigates nested dicts" {
let pdf = @pdf.Pdf::empty()
let inner = @pdf.PdfObject::Dictionary([
("/Value", @pdf.PdfObject::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")
}///|
test "add_dict_entry" {
let dict = @pdf.PdfObject::Dictionary([
("/Type", @pdf.PdfObject::Name(@pdf.PdfName::of_string("/Page"))),
])
let updated = dict.add_entry("/Count", @pdf.PdfObject::Integer(1))
match updated {
Dictionary(entries) => inspect(entries.length(), content="2")
_ => fail("expected dictionary")
}
}///|
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)")
}
}///|
test "replace_dict_entry" {
let dict = @pdf.PdfObject::Dictionary([("/Count", @pdf.PdfObject::Integer(1))])
let updated = dict.replace_entry("/Count", @pdf.PdfObject::Integer(5))
guard updated.lookup_immediate("/Count") is Some(Integer(n)) else {
fail("expected Integer")
}
inspect(n, content="5")
}///|
test "remove_dict_entry" {
let dict = @pdf.PdfObject::Dictionary([
("/Type", @pdf.PdfObject::Name(@pdf.PdfName::of_string("/Page"))),
("/Count", @pdf.PdfObject::Integer(1)),
])
let updated = dict.remove_entry("/Count")
match updated {
Dictionary(entries) => inspect(entries.length(), content="1")
_ => fail("expected dictionary")
}
}obj.objiter(fn(objnum) {
println("Object \{objnum}: \{obj}")
},
pdf,
)// Find all page objects
///|
let page_nums = obj.objselect(
fn(obj) {
match pdf.lookup_direct("/Type") {
Some(Name("/Page")) => true
_ => false
}
},
pdf,
)// Apply a transformation to every object
pdf,
.objselfmap(fn(obj) {
// Return transformed object
obj
})match obj {
Stream(_) => {
obj.getstream!() // Loads data if deferred
let bytes = obj.bigarray_of_stream!()
// Use bytes...
}
_ => ()
}///|
test "parse_rectangle" {
let pdf = @pdf.Pdf::empty()
let rect = @pdf.PdfObject::Array([
@pdf.PdfObject::Real(0.0),
@pdf.PdfObject::Real(0.0),
@pdf.PdfObject::Real(612.0),
@pdf.PdfObject::Real(792.0),
])
let (minx, miny, maxx, maxy) = pdf.parse_rectangle(rect)
inspect((minx, miny, maxx, maxy), content="(0, 0, 612, 792)")
}// 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())// Find all objects reachable from a starting object
///|
let refs = pdf.objects_referenced([], [], start_obj)// Garbage collect unreferenced objects
pdf.remove_unreferenced!()// Calculate changes to renumber 1..n
///|
let change_table = pdf.changes()
// Apply renumbering
///|
let renumbered = pdf.renumber(change_table)// Create an independent copy
///|
let copy = pdf.deep_copy()// Make object numbers mutually exclusive across documents
///|
let renumbered = @pdf.renumber_pdfs([pdf1, pdf2, pdf3])// 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)///|
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")
}///|
pub(all) suberror PdfError {
Msg(String)
}test {
let map = { 3: "three", 8: "eight", 1: "one" }
assert_eq(map.get(2), None)
assert_eq(map.get(3), Some("three"))
map.set(3, "updated")
assert_eq(map.get(3), Some("updated"))
}impl ToPdfNumber for Intimpl ToPdfNumber for Doublepub(all) struct DeferredEncryption {
crypt_type : Encryption
file_encryption_key : String?
obj : Int
gen : Int
key : Array[Int]
keylength : Int
r : Int
}pub(all) struct Pdf {
major : Int
minor : Int
root : Int
objects : PdfObjects
trailerdict : PdfObject
was_linearized : Bool
saved_encryption : SavedEncryption?
}fn Pdf::transform_quadpoints(self : Pdf, transform : TransformMatrix, qp : PdfObject) -> PdfObject raisepub(all) struct PdfName {
bytes : Bytes
}pub(all) struct PdfObjects {
max_obj_num : Int
parse : (Int) -> PdfObject?
objects : Map[Int, (Ref[ObjectData], Int)]
object_stream_ids : Map[Int, Int]
}pub(all) struct SavedEncryption {
from_get_encryption_values : (Encryption, String, String, Int, String, String?, String?)
encrypt_metadata : Bool
perms : String
}fn pdfobjmap_bindings(map : Map[Int, (Ref[ObjectData], Int)]) -> Array[(Int, (Ref[ObjectData], Int))]fn pdfobjmap_find(key : Int, map : Map[Int, (Ref[ObjectData], Int)]) -> (Ref[ObjectData], Int) raiseDependencies