From 43ff762c2dcea0c02fabeef6e3e916bd1234b8c1 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Fri, 12 May 2017 22:07:23 -0700 Subject: [PATCH] one offset is wrong --- pitfall/pdfkit/lib/reference.js | 117 ++++++++++++++++++++++++++ pitfall/pitfall/kit/document.rkt | 18 +++- pitfall/pitfall/kit/mixins/vector.rkt | 2 +- pitfall/pitfall/kit/object.rkt | 9 +- pitfall/pitfall/kit/reference.rkt | 31 +++++-- pitfall/pitfall/kit/zzz.pdf | 63 ++++++++++++++ 6 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 pitfall/pdfkit/lib/reference.js create mode 100644 pitfall/pitfall/kit/zzz.pdf diff --git a/pitfall/pdfkit/lib/reference.js b/pitfall/pdfkit/lib/reference.js new file mode 100644 index 00000000..cf745f25 --- /dev/null +++ b/pitfall/pdfkit/lib/reference.js @@ -0,0 +1,117 @@ +// Generated by CoffeeScript 1.12.5 + +/* +PDFReference - represents a reference to another object in the PDF object heirarchy +By Devon Govett + */ + +(function() { + var PDFObject, PDFReference, stream, zlib, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + zlib = require('zlib'); + + stream = require('stream'); + + PDFReference = (function(superClass) { + extend(PDFReference, superClass); + + function PDFReference(document, id, data) { + this.document = document; + this.id = id; + this.data = data != null ? data : {}; + this.finalize = bind(this.finalize, this); + PDFReference.__super__.constructor.call(this, { + decodeStrings: false + }); + this.gen = 0; + this.deflate = null; + this.compress = false; + this.uncompressedLength = 0; + this.chunks = []; + } + + PDFReference.prototype.initDeflate = function() { + this.data.Filter = 'FlateDecode'; + this.deflate = zlib.createDeflate(); + this.deflate.on('data', (function(_this) { + return function(chunk) { + console.log("got data event for ref " + _this.id + " from " + _this.toString()); + _this.chunks.push(chunk); + return _this.data.Length += chunk.length; + }; + })(this)); + return this.deflate.on('end', (function(_this) { + return function() { + console.log("got end event for ref " + _this.id + " from " + _this.toString()); + return _this.finalize(); + }; + })(this)); + }; + + PDFReference.prototype._write = function(chunk, encoding, callback) { + var base; + if (!Buffer.isBuffer(chunk)) { + chunk = new Buffer(chunk + '\n', 'binary'); + } + this.uncompressedLength += chunk.length; + if ((base = this.data).Length == null) { + base.Length = 0; + } + if (this.compress) { + if (!this.deflate) { + this.initDeflate(); + } + console.log("chunk = " + chunk); + this.deflate.write(chunk); + console.log("wrote chunk"); + } else { + this.chunks.push(chunk); + this.data.Length += chunk.length; + } + return callback(); + }; + + PDFReference.prototype.end = function(chunk) { + PDFReference.__super__.end.apply(this, arguments); + if (this.deflate) { + return this.deflate.end(); + } else { + return this.finalize(); + } + }; + + PDFReference.prototype.finalize = function() { + var chunk, i, len, ref; + this.offset = this.document._offset; + this.document._write(this.id + " " + this.gen + " obj"); + this.document._write(PDFObject.convert(this.data)); + if (this.chunks.length) { + this.document._write('stream'); + ref = this.chunks; + for (i = 0, len = ref.length; i < len; i++) { + chunk = ref[i]; + this.document._write(chunk); + } + this.chunks.length = 0; + this.document._write('\nendstream'); + } + this.document._write('endobj'); + return this.document._refEnd(this); + }; + + PDFReference.prototype.toString = function() { + return this.id + " " + this.gen + " R"; + }; + + return PDFReference; + + })(stream.Writable); + + module.exports = PDFReference; + + PDFObject = require('./object'); + +}).call(this); diff --git a/pitfall/pitfall/kit/document.rkt b/pitfall/pitfall/kit/document.rkt index 5a2da0ce..a2eb1fd6 100644 --- a/pitfall/pitfall/kit/document.rkt +++ b/pitfall/pitfall/kit/document.rkt @@ -56,8 +56,8 @@ ;; Initialize the metadata (field [(@info info) (mhash - 'Producer "PitfallKit" - 'Creator "PitfallKit" + 'Producer "PDFKit" + 'Creator "PDFKit" 'CreationDate (seconds->date (current-seconds)))]) (when (hash-ref @options 'info #f) @@ -154,6 +154,16 @@ (send @page write data) this) + (define/public (_refEnd ref) + (set! @_offsets (for/list ([(offset idx) (in-indexed @_offsets)]) + (if (= idx (sub1 (· ref id))) + (· ref offset) + offset))) + (-- @_waiting) + (if (and (zero? @_waiting) @_ended) + (@_finalize) + (set! @_ended #f))) + (field [op #f]) (define/public (pipe port) (set! op port)) @@ -217,5 +227,7 @@ (require rackunit racket/file) (define ob (open-output-bytes)) (send doc pipe ob) + (send doc pipe (open-output-file "zzz.pdf" #:exists 'replace)) (check-equal? (send doc end) 'done) - (check-equal? (get-output-bytes ob) (file->bytes "demo.pdf"))) \ No newline at end of file + #;(display (get-output-bytes ob)) + #;(check-equal? (get-output-bytes ob) (file->bytes "demo.pdf"))) \ No newline at end of file diff --git a/pitfall/pitfall/kit/mixins/vector.rkt b/pitfall/pitfall/kit/mixins/vector.rkt index 88e5d917..55456d48 100644 --- a/pitfall/pitfall/kit/mixins/vector.rkt +++ b/pitfall/pitfall/kit/mixins/vector.rkt @@ -11,4 +11,4 @@ (define/public (initVector) (void)) (define/public (transform m11 m12 m21 m22 dx dy) - (send this addContent "from vector/transform")))) ; todo \ No newline at end of file + (send this addContent "1 0 0 -1 0 792 cm")))) ; todo \ No newline at end of file diff --git a/pitfall/pitfall/kit/object.rkt b/pitfall/pitfall/kit/object.rkt index e6e97a94..3c630571 100644 --- a/pitfall/pitfall/kit/object.rkt +++ b/pitfall/pitfall/kit/object.rkt @@ -1,6 +1,6 @@ #lang at-exp br (require racket/class racket/string racket/list srfi/19) -(require "struct.rkt" "reference.rkt") +(require "struct.rkt") (provide PDFObject) (define PDFObject @@ -51,6 +51,7 @@ (define result (/ (round (* n 1e6)) 1e6)) (if (integer? result) (inexact->exact result) result)) + (define/public (convert object) (let loop ([x object]) (cond @@ -73,7 +74,7 @@ [(bytes? x) (format "<~a>" (string-append* (for/list ([b (in-bytes x)]) (number->string b 16))))] - [(is-a? x PDFReference) (send x toString)] + [(object? x) (send x toString)] [(date? x) (format "(D:~aZ)" (date->string x "~Y~m~d~H~M~S"))] [(list? x) (format "[~a]" (string-join (map loop x) " "))] [(hash? x) (string-join (append (list "<<") @@ -86,7 +87,7 @@ (module+ test - (require rackunit) + (require rackunit ) (define o (new PDFObject)) (check-equal? (send o pad "foobar" -1) "oobar") (check-equal? (send o pad "foobar" 0) "foobar") @@ -106,7 +107,7 @@ (check-equal? (send o convert (String "öéÿ")) "(þÿ\u0000ö\u0000é\u0000ÿ)") (check-equal? (send o convert (String "fôobár")) "(þÿ\u0000f\u0000ô\u0000o\u0000b\u0000á\u0000r)") (check-equal? (send o convert #"foobar") "<666f6f626172>") - (check-equal? (send o convert (make-object PDFReference "foobar" 42)) "42 0 R") + #;(check-equal? (send o convert (make-object PDFReference "foobar" 42)) "42 0 R") (check-equal? (send o convert (seconds->date (quotient 1494483337320 1000) #f)) "(D:20170511061537Z)") (check-equal? (send o convert (list "foobar" (String "öéÿ") #"foobar")) "[/foobar (þÿ\u0000ö\u0000é\u0000ÿ) <666f6f626172>]") (check-equal? (send o convert (hash "foo" 42 "bar" "fly")) "<<\n/foo 42\n/bar /fly\n>>") diff --git a/pitfall/pitfall/kit/reference.rkt b/pitfall/pitfall/kit/reference.rkt index 977bbc9e..2bed4bfd 100644 --- a/pitfall/pitfall/kit/reference.rkt +++ b/pitfall/pitfall/kit/reference.rkt @@ -1,5 +1,5 @@ #lang br -(require "helper.rkt") +(require "helper.rkt" "object.rkt" file/gzip) (provide PDFReference) (define PDFReference @@ -16,16 +16,19 @@ (public [@initDeflate initDeflate]) (define (@initDeflate) - ;; todo - (void)) + (hash-ref! @data 'Filter "FlateDecode") + + (set! @deflate deflate) + (report 'initDeflate-unimplemented)) (define/public (write data) - (report data)) + (_write data #f void)) (define/public (_write chunk-in encoding callback) + (report 'boom) (define chunk (if (isBuffer? chunk-in) chunk-in - (newBuffer (string-append chunk "\n")))) + (newBuffer (string-append chunk-in "\n")))) (+= @uncompressedLength (buffer-length chunk)) (hash-ref! @data 'Length 0) (cond @@ -41,12 +44,24 @@ (void) ; todo (deflate-end) (@finalize))) - (field [offset #f]) + (field [(@offset offset) #f]) (public [@finalize finalize]) (define (@finalize) - (set! offset (· @document _offset)) + (set! @offset (· @document _offset)) + (send @document _write (format "~a ~a obj" @id @gen)) - ) + (send @document _write (send (new PDFObject) convert @data)) + + (when (positive? (length @chunks)) + (send @document _write "stream") + (for ([chunk (in-list @chunks)]) + (send @document _write chunk)) + + (set! @chunks null) ; free up memory + (send @document _write "\nendstream")) + + (send @document _write "endobj") + (send @document _refEnd this)) (define/public (toString) (format "~a ~a R" @id @gen)) diff --git a/pitfall/pitfall/kit/zzz.pdf b/pitfall/pitfall/kit/zzz.pdf new file mode 100644 index 00000000..778e96a9 --- /dev/null +++ b/pitfall/pitfall/kit/zzz.pdf @@ -0,0 +1,63 @@ +%PDF-1.3 +%ÿÿÿÿ +5 0 obj +<< +/Parent 1 0 R +/Resources 4 0 R +/Contents 3 0 R +/MediaBox [0 0 612 792] +/Type /Page +>> +endobj +4 0 obj +<< +/ProcSet [/PDF /Text /ImageB /ImageC /ImageI] +>> +endobj +3 0 obj +<< +/Length 18 +>> +stream +1 0 0 -1 0 792 cm + +endstream +endobj +6 0 obj +<< +/Producer (PDFKit) +/CreationDate (D:20170512220606Z) +/Creator (PDFKit) +>> +endobj +2 0 obj +<< +/Pages 1 0 R +/Type /Catalog +>> +endobj +1 0 obj +<< +/Kids [5 0 R] +/Count 1 +/Type /Pages +>> +endobj +xref +0 7 +0000000000 65535 f +0000000395 00000 n +0000000346 00000 n +0000057005 00000 n +0000000186 00000 n +0000000119 00000 n +0000000254 00000 n +trailer +<< +/Info 6 0 R +/Root 2 0 R +/Size 7 +>> +startxref +452 +%%EOF