91 lines
4.0 KiB
Odin
91 lines
4.0 KiB
Odin
/*
|
|
Package pdf is a self-contained PDF reader and renderer.
|
|
|
|
It parses PDF files (ISO 32000-2 / PDF 2.0, and every earlier revision in
|
|
practice), decodes their content, rasterises pages to RGBA8 bitmaps and
|
|
extracts their text. It depends only on Odin's core collection -- no C
|
|
libraries, no FreeType, no libjpeg -- so it can be dropped into any Odin
|
|
project as-is.
|
|
|
|
Reading a file and walking its pages works today:
|
|
|
|
doc, err := pdf.open_file("thesis.pdf")
|
|
if err != .None {
|
|
fmt.eprintln(pdf.error_string(err))
|
|
return
|
|
}
|
|
defer pdf.close(doc)
|
|
|
|
for i in 0 ..< pdf.page_count(doc) {
|
|
page, perr := pdf.page_get(doc, i) // Page is a value, not a pointer
|
|
if perr != .None { continue }
|
|
fmt.println(i, page.media_box, page.rotate)
|
|
}
|
|
|
|
`open_bytes` opens a file already in memory, and `Open_Options` carries the
|
|
password for an encrypted document and `skip_page_tree` for a tool that only
|
|
wants to inspect objects. Below the page tree, `get_object`, `get_dict`,
|
|
`get_stream`, `resolve`, the `dict_*` accessors and `stream_data` read the
|
|
object graph directly.
|
|
|
|
The document's navigation model is readable today too. `outline_load` returns
|
|
the bookmark tree as `Outline_Item`s, `dest_resolve` turns a destination --
|
|
written out in full, named, or carried by a /GoTo action -- into a
|
|
`Destination` naming a page index, and `page_label` gives the label a viewer
|
|
shows for a page, "iv" rather than "4". Unlike everything above they hand back
|
|
memory the caller owns: each takes an `allocator`, and the outline is freed
|
|
with `outline_destroy`.
|
|
|
|
Rendering and text extraction are the other half:
|
|
|
|
page, _ := pdf.page_get(doc, 0)
|
|
|
|
bmp, rerr := pdf.render_page(&page, {ctm = pdf.scale_matrix(2, 2)})
|
|
if rerr == .None {
|
|
defer pdf.bitmap_destroy(&bmp) // RGBA8, straight alpha
|
|
upload_texture(bmp.width, bmp.height, bmp.pixels)
|
|
}
|
|
|
|
text, _ := pdf.extract_text(&page)
|
|
defer delete(text)
|
|
|
|
`Render_Options` carries the transform, the background colour, whether the page
|
|
keeps an alpha channel and whether annotations are drawn. `render_page_to`
|
|
renders into a bitmap the caller already has, which is how a large page is
|
|
rendered in tiles. `page_base_matrix` is the transform all of them share -- PDF
|
|
user space to device pixels, with the crop box, /Rotate and /UserUnit folded in
|
|
-- and inverting it is what turns a pixel a user clicked on back into a point
|
|
on the page. Below those, `run_page` runs a page into a `Device` of the
|
|
caller's own and `page_display_list` records one for replay. A `Bitmap`'s
|
|
pixels are reachable directly -- it is a plain struct -- and `bitmap_row`,
|
|
`bitmap_make`, `bitmap_destroy` and `bitmap_to_rgba` are the accessors for
|
|
callers that would rather not compute `stride` themselves.
|
|
|
|
Memory model: a Document owns a growing arena. Every object, stream and page
|
|
parsed out of the file lives in that arena and is freed in one shot by
|
|
`close`. Nothing the library hands back out of a Document needs to be freed
|
|
individually -- a `Page`, a `^Dict` and the bytes from `stream_data` are all
|
|
borrowed and die with the Document. The exceptions are the few procedures that
|
|
take an explicit `allocator` parameter and say so in their doc comment:
|
|
`render_page`'s bitmap, `extract_text`'s string, `page_display_list`'s list and
|
|
the navigation procedures above. Rendering allocates nothing else that outlives
|
|
the call -- per-page scratch lives in an arena that is destroyed when the call
|
|
returns, so a 900-page loop peaks at one page's working set.
|
|
|
|
Thread safety: a Document is not internally synchronised. Parsing mutates
|
|
caches, so one Document should be driven by one thread at a time. Two
|
|
Documents are fully independent.
|
|
*/
|
|
package pdf
|
|
|
|
// The PDF version this library targets. Files declaring a newer version are
|
|
// still parsed -- PDF readers are required to be forward tolerant, and a
|
|
// version number in a header is not a promise about the bytes that follow.
|
|
SPEC_VERSION_MAJOR :: 2
|
|
SPEC_VERSION_MINOR :: 0
|
|
|
|
// Library version, for callers that want to report it.
|
|
VERSION_MAJOR :: 0
|
|
VERSION_MINOR :: 1
|
|
VERSION_PATCH :: 0
|