GSUB: resume in spurious ligature lookup

main
Matthew Butterick 7 years ago
parent 5cfd6ae98a
commit c1bc246b19

@ -0,0 +1,7 @@
fontkit = require '../pdfkit/node_modules/fontkit'
fira_path = "../pitfall/test/assets/fira.ttf"
f = fontkit.openSync(fira_path)
console.log "*************************** start decode"
thing = f.GSUB.lookupList.get(19)
console.log thing

@ -0,0 +1,89 @@
#lang fontkit/racket
(require xenomorph br/cond "opentype.rkt")
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/GSUB.js
|#
(define Sequence (+Array uint16be uint16be))
(define AlternateSet Sequence)
(define Ligature (+Struct
(dictify
'glyph uint16be
'compCount uint16be
'components (+Array uint16be (λ (t) (sub1 (· t compCount)))))))
(define LigatureSet (+Array (+Pointer uint16be Ligature) uint16be))
(define-subclass VersionedStruct (GSUBLookup-VersionedStruct))
(define GSUBLookup
(+GSUBLookup-VersionedStruct
'lookupType
(dictify
;; Single Substitution
1 (+VersionedStruct uint16be
(dictify
1 (dictify
'coverage (+Pointer uint16be Coverage)
'deltaGlyphID int16be)
2 (dictify
'coverage (+Pointer uint16be Coverage)
'glyphCount uint16be
'substitute (+LazyArray uint16be 'glyphCount))))
2 ;; Multiple Substitution
(dictify
'substFormat uint16be
'coverage (+Pointer uint16be Coverage)
'count uint16be
'sequences (+LazyArray (+Pointer uint16be Sequence) 'count))
3 ;; Alternate Substitution
(dictify
'substFormat uint16be
'coverage (+Pointer uint16be Coverage)
'count uint16be
'alternateSet (+LazyArray (+Pointer uint16be AlternateSet) 'count))
4 ;; Ligature Substitution
(dictify
'substFormat uint16be
'coverage (+Pointer uint16be Coverage)
'count uint16be
'ligatureSets (+LazyArray (+Pointer uint16be LigatureSet) 'count))
5 Context ;; Contextual Substitution
6 ChainingContext ;; Chaining Contextual Substitution
7 ;; Extension Substitution
(dictify
'substFormat uint16be
'lookupType uint16be ; cannot also be 7
'extension (+Pointer uint32be (λ () (error 'circular-reference-unfixed))))
8 ;; Reverse Chaining Contextual Single Substitution
(dictify
'substFormat uint16be
'coverage (+Pointer uint16be Coverage)
'backTrackCoverage (+Array (+Pointer uint16be Coverage) 'backtrackGlyphCount)
'lookaheadGlyphCount uint16be
'lookaheadCoverage (+Array (+Pointer uint16be Coverage) 'lookaheadGlyphCount)
'glyphCount uint16be
'substitute (+Array uint16be 'glyphCount)))))
;; Fix circular reference
(ref*-set! GSUBLookup 'versions 7 'extension 'type GSUBLookup)
(define-subclass VersionedStruct (GSUB-MainVersionedStruct))
(define GSUB (+GSUB-MainVersionedStruct uint32be
(dictify
'header (dictify 'scriptList (+Pointer uint16be ScriptList)
'featureList (+Pointer uint16be FeatureList)
'lookupList (+Pointer uint16be (LookupList GSUBLookup))
)
#x00010000 (dictify)
#;#x00010001 #;(+Pointer uint32be FeatureVariations))))
(test-module)

@ -0,0 +1,160 @@
#lang fontkit/racket
(require "ot-processor.rkt" "glyphinfo.rkt" br/cond)
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/opentype/GSUBProcessor.js
|#
(define-subclass OTProcessor (GSUBProcessor)
(define/override (applyLookup lookupType table)
(report/file 'GSUBProcessor:applyLookup)
(case lookupType
[(1) ;; Single Substitution
(report 'single-substitution)
(define index (send this coverageIndex (· table coverage)))
(cond
[(= index -1) #f]
[else (define glyph (· this glyphIterator cur))
(set-field! id glyph
(case (· table version)
[(1) (bitwise-and (+ (· glyph id) (· table deltaGlyphID)) #xffff)]
[(2) (send (· table substitute) get index)]))
#t])]
[(2) ;; Multiple Substitution
(report 'multiple-substitution)
(define index (send this coverageIndex (· table coverage)))
(cond
[(= index -1) #f]
[else (define sequence (send (· table sequences) get index))
(set-field! id (· this glyphIterator cur) (list-ref sequence 0))
(set-field! ligatureComponent (· this glyphIterator cur) 0)
(define features (· this glyphIterator cur features))
(define curGlyph (· this glyphIterator cur))
(define replacement (for/list ([(gid i) (in-indexed (cdr sequence))])
(define glyph (+GlyphInfo (· this font) gid #f features))
(set-field! shaperInfo glyph (· curGlyph shaperInfo))
(set-field! isLigated glyph (· curGlyph isLigated))
(set-field! ligatureComponent glyph (add1 i))
(set-field! substituted glyph #t)
glyph))
(set-field! glyphs this (let-values ([(head tail) (split-at (· this glyphs) (add1 (· this glyphIterator index)))])
(append head replacement tail)))
#t])]
[(3) ;; Alternate substitution
(report 'altnernate-substitution)
(define index (send this coverageIndex (· table coverage)))
(cond
[(= index -1) #f]
[else (define USER_INDEX 0)
(set-field! id (· this glyphIterator cur) (list-ref (send (· table alternateSet) get index) USER_INDEX))
#t])]
[(4) ;; Ligature substitution
(report 'ligature-substitution)
(define index (report* (· table coverage) (send this coverageIndex (· table coverage))))
(cond
[(= index -1) #f]
[(for* ([ligature (in-list (send (· table ligatureSets) get index))]
[matched (in-value (send this sequenceMatchIndices 1 (report* ligature (· ligature components))))]
#:when matched)
(report*/file matched (· this glyphs) index)
(define curGlyph (· this glyphIterator cur))
;; Concatenate all of the characters the new ligature will represent
(define characters
(append (· curGlyph codePoints)
(append* (for/list ([index (in-list matched)])
(get-field codePoints (list-ref (· this glyphs) index))))))
;; Create the replacement ligature glyph
(define ligatureGlyph (+GlyphInfo (· this font) (· ligature glyph) characters (· curGlyph features)))
(set-field! shaperInfo ligatureGlyph (· curGlyph shaperInfo))
(set-field! ligated ligatureGlyph #t)
(set-field! substituted ligatureGlyph #t)
(report 'from-harfbuzz)
;; From Harfbuzz:
;; - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
;; the ligature to keep its old ligature id. This will allow it to attach to
;; a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
;; and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA with a
;; ligature id and component value of 2. Then if SHADDA,FATHA form a ligature
;; later, we don't want them to lose their ligature id/component, otherwise
;; GPOS will fail to correctly position the mark ligature on top of the
;; LAM,LAM,HEH ligature. See https://bugzilla.gnome.org/show_bug.cgi?id=676343
;;
;; - If a ligature is formed of components that some of which are also ligatures
;; themselves, and those ligature components had marks attached to *their*
;; components, we have to attach the marks to the new ligature component
;; positions! Now *that*'s tricky! And these marks may be following the
;; last component of the whole sequence, so we should loop forward looking
;; for them and update them.
;;
;; Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
;; 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
;; id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature
;; form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to
;; the new ligature with a component value of 2.
;;
;; This in fact happened to a font... See
;; https://bugzilla.gnome.org/show_bug.cgi?id=437633
(define isMarkLigature
(and (· curGlyph isMark)
(for/and ([match-idx (in-list matched)])
(· (list-ref (· this glyphs) match-idx) isMark))))
(report isMarkLigature)
(set-field! ligatureID ligatureGlyph (cond
[isMarkLigature #f]
[else (define id (· this ligatureID))
(increment-field! ligatureID this)
id]))
(define lastLigID (· curGlyph ligatureID))
(define lastNumComps (length (· curGlyph codePoints)))
(define curComps lastNumComps)
(define idx (add1 (· this glyphIterator index)))
(report/file 'set-ligature-id)
;; Set ligatureID and ligatureComponent on glyphs that were skipped in the matched sequence.
;; This allows GPOS to attach marks to the correct ligature components.
(for ([matchIndex (in-list matched)])
(report/file matchIndex)
;; Don't assign new ligature components for mark ligatures (see above)
(cond
[isMarkLigature (set! idx matchIndex)]
[else (while (< idx matchIndex)
(define ligatureComponent (+ curComps (- lastNumComps) (min (or (get-field ligatureComponent (list-ref (· this glyphs) idx)) 1) lastNumComps)))
(set-field! ligatureID (list-ref (· this glyphs) idx) (· ligatureGlyph ligatureID))
(set-field! ligatureComponent (list-ref (· this glyphs) idx) ligatureComponent)
(increment! idx))])
(define lastLigID (· (list-ref (· this glyphs) idx) ligatureID))
(define lastNumComps (length (· (list-ref (· this glyphs) idx) codePoints)))
(increment! curComps lastNumComps)
(increment! idx)) ;; skip base glyph
;; Adjust ligature components for any marks following
(when (and lastLigID (not isMarkLigature))
(for ([i (in-range idx (length (· this glyphs)))]
#:when (= (· (list-ref (· this glyphs) idx) ligatureID) lastLigID))
(define ligatureComponent (+ curComps (- lastNumComps) (min (or (get-field ligatureComponent (list-ref (· this glyphs) i)) 1) lastNumComps)))
(set-field! ligatureComponent (list-ref (· this glyphs) i) ligatureComponent)))
;; Delete the matched glyphs, and replace the current glyph with the ligature glyph
(set-field! glyphs this (drop-right (· this glyphs) (length matched)))
(set-field! glyphs this (list-set (· this glyphs) (· this glyphIterator index) ligatureGlyph))
#t)]
[else #f])])))

@ -18,13 +18,6 @@
;; approximates
;; https://github.com/devongovett/fontkit/blob/master/src/glyph/Glyph.js
(define (is-mark? codepoint)
;; mark classes = Mn Me Mc
(regexp-match #px"\\p{Mn}|\\p{Me}|\\p{Mc}" (string (integer->char codepoint))))
(module+ test
(check-true (and (is-mark? #x300) #t))
(check-false (and (is-mark? #x2ee) #t)))
(define-subclass object% (Glyph id codePoints font)
(field [_font font]

@ -1,10 +1,16 @@
#lang fontkit/racket
(require "ot-processor.rkt")
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/opentype/GlyphInfo.js
|#
(define-subclass object% (GlyphInfo font-in id-in [codePoints-in empty] [features-in (mhasheq)])
(field [_font font-in]
[codePoints codePoints-in]
[id id-in]
[_id id-in]
[features (mhasheq)])
(cond
@ -17,8 +23,24 @@
(field [ligatureID #f]
[ligatureComponent #f]
[ligated #f]
[isLigated #f] ;todo: is this deliberate or accidental? see gsub-processor
[cursiveAttachment #f]
[markattachment #f]
[shaperInfo #f]
[substituted #f]))
[substituted #f])
(define/public (id [id-in #f])
(cond
[(not id-in) _id]
[else (set! _id id-in)
(set! substituted #t)
(cond
[(and (· this _font GDEF) (· this _font GDEF glyphClassDef))
(define classID (send (+OTProcessor) getClassID id-in (· this _font GDEF glyphClassDef)))
(set-field! isMark this (= classID 3))
(set-field! isLigature this (= classID 2))]
[else
(set-field! isMark this (andmap is-mark? (· this codePoints)))
(set-field! isLigature this (> (length (· this codePoints)) 1))])])))

@ -0,0 +1,60 @@
#lang fontkit/racket
(require fontkit "gsub-processor.rkt" rackunit xenomorph racket/serialize describe)
(define fira-path "../pitfall/test/assets/fira.ttf")
(define f (openSync fira-path))
(define gsub (· f GSUB))
(define proc (+GSUBProcessor f gsub))
;; liga lookupList
(car (· (get (· gsub lookupList) 30) subTables)) ; f gid = 450
(send (· (car (· (get (· gsub lookupList) 30) subTables)) ligatureSets) to-list) ; i gid = 480, l gid = 514
;; fi glyph = 731 fl glyph = 732
(check-equal? (dump (· proc features))
'((c2sc (lookupCount . 1) (lookupListIndexes 26) (featureParams . 0))
(pnum (lookupCount . 1) (lookupListIndexes 23) (featureParams . 0))
(liga (lookupCount . 1) (lookupListIndexes 30) (featureParams . 0))
(tnum (lookupCount . 1) (lookupListIndexes 24) (featureParams . 0))
(onum (lookupCount . 1) (lookupListIndexes 25) (featureParams . 0))
(ss01 (lookupCount . 1) (lookupListIndexes 33) (featureParams . 0))
(dlig (lookupCount . 1) (lookupListIndexes 29) (featureParams . 0))
(lnum (lookupCount . 1) (lookupListIndexes 22) (featureParams . 0))
(sups (lookupCount . 1) (lookupListIndexes 14) (featureParams . 0))
(zero (lookupCount . 1) (lookupListIndexes 31) (featureParams . 0))
(ss02 (lookupCount . 1) (lookupListIndexes 34) (featureParams . 0))
(aalt (lookupCount . 2) (lookupListIndexes 0 1) (featureParams . 0))
(subs (lookupCount . 1) (lookupListIndexes 13) (featureParams . 0))
(ss03 (lookupCount . 1) (lookupListIndexes 35) (featureParams . 0))
(ordn (lookupCount . 2) (lookupListIndexes 20 21) (featureParams . 0))
(calt (lookupCount . 4) (lookupListIndexes 36 37 38 39) (featureParams . 0))
(dnom (lookupCount . 1) (lookupListIndexes 16) (featureParams . 0))
(smcp (lookupCount . 1) (lookupListIndexes 27) (featureParams . 0))
(salt (lookupCount . 1) (lookupListIndexes 32) (featureParams . 0))
(case (lookupCount . 1) (lookupListIndexes 28) (featureParams . 0))
(numr (lookupCount . 1) (lookupListIndexes 15) (featureParams . 0))
(frac (lookupCount . 3) (lookupListIndexes 17 18 19) (featureParams . 0))
(mgrk (lookupCount . 1) (lookupListIndexes 12) (featureParams . 0))))
(check-equal? (dump (· proc script))
'((count . 0)
(defaultLangSys
(featureIndexes 0 14 28 42 56 70 84 98 112 136 150 164 178 192 206 220 234 248 262 276 290 304 318)
(reserved . 0)
(reqFeatureIndex . 65535)
(featureCount . 23))
(langSysRecords)))
(check-equal? (dump (· proc scriptTag)) 'DFLT)
(check-equal? (dump (· proc language))
'((featureIndexes 0 14 28 42 56 70 84 98 112 136 150 164 178 192 206 220 234 248 262 276 290 304 318)
(reserved . 0)
(reqFeatureIndex . 65535)
(featureCount . 23)))
(check-equal? (dump (· proc languageTag)) #f)
(check-equal? (dump (· proc lookups)) empty)
(check-equal? (dump (· proc direction)) 'ltr)

@ -1,11 +1,158 @@
#lang fontkit/racket
(require "ot-processor.rkt")
(require "ot-processor.rkt" "glyphinfo.rkt" br/cond)
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/opentype/GSUBProcessor.js
|#
(define-subclass OTProcessor (GSUBProcessor)
)
(define/override (applyLookup lookupType table)
(report* 'GSUBProcessor:applyLookup lookupType)
(case lookupType
[(1) ;; Single Substitution
(report 'single-substitution)
(define index (send this coverageIndex (· table coverage)))
(cond
[(= index -1) #f]
[else (define glyph (· this glyphIterator cur))
(set-field! id glyph
(case (· table version)
[(1) (bitwise-and (+ (· glyph id) (· table deltaGlyphID)) #xffff)]
[(2) (send (· table substitute) get index)]))
#t])]
[(2) ;; Multiple Substitution
(report 'multiple-substitution)
(define index (send this coverageIndex (· table coverage)))
(cond
[(= index -1) #f]
[else (define sequence (send (· table sequences) get index))
(set-field! id (· this glyphIterator cur) (list-ref sequence 0))
(set-field! ligatureComponent (· this glyphIterator cur) 0)
(define features (· this glyphIterator cur features))
(define curGlyph (· this glyphIterator cur))
(define replacement (for/list ([(gid i) (in-indexed (cdr sequence))])
(define glyph (+GlyphInfo (· this font) gid #f features))
(set-field! shaperInfo glyph (· curGlyph shaperInfo))
(set-field! isLigated glyph (· curGlyph isLigated))
(set-field! ligatureComponent glyph (add1 i))
(set-field! substituted glyph #t)
glyph))
(set-field! glyphs this (let-values ([(head tail) (split-at (· this glyphs) (add1 (· this glyphIterator index)))])
(append head replacement tail)))
#t])]
[(3) ;; Alternate substitution
(report 'alternate-substitution)
(define index (send this coverageIndex (· table coverage)))
(cond
[(= index -1) #f]
[else (define USER_INDEX 0)
(set-field! id (· this glyphIterator cur) (list-ref (send (· table alternateSet) get index) USER_INDEX))
#t])]
[(4) ;; Ligature substitution
(report '---------------------------)
(report 'ligature-substitution)
(report* lookupType (· table coverage glyphs))
(define index (send this coverageIndex (· table coverage)))
(report index 'forker)
(cond
[(= index -1) #f]
[(for*/or ([ligature (in-list (report (send (· table ligatureSets) get (report index 'starting-index))))]
[matched (in-value (send this sequenceMatchIndices 1 (report (· ligature components))))]
#:when (report matched))
(define curGlyph (· this glyphIterator cur))
;; Concatenate all of the characters the new ligature will represent
(define characters
(append (· curGlyph codePoints)
(append* (for/list ([index (in-list matched)])
(report index)
(get-field codePoints (list-ref (· this glyphs) index))))))
(report characters)
;; Create the replacement ligature glyph
(define ligatureGlyph (+GlyphInfo (· this font) (· ligature glyph) characters (· curGlyph features)))
(report (· ligatureGlyph id))
(set-field! shaperInfo ligatureGlyph (· curGlyph shaperInfo))
(set-field! isLigated ligatureGlyph #t)
(set-field! substituted ligatureGlyph #t)
;; From Harfbuzz:
;; - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
;; the ligature to keep its old ligature id. This will allow it to attach to
;; a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
;; and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA with a
;; ligature id and component value of 2. Then if SHADDA,FATHA form a ligature
;; later, we don't want them to lose their ligature id/component, otherwise
;; GPOS will fail to correctly position the mark ligature on top of the
;; LAM,LAM,HEH ligature. See https://bugzilla.gnome.org/show_bug.cgi?id=676343
;;
;; - If a ligature is formed of components that some of which are also ligatures
;; themselves, and those ligature components had marks attached to *their*
;; components, we have to attach the marks to the new ligature component
;; positions! Now *that*'s tricky! And these marks may be following the
;; last component of the whole sequence, so we should loop forward looking
;; for them and update them.
;;
;; Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
;; 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
;; id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature
;; form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to
;; the new ligature with a component value of 2.
;;
;; This in fact happened to a font... See
;; https://bugzilla.gnome.org/show_bug.cgi?id=437633
(define isMarkLigature
(and (· curGlyph isMark)
(for/and ([match-idx (in-list matched)])
(· (list-ref (· this glyphs) match-idx) isMark))))
(set-field! ligatureID ligatureGlyph (cond
[isMarkLigature #f]
[else (define id (· this ligatureID))
(increment-field! ligatureID this)
id]))
(define lastLigID (· curGlyph ligatureID))
(define lastNumComps (length (· curGlyph codePoints)))
(define curComps lastNumComps)
(define idx (add1 (· this glyphIterator index)))
;; Set ligatureID and ligatureComponent on glyphs that were skipped in the matched sequence.
;; This allows GPOS to attach marks to the correct ligature components.
(for ([matchIndex (in-list matched)])
;; Don't assign new ligature components for mark ligatures (see above)
(cond
[isMarkLigature (set! idx matchIndex)]
[else (while (< idx matchIndex)
(define ligatureComponent (+ curComps (- lastNumComps) (min (or (get-field ligatureComponent (list-ref (· this glyphs) idx)) 1) lastNumComps)))
(set-field! ligatureID (list-ref (· this glyphs) idx) (· ligatureGlyph ligatureID))
(set-field! ligatureComponent (list-ref (· this glyphs) idx) ligatureComponent)
(increment! idx))])
(define lastLigID (· (list-ref (· this glyphs) idx) ligatureID))
(define lastNumComps (length (· (list-ref (· this glyphs) idx) codePoints)))
(increment! curComps lastNumComps)
(increment! idx)) ;; skip base glyph
;; Adjust ligature components for any marks following
(when (and lastLigID (not isMarkLigature))
(for ([i (in-range idx (length (· this glyphs)))]
#:when (= (· (list-ref (· this glyphs) idx) ligatureID) lastLigID))
(define ligatureComponent (+ curComps (- lastNumComps) (min (or (get-field ligatureComponent (list-ref (· this glyphs) i)) 1) lastNumComps)))
(set-field! ligatureComponent (list-ref (· this glyphs) i) ligatureComponent)))
;; Delete the matched glyphs, and replace the current glyph with the ligature glyph
(set-field! glyphs this (drop-right (· this glyphs) (length matched)))
(set-field! glyphs this (list-set (· this glyphs) (· this glyphIterator index) ligatureGlyph))
#t)]
[else #f])])))

@ -13,4 +13,14 @@
(define-macro (test-module . EXPRS)
#`(module+ test
(require #,(datum->syntax caller-stx 'rackunit) #,(datum->syntax caller-stx 'racket/serialize))
. EXPRS))
. EXPRS))
(define (is-mark? codepoint)
;; mark classes = Mn Me Mc
(regexp-match #px"\\p{Mn}|\\p{Me}|\\p{Mc}" (string (integer->char codepoint))))
(module+ test
(require rackunit)
(check-true (and (is-mark? #x300) #t))
(check-false (and (is-mark? #x2ee) #t)))

@ -52,8 +52,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/layout/LayoutEngine.js
(send (· this engine) setup glyphs features script language))
;; Substitute and position the glyphs
;; todo: glyph substitution
#;(set! glyphs (send this substitute glyphs features script language))
(set! glyphs (send this substitute glyphs features script language))
(report/file 'ready-position)
(define positions (send this position glyphs features script language))
(report/file 'fired-position)

@ -141,13 +141,13 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/opentype.js
(dictify
;; Simple context
1 (dictify
'coverage (+Pointer uint16be 'Coverage)
'coverage (+Pointer uint16be Coverage)
'ruleSetCount uint16be
'ruleSets (+Array (+Pointer uint16be 'RuleSet) 'ruleSetCount))
;; Class-based context
2 (dictify
'coverage (+Pointer uint16be 'Coverage)
'coverage (+Pointer uint16be Coverage)
'classDef (+Pointer uint16be 'ClassDef)
'classSetCnt uint16be
'classSet (+Array (+Pointer uint16be 'ClassSet) 'classSetCnt))
@ -155,7 +155,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/opentype.js
3 (dictify
'glyphCount uint16be
'lookupCount uint16be
'coverages (+Array (+Pointer uint16be 'Coverage) 'glyphCount)
'coverages (+Array (+Pointer uint16be Coverage) 'glyphCount)
'lookupRecords (+Array LookupRecord 'lookupCount)))))
@ -169,13 +169,13 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/opentype.js
(dictify
;; Simple context glyph substitution
1 (dictify
'coverage (+Pointer uint16be 'Coverage)
'coverage (+Pointer uint16be Coverage)
'chainCount uint16be
'chainRuleSets (+Array (+Pointer uint16be 'ChainRuleSet) 'chainCount))
;; Class-based chaining context
2 (dictify
'coverage (+Pointer uint16be 'Coverage)
'coverage (+Pointer uint16be Coverage)
'backtrackClassDef (+Pointer uint16be 'ClassDef)
'inputClassDef (+Pointer uint16be 'ClassDef)
'lookaheadClassDef (+Pointer uint16be 'ClassDef)
@ -185,10 +185,10 @@ https://github.com/mbutterick/fontkit/blob/master/src/tables/opentype.js
;; Coverage-based chaining context
3 (dictify
'backtrackGlyphCount uint16be
'backtrackCoverage (+Array (+Pointer uint16be 'Coverage) 'backtrackGlyphCount)
'backtrackCoverage (+Array (+Pointer uint16be Coverage) 'backtrackGlyphCount)
'inputGlyphCount uint16be
'inputCoverage (+Array (+Pointer uint16be 'Coverage) 'inputGlyphCount)
'inputCoverage (+Array (+Pointer uint16be Coverage) 'inputGlyphCount)
'lookaheadGlyphCount uint16be
'lookaheadCoverage (+Array (+Pointer uint16be 'Coverage) 'lookaheadGlyphCount)
'lookaheadCoverage (+Array (+Pointer uint16be Coverage) 'lookaheadGlyphCount)
'lookupCount uint16be
'lookupRecords (+Array LookupRecord 'lookupCount)))))

@ -14,14 +14,12 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTLayoutEngine.js
[GPOSProcessor #f])
(report/file 'starting-ot-layout-engine)
;; todo: gsub
#;(when (· font has-gsub-table?)
(set-field! GSUBProcessor this (+GSUBProcessor font (· font GSUB))))
(when (· font has-gsub-table?)
(set-field! GSUBProcessor this (+GSUBProcessor font (or (· font GSUB) (error 'no-gsub-table)))))
(report* 'dingdong!-starting-gpos)
(when (· font has-gpos-table?)
(set-field! GPOSProcessor this (+GPOSProcessor font (· font GPOS))))
(set-field! GPOSProcessor this (+GPOSProcessor font (or (· font GPOS) (error 'no-gpos-table)))))
(define/public (setup glyphs features script language)
@ -36,6 +34,16 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTLayoutEngine.js
(report/file shaper)
(send (make-object shaper) plan (· this plan) (· this glyphInfos) features))
(define/public (substitute glyphs . _)
(cond
[(· this GSUBProcessor)
(send (· this plan) process (· this GSUBProcessor) (· 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))
@ -61,10 +69,10 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTLayoutEngine.js
(set! positions
(for/list ([glyphInfo (in-list glyphInfos)]
[position (in-list positions)])
(when (· glyphInfo isMark)
(dict-set*! position
'xAdvance 0
'yAdvance 0))
position)))
(when (· glyphInfo isMark)
(dict-set*! position
'xAdvance 0
'yAdvance 0))
position)))
)

@ -33,7 +33,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
(for*/first ([entry (in-list (· this table scriptList))]
[s (in-list scripts)]
#:when (eq? (· entry tag) s))
entry))))
entry))))
(define/public (selectScript [script #f] [language #f])
@ -57,9 +57,9 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
(when (and (not language) (not (equal? language (· this languageTag))))
(for/first ([lang (in-list (· this script langSysRecords))]
#:when (equal? (· lang tag) language))
(set-field! language this (· lang langSys))
(set-field! languageTag this (· lang tag))
(set! changed #t)))
(set-field! language this (· lang langSys))
(set-field! languageTag this (· lang tag))
(set! changed #t)))
(when (not (· this language))
(set-field! language this (· this script defaultLangSys)))
@ -69,8 +69,8 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
(set-field! features this (mhash))
(when (· this language)
(for ([featureIndex (in-list (· this language featureIndexes))])
(define record (list-ref (· this table featureList) featureIndex))
(dict-set! (· this features) (· record tag) (· record feature)))))))
(define record (list-ref (· this table featureList) featureIndex))
(dict-set! (· this features) (· record tag) (· record feature)))))))
(define/public (lookupsForFeatures [userFeatures empty] [exclude #f])
@ -80,10 +80,10 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
#:when feature
[lookupIndex (in-list (· feature lookupListIndexes))]
#:unless (and exclude (index-of exclude lookupIndex)))
(report*/file tag lookupIndex)
(mhasheq 'feature tag
'index lookupIndex
'lookup (send (· this table lookupList) get lookupIndex)))
(report*/file tag lookupIndex)
(mhasheq 'feature tag
'index lookupIndex
'lookup (send (· this table lookupList) get lookupIndex)))
< #:key (λ (i) (report*/file (· i index)))))
@ -91,6 +91,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
(report/file 'ot-proc:applyFeatures-part1)
(define lookups (send this lookupsForFeatures userFeatures))
(report/file 'ot-proc:applyFeatures-part2)
(report (length lookups))
(send this applyLookups lookups glyphs advances))
@ -100,14 +101,14 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
(report/file 'ot-proc:applyLookups)
(set-field! glyphIterator this (+GlyphIterator glyphs))
(for* ([lookup-entry (in-list lookups)])
(define feature (dict-ref lookup-entry 'feature))
(define lookup (dict-ref lookup-entry 'lookup))
(send (· this glyphIterator) reset (· lookup flags))
(while (< (· this glyphIterator index) (length glyphs))
(when (dict-has-key? (· this glyphIterator cur features) feature)
(for/or ([table (in-list (· lookup subTables))])
(send this applyLookup (· lookup lookupType) table)))
(send (· this glyphIterator) next))))
(define feature (dict-ref lookup-entry 'feature))
(define lookup (dict-ref lookup-entry 'lookup))
(send (· this glyphIterator) reset (· lookup flags))
(while (< (· this glyphIterator index) (length glyphs))
(when (dict-has-key? (· this glyphIterator cur features) feature)
(for/or ([table (in-list (· lookup subTables))])
(send this applyLookup (· lookup lookupType) table)))
(send (· this glyphIterator) next))))
(abstract applyLookup)
@ -118,13 +119,34 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
(define/public (coverageIndex coverage [glyph #f])
(unless glyph
(set! glyph (· this glyphIterator cur id)))
(or (case (report (· coverage version))
(or (case (· coverage version)
[(1) (index-of (· coverage glyphs) glyph)]
[(2) (for/first ([range (in-list (· coverage rangeRecords))]
#:when (<= (· range start) glyph (· range end)))
(+ (· range startCoverageIndex) glyph (- (· range start))))]
(+ (· range startCoverageIndex) glyph (- (· range start))))]
[else #f]) -1))
(define/public (match sequenceIndex sequence fn [matched #f])
(define pos (· this glyphIterator index))
(define glyph (send (· this glyphIterator) increment sequenceIndex))
(define idx 0)
(report* (list-ref sequence idx) (· glyph id))
(while (and (< idx (length sequence)) glyph (fn (list-ref sequence idx) (· glyph id)))
(report* 'in-match-loop idx (· glyph id))
(when matched
(push-end! matched (· this glyphIterator index)))
(increment! idx)
(set! glyph (· this glyphIterator next)))
(set-field! index (· this glyphIterator) pos)
(cond
[(< idx (length sequence)) #f]
[else (or matched #t)]))
(define/public (sequenceMatchIndices sequenceIndex sequence)
(send this match sequenceIndex sequence (λ (component glyph) (= component glyph)) empty))
(define/public (getClassID glyph classDef)
(or
(case (· classDef version)
@ -136,7 +158,6 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/OTProcessor.js
[(2)
(for/first ([range (in-list (· classDef classRangeRecord))]
#:when (<= (· range start) glyph (· range end)))
(· range class))])
(· range class))])
0)))

@ -75,7 +75,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/opentype/ShapingPlan.js
(dict-set! (· glyph features) feature #t)))
;; Executes the planned stages using the given OTProcessor
(define/public (process processor glyphs positions)
(define/public (process processor glyphs [positions #f])
(report*/file 'shaping-plan-process processor)
(send processor selectScript (· this script) (· this language))

@ -10,4 +10,4 @@
(test-module (require (submod TABLE-ID-STRING test) ...))
(define ID (make-hasheq (map cons (list 'TABLE-ID ...) (list TABLE-ID ...)))))))
(define-table-codecs table-codecs maxp hhea head loca prep fpgm hmtx cvt_ glyf OS/2 post GPOS)
(define-table-codecs table-codecs maxp hhea head loca prep fpgm hmtx cvt_ glyf OS/2 post GPOS GSUB)

@ -7410,7 +7410,12 @@ var OTProcessor = function () {
var glyph = this.glyphIterator.increment(sequenceIndex);
var idx = 0;
//console.log("sequence[idx] = " + sequence[idx]);
while (idx < sequence.length && glyph && fn(sequence[idx], glyph.id)) {
console.log("in match loop");
console.log("idx = " + idx);
console.log("glyph.id = " + glyph.id);
if (matched) {
matched.push(this.glyphIterator.index);
}
@ -8374,6 +8379,7 @@ var GSUBProcessor = function (_OTProcessor) {
GSUBProcessor.prototype.applyLookup = function applyLookup(lookupType, table) {
var _this2 = this;
console.log("'GSUBProcessor:applyLookup " + lookupType);
switch (lookupType) {
case 1:
@ -8449,11 +8455,18 @@ var GSUBProcessor = function (_OTProcessor) {
case 4:
{
// Ligature Substitution
console.log("----------------------------");
console.log("start ligature-substitution");
console.log("lookupType = " + lookupType);
console.log("table.coverage.glyphs = " + table.coverage.glyphs);
var _index3 = this.coverageIndex(table.coverage);
console.log("forker = " + _index3);
if (_index3 === -1) {
return false;
}
console.log("table.ligatureSets.get(_index3) = " + table.ligatureSets.get(_index3))
for (var _iterator = table.ligatureSets.get(_index3), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) {
var _ref;
@ -8468,7 +8481,13 @@ var GSUBProcessor = function (_OTProcessor) {
var ligature = _ref;
console.log("starting index = "+ _index3);
console.log("ligature.components = " + ligature.components);
var matched = this.sequenceMatchIndices(1, ligature.components);
console.log("matched = " + matched);
if (!matched) {
continue;
}
@ -8477,6 +8496,7 @@ var GSUBProcessor = function (_OTProcessor) {
// Concatenate all of the characters the new ligature will represent
var characters = _curGlyph.codePoints.slice();
for (var _iterator2 = matched, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) {
var _ref2;
@ -8491,11 +8511,16 @@ var GSUBProcessor = function (_OTProcessor) {
var _index4 = _ref2;
console.log("index = "+ _index4);
characters.push.apply(characters, this.glyphs[_index4].codePoints);
}
console.log("characters = "+ characters);
// Create the replacement ligature glyph
var ligatureGlyph = new GlyphInfo(this.font, ligature.glyph, characters, _curGlyph.features);
console.log("ligatureGlyph.id = " + ligatureGlyph.id);
ligatureGlyph.shaperInfo = _curGlyph.shaperInfo;
ligatureGlyph.isLigated = true;
ligatureGlyph.substituted = true;

Binary file not shown.
Loading…
Cancel
Save