From 697f79d125b37953173b71e7ddf696c40a2a4f63 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Thu, 7 Mar 2019 13:44:58 -0800 Subject: [PATCH] next: cff-dict-encodeOperands-undefined --- fontland/fontland/struct.rkt | 2 +- fontland/fontland/subset.rkt | 101 ++++++++++++++++-- fontland/fontland/table/cff/cff-dict.rkt | 30 +++++- fontland/fontland/table/cff/cff-font.rkt | 18 +++- fontland/fontland/table/cff/cff-index.rkt | 24 ++++- .../table/cff/cff-standard-strings.rkt | 76 +++++++++++++ fontland/fontland/table/cff/cff-top.rkt | 5 + 7 files changed, 243 insertions(+), 13 deletions(-) create mode 100644 fontland/fontland/table/cff/cff-standard-strings.rkt diff --git a/fontland/fontland/struct.rkt b/fontland/fontland/struct.rkt index ac174eef..c7100dc6 100644 --- a/fontland/fontland/struct.rkt +++ b/fontland/fontland/struct.rkt @@ -21,6 +21,6 @@ (struct ttf-glyph glyph () #:transparent) -(struct cff-glyph glyph ([usedGsubrs #:auto] [usedSubrs #:auto]) +(struct cff-glyph glyph ([_usedGsubrs #:auto] [_usedSubrs #:auto]) #:transparent #:auto-value (make-hash)) \ No newline at end of file diff --git a/fontland/fontland/subset.rkt b/fontland/fontland/subset.rkt index 4690f8e7..5c7aeb34 100644 --- a/fontland/fontland/subset.rkt +++ b/fontland/fontland/subset.rkt @@ -11,7 +11,10 @@ fontland/ttf-glyph xenomorph racket/dict - fontland/table/cff/cff-font) + fontland/table/cff/cff-font + fontland/table/cff/cff-top + racket/class + fontland/table/cff/cff-standard-strings) (provide subset +subset ttf-subset +ttf-subset @@ -86,26 +89,110 @@ https://github.com/mbutterick/fontkit/blob/master/src/subset/CFFSubset.js #R gid (define glyph (get-glyph (subset-font this) gid)) - (define path (hash-ref glyph 'path)) ;; this causes the glyph to be parsed + ;; apparently path parsing is not necessary? + #;(define path (hash-ref glyph 'path)) ;; this causes the glyph to be parsed - (for ([subr (in-list (hash-ref glyph '_usedGsubrs))]) + (for ([subr (in-hash-keys (cff-glyph-_usedGsubrs glyph))]) (hash-set! gsubrs subr #true))) - (set-cff-subset-gsubrs! this (subsetSubrs (hash-ref (cff-subset-cff this) 'globalSubrIndex gsubrs)))) + (set-cff-subset-gsubrs! this (subsetSubrs + this + (hash-ref (cff-subset-cff this) 'globalSubrIndex) + gsubrs))) (define (subsetSubrs this subrs used) (for/list ([(subr i) (in-indexed subrs)]) (cond - [(list-ref used i) + [(hash-ref used i #false) (peek-bytes (hash-ref subr 'length) (hash-ref subr 'offset) (hash-ref (cff-subset-cff this) 'stream))] [else (bytes 11)]))) + + +(define (subsetFontdict this topDict) + (error 'subsetFontdict-unimplemented)) + + +(define (createCIDFontdict this topDict) + (define used_subrs (make-hash)) + (for ([gid (in-list (subset-glyphs this))]) + (define glyph (get-glyph (subset-font this) gid)) + ;; skip path parsing + + (for ([subr (in-hash-keys (cff-glyph-_usedSubrs glyph))]) + (hash-set! used_subrs subr #true))) + + (define cff-topDict (hash-ref (cff-subset-cff this) 'topDict)) + (define privateDict (hash-copy (hash-ref cff-topDict 'Private (make-hash)))) + (when (and (hash-has-key? cff-topDict 'Private) (hash-has-key? (hash-ref cff-topDict 'Private) 'Subrs)) + (hash-set! privateDict 'Subrs (subsetSubrs this + (hash-ref (hash-ref cff-topDict 'Private) 'Subrs) + used_subrs))) + + (hash-set! topDict 'FDArray (list (dictify 'Private privateDict))) + (hash-set! topDict 'FDSelect (dictify 'version 3 + 'nRanges 1 + 'ranges (list (dictify 'first 0 'fd 0)) + 'sentinel (length (cff-subset-charstrings this)))) + (hash-ref topDict 'FDSelect)) - +(define (addString this [string #f]) + (cond + [(not string) #false] + [else + (unless (cff-subset-strings this) + (set-cff-subset-strings! this null)) + + (set-cff-subset-strings! this + (append (cff-subset-strings this) (list string))) + (+ (length standardStrings) (sub1 (length (cff-subset-strings this))))])) + + (define (cff-subset-encode this stream) (subsetCharstrings this) - (error 'cff-subset-encode-unfinished)) + + (define charset + (dictify 'version (if (> (length (cff-subset-charstrings this)) 255) + 2 + 1) + 'ranges (list (dictify 'first 1 'nLeft (- (length (cff-subset-charstrings this)) 2))))) + + (define topDict (hash-copy (hash-ref (cff-subset-cff this) 'topDict))) + (hash-set*! topDict + 'Private #false + 'charset charset + 'Encoding #false + 'CharStrings (cff-subset-charstrings this)) + + (for ([key (in-list '(version Notice Copyright FullName + FamilyName Weight PostScript + BaseFontName FontName))]) + (hash-update! topDict key + (λ (tdk-val) (addString this (CFFont-string (cff-subset-cff this) tdk-val))))) + + (hash-set! topDict 'ROS (list (addString this "Adobe") + (addString this "Identity") + 0)) + (hash-set! topDict 'CIDCount (length (cff-subset-charstrings this))) + + (if (hash-ref (cff-subset-cff this) 'isCIDFont) + (subsetFontdict this topDict) + (createCIDFontdict this topDict)) + + (define top + (mhasheq 'version 1 + 'hdrSize (hash-ref (cff-subset-cff this) 'hdrSize) + 'offSize 4 + 'header (hash-ref (cff-subset-cff this) 'header #f) + 'nameIndex (list (CFFFont-postscriptName (cff-subset-cff this))) + 'topDictIndex (list topDict) + 'stringIndex (cff-subset-strings this) + 'globalSubrIndex (cff-subset-gsubrs this))) + + (encode CFFTop top stream) + + (error 'boom)) #;(module+ test (require "font.rkt" "helper.rkt") diff --git a/fontland/fontland/table/cff/cff-dict.rkt b/fontland/fontland/table/cff/cff-dict.rkt index 609f823e..ac1ee837 100644 --- a/fontland/fontland/table/cff/cff-dict.rkt +++ b/fontland/fontland/table/cff/cff-dict.rkt @@ -73,9 +73,33 @@ https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFDict.js ret) (define/augment (size dict parent [includePointers #true]) - (error 'cff-dict-size-undefined)) + (define ctx + (mhasheq x:parent-key parent + x:val-key dict + x:pointer-size-key 0 + x:start-offset-key (hash-ref parent x:start-offset-key 0))) - (define/augment (encode stream dict parent) - (error 'cff-dict-encode-undefined)))) + (define len 0) + + (for* ([(k field) (in-hash @fields)] + [val (in-value (hash-ref dict (list-ref field 1)))] + #:unless (or (not val) (equal? val (list-ref field 3)))) + (define operands (encodeOperands (list-ref field 2) #f ctx val)) + (set! len (+ len + (for/sum ([op (in-list operands)]) + (size CFFOperand op)))) + + (define key (if (list? (list-ref field 0)) + (list-ref field 0) + (list (list-ref field 0)))) + (set! len (+ len (length key)))) + + (when includePointers + (set! len (+ len (hash-ref ctx x:pointer-size-key)))) + + len) + + (define/augment (encode stream dict parent) + (error 'cff-dict-encode-undefined)))) (define (CFFDict [ops null]) (make-object CFFDict% ops)) \ No newline at end of file diff --git a/fontland/fontland/table/cff/cff-font.rkt b/fontland/fontland/table/cff/cff-font.rkt index d6a592dc..55646872 100644 --- a/fontland/fontland/table/cff/cff-font.rkt +++ b/fontland/fontland/table/cff/cff-font.rkt @@ -1,5 +1,7 @@ #lang debug racket/base -(require racket/class racket/match racket/list xenomorph "cff-top.rkt") +(require racket/class racket/match racket/list xenomorph + "cff-top.rkt" + "cff-standard-strings.rkt") (provide (all-defined-out)) #| @@ -33,6 +35,20 @@ https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFFont.js (hash-set! cff-font 'isCIDFont (hash-ref (hash-ref cff-font 'topDict) 'ROS)) cff-font))) +(define (CFFont-string this sid) + (let ([sid (or sid 0)]) + #R sid + (cond + [(>= (hash-ref this 'version) 2) #false] + [(< sid (length standardStrings)) (list-ref standardStrings sid)] + [else (list-ref (hash-ref this 'stringIndex) (- sid (length standardStrings)))]))) + +(define (CFFFont-postscriptName this) + (cond + [(< (hash-ref this 'version) 2) + (list-ref (hash-ref this 'nameIndex) 0)] + [else #false])) + (define CFFFont (make-object CFFFont%)) (define (getCharString cff-font glyph-id) diff --git a/fontland/fontland/table/cff/cff-index.rkt b/fontland/fontland/table/cff/cff-index.rkt index 21e59d07..fe130743 100644 --- a/fontland/fontland/table/cff/cff-index.rkt +++ b/fontland/fontland/table/cff/cff-index.rkt @@ -47,7 +47,29 @@ (values (cons val vals) end))])) (define/augride (size arr parent) - (error 'cff-index-size-not-implemented)) + (define size 2) + (cond + [(zero? (length arr)) size] + [else + (define type (or @type (bytes))) + + ;; find maximum offset to determinine offset type + (define offset 1) + (for ([(item i) (in-indexed arr)]) + (set! offset (+ offset (send type size item parent)))) + + (define offsetType + (cond + [(<= offset #xff) uint8] + [(<= offset #xffff) uint16be] + [(<= offset #xffffff) uint24be] + [(<= offset #xffffffff) uint32be] + [else (error 'CFFIndex-size (format "bad offset: ~a" offset))])) + + (set! size (+ size 1 (* (send offsetType size) (add1 (length arr))))) + (set! size (+ size (sub1 offset))) + + size])) (define/augride (encode stream arr parent) (error 'cff-index-encode-not-implemented)))) diff --git a/fontland/fontland/table/cff/cff-standard-strings.rkt b/fontland/fontland/table/cff/cff-standard-strings.rkt new file mode 100644 index 00000000..5559c6b1 --- /dev/null +++ b/fontland/fontland/table/cff/cff-standard-strings.rkt @@ -0,0 +1,76 @@ +#lang racket/base +(provide (all-defined-out)) + +#| +approximates +https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFStandardStrings.js +|# + +(define standardStrings + '(".notdef" "space" "exclam" "quotedbl" "numbersign" "dollar" + "percent" "ampersand" "quoteright" "parenleft" "parenright" + "asterisk" "plus" "comma" "hyphen" "period" "slash" "zero" "one" + "two" "three" "four" "five" "six" "seven" "eight" "nine" "colon" + "semicolon" "less" "equal" "greater" "question" "at" "A" "B" "C" + "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" + "S" "T" "U" "V" "W" "X" "Y" "Z" "bracketleft" "backslash" + "bracketright" "asciicircum" "underscore" "quoteleft" "a" "b" "c" + "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" + "s" "t" "u" "v" "w" "x" "y" "z" "braceleft" "bar" "braceright" + "asciitilde" "exclamdown" "cent" "sterling" "fraction" "yen" + "florin" "section" "currency" "quotesingle" "quotedblleft" + "guillemotleft" "guilsinglleft" "guilsinglright" "fi" "fl" "endash" + "dagger" "daggerdbl" "periodcentered" "paragraph" "bullet" + "quotesinglbase" "quotedblbase" "quotedblright" "guillemotright" + "ellipsis" "perthousand" "questiondown" "grave" "acute" "circumflex" + "tilde" "macron" "breve" "dotaccent" "dieresis" "ring" "cedilla" + "hungarumlaut" "ogonek" "caron" "emdash" "AE" "ordfeminine" "Lslash" + "Oslash" "OE" "ordmasculine" "ae" "dotlessi" "lslash" "oslash" "oe" + "germandbls" "onesuperior" "logicalnot" "mu" "trademark" "Eth" + "onehalf" "plusminus" "Thorn" "onequarter" "divide" "brokenbar" + "degree" "thorn" "threequarters" "twosuperior" "registered" "minus" + "eth" "multiply" "threesuperior" "copyright" "Aacute" "Acircumflex" + "Adieresis" "Agrave" "Aring" "Atilde" "Ccedilla" "Eacute" + "Ecircumflex" "Edieresis" "Egrave" "Iacute" "Icircumflex" "Idieresis" + "Igrave" "Ntilde" "Oacute" "Ocircumflex" "Odieresis" "Ograve" + "Otilde" "Scaron" "Uacute" "Ucircumflex" "Udieresis" "Ugrave" + "Yacute" "Ydieresis" "Zcaron" "aacute" "acircumflex" "adieresis" + "agrave" "aring" "atilde" "ccedilla" "eacute" "ecircumflex" + "edieresis" "egrave" "iacute" "icircumflex" "idieresis" "igrave" + "ntilde" "oacute" "ocircumflex" "odieresis" "ograve" "otilde" + "scaron" "uacute" "ucircumflex" "udieresis" "ugrave" "yacute" + "ydieresis" "zcaron" "exclamsmall" "Hungarumlautsmall" + "dollaroldstyle" "dollarsuperior" "ampersandsmall" "Acutesmall" + "parenleftsuperior" "parenrightsuperior" "twodotenleader" + "onedotenleader" "zerooldstyle" "oneoldstyle" "twooldstyle" + "threeoldstyle" "fouroldstyle" "fiveoldstyle" "sixoldstyle" + "sevenoldstyle" "eightoldstyle" "nineoldstyle" "commasuperior" + "threequartersemdash" "periodsuperior" "questionsmall" "asuperior" + "bsuperior" "centsuperior" "dsuperior" "esuperior" "isuperior" + "lsuperior" "msuperior" "nsuperior" "osuperior" "rsuperior" + "ssuperior" "tsuperior" "ff" "ffi" "ffl" "parenleftinferior" + "parenrightinferior" "Circumflexsmall" "hyphensuperior" "Gravesmall" + "Asmall" "Bsmall" "Csmall" "Dsmall" "Esmall" "Fsmall" "Gsmall" + "Hsmall" "Ismall" "Jsmall" "Ksmall" "Lsmall" "Msmall" "Nsmall" + "Osmall" "Psmall" "Qsmall" "Rsmall" "Ssmall" "Tsmall" "Usmall" + "Vsmall" "Wsmall" "Xsmall" "Ysmall" "Zsmall" "colonmonetary" + "onefitted" "rupiah" "Tildesmall" "exclamdownsmall" "centoldstyle" + "Lslashsmall" "Scaronsmall" "Zcaronsmall" "Dieresissmall" "Brevesmall" + "Caronsmall" "Dotaccentsmall" "Macronsmall" "figuredash" + "hypheninferior" "Ogoneksmall" "Ringsmall" "Cedillasmall" + "questiondownsmall" "oneeighth" "threeeighths" "fiveeighths" + "seveneighths" "onethird" "twothirds" "zerosuperior" "foursuperior" + "fivesuperior" "sixsuperior" "sevensuperior" "eightsuperior" + "ninesuperior" "zeroinferior" "oneinferior" "twoinferior" + "threeinferior" "fourinferior" "fiveinferior" "sixinferior" + "seveninferior" "eightinferior" "nineinferior" "centinferior" + "dollarinferior" "periodinferior" "commainferior" "Agravesmall" + "Aacutesmall" "Acircumflexsmall" "Atildesmall" "Adieresissmall" + "Aringsmall" "AEsmall" "Ccedillasmall" "Egravesmall" "Eacutesmall" + "Ecircumflexsmall" "Edieresissmall" "Igravesmall" "Iacutesmall" + "Icircumflexsmall" "Idieresissmall" "Ethsmall" "Ntildesmall" + "Ogravesmall" "Oacutesmall" "Ocircumflexsmall" "Otildesmall" + "Odieresissmall" "OEsmall" "Oslashsmall" "Ugravesmall" "Uacutesmall" + "Ucircumflexsmall" "Udieresissmall" "Yacutesmall" "Thornsmall" + "Ydieresissmall" "001.000" "001.001" "001.002" "001.003" "Black" + "Bold" "Book" "Light" "Medium" "Regular" "Roman" "Semibold")) \ No newline at end of file diff --git a/fontland/fontland/table/cff/cff-top.rkt b/fontland/fontland/table/cff/cff-top.rkt index a5a65683..f09ad260 100644 --- a/fontland/fontland/table/cff/cff-top.rkt +++ b/fontland/fontland/table/cff/cff-top.rkt @@ -174,6 +174,11 @@ https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFTop.js (define CFFTop (x:versioned-struct + #:pre-encode + (λ (val) + ;; because fontkit depends on overloading 'version key, and we don't + (hash-set! val 'x:version (hash-ref val 'version)) + val) fixed16be (dictify 1 (dictify 'hdrSize uint8