#lang fontkit/racket (require "gsub-processor.rkt" "gpos-processor.rkt" "glyphinfo.rkt" (prefix-in Shapers- "shapers.rkt") "shaping-plan.rkt") (provide (all-defined-out)) #| 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]) #;(report/file 'starting-ot-layout-engine) (when (· font has-gsub-table?) (set-field! GSUBProcessor this (+GSUBProcessor font (or (· font GSUB) (error 'no-gsub-table))))) (when (· font has-gpos-table?) (set-field! GPOSProcessor this (+GPOSProcessor font (or (· font GPOS) (error 'no-gpos-table))))) (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)) ;; 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)) #;(report/file shaper) (send (make-object shaper) plan (· this plan) (· this glyphInfos) features)) (define/public (substitute glyphs . _) (cond [(· this GSUBProcessor) #;(report/file (· this glyphInfos)) (send (· this plan) process (· this GSUBProcessor) (· this glyphInfos)) #;(report/file (· this glyphInfos)) ;; Map glyph infos back to normal Glyph objects (for/list ([glyphInfo (in-list (· this glyphInfos))]) (send (· this font) getGlyph (· glyphInfo id) (· glyphInfo codePoints)))] [else glyphs])) (define/public (position glyphs positions . _) (report*/file glyphs positions shaper) (define static-shaper (make-object shaper)) (when (eq? (· static-shaper zeroMarkWidths) 'BEFORE_GPOS) (zeroMarkAdvances positions)) (when GPOSProcessor (report/file GPOSProcessor) (send (· this plan) process GPOSProcessor glyphInfos positions)) (when (eq? (· static-shaper zeroMarkWidths) 'AFTER_GPOS) (zeroMarkAdvances positions)) ;; Reverse the glyphs and positions if the script is right-to-left (when (eq? (· this plan direction) 'rtl) (set! glyphs (reverse glyphs)) (set! positions (reverse positions))) (report/file (and GPOSProcessor (· GPOSProcessor features)))) (define/public (zeroMarkAdvances positions) (set! positions (for/list ([glyphInfo (in-list glyphInfos)] [position (in-list positions)]) (when (· glyphInfo isMark) (dict-set*! position 'xAdvance 0 'yAdvance 0)) position))) )