start to finish

main
Matthew Butterick 7 years ago
parent 741a8e2597
commit 6bf2ee32be

@ -2,7 +2,7 @@
(require racket/draw)
(provide PDFDocument)
(require "reference.rkt")
(require "reference.rkt" "struct.rkt" "object.rkt")
;(require "page.rkt")
(define PDFDocument
@ -31,9 +31,9 @@
(field [_offset 0])
(field [_root (ref
(hasheq 'Type 'Catalog'
(hasheq 'Type "Catalog"
'Pages (ref
(hasheq 'Type 'Pages'
(hasheq 'Type "Pages"
'Count 0
'Kids empty))))])
@ -122,37 +122,89 @@
(set! _pageBufferStart (+ _pageBufferStart (length pages)))
(for ([page (in-list pages)])
(send page end)))
(define/public (ref data)
;; every js function argument is 'undefined' by default
;; so even a function defined without default values
;; can be called without arguments
(define/public (ref [data (make-hasheq)])
(define ref (make-object PDFReference this (add1 (length _offsets)) data))
(push! _offsets #f) ; placeholder for this object's offset once it is finalized
(set! _waiting (add1 _waiting))
ref)
(define/public (push chunk)
(push! byte-strings chunk))
(define/public (_write data)
(let ([data (if (not (bytes? data))
; `string->bytes/latin-1` is equivalent to plain binary encoding
(string->bytes/latin-1 (string-append data "\n"))
data)])
(push! byte-strings data)
(push data)
(report byte-strings)
(set! _offset (+ _offset (bytes-length data)))))
(define/public (pipe op)
(copy-port (open-input-bytes (apply bytes-append (reverse byte-strings))) op)
(close-output-port op))
(field [op #f])
(define/public (pipe port)
(set! op port))
(define _info #f)
(define/public (end)
(flushPages)
(set! _info (ref))
'done))) ; temp
(for ([(key val) (in-hash info)])
;; upgrade string literal to String struct
(hash-set! (get-field data _info) key
(if (string? val) (String val) val)))
(send _info end)
;; todo: fonts
;; for name, font of @_fontFamilies
;; font.finalize()
(send _root end)
(send (hash-ref (get-field data _root) 'Pages) end)
(if (or (zero? _waiting) #t) ; debug
(_finalize)
(set! _ended #t))
'done)
(define/public (_finalize [fn #f])
;; generate xref
(define xRefOffset _offset)
(_write "xref")
(_write (format "0 ~a" (add1 (length _offsets))))
(_write "0000000000 65535 f ")
(for ([offset (in-list _offsets)])
(_write (string-append
(~r (or offset 42) #;debug #:min-width 10 #:pad-string "0")
" 00000 n ")))
;; trailer
(_write "trailer")
;; todo: make `PDFObject:convert` a static method
(_write (send (make-object PDFObject) convert
(hasheq 'Size (add1 (length _offsets))
'Root _root
'Info _info)))
(_write "startxref")
(_write (number->string xRefOffset))
(_write "%%EOF")
;; end the stream
;; in node you (push null) which signals to the stream
;; to copy to its output port
(copy-port (open-input-bytes
(apply bytes-append (reverse byte-strings)))
op)
(close-output-port op))))
(define doc (new PDFDocument))
(module+ test
(require rackunit)
(send doc _write "foobar")
(send doc pipe (open-output-file "testrkt0.pdf" #:exists 'replace))
(check-equal? (send doc end) 'done))

@ -1,6 +1,7 @@
#lang at-exp br
(require racket/class racket/string racket/list srfi/19)
(require "struct.rkt" "reference.rkt")
(provide PDFObject)
(define PDFObject
(class object%
@ -55,7 +56,9 @@
(cond
;; String literals are converted to the PDF name type
[(string? x) (string-append "/" x)]
;; String objects are converted to PDF strings (UTF-16)
;; symbols are used for convenience - convert to string
[(symbol? x) (loop (symbol->string x))]
;; String objects (structs) are converted to PDF strings (UTF-16)
[(String? x)
;; Escape characters as required by the spec
(define string (regexp-replace* escapableRe (String-string x) (λ (c) (hash-ref escapable c))))

@ -4,7 +4,7 @@
(define PDFReference
(class object%
(init-field document id [data (hasheq)])
(init-field document id [data (make-hasheq)])
(super-new)
(field [gen 0])
(field [deflate #f])
@ -28,7 +28,7 @@
(hash-update! data 'Length (λ (len) (+ len (string-length chunk))))])
(callback))
(define/public (end chunk)
(define/public (end [chunk #f])
; (super) ; todo
(if deflate
(void) ; todo (deflate-end)

Loading…
Cancel
Save