initial commit
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
// pdfinfo prints the structure of a PDF: version, page count, metadata, page
|
||||
// geometry, and how much content each page carries.
|
||||
//
|
||||
// It exists as much to keep the library's public surface honest as to be
|
||||
// useful. Everything here is reachable from outside the package, so if a
|
||||
// procedure a consumer needs is accidentally @(private), this example stops
|
||||
// compiling and scripts/check.sh catches it.
|
||||
//
|
||||
// odin run examples/pdfinfo -- file.pdf
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import pdf "../.."
|
||||
|
||||
main :: proc() {
|
||||
if len(os.args) < 2 {
|
||||
fmt.eprintfln("usage: %s <file.pdf> [more.pdf ...]", os.args[0])
|
||||
os.exit(2)
|
||||
}
|
||||
|
||||
failures := 0
|
||||
for path in os.args[1:] {
|
||||
if !report(path) {
|
||||
failures += 1
|
||||
}
|
||||
}
|
||||
if failures > 0 {
|
||||
os.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
report :: proc(path: string) -> bool {
|
||||
doc, err := pdf.open_file(path)
|
||||
if err != .None {
|
||||
fmt.eprintfln("%s: %s", path, pdf.error_string(err))
|
||||
return false
|
||||
}
|
||||
defer pdf.close(doc)
|
||||
|
||||
major, minor := pdf.document_version(doc)
|
||||
fmt.printfln("%s", path)
|
||||
fmt.printfln(" PDF %d.%d, %d pages, %d objects", major, minor,
|
||||
pdf.page_count(doc), pdf.object_count(doc))
|
||||
|
||||
print_info(doc)
|
||||
|
||||
total := 0
|
||||
for i in 0 ..< pdf.page_count(doc) {
|
||||
page, perr := pdf.page_get(doc, i)
|
||||
if perr != .None {
|
||||
fmt.printfln(" page %d: %s", i + 1, pdf.error_string(perr))
|
||||
continue
|
||||
}
|
||||
|
||||
content, cerr := pdf.page_content(&page)
|
||||
defer delete(content)
|
||||
if cerr == .None {
|
||||
total += len(content)
|
||||
}
|
||||
|
||||
// Only the first few, or a thousand-page standard buries everything.
|
||||
if i < 5 {
|
||||
b := pdf.page_bounds(&page)
|
||||
w, h := pdf.page_size(&page, 1)
|
||||
fmt.printfln(" page %d: %.1f x %.1f pt, rotate %d, %d x %d px, %d bytes of content",
|
||||
i + 1, pdf.rect_width(b), pdf.rect_height(b), page.rotate, w, h, len(content))
|
||||
}
|
||||
}
|
||||
fmt.printfln(" %d bytes of content stream decoded, %d KB parsed",
|
||||
total, pdf.document_memory_used(doc) / 1024)
|
||||
|
||||
// A file that needed repairs still opened; say what was worked around, so
|
||||
// the difference between "clean" and "damaged but readable" is visible.
|
||||
if doc.is_damaged {
|
||||
fmt.printfln(" %d repairs:", len(doc.repairs))
|
||||
shown := 0
|
||||
for r in doc.repairs {
|
||||
if shown >= 5 { break }
|
||||
fmt.printfln(" %s", pdf.repair_string(r.kind))
|
||||
shown += 1
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Every value in the document information dictionary is a text string, so it
|
||||
// has to go through decode_text_string: the common encodings are UTF-16BE with
|
||||
// a byte-order mark and PDFDocEncoding, and neither is UTF-8.
|
||||
print_info :: proc(doc: ^pdf.Document) {
|
||||
info := pdf.dict_dict(doc, doc.trailer, "Info")
|
||||
if info == nil { return }
|
||||
for key in ([]string{"Title", "Author", "Subject", "Creator", "Producer", "CreationDate", "ModDate"}) {
|
||||
raw := pdf.dict_bytes(doc, info, key)
|
||||
if len(raw) == 0 { continue }
|
||||
text := pdf.decode_text_string(raw)
|
||||
defer delete(text)
|
||||
fmt.printfln(" %-13s %s", key, text)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user