You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/pitfall/pitfall/document.rkt

151 lines
5.4 KiB
Racket

6 years ago
#lang debug racket/base
(require
6 years ago
"core.rkt"
racket/class
racket/format
6 years ago
racket/generator
6 years ago
racket/match
racket/list
sugar/unstable/dict
"reference.rkt"
"object.rkt"
"page.rkt"
"vector.rkt"
"color.rkt"
"fonts.rkt"
"text.rkt"
"images.rkt"
"annotations.rkt")
7 years ago
(provide PDFDocument)
6 years ago
(define PDFDocument
(class (annotation-mixin (image-mixin (text-mixin (fonts-mixin (color-mixin (vector-mixin object%))))))
(super-new)
(init-field [(@options options) (mhasheq)])
(field [@pages null]
[@refs null]
[@ref-gen (generator () (let loop ([refid 1])
6 years ago
(yield refid)
(loop (add1 refid))))]
[@root (ref 'Type "Catalog"
'Pages (ref 'Type "Pages"
'Count 0
'Kids empty))] ; top object
6 years ago
[(@x x) 0]
[(@y y) 0]
[@info (mhasheq 'Producer "PITFALL"
'Creator "PITFALL"
'CreationDate (seconds->date (if (test-mode)
0
(current-seconds)) #f))]) ; Initialize the metadata
6 years ago
;; Initialize mixins
(send this initColor)
(send this initVector)
(inherit-field _ctm)
(send this initFonts)
(inherit-field @font-families)
(send this initText)
(send this initImages)
;; initialize params
(current-compress-streams? (hash-ref @options 'compress #t))
(current-auto-first-page (hash-ref @options 'autoFirstPage #t))
(current-doc-offset 0)
6 years ago
(when (current-auto-first-page) (add-page))
6 years ago
(define/public (page) (first @pages))
6 years ago
(define/public (ref . args)
(define refid (@ref-gen))
6 years ago
(define payload (match args
[(list (? hash? h)) h]
[_ (define h (make-hasheq))
(for ([pr (in-hash-pairs (apply hasheq args))])
(hash-set! h (car pr) (cdr pr)))
h]))
(define new-ref (make-object PDFReference this refid payload))
(set! @refs (cons new-ref @refs))
new-ref)
6 years ago
(define/public (write x)
(define bstr (if (bytes? x) x (string->bytes/latin-1 (string-append x "\n"))))
(write-bytes bstr)
(current-doc-offset (file-position (current-output-port))))
6 years ago
6 years ago
(define/public (add-page [options-arg @options])
6 years ago
;; end the current page if needed
(unless (hash-ref @options 'bufferPages #f)
(flush-pages))
;; create a page object
6 years ago
(define page-parent (send @root get-key 'Pages))
(set! @pages (cons (make-object PDFPage this page-parent options-arg) @pages))
7 years ago
6 years ago
;; in Kids, store page dictionaries in correct order
;; this determines order in document
(define pages (send @root get-key 'Pages))
(send pages update-key! 'Kids (λ (val) (append val (list (get-field dictionary (page))))))
(send pages set-key! 'Count (length (send pages get-key 'Kids)))
6 years ago
;; reset x and y coordinates
(set! @x (hash-ref (get-field margins (page)) 'left))
(set! @y (hash-ref (get-field margins (page)) 'top))
;; flip PDF coordinate system so that the origin is in
;; the top left rather than the bottom left
(set! _ctm default-ctm-value)
(send this transform 1 0 0 -1 0 (get-field height (page)))
this)
(define/public (flush-pages)
(for-each (λ (p) (send p end)) @pages)
(set! @pages empty))
(define/public (addContent data)
(send (page) write data)
this)
6 years ago
(define/public (end)
(write (format "%PDF-~a\n%ÿÿÿÿ" (current-pdf-version)))
6 years ago
(flush-pages)
(define doc-info (ref))
(for ([(key val) (in-hash @info)])
(send doc-info set-key! key (if (string? val) (String val) val)))
6 years ago
(send doc-info end)
6 years ago
6 years ago
(for ([font (in-hash-values @font-families)])
(send font finalize))
(send @root end)
(send (send @root get-key 'Pages) end)
6 years ago
(define xref-offset (current-doc-offset))
(match-define (list this-idxs this-offsets)
(match (reverse @refs)
[(list refs ...) (list (map (λ (ref) (get-field id ref)) refs)
(map (λ (ref) (get-field offset ref)) refs))]))
6 years ago
(write "xref")
(write (format "0 ~a" (add1 (length this-offsets))))
(write "0000000000 65535 f ")
(let ([missing-offsets (for/list ([offset (in-list this-offsets)]
[idx (in-list this-idxs)]
#:unless (number? offset))
idx)])
(unless (empty? missing-offsets)
(raise-argument-error 'document:end "numerical offsets" missing-offsets)))
(for ([offset (in-list this-offsets)]
[idx (in-list this-idxs)])
(write (string-append (~r offset #:min-width 10 #:pad-string "0") " 00000 n ")))
6 years ago
6 years ago
(write "trailer")
6 years ago
(write (convert (mhasheq 'Size (add1 (length this-offsets))
'Root @root
'Info doc-info)))
6 years ago
(write "startxref")
(write (numberizer xref-offset))
(write "%%EOF"))
6 years ago
(for ([(key val) (in-hash (hash-ref @options 'info (hasheq)))])
(hash-set! @info key val))))
6 years ago
(module+ test
(define d (new PDFDocument)))