From 9a433cc96b09c1080c1f6ba0c40f276c22a31e80 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Sat, 10 Jun 2017 19:45:41 -0700 Subject: [PATCH] resume in bad stream length of loca --- pitfall/fontkit/directory.rkt | 3 +- pitfall/fontkit/head.rkt | 16 +- pitfall/fontkit/helper.rkt | 3 +- pitfall/fontkit/loca.rkt | 259 +++++++++++++++++- pitfall/fontkit/subset.rkt | 8 +- .../pitfall/test/assets/charter-italic.ttf | Bin 0 -> 40796 bytes pitfall/restructure/array.rkt | 3 +- pitfall/restructure/decodestream.rkt | 8 +- pitfall/restructure/struct.rkt | 20 +- pitfall/restructure/versioned-struct.rkt | 20 +- 10 files changed, 306 insertions(+), 34 deletions(-) create mode 100644 pitfall/pitfall/test/assets/charter-italic.ttf diff --git a/pitfall/fontkit/directory.rkt b/pitfall/fontkit/directory.rkt index 19b7e1c0..cc85b418 100644 --- a/pitfall/fontkit/directory.rkt +++ b/pitfall/fontkit/directory.rkt @@ -15,6 +15,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/directory.js (define-subclass RStruct (RDirectory) (define/override (process res stream) + ;; in `restructure` `process` method, `res` is aliased as `this` (define new-tables-val (mhash)) (for ([table (in-list (· res tables))]) (hash-set! new-tables-val (string->symbol (· table tag)) table)) @@ -29,7 +30,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/directory.js 'tables (make-object RArray TableEntry 'numTables)))) (define (directory-decode ip [options (mhash)]) - (define is (make-object RDecodeStream ip)) + (define is (make-object RDecodeStream (port->bytes ip))) (send Directory decode is)) diff --git a/pitfall/fontkit/head.rkt b/pitfall/fontkit/head.rkt index 9af73b5d..8e46e09c 100644 --- a/pitfall/fontkit/head.rkt +++ b/pitfall/fontkit/head.rkt @@ -32,27 +32,29 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/head.js (test-module (require "directory.rkt") - (define ip (open-input-file charter-path)) + (define ip (open-input-file charter-italic-path)) ; use italic to make sure style flags are set correctly (define dir (directory-decode ip)) (define offset (· dir tables head offset)) (define length (· dir tables head length)) (check-equal? offset 236) (check-equal? length 54) - (define table-bytes #"\0\1\0\0\0\2\0\0@\247\22 _\17<\365\0\t\3\350\0\0\0\0\316\3\301?\0\0\0\0\316\3\304\363\377_\377\24\4\251\3\303\0\0\0\t\0\2\0\0\0\0") + (define table-bytes #"\0\1\0\0\0\2\0\0.\252t<_\17<\365\0\t\3\350\0\0\0\0\316\3\301\261\0\0\0\0\316\3\304\364\377\36\377\24\4\226\3\324\0\2\0\t\0\2\0\0\0\0") (set-port-position! ip 0) (check-equal? (peek-bytes length offset ip) table-bytes) (define table-data (send head decode (make-object RDecodeStream table-bytes))) (check-equal? (· table-data unitsPerEm) 1000) (check-equal? (· table-data yMin) -236) - (check-equal? (· table-data yMax) 963) - (check-equal? (· table-data xMax) 1193) - (check-equal? (· table-data xMin) -161) + (check-equal? (· table-data yMax) 980) + (check-equal? (· table-data xMax) 1174) + (check-equal? (· table-data xMin) -226) (check-equal? (· table-data macStyle) (make-hash '((shadow . #f) (extended . #f) (condensed . #f) (underline . #f) (outline . #f) (bold . #f) - (italic . #f)))) - (check-equal? (· table-data magicNumber) #x5F0F3CF5)) + (italic . #t)))) + (check-equal? (· table-data magicNumber) #x5F0F3CF5) + (check-equal? (· table-data indexToLocFormat) 0) ; used in loca table + ) diff --git a/pitfall/fontkit/helper.rkt b/pitfall/fontkit/helper.rkt index 176f6716..a20f7701 100644 --- a/pitfall/fontkit/helper.rkt +++ b/pitfall/fontkit/helper.rkt @@ -4,7 +4,8 @@ (define index? (λ (x) (and (number? x) (integer? x) (not (negative? x))))) -(define-runtime-path charter-path "../pitfall/test/assets/Charter.ttf") +(define-runtime-path charter-path "../pitfall/test/assets/charter.ttf") +(define-runtime-path charter-italic-path "../pitfall/test/assets/charter-italic.ttf") (define-macro (test-module . EXPRS) #`(module+ test diff --git a/pitfall/fontkit/loca.rkt b/pitfall/fontkit/loca.rkt index 9894b70d..fb5c02c2 100644 --- a/pitfall/fontkit/loca.rkt +++ b/pitfall/fontkit/loca.rkt @@ -2,20 +2,269 @@ (require restructure) (provide (all-defined-out)) +(define short-style 0) + #| approximates https://github.com/mbutterick/fontkit/blob/master/src/tables/loca.js |# -(define-subclass RVersionedStruct (Rloca)) +(define-subclass RVersionedStruct (Rloca) + (define/override (process res stream) + ;; in `restructure` `process` method, `res` is aliased as `this` + ;; + (when (= short-style (· res version)) + ;; in a short-style loca table, actual offset values are divided by 2 (to fit into 16 bits) + ;; so we re-inflate them. + (hash-update! res 'offsets (λ (offsets) (map (curry * 2) offsets)))))) (define loca (make-object Rloca - (λ (this) (hash-ref (send this _getTable 'head) 'indexToLocFormat)) + (λ (parent) (hash-ref (send parent _getTable 'head) 'indexToLocFormat)) (dictify - - + 0 (dictify 'offsets (make-object RArray uint16be)) + 1 (dictify 'offsets (make-object RArray uint32be)) ))) (test-module - ) + (require "directory.rkt") + (define ip (open-input-file charter-path)) + (define dir (directory-decode ip)) + (define offset (· dir tables loca offset)) + (define len (· dir tables loca length)) + (check-equal? offset 38692) + (check-equal? len 460) + (set-port-position! ip 0) + (define ds (make-object RDecodeStream (peek-bytes len offset ip))) + (define table-data (send loca decode ds #:version 0)) + (check-equal? (length (· table-data offsets)) 230) + (check-equal? (· table-data offsets) '(0 + 0 + 0 + 136 + 296 + 500 + 864 + 1168 + 1548 + 1628 + 1716 + 1804 + 1944 + 2048 + 2128 + 2176 + 2256 + 2312 + 2500 + 2596 + 2788 + 3052 + 3168 + 3396 + 3624 + 3732 + 4056 + 4268 + 4424 + 4564 + 4640 + 4728 + 4804 + 5012 + 5384 + 5532 + 5808 + 6012 + 6212 + 6456 + 6672 + 6916 + 7204 + 7336 + 7496 + 7740 + 7892 + 8180 + 8432 + 8648 + 8892 + 9160 + 9496 + 9764 + 9936 + 10160 + 10312 + 10536 + 10780 + 10992 + 11148 + 11216 + 11272 + 11340 + 11404 + 11444 + 11524 + 11820 + 12044 + 12216 + 12488 + 12728 + 12932 + 13324 + 13584 + 13748 + 13924 + 14128 + 14232 + 14592 + 14852 + 15044 + 15336 + 15588 + 15776 + 16020 + 16164 + 16368 + 16520 + 16744 + 16984 + 17164 + 17320 + 17532 + 17576 + 17788 + 17896 + 18036 + 18284 + 18552 + 18616 + 18988 + 19228 + 19512 + 19712 + 19796 + 19976 + 20096 + 20160 + 20224 + 20536 + 20836 + 20876 + 21000 + 21200 + 21268 + 21368 + 21452 + 21532 + 21720 + 21908 + 22036 + 22244 + 22664 + 22872 + 22932 + 22992 + 23088 + 23220 + 23268 + 23372 + 23440 + 23600 + 23752 + 23868 + 23988 + 24084 + 24184 + 24224 + 24548 + 24788 + 25012 + 25292 + 25716 + 25884 + 26292 + 26396 + 26540 + 26796 + 27172 + 27488 + 27512 + 27536 + 27560 + 27584 + 27912 + 27936 + 27960 + 27984 + 28008 + 28032 + 28056 + 28080 + 28104 + 28128 + 28152 + 28176 + 28200 + 28224 + 28248 + 28272 + 28296 + 28320 + 28344 + 28368 + 28392 + 28416 + 28440 + 28464 + 28488 + 28512 + 28536 + 28560 + 28968 + 28992 + 29016 + 29040 + 29064 + 29088 + 29112 + 29136 + 29160 + 29184 + 29208 + 29232 + 29256 + 29280 + 29304 + 29328 + 29352 + 29376 + 29400 + 29424 + 29448 + 29472 + 29496 + 29520 + 29824 + 30164 + 30220 + 30652 + 30700 + 30956 + 31224 + 31248 + 31332 + 31488 + 31636 + 31916 + 32104 + 32176 + 32484 + 32744 + 32832 + 32956 + 33248 + 33664 + 33884 + 34048 + 34072))) diff --git a/pitfall/fontkit/subset.rkt b/pitfall/fontkit/subset.rkt index f809e145..0848f905 100644 --- a/pitfall/fontkit/subset.rkt +++ b/pitfall/fontkit/subset.rkt @@ -73,13 +73,15 @@ https://github.com/mbutterick/fontkit/blob/master/src/subset/TTFSubset.js (hash-set! maxp 'numGlyphs (length (· this glyf))) ; todo ;; todo - (hash-update! (send (· this font) _getTable 'loca) 'offsets (λ (val) (push-end! val (· this offset)))) + (report 'boing) + (report (send (· this font) _getTable 'loca) 'offsets) + #;(hash-update! (report (send (· this font) _getTable 'loca)) 'offsets (λ (val) (push-end! val (· this offset)))) ;; Tables.loca.preEncode.call(this.loca); - (define head (cloneDeep (send (· this font) _getTable 'head))) + #;(define head (cloneDeep (send (· this font) _getTable 'head))) ;; head.indexToLocFormat = this.loca.version; ; todo - (define hhea (cloneDeep (send (· this font) _getTable 'hhea))) + #;(define hhea (cloneDeep (send (· this font) _getTable 'hhea))) ;; hhea.numberOfMetrics = this.hmtx.metrics.length; (unfinished) diff --git a/pitfall/pitfall/test/assets/charter-italic.ttf b/pitfall/pitfall/test/assets/charter-italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a65012142b936d695290fe0530c82d86d7edc421 GIT binary patch literal 40796 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 (format "~a~a" key endian)) value)))) ;; basically just a wrapper for a Racket port -(define-subclass object% (RDecodeStream [buffer-in #""]) +(define-subclass object% (RDecodeStream [buffer-in #f]) (field [_port (cond + [(not buffer-in) (open-input-bytes (bytes))] [(bytes? buffer-in) (open-input-bytes buffer-in)] [(input-port? buffer-in) buffer-in] [else (raise-argument-error 'RDecodeStream "bytes or input port" buffer-in)])]) (getter-field [pos (port-position _port)]) + (getter-field [length (and (bytes? buffer-in) (bytes-length buffer-in))]) (define/public (read count) (read-bytes-exact count _port))) \ No newline at end of file diff --git a/pitfall/restructure/struct.rkt b/pitfall/restructure/struct.rkt index e433dbe4..3db94a1e 100644 --- a/pitfall/restructure/struct.rkt +++ b/pitfall/restructure/struct.rkt @@ -7,12 +7,16 @@ approximates https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee |# -(define-subclass RBase (RStruct assocs) - (field [key-index (map car assocs)] +(define-subclass RBase (RStruct [assocs (dictify)]) + (field [key-index #f] [fields (mhash)]) (for ([(k v) (in-dict assocs)]) (hash-set! fields k v)) + (define/public (make-key-index! [fields assocs]) + (set! key-index (map car fields))) + (make-key-index!) + (define/override (decode stream [parent #f] [length 0]) (define res (_setup stream parent length)) (_parseFields stream res fields) @@ -24,7 +28,7 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee (for ([key (in-list key-index)]) (send (hash-ref fields key) encode stream (hash-ref val key)))) - (define/private (_setup stream parent length) + (define/public-final (_setup stream parent length) (define res (mhasheq)) ;; define hidden properties @@ -35,13 +39,13 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee '_length (mhasheq 'value length))) res) - (define/private (_parseFields stream res field) + (define/public-final (_parseFields stream res fields) (for ([key (in-list key-index)]) - (define hashvalue (hash-ref fields key)) + (define dictvalue (dict-ref fields key)) (define val - (if (procedure? hashvalue) - (hashvalue res) - (send hashvalue decode stream res))) + (if (procedure? dictvalue) + (dictvalue res) + (send dictvalue decode stream res))) (hash-set! res key val))) ) diff --git a/pitfall/restructure/versioned-struct.rkt b/pitfall/restructure/versioned-struct.rkt index c46e0cc7..7c091ab0 100644 --- a/pitfall/restructure/versioned-struct.rkt +++ b/pitfall/restructure/versioned-struct.rkt @@ -8,14 +8,22 @@ https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee |# (define-subclass RStruct (RVersionedStruct type [versions (dictify)]) - - (define/override (decode stream [parent #f] [length 0]) + + (define/override (decode stream [parent #f] [length 0] #:version [maybe-version #f]) (define res (send this _setup stream parent length)) (define version (cond + [maybe-version] [(procedure? type) (type parent)] [(is-a? type RBase) (send type decode stream)] [else (raise-argument-error 'decode "way of finding version" type)])) - (report version 'yay) - #;(_parseFields stream res fields) - #;(send this process res stream) - res)) \ No newline at end of file + (hash-set! res 'version version) + (define fields (dict-ref versions version (λ () (raise-argument-error 'RVersionedStruct:decode "valid version key" version)))) + (send this make-key-index! fields) + (cond + [(is-a? fields RVersionedStruct) (send fields decode stream parent)] + [else + (send this _parseFields stream res fields) + (send this process res stream) + res])) + + ) \ No newline at end of file