diff --git a/pitfall/fontkit/default-shaper.rkt b/pitfall/fontkit/default-shaper.rkt new file mode 100644 index 00000000..f850f08a --- /dev/null +++ b/pitfall/fontkit/default-shaper.rkt @@ -0,0 +1,43 @@ +#lang fontkit/racket +(provide (all-defined-out)) + +(define VARIATION_FEATURES '(rvrn)) +(define COMMON_FEATURES '(ccmp locl rlig mark mkmk)) +(define FRACTIONAL_FEATURES '(frac numr dnom)) +(define HORIZONTAL_FEATURES '(calt clig liga rclt curs kern)) +(define VERTICAL_FEATURES '(vert)) +(define DIRECTIONAL_FEATURES (mhasheq + 'ltr '(ltra ltrm) + 'rtl '(rtla rtlm))) + +(define zeroMarkWidths 'AFTER_GPOS) + +(define-subclass object% (DefaultShaper) + + (define/public (plan plan_ glyphs features) + ;; Plan the features we want to apply + (planPreprocessing plan_) + (planFeatures plan_) + (planPostprocessing plan_ features) + + ;; Assign the global features to all the glyphs + (send plan_ assignGlobalFeatures glyphs) + + ;; Assign local features to glyphs + (assignFeatures plan_ glyphs)) + + (define/public (planPreprocessing plan) + (send plan add (mhasheq + 'global (append VARIATION_FEATURES (dict-ref DIRECTIONAL_FEATURES (· plan direction))) + 'local FRACTIONAL_FEATURES))) + + (define/public (planFeatures plan) + ;; Do nothing by default. Let subclasses override this. + (void)) + +(define/public (planPostprocessing plan userFeatures) + (send plan add (append COMMON_FEATURES HORIZONTAL_FEATURES userFeatures))) + + (define/public (assignFeatures plan glyphs) + ;; todo: Enable contextual fractions + (void))) diff --git a/pitfall/fontkit/font.rkt b/pitfall/fontkit/font.rkt index 612ab1a9..11e267b3 100644 --- a/pitfall/fontkit/font.rkt +++ b/pitfall/fontkit/font.rkt @@ -236,7 +236,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/TTFFont.js ((string?) ((option/c (listof symbol?)) (option/c symbol?) (option/c symbol?)) . ->*m . GlyphRun?) (unless (· this _layoutEngine) (set-field! _layoutEngine this (+LayoutEngine this))) - (report* 'in-layout (· this _layoutEngine)) + (report*/file 'in-layout (· this _layoutEngine)) (send (· this _layoutEngine) layout string userFeatures script language)) diff --git a/pitfall/fontkit/glyphinfo.rkt b/pitfall/fontkit/glyphinfo.rkt new file mode 100644 index 00000000..2996e014 --- /dev/null +++ b/pitfall/fontkit/glyphinfo.rkt @@ -0,0 +1,24 @@ +#lang fontkit/racket +(provide (all-defined-out)) + +(define-subclass object% (GlyphInfo font-in id-in [codePoints-in empty] [features-in (mhasheq)]) + (field [_font font-in] + [codePoints codePoints-in] + [id id-in] + [features (mhasheq)]) + + (cond + [(list? features-in) + (for ([feature (in-list features-in)]) + (hash-set! features feature #t))] + [(object? features-in) + (hash-set! features (· features-in features))]) + + (field [ligatureID #f] + [ligatureComponent #f] + [ligated #f] + [cursiveAttachment #f] + [markattachment #f] + [shaperInfo #f] + [substituted #f])) + \ No newline at end of file diff --git a/pitfall/fontkit/glyphrun.rkt b/pitfall/fontkit/glyphrun.rkt index 4b43dd53..5fc90eb5 100644 --- a/pitfall/fontkit/glyphrun.rkt +++ b/pitfall/fontkit/glyphrun.rkt @@ -6,31 +6,5 @@ ;; Returned by the font layout method. (define-subclass object% (GlyphRun glyphs ; An array of Glyph objects in the run - features-in - script ; The script that was requested for shaping. This was either passed in or detected automatically. - language) ; The language requested for shaping, as passed in. If `null`, the default language for the script was used. - - - ;; An array of GlyphPosition objects for each glyph in the run - (field [positions #f]) - - ;; The directionality of the requested script (either ltr or rtl). - (field [direction (Script-direction script)]) - - ;; The features requested during shaping. This is a combination of user - ;; specified features and features chosen by the shaper. - (field [features (cond - [(hash? features-in) features-in] - ;; Convert features to an object - [(list? features-in) - (define f (mhash)) - (for ([tag (in-list features)]) - (hash-set! f tag #t)) - f] - [(not features-in) (mhash)] - [else (error 'glyphrun:unknown-features-type)])]) - - - - -) \ No newline at end of file + positions) ; An array of GlyphPosition objects for each glyph in the run + ) \ No newline at end of file diff --git a/pitfall/fontkit/layout-engine.rkt b/pitfall/fontkit/layout-engine.rkt index 4d763580..0a40e559 100644 --- a/pitfall/fontkit/layout-engine.rkt +++ b/pitfall/fontkit/layout-engine.rkt @@ -31,53 +31,55 @@ https://github.com/mbutterick/fontkit/blob/master/src/layout/LayoutEngine.js (define/contract (layout this str-or-glyphs [features #f] ;; Attempt to detect the script if not provided. - [script (report (if (string? str-or-glyphs) + [script (if (string? str-or-glyphs) (Script-forString str-or-glyphs) - (Script-forCodePoints (append-map (λ (g) (· g codePoints)) str-or-glyphs))))] + (Script-forCodePoints (append-map (λ (g) (· g codePoints)) str-or-glyphs)))] [language #f]) (((or/c string? (listof Glyph?))) ((option/c list?) (option/c symbol?) (option/c symbol?)) . ->*m . GlyphRun?) (define glyphs + ;; Map string to glyphs if needed (if (string? str-or-glyphs) (send (· this font) glyphsForString str-or-glyphs) str-or-glyphs)) - (define glyphRun (make-object GlyphRun glyphs features script language)) + (report*/file 'starting-layout-in-layout-engine glyphs) + (cond + [(empty? glyphs) (+GlyphRun glyphs empty)] ; Return early if there are no glyphs + [else + ;; Setup the advanced layout engine + (when (and (· this engine) #;(·? engine setup)) + (send (· this engine) setup glyphs features script language)) - (if (empty? glyphs) - (set-field! positions glyphRun empty) - (begin - ;; Setup the advanced layout engine ; todo + ;; Substitute and position the glyphs + (set! glyphs (send this substitute glyphs features script language)) + (define positions (send this position glyphs features script language)) - ;; Substitute and position the glyphs - (send this substitute glyphRun) - (send this position glyphRun) - (send this hideDefaultIgnorables glyphRun) - - ;; Let the layout engine clean up any state it might have - (and (· this engine) (· this engine cleanup)))) - - glyphRun) + ;; Let the layout engine clean up any state it might have + (when (and (· this engine) #;(·? this engine cleanup)) + (· this engine cleanup)) + (+GlyphRun glyphs positions)])) -(define/contract (substitute this glyphRun) - ((is-a?/c GlyphRun) . ->m . void?) +(define (substitute this glyphs features script language) + #;((is-a?/c GlyphRun) . ->m . void?) ;; Call the advanced layout engine to make substitutions - (when (and (· this engine) (· this engine substitute)) - (send (· this engine) substitute glyphRun))) + (when (and (· this engine) #;(· this engine substitute)) + (send (· this engine) substitute glyphs features script language)) + glyphs) -(define/contract (position this glyphRun) - ((is-a?/c GlyphRun) . ->m . void?) +(define (position this glyphs features script language) + ((listof Glyph?) (option/c list?) (option/c symbol?) (option/c symbol?) . ->m . (listof GlyphPosition?)) - (define positions (for/list ([g (in-list (· glyphRun glyphs))]) - (make-object GlyphPosition (· g advanceWidth)))) - (set-field! positions glyphRun positions) + (define positions (for/list ([glyph (in-list glyphs)]) + (make-object GlyphPosition (· glyph advanceWidth)))) + #| ;; Call the advanced layout engine. Returns the features applied. (define positioned - (and (· this engine) (· this engine position) - (send (· this engine) position glyphRun))) + (and (· this engine) #;(· this engine position) + (send (· this engine) position glyphs positions features script language))) ;; if there is no GPOS table, use unicode properties to position marks. ;; todo: implement unicodelayoutengine @@ -85,7 +87,8 @@ https://github.com/mbutterick/fontkit/blob/master/src/layout/LayoutEngine.js ;; if kerning is not supported by GPOS, do kerning with the TrueType/AAT kern table ;; todo: implement kerning - (void) +|# + positions ) @@ -94,15 +97,15 @@ https://github.com/mbutterick/fontkit/blob/master/src/layout/LayoutEngine.js (define space (send (· this font) glyphForCodePoint #x20)) (define-values (new-glyphs new-positions) (for/lists (ngs nps) - ([glyph (in-list (· glyphRun glyphs))] - [pos (in-list (· glyphRun positions))]) - (cond - [(send this isDefaultIgnorable (car (· glyph codePoints))) - (define new-pos pos) - (set-field! xAdvance new-pos 0) - (set-field! yAdvance new-pos 0) - (values space new-pos)] - [else (values glyph pos)]))) + ([glyph (in-list (· glyphRun glyphs))] + [pos (in-list (· glyphRun positions))]) + (cond + [(send this isDefaultIgnorable (car (· glyph codePoints))) + (define new-pos pos) + (set-field! xAdvance new-pos 0) + (set-field! yAdvance new-pos 0) + (values space new-pos)] + [else (values glyph pos)]))) (set-field! glyphs glyphRun new-glyphs) (set-field! positions glyphRun new-positions)) diff --git a/pitfall/fontkit/ot-layout-engine.rkt b/pitfall/fontkit/ot-layout-engine.rkt index dc61f821..0574a37a 100644 --- a/pitfall/fontkit/ot-layout-engine.rkt +++ b/pitfall/fontkit/ot-layout-engine.rkt @@ -1,5 +1,5 @@ #lang fontkit/racket -(require "gsub-processor.rkt" "gpos-processor.rkt") +(require "gsub-processor.rkt" "gpos-processor.rkt" "glyphinfo.rkt" (prefix-in Shapers- "shapers.rkt") "shaping-plan.rkt") (provide (all-defined-out)) #| @@ -8,6 +8,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTLayoutEngine.js (define-subclass object% (OTLayoutEngine font) (field [glyphInfos #f] + [shaper #f] [plan #f] [GSUBProcessor #f] [GPOSProcessor #f]) @@ -18,10 +19,18 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTLayoutEngine.js (set-field! GSUBProcessor this (+GSUBProcessor font (· font GSUB)))) - (report* 'starting-gpos-dingdong) + (report* 'dingdong!-starting-gpos) (when (· font has-gpos-table?) (set-field! GPOSProcessor this (+GPOSProcessor font (· font GPOS)))) + (define/public (setup glyphs features script language) + ;; Map glyphs to GlyphInfo objects so data can be passed between + ;; GSUB and GPOS without mutating the real (shared) Glyph objects. + (set! glyphInfos (map (λ (glyph) (+GlyphInfo (· this font) (· glyph id) (· glyph codePoints))) glyphs)) - ) \ No newline at end of file + ;; Choose a shaper based on the script, and setup a shaping plan. + ;; This determines which features to apply to which glyphs. + (set! shaper (Shapers-choose script)) + #;(set! plan (+ShapingPlan (· this font) script language)) + (send (· this shaper) plan (· this plan) (· this glyphInfos) features))) \ No newline at end of file diff --git a/pitfall/fontkit/shapers.rkt b/pitfall/fontkit/shapers.rkt new file mode 100644 index 00000000..eedaaa26 --- /dev/null +++ b/pitfall/fontkit/shapers.rkt @@ -0,0 +1,68 @@ +#lang fontkit/racket +(require "default-shaper.rkt") +(provide (all-defined-out)) + +;; todo: alternative shapers +(define SHAPERS + (hasheq +; 'arab ArabicShaper ;; Arabic +; 'mong ArabicShaper ;; Mongolian +; 'syrc ArabicShaper ;; Syriac +; '|nko | ArabicShaper ;; N'Ko +; 'phag ArabicShaper ;; Phags Pa +; 'mand ArabicShaper ;; Mandaic +; 'mani ArabicShaper ;; Manichaean +; 'phlp ArabicShaper ;; Psalter Pahlavi +; +; 'hang HangulShaper ;; Hangul +; +; 'bali UniversalShaper ;; Balinese +; 'batk UniversalShaper ;; Batak +; 'brah UniversalShaper ;; Brahmi +; 'bugi UniversalShaper ;; Buginese +; 'buhd UniversalShaper ;; Buhid +; 'cakm UniversalShaper ;; Chakma +; 'cham UniversalShaper ;; Cham +; 'dupl UniversalShaper ;; Duployan +; 'egyp UniversalShaper ;; Egyptian Hieroglyphs +; 'gran UniversalShaper ;; Grantha +; 'hano UniversalShaper ;; Hanunoo +; 'java UniversalShaper ;; Javanese +; 'kthi UniversalShaper ;; Kaithi +; 'kali UniversalShaper ;; Kayah Li +; 'khar UniversalShaper ;; Kharoshthi +; 'khoj UniversalShaper ;; Khojki +; 'sind UniversalShaper ;; Khudawadi +; 'lepc UniversalShaper ;; Lepcha +; 'limb UniversalShaper ;; Limbu +; 'mahj UniversalShaper ;; Mahajani +; ';; mand UniversalShaper ;; Mandaic +; ';; mani UniversalShaper ;; Manichaean +; 'mtei UniversalShaper ;; Meitei Mayek +; 'modi UniversalShaper ;; Modi +; ';; mong UniversalShaper ;; Mongolian +; ';; 'nko ' UniversalShaper ;; N’Ko +; 'hmng UniversalShaper ;; Pahawh Hmong +; ';; phag UniversalShaper ;; Phags-pa +; ';; phlp UniversalShaper ;; Psalter Pahlavi +; 'rjng UniversalShaper ;; Rejang +; 'saur UniversalShaper ;; Saurashtra +; 'shrd UniversalShaper ;; Sharada +; 'sidd UniversalShaper ;; Siddham +; 'sinh UniversalShaper ;; Sinhala +; 'sund UniversalShaper ;; Sundanese +; 'sylo UniversalShaper ;; Syloti Nagri +; 'tglg UniversalShaper ;; Tagalog +; 'tagb UniversalShaper ;; Tagbanwa +; 'tale UniversalShaper ;; Tai Le +; 'lana UniversalShaper ;; Tai Tham +; 'tavt UniversalShaper ;; Tai Viet +; 'takr UniversalShaper ;; Takri +; 'tibt UniversalShaper ;; Tibetan +; 'tfng UniversalShaper ;; Tifinagh +; 'tirh UniversalShaper ;; Tirhuta + 'latn DefaultShaper ;; Latin + 'DFLT DefaultShaper)) ;; Default + +(define (choose script) + (dict-ref SHAPERS script DefaultShaper)) \ No newline at end of file diff --git a/pitfall/fontkit/shaping-plan.rkt b/pitfall/fontkit/shaping-plan.rkt new file mode 100644 index 00000000..fadd1001 --- /dev/null +++ b/pitfall/fontkit/shaping-plan.rkt @@ -0,0 +1,9 @@ +#lang fontkit/racket +(provide (all-defined-out)) + +; * ShapingPlans are used by the OpenType shapers to store which +; * features should by applied, and in what order to apply them. +; * The features are applied in groups called stages. A feature +; * can be applied globally to all glyphs, or locally to only +; * specific glyphs. + diff --git a/pitfall/pdfkit/node_modules/fontkit/index.js b/pitfall/pdfkit/node_modules/fontkit/index.js index 2792b33f..33c6e740 100644 --- a/pitfall/pdfkit/node_modules/fontkit/index.js +++ b/pitfall/pdfkit/node_modules/fontkit/index.js @@ -12417,6 +12417,9 @@ var TTFFont = (_class = function () { * @return {GlyphRun} */ TTFFont.prototype.layout = function layout(string, userFeatures, script, language) { + console.log("userFeatures="+userFeatures); + console.log("script="+script); + console.log("language="+language); return this._layoutEngine.layout(string, userFeatures, script, language); }; diff --git a/pitfall/pdfkit/node_modules/restructure/src/Pointer.js b/pitfall/pdfkit/node_modules/restructure/src/Pointer.js index daf089b9..b35a51dd 100644 --- a/pitfall/pdfkit/node_modules/restructure/src/Pointer.js +++ b/pitfall/pdfkit/node_modules/restructure/src/Pointer.js @@ -37,7 +37,7 @@ return null; } relative = (function() { - console.log("pointer-scope="+this.options.type) + //console.log("pointer-scope="+this.options.type) switch (this.options.type) { case 'local': return ctx._startOffset; @@ -56,13 +56,13 @@ if (this.options.relativeTo) { relative += this.relativeToGetter(ctx); } - console.log("this._startOffset= "+this._startOffset); - console.log("ctx= "+ctx); - console.log("ctx._startOffset= "+ctx._startOffset); - console.log("offset= "+ offset) - console.log("relative= "+ relative) + //console.log("this._startOffset= "+this._startOffset); + //console.log("ctx= "+ctx); + //console.log("ctx._startOffset= "+ctx._startOffset); + //console.log("offset= "+ offset) + //console.log("relative= "+ relative) ptr = offset + relative; - console.log("ptr="+ptr+" size="+this.offsetType.size()); + //console.log("ptr="+ptr+" size="+this.offsetType.size()); if (this.type != null) { val = null; decodeValue = (function(_this) { diff --git a/pitfall/pdfkit/node_modules/restructure/src/Struct.js b/pitfall/pdfkit/node_modules/restructure/src/Struct.js index e7ed22c5..823820a9 100644 --- a/pitfall/pdfkit/node_modules/restructure/src/Struct.js +++ b/pitfall/pdfkit/node_modules/restructure/src/Struct.js @@ -48,7 +48,7 @@ for (key in fields) { type = fields[key]; - console.log("key= " + key ); + //console.log("key= " + key ); if (typeof type === 'function') { val = type.call(res, res); } else { diff --git a/pitfall/pitfall/alltest.rkt b/pitfall/pitfall/alltest.rkt index d9a44839..870873e6 100644 --- a/pitfall/pitfall/alltest.rkt +++ b/pitfall/pitfall/alltest.rkt @@ -14,6 +14,7 @@ pitfall/test/test11 pitfall/test/test12 ; ttf subset pitfall/test/test13 ; subset with composites - pitfall/test/test14 ; Fira ttf with GPOS + pitfall/test/test14 ; Fira ttf with GPOS (no kerning) + ;pitfall/test/test15 ; Fira ttf with GPOS kerning pitfall/page-test (submod pitfall/zlib test))) \ No newline at end of file diff --git a/pitfall/pitfall/embedded.rkt b/pitfall/pitfall/embedded.rkt index aa75bbfe..1e62c480 100644 --- a/pitfall/pitfall/embedded.rkt +++ b/pitfall/pitfall/embedded.rkt @@ -43,11 +43,13 @@ https://github.com/mbutterick/pdfkit/blob/master/lib/font/embedded.coffee (define/contract (encode this text [features #f]) ((string?) ((option/c list?)) . ->*m . (list/c (listof string?) (listof GlyphPosition?))) + (report*/file 'starting-layout-in-embedded (description (· this font))) (define glyphRun (send (· this font) layout text features)) (define glyphs (· glyphRun glyphs)) (for ([g (in-list glyphs)]) (· g id)) (define positions (· glyphRun positions)) + (report positions) (define-values (subset-idxs new-positions) (for/lists (idxs posns) ([(glyph i) (in-indexed glyphs)] diff --git a/pitfall/pitfall/racket.rkt b/pitfall/pitfall/racket.rkt index 9a2246c9..3381e843 100644 --- a/pitfall/pitfall/racket.rkt +++ b/pitfall/pitfall/racket.rkt @@ -24,7 +24,8 @@ sugar/dict sugar/stub sugar/port - sugar/contract) + sugar/contract + describe) (module reader syntax/module-reader #:language 'pitfall/racket diff --git a/pitfall/pitfall/test/test14rkt.pdf b/pitfall/pitfall/test/test14rkt.pdf index a2e57e9f..e69de29b 100644 Binary files a/pitfall/pitfall/test/test14rkt.pdf and b/pitfall/pitfall/test/test14rkt.pdf differ diff --git a/pitfall/pitfall/test/test15.pdf b/pitfall/pitfall/test/test15.pdf index 26a36cda..d91e9f3c 100644 Binary files a/pitfall/pitfall/test/test15.pdf and b/pitfall/pitfall/test/test15.pdf differ diff --git a/pitfall/pitfall/test/test15.rkt b/pitfall/pitfall/test/test15.rkt index e2295473..2a3f8818 100644 --- a/pitfall/pitfall/test/test15.rkt +++ b/pitfall/pitfall/test/test15.rkt @@ -1,5 +1,7 @@ #lang pitfall/pdftest +;; test kerning from GPOS + (define-runtime-path ttf-path "assets/fira.ttf") (define (proc doc) @@ -12,7 +14,7 @@ [fontSize 25] [text "ATAVATA" 100 100 (hash 'width #f)])) -;; test against non-subsetted font version + (define-runtime-path this "test15rkt.pdf") (make-doc this #f proc #:test #f) diff --git a/pitfall/pitfall/text.rkt b/pitfall/pitfall/text.rkt index 38dab5dd..fdd1894b 100644 --- a/pitfall/pitfall/text.rkt +++ b/pitfall/pitfall/text.rkt @@ -2,6 +2,11 @@ (require sugar/list) (provide text-mixin) +#| +approximates +https://github.com/mbutterick/pdfkit/blob/master/lib/mixins/text.coffee +|# + (define (text-mixin [% mixin-tester%]) (class % (super-new)