move it
parent
73df28cfd7
commit
4610e957d6
@ -0,0 +1,5 @@
|
||||
#lang fontkit/racket
|
||||
(require "CFF_.rkt")
|
||||
(provide (rename-out [CFF_ CFF2]))
|
||||
|
||||
(test-module)
|
@ -0,0 +1,18 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide CFF_)
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFFont.js
|
||||
|#
|
||||
|
||||
;; the CFFFont object acts as the decoder for the `CFF ` table.
|
||||
|
||||
|
||||
(define-subclass BufferT (RCFF_)
|
||||
)
|
||||
|
||||
(define CFF_ (+RCFF_))
|
||||
|
||||
|
||||
(test-module)
|
@ -0,0 +1,224 @@
|
||||
fontkit = require '../pdfkit/node_modules/fontkit'
|
||||
|
||||
fira_path = "../pitfall/test/assets/fira.ttf"
|
||||
f = fontkit.openSync(fira_path)
|
||||
console.log "*************************** start decode"
|
||||
thing = f.GPOS.lookupList.get(1)
|
||||
console.log thing
|
||||
|
||||
###
|
||||
{ version: 65536,
|
||||
scriptList:
|
||||
[ { tag: 'DFLT', script: [Object] },
|
||||
{ tag: 'cyrl', script: [Object] },
|
||||
{ tag: 'grek', script: [Object] },
|
||||
{ tag: 'latn', script: [Object] } ],
|
||||
featureList:
|
||||
[ { tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'cpsp', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'kern', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mark', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] },
|
||||
{ tag: 'mkmk', feature: [Object] } ],
|
||||
lookupList:
|
||||
[ { lookupType: 1,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 2 },
|
||||
{ lookupType: 2,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 6,
|
||||
subTables: [ [Object], [Object], [Object], [Object], [Object], [Object] ],
|
||||
markFilteringSet: 2 },
|
||||
{ lookupType: 2,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 7,
|
||||
subTables:
|
||||
[ [Object],
|
||||
[Object],
|
||||
[Object],
|
||||
[Object],
|
||||
[Object],
|
||||
[Object],
|
||||
[Object] ],
|
||||
markFilteringSet: 2 },
|
||||
{ lookupType: 2,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 6,
|
||||
subTables: [ [Object], [Object], [Object], [Object], [Object], [Object] ],
|
||||
markFilteringSet: 2 },
|
||||
{ lookupType: 2,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 2,
|
||||
subTables: [ [Object], [Object] ],
|
||||
markFilteringSet: 4 },
|
||||
{ lookupType: 4,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 4 },
|
||||
{ lookupType: 4,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 4 },
|
||||
{ lookupType: 4,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 6 },
|
||||
{ lookupType: 6,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 6 },
|
||||
{ lookupType: 6,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 6 },
|
||||
{ lookupType: 6,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 6 },
|
||||
{ lookupType: 6,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 6 },
|
||||
{ lookupType: 6,
|
||||
flags:
|
||||
{ rightToLeft: false,
|
||||
ignoreBaseGlyphs: false,
|
||||
ignoreLigatures: false,
|
||||
ignoreMarks: false,
|
||||
useMarkFilteringSet: false,
|
||||
markAttachmentType: false },
|
||||
subTableCount: 1,
|
||||
subTables: [ [Object] ],
|
||||
markFilteringSet: 1 } ] }
|
||||
###
|
@ -0,0 +1,13 @@
|
||||
#lang fontkit/racket
|
||||
(require fontkit fontkit/gpos-processor "subset.rkt" rackunit xenomorph racket/serialize)
|
||||
|
||||
(define fira-path "../pitfall/test/assets/fira.ttf")
|
||||
(define f (openSync fira-path))
|
||||
(define gpos (· f GPOS))
|
||||
#;(get (· gpos lookupList) 11)
|
||||
|
||||
(define gp (+GPOSProcessor f gpos))
|
||||
|
||||
(· gpos scriptList)
|
||||
(send gp selectScript 'cyrl)
|
||||
|
@ -0,0 +1,197 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph br/cond "opentype.rkt")
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/GPOS.js
|
||||
|#
|
||||
|
||||
(define ValueFormat (+Bitfield uint16be '(xPlacement yPlacement xAdvance yAdvance xPlaDevice yPlaDevice xAdvDevice yAdvDevice)))
|
||||
|
||||
(define types
|
||||
(mhash
|
||||
'xPlacement int16be
|
||||
'yPlacement int16be
|
||||
'xAdvance int16be
|
||||
'yAdvance int16be
|
||||
'xPlaDevice (+Pointer uint16be Device (mhash 'type 'global 'relativeTo 'rel))
|
||||
'yPlaDevice (+Pointer uint16be Device (mhash 'type 'global 'relativeTo 'rel))
|
||||
'xAdvDevice (+Pointer uint16be Device (mhash 'type 'global 'relativeTo 'rel))
|
||||
'yAdvDevice (+Pointer uint16be Device (mhash 'type 'global 'relativeTo 'rel))))
|
||||
|
||||
(define-subclass object% (ValueRecord [key 'valueFormat])
|
||||
(define/public (buildStruct parent)
|
||||
;; set `struct` to the first dict in the chain of ancestors
|
||||
;; with the target key
|
||||
(define struct (let loop ([x parent])
|
||||
(cond
|
||||
[(and x (dict? x) (dict-ref x key #f)) x]
|
||||
[(· x parent) => loop]
|
||||
[else #f])))
|
||||
(and struct
|
||||
(let ()
|
||||
(define format (dict-ref struct key))
|
||||
(define fields
|
||||
(append
|
||||
(dictify 'rel (λ _ (dict-ref struct '_startOffset)))
|
||||
(for/list ([(key val) (in-dict format)]
|
||||
#:when val)
|
||||
(cons key (dict-ref types key)))))
|
||||
(+Struct fields))))
|
||||
|
||||
(define/public (size val ctx)
|
||||
(send (buildStruct ctx) size val ctx))
|
||||
|
||||
(define/public (decode port parent)
|
||||
(define res (send (buildStruct parent) decode port parent))
|
||||
(dict-remove! res 'rel)
|
||||
res)
|
||||
|
||||
(define/public (encode . args)
|
||||
(error 'GPOS-encode-not-implemented)))
|
||||
|
||||
(define PairValueRecord (+Struct
|
||||
(dictify
|
||||
'secondGlyph uint16be
|
||||
'value1 (+ValueRecord 'valueFormat1)
|
||||
'value2 (+ValueRecord 'valueFormat2))))
|
||||
|
||||
(define PairSet (+Array PairValueRecord uint16be))
|
||||
|
||||
(define Class2Record (+Struct
|
||||
(dictify
|
||||
'value1 (+ValueRecord 'valueFormat1)
|
||||
'value2 (+ValueRecord 'valueFormat2))))
|
||||
|
||||
(define Anchor (+VersionedStruct uint16be
|
||||
(dictify
|
||||
;; Design units only
|
||||
1 (dictify 'xCoordinate int16be
|
||||
'yCoordinate int16be)
|
||||
|
||||
;; Design units plus contour point
|
||||
2 (dictify 'xCoordinate int16be
|
||||
'yCoordinate int16be
|
||||
'anchorPoint uint16be)
|
||||
;; Design units plus Device tables
|
||||
3 (dictify 'xCoordinate int16be
|
||||
'yCoordinate int16be
|
||||
'xDeviceTable (+Pointer uint16be Device)
|
||||
'yDeviceTable (+Pointer uint16be Device)))))
|
||||
|
||||
(define EntryExitRecord (+Struct
|
||||
(dictify 'entryAnchor (+Pointer uint16be Anchor (mhash 'type 'parent))
|
||||
'exitAnchor (+Pointer uint16be Anchor (mhash 'type 'parent)))))
|
||||
|
||||
(define MarkRecord (+Struct
|
||||
(dictify 'class uint16be
|
||||
'markAnchor uint16be)))
|
||||
|
||||
(define MarkArray (+Array MarkRecord uint16be))
|
||||
|
||||
(define BaseRecord (+Array (+Pointer uint16be Anchor) (λ (t) (ref* t 'parent 'classCount))))
|
||||
|
||||
(define BaseArray (+Array BaseRecord uint16be))
|
||||
|
||||
(define ComponentRecord (+Array (+Pointer uint16be Anchor) (λ (t) (ref* t 'parent 'parent 'classCount))))
|
||||
(define LigatureAttach (+Array ComponentRecord uint16be))
|
||||
(define LigatureArray (+Array (+Pointer uint16be LigatureAttach) uint16be))
|
||||
|
||||
(define-subclass VersionedStruct (GPOSLookup-VersionedStruct))
|
||||
(define GPOSLookup
|
||||
(+GPOSLookup-VersionedStruct
|
||||
'lookupType
|
||||
(dictify
|
||||
;; Single Adjustment
|
||||
1 (+VersionedStruct uint16be
|
||||
(dictify
|
||||
;; Single positioning value
|
||||
1 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'valueFormat ValueFormat
|
||||
'value (+ValueRecord))
|
||||
2 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'valueFormat ValueFormat
|
||||
'valueCount uint16be
|
||||
'values (+LazyArray (+ValueRecord) 'valueCount))))
|
||||
;; Pair Adjustment Positioning
|
||||
2 (+VersionedStruct uint16be
|
||||
(dictify
|
||||
;; Adjustments for glyph pairs
|
||||
1 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'valueFormat1 ValueFormat
|
||||
'valueFormat2 ValueFormat
|
||||
'pairSetCount uint16be
|
||||
'pairSets (+LazyArray (+Pointer uint16be PairSet) 'pairSetCount))
|
||||
|
||||
;; Class pair adjustment
|
||||
2 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'valueFormat1 ValueFormat
|
||||
'valueFormat2 ValueFormat
|
||||
'classDef1 (+Pointer uint16be ClassDef)
|
||||
'classDef2 (+Pointer uint16be ClassDef)
|
||||
'class1Count uint16be
|
||||
'class2Count uint16be
|
||||
'classRecords (+LazyArray (+LazyArray Class2Record 'class2Count) 'class1Count))))
|
||||
|
||||
;; Cursive Attachment Positioning
|
||||
3 (dictify
|
||||
'format uint16be
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'entryExitCount uint16be
|
||||
'entryExitRecords (+Array EntryExitRecord 'entryExitCount))
|
||||
|
||||
;; MarkToBase Attachment Positioning
|
||||
4 (dictify
|
||||
'format uint16be
|
||||
'markCoverage (+Pointer uint16be Coverage)
|
||||
'baseCoverage (+Pointer uint16be Coverage)
|
||||
'classCount uint16be
|
||||
'markArray (+Pointer uint16be MarkArray)
|
||||
'baseArray (+Pointer uint16be BaseArray))
|
||||
|
||||
;; MarkToLigature Attachment Positioning
|
||||
5 (dictify
|
||||
'format uint16be
|
||||
'markCoverage (+Pointer uint16be Coverage)
|
||||
'ligatureCoverage (+Pointer uint16be Coverage)
|
||||
'classCount uint16be
|
||||
'markArray (+Pointer uint16be MarkArray)
|
||||
'ligatureArray (+Pointer uint16be LigatureArray))
|
||||
|
||||
;; MarkToMark Attachment Positioning
|
||||
6 (dictify
|
||||
'format uint16be
|
||||
'mark1Coverage (+Pointer uint16be Coverage)
|
||||
'mark2Coverage (+Pointer uint16be Coverage)
|
||||
'classCount uint16be
|
||||
'mark1Array (+Pointer uint16be MarkArray)
|
||||
'mark2Array (+Pointer uint16be BaseArray))
|
||||
|
||||
7 Context ;; Contextual positioning
|
||||
8 ChainingContext ;; Chaining contextual positioning
|
||||
|
||||
;; Extension positioning
|
||||
9 (dictify
|
||||
'posFormat uint16be
|
||||
'lookupType uint16be ;; cannot also be 9
|
||||
'extension (+Pointer uint32be (λ () (error 'circular-reference-unfixed))))
|
||||
)))
|
||||
|
||||
;; Fix circular reference
|
||||
(ref*-set! GPOSLookup 'versions 9 'extension 'type GPOSLookup)
|
||||
|
||||
(define-subclass VersionedStruct (GPOS-MainVersionedStruct))
|
||||
(define GPOS (+GPOS-MainVersionedStruct uint32be
|
||||
(dictify
|
||||
'header (dictify 'scriptList (+Pointer uint16be ScriptList)
|
||||
'featureList (+Pointer uint16be FeatureList)
|
||||
'lookupList (+Pointer uint16be (LookupList GPOSLookup)))
|
||||
#x00010000 (dictify)
|
||||
#;#x00010001 #;(+Pointer uint32be FeatureVariations))))
|
||||
|
||||
(test-module)
|
@ -0,0 +1,6 @@
|
||||
fontkit = require '../pdfkit/node_modules/fontkit'
|
||||
|
||||
fira_path = "../pitfall/test/assets/fira.ttf"
|
||||
f = fontkit.openSync(fira_path)
|
||||
console.log f.GSUB.lookupList.get(30).subTables
|
||||
console.log f.GSUB.lookupList.get(30).subTables[0].ligatureSets.get(0)
|
@ -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,78 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/OS2.js
|
||||
|#
|
||||
|
||||
(define-subclass VersionedStruct (ROS/2))
|
||||
|
||||
(define OS/2 (let ()
|
||||
(define type-1
|
||||
(dictify 'typoAscender int16be
|
||||
'typoDescender int16be
|
||||
'typoLineGap int16be
|
||||
'winAscent uint16be
|
||||
'winDescent uint16be
|
||||
'codePageRange (+Array uint32be 2)))
|
||||
|
||||
(define type-2
|
||||
(dictify 'xHeight int16be
|
||||
'capHeight int16be
|
||||
'defaultChar uint16be
|
||||
'breakChar uint16be
|
||||
'maxContent uint16be))
|
||||
|
||||
(define type-5
|
||||
(dictify 'usLowerOpticalPointSize uint16be
|
||||
'usUpperOpticalPointSize uint16be))
|
||||
|
||||
(+ROS/2
|
||||
uint16be
|
||||
(dictify
|
||||
'header (dictify 'xAvgCharWidth int16be ;; average weighted advance width of lower case letters and space
|
||||
'usWeightClass uint16be ;; visual weight of stroke in glyphs
|
||||
'usWidthClass uint16be ;; relative change from the normal aspect ratio (width to height ratio)
|
||||
;; Indicates font embedding licensing rights
|
||||
'fsType (+Bitfield uint16be '(null noEmbedding viewOnly editable null null null null noSubsetting bitmapOnly))
|
||||
'ySubscriptXSize int16be ;; recommended horizontal size in pixels for subscripts
|
||||
'ySubscriptYSize int16be ;; recommended vertical size in pixels for subscripts
|
||||
'ySubscriptXOffset int16be ;; recommended horizontal offset for subscripts
|
||||
'ySubscriptYOffset int16be ;; recommended vertical offset form the baseline for subscripts
|
||||
'ySuperscriptXSize int16be ;; recommended horizontal size in pixels for superscripts
|
||||
'ySuperscriptYSize int16be ;; recommended vertical size in pixels for superscripts
|
||||
'ySuperscriptXOffset int16be ;; recommended horizontal offset for superscripts
|
||||
'ySuperscriptYOffset int16be ;; recommended vertical offset from the baseline for superscripts
|
||||
'yStrikeoutSize int16be ;; width of the strikeout stroke
|
||||
'yStrikeoutPosition int16be ;; position of the strikeout stroke relative to the baseline
|
||||
'sFamilyClass int16be ;; classification of font-family design
|
||||
'panose (+Array uint8 10) ;; describe the visual characteristics of a given typeface
|
||||
'ulCharRange (+Array uint32be 4)
|
||||
'vendorID (+Symbol 4) ;; four character identifier for the font vendor
|
||||
;; bit field containing information about the font
|
||||
'fsSelection (+Bitfield uint16 '(italic underscore negative outlined strikeout bold regular useTypoMetrics wws oblique))
|
||||
'usFirstCharIndex uint16be ;; The minimum Unicode index in this font
|
||||
'usLastCharIndex uint16be) ;; The maximum Unicode index in this font
|
||||
|
||||
0 null
|
||||
1 type-1
|
||||
2 (append type-1 type-2)
|
||||
3 (append type-1 type-2)
|
||||
4 (append type-1 type-2)
|
||||
5 (append type-1 type-2 type-5)))))
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables OS/2 offset))
|
||||
(define len (· dir tables OS/2 length))
|
||||
(check-equal? offset 360)
|
||||
(check-equal? len 96)
|
||||
(define ds (open-input-bytes (peek-bytes len offset ip)))
|
||||
(define version (send uint16be decode ds))
|
||||
(send OS/2 force-version! version)
|
||||
(define table-data (send OS/2 decode ds))
|
||||
(check-equal? (· table-data panose) '(2 0 5 3 6 0 0 2 0 4))
|
||||
(check-equal? (· table-data sFamilyClass) 0))
|
@ -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])])))
|
||||
|
@ -0,0 +1,32 @@
|
||||
((3)
|
||||
0
|
||||
()
|
||||
0
|
||||
()
|
||||
()
|
||||
(h
|
||||
!
|
||||
()
|
||||
(tag u . "\u0000\u0001\u0000\u0000")
|
||||
(rangeShift . 96)
|
||||
(searchRange . 128)
|
||||
(numTables . 14)
|
||||
(tables
|
||||
h
|
||||
!
|
||||
(equal)
|
||||
(loca h ! () (tag u . "loca") (offset . 38692) (checkSum . 2795817194) (length . 460))
|
||||
(OS/2 h ! () (tag u . "OS/2") (offset . 360) (checkSum . 2351070438) (length . 96))
|
||||
(glyf h ! () (tag u . "glyf") (offset . 4620) (checkSum . 1143629849) (length . 34072))
|
||||
(hhea h ! () (tag u . "hhea") (offset . 292) (checkSum . 132056097) (length . 36))
|
||||
(post h ! () (tag u . "post") (offset . 41520) (checkSum . 1670855689) (length . 514))
|
||||
(cvt_ h ! () (tag u . "cvt ") (offset . 4592) (checkSum . 10290865) (length . 26))
|
||||
(VDMX h ! () (tag u . "VDMX") (offset . 1372) (checkSum . 1887795202) (length . 1504))
|
||||
(prep h ! () (tag u . "prep") (offset . 4512) (checkSum . 490862356) (length . 78))
|
||||
(maxp h ! () (tag u . "maxp") (offset . 328) (checkSum . 50135594) (length . 32))
|
||||
(hmtx h ! () (tag u . "hmtx") (offset . 456) (checkSum . 3982043058) (length . 916))
|
||||
(cmap h ! () (tag u . "cmap") (offset . 2876) (checkSum . 1723761408) (length . 1262))
|
||||
(name h ! () (tag u . "name") (offset . 39152) (checkSum . 2629707307) (length . 2367))
|
||||
(head h ! () (tag u . "head") (offset . 236) (checkSum . 4281190895) (length . 54))
|
||||
(fpgm h ! () (tag u . "fpgm") (offset . 4140) (checkSum . 106535991) (length . 371)))
|
||||
(entrySelector . 3)))
|
@ -0,0 +1,32 @@
|
||||
((3)
|
||||
0
|
||||
()
|
||||
0
|
||||
()
|
||||
()
|
||||
(h
|
||||
!
|
||||
()
|
||||
(tag u . "\u0000\u0001\u0000\u0000")
|
||||
(rangeShift . 96)
|
||||
(searchRange . 128)
|
||||
(numTables . 14)
|
||||
(tables
|
||||
h
|
||||
!
|
||||
(equal)
|
||||
(loca h ! () (tag u . "loca") (offset . 37392) (checkSum . 46801904) (length . 460))
|
||||
(OS/2 h ! () (tag u . "OS/2") (offset . 360) (checkSum . 2367847603) (length . 96))
|
||||
(glyf h ! () (tag u . "glyf") (offset . 4620) (checkSum . 2099535230) (length . 32772))
|
||||
(hhea h ! () (tag u . "hhea") (offset . 292) (checkSum . 113838023) (length . 36))
|
||||
(post h ! () (tag u . "post") (offset . 40280) (checkSum . 1671576585) (length . 514))
|
||||
(cvt_ h ! () (tag u . "cvt ") (offset . 4592) (checkSum . 9307818) (length . 26))
|
||||
(VDMX h ! () (tag u . "VDMX") (offset . 1372) (checkSum . 1905948947) (length . 1504))
|
||||
(prep h ! () (tag u . "prep") (offset . 4512) (checkSum . 776081685) (length . 78))
|
||||
(maxp h ! () (tag u . "maxp") (offset . 328) (checkSum . 50135583) (length . 32))
|
||||
(hmtx h ! () (tag u . "hmtx") (offset . 456) (checkSum . 3798537071) (length . 916))
|
||||
(cmap h ! () (tag u . "cmap") (offset . 2876) (checkSum . 1723761408) (length . 1262))
|
||||
(name h ! () (tag u . "name") (offset . 37852) (checkSum . 2313429994) (length . 2427))
|
||||
(head h ! () (tag u . "head") (offset . 236) (checkSum . 4275817075) (length . 54))
|
||||
(fpgm h ! () (tag u . "fpgm") (offset . 4140) (checkSum . 106535991) (length . 371)))
|
||||
(entrySelector . 3)))
|
@ -0,0 +1,7 @@
|
||||
#lang fontkit/racket
|
||||
(require racket/serialize)
|
||||
(provide cloneDeep)
|
||||
|
||||
(define (cloneDeep val)
|
||||
(deserialize (serialize val)))
|
||||
|
@ -0,0 +1,5 @@
|
||||
#lang fontkit/racket
|
||||
(provide CmapProcessor)
|
||||
|
||||
(define-subclass object% (CmapProcessor cmapTable)
|
||||
(super-new))
|
@ -0,0 +1,29 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/cvt.js
|
||||
|#
|
||||
|
||||
(define-subclass Struct (Rcvt_))
|
||||
|
||||
(define cvt_ (+Rcvt_
|
||||
(dictify
|
||||
'controlValues (+Array int16be))))
|
||||
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables cvt_ offset))
|
||||
(define len (· dir tables cvt_ length))
|
||||
(check-equal? offset 4592)
|
||||
(check-equal? len 26)
|
||||
(set-port-position! ip 0)
|
||||
(define table-bytes #"\0\24\0+\0S\0\0\0\20\377&\0\0\1\341\0\v\2\237\0\22\2\340\0\b")
|
||||
(check-equal? table-bytes (peek-bytes len offset ip))
|
||||
(define ds (open-input-bytes (peek-bytes len offset ip)))
|
||||
(define cvt-array '(20 43 83 0 16 -218 0 481 11 671 18 736 8))
|
||||
(check-equal? (dict-ref (decode cvt_ ds) 'controlValues) cvt-array)
|
||||
(check-equal? (encode cvt_ (mhash 'controlValues cvt-array) #f) table-bytes))
|
@ -0,0 +1,47 @@
|
||||
#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)
|
||||
#;(report*/file 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)
|
||||
(when userFeatures
|
||||
(unless (and (list? userFeatures) (andmap symbol? userFeatures))
|
||||
(raise-argument-error 'DefaultShaper:planPostprocessing "list of features" userFeatures)))
|
||||
(send plan add (append COMMON_FEATURES HORIZONTAL_FEATURES (or userFeatures empty))))
|
||||
|
||||
(define/public (assignFeatures plan glyphs)
|
||||
;; todo: Enable contextual fractions
|
||||
(void)))
|
@ -0,0 +1,68 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph "tables.rkt" describe)
|
||||
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/directory.js
|
||||
|#
|
||||
|
||||
(define TableEntry (+Struct
|
||||
(dictify 'tag (+Symbol 4)
|
||||
'checkSum uint32be
|
||||
'offset (+Pointer uint32be 'void (mhash 'type 'global))
|
||||
'length uint32be)))
|
||||
|
||||
;; for stupid tags like 'cvt '
|
||||
(define (symbol-replace sym this that)
|
||||
(string->symbol (string-replace (if (string? sym) sym (symbol->string sym)) this that)))
|
||||
(define (escape-tag tag) (symbol-replace tag " " "_"))
|
||||
(define (unescape-tag tag) (symbol-replace tag "_" " "))
|
||||
|
||||
(define-subclass Struct (RDirectory)
|
||||
(define/augride (post-decode this-res stream ctx)
|
||||
(define new-tables-val (mhash))
|
||||
(for ([table (in-list (· this-res tables))])
|
||||
(hash-set! new-tables-val (escape-tag (· table tag)) table))
|
||||
(dict-set! this-res 'tables new-tables-val)
|
||||
this-res)
|
||||
|
||||
(define/augride (pre-encode this-val port)
|
||||
(define tables (for/list ([(tag table) (in-hash (· this-val tables))])
|
||||
(define table-codec (hash-ref table-codecs tag))
|
||||
(mhash 'tag (unescape-tag tag)
|
||||
'checkSum 0
|
||||
'offset (+VoidPointer table-codec table)
|
||||
'length (send table-codec size table))))
|
||||
|
||||
(define numTables (length tables))
|
||||
(define searchRange (* (floor (log numTables 2)) 16))
|
||||
|
||||
(hash-set*! this-val
|
||||
'tag 'true
|
||||
'numTables numTables
|
||||
'tables tables
|
||||
'searchRange searchRange
|
||||
'entrySelector (floor (/ searchRange (log 2)))
|
||||
'rangeShift (- (* numTables 16) searchRange))
|
||||
|
||||
this-val))
|
||||
|
||||
(define Directory (+RDirectory (dictify 'tag (+Symbol 4)
|
||||
'numTables uint16be
|
||||
'searchRange uint16be
|
||||
'entrySelector uint16be
|
||||
'rangeShift uint16be
|
||||
'tables (+Array TableEntry 'numTables))))
|
||||
|
||||
|
||||
(define (directory-decode ip [options (mhash)])
|
||||
(send Directory decode ip))
|
||||
|
||||
(define (file-directory-decode ps)
|
||||
(directory-decode (open-input-file ps)))
|
||||
|
||||
#;(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define decoded-dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(check-equal? (directory-decode ip) decoded-dir))
|
@ -0,0 +1,30 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/fpgm.js
|
||||
|#
|
||||
|
||||
;; A list of instructions that are executed once when a font is first used.
|
||||
;; These instructions are known as the font program. The main use of this table
|
||||
;; is for the definition of functions that are used in many different glyph programs.
|
||||
|
||||
|
||||
(define-subclass Struct (fpgm%))
|
||||
|
||||
(define fpgm (make-object fpgm%
|
||||
(dictify
|
||||
'instructions (make-object Array uint8))))
|
||||
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables fpgm offset))
|
||||
(define len (· dir tables fpgm length))
|
||||
(check-equal? offset 4140)
|
||||
(check-equal? len 371)
|
||||
(check-equal? (pos ip 0) 0)
|
||||
(check-equal? (dict-ref (send fpgm decode (peek-bytes len offset ip)) 'instructions) '(184 0 0 44 75 184 0 9 80 88 177 1 1 142 89 184 1 255 133 184 0 68 29 185 0 9 0 3 95 94 45 184 0 1 44 32 32 69 105 68 176 1 96 45 184 0 2 44 184 0 1 42 33 45 184 0 3 44 32 70 176 3 37 70 82 88 35 89 32 138 32 138 73 100 138 32 70 32 104 97 100 176 4 37 70 32 104 97 100 82 88 35 101 138 89 47 32 176 0 83 88 105 32 176 0 84 88 33 176 64 89 27 105 32 176 0 84 88 33 176 64 101 89 89 58 45 184 0 4 44 32 70 176 4 37 70 82 88 35 138 89 32 70 32 106 97 100 176 4 37 70 32 106 97 100 82 88 35 138 89 47 253 45 184 0 5 44 75 32 176 3 38 80 88 81 88 176 128 68 27 176 64 68 89 27 33 33 32 69 176 192 80 88 176 192 68 27 33 89 89 45 184 0 6 44 32 32 69 105 68 176 1 96 32 32 69 125 105 24 68 176 1 96 45 184 0 7 44 184 0 6 42 45 184 0 8 44 75 32 176 3 38 83 88 176 64 27 176 0 89 138 138 32 176 3 38 83 88 35 33 176 128 138 138 27 138 35 89 32 176 3 38 83 88 35 33 184 0 192 138 138 27 138 35 89 32 176 3 38 83 88 35 33 184 1 0 138 138 27 138 35 89 32 176 3 38 83 88 35 33 184 1 64 138 138 27 138 35 89 32 184 0 3 38 83 88 176 3 37 69 184 1 128 80 88 35 33 184 1 128 35 33 27 176 3 37 69 35 33 35 33 89 27 33 89 68 45 184 0 9 44 75 83 88 69 68 27 33 33 89 45)))
|
||||
|
@ -0,0 +1,21 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/glyf.js
|
||||
|#
|
||||
|
||||
(define-subclass Struct (Rglyf))
|
||||
|
||||
(define glyf (+Array (+BufferT)))
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables glyf offset))
|
||||
(define len (· dir tables glyf length))
|
||||
(check-equal? offset 4620)
|
||||
(check-equal? len 34072)
|
||||
(set-port-position! ip 0)
|
||||
(define table-bytes (peek-bytes len offset ip)))
|
@ -0,0 +1,79 @@
|
||||
#lang fontkit/racket
|
||||
(require br/cond)
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/opentype/GlyphIterator.js
|
||||
|#
|
||||
|
||||
(define-subclass object%
|
||||
(GlyphIterator glyphs [flags (mhasheq)])
|
||||
(field [index #f])
|
||||
(reset flags)
|
||||
|
||||
(define/public (reset flags)
|
||||
(set-field! flags this flags)
|
||||
(set-field! index this 0))
|
||||
|
||||
(define/public (cur)
|
||||
(and (< (· this index) (length (· this glyphs)))
|
||||
(list-ref (· this glyphs) (· this index))))
|
||||
|
||||
(define/public (shouldIgnore glyph flags)
|
||||
(or (and (· flags ignoreMarks) (· glyph isMark))
|
||||
(and (· flags ignoreBaseGlyphs) (not (· glyph isMark)))
|
||||
(and (· flags ignoreLigatures) (· glyph isLigature))))
|
||||
|
||||
(define/public (move dir)
|
||||
(unless (= (abs dir) 1)
|
||||
(raise-argument-error 'GlyphIterator:move "1 or -1" dir))
|
||||
(increment-field! index this dir)
|
||||
(while (and (<= 0 (· this index))
|
||||
(< (· this index) (length (· this glyphs)))
|
||||
(send this shouldIgnore (list-ref (· this glyphs) (· this index)) (· this flags)))
|
||||
(increment-field! index this dir))
|
||||
|
||||
(if (or (> 0 (· this index))
|
||||
(>= (· this index) (length (· this glyphs))))
|
||||
#f
|
||||
(list-ref (· this glyphs) (· this index))))
|
||||
|
||||
(define/public (next) (move 1))
|
||||
|
||||
(define/public (prev) (move -1))
|
||||
|
||||
(define/public (peek [count 1])
|
||||
(define idx (· this index))
|
||||
(define res (send this increment count))
|
||||
(set-field! index this idx)
|
||||
res)
|
||||
|
||||
(define/public (peekIndex [count 1])
|
||||
(define idx (· this index))
|
||||
(send this increment count)
|
||||
(define res (· this index))
|
||||
(set-field! index this idx)
|
||||
res)
|
||||
|
||||
(define/public (increment [count 1])
|
||||
(for/last ([i (in-range (abs count))])
|
||||
(send this move (if (negative? count) -1 1)))))
|
||||
|
||||
(test-module
|
||||
(define gi (+GlyphIterator '(a b c)))
|
||||
(check-equal? (· gi index) 0)
|
||||
(check-equal? (send gi cur) 'a)
|
||||
(check-equal? (send gi move 1) 'b)
|
||||
(check-equal? (send gi move 1) 'c)
|
||||
(check-false (send gi move 1))
|
||||
(check-false (send gi cur))
|
||||
(check-equal? (send gi increment -3) 'a)
|
||||
(check-equal? (send gi cur) 'a)
|
||||
(check-equal? (send gi peek 1) 'b)
|
||||
(check-equal? (send gi peek 2) 'c)
|
||||
(check-equal? (send gi peek 3) #f)
|
||||
(check-equal? (send gi cur) 'a)
|
||||
|
||||
|
||||
)
|
@ -0,0 +1,29 @@
|
||||
#lang fontkit/racket
|
||||
(provide (all-defined-out))
|
||||
|
||||
;; Represents positioning information for a glyph in a GlyphRun.
|
||||
(define-subclass object% (GlyphPosition
|
||||
;; The amount to move the virtual pen in the X direction after rendering this glyph.
|
||||
[xAdvance 0]
|
||||
;; The amount to move the virtual pen in the Y direction after rendering this glyph.
|
||||
[yAdvance 0]
|
||||
;; The offset from the pen position in the X direction at which to render this glyph.
|
||||
|
||||
[xOffset 0]
|
||||
;; The offset from the pen position in the Y direction at which to render this glyph.
|
||||
[yOffset 0]
|
||||
[advanceWidth 0])
|
||||
|
||||
(as-methods
|
||||
scale)
|
||||
)
|
||||
|
||||
|
||||
(define/contract (scale this factor)
|
||||
(number? . ->m . (is-a?/c GlyphPosition))
|
||||
(set-field! xAdvance this (* factor (· this xAdvance)))
|
||||
(set-field! yAdvance this (* factor (· this yAdvance)))
|
||||
(set-field! xOffset this (* factor (· this xOffset)))
|
||||
(set-field! yOffset this (* factor (· this yOffset)))
|
||||
(set-field! advanceWidth this (* factor (· this advanceWidth)))
|
||||
this)
|
@ -0,0 +1,49 @@
|
||||
#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]
|
||||
[features (mhasheq)])
|
||||
|
||||
(cond
|
||||
[(list? features-in)
|
||||
(for ([feature (in-list features-in)])
|
||||
(hash-set! features feature #t))]
|
||||
[(dict? features-in)
|
||||
(for ([(feature val) (in-dict features-in)])
|
||||
(hash-set! features feature val))])
|
||||
|
||||
(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]
|
||||
[isMark #f]
|
||||
[isLigature #f])
|
||||
|
||||
(define/public (id [id-in #f])
|
||||
(cond
|
||||
[(not id-in) _id]
|
||||
[else (set-field! _id this id-in)
|
||||
(set-field! substituted this #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,19 @@
|
||||
#lang fontkit/racket
|
||||
(require "bbox.rkt" (prefix-in Script- "script.rkt"))
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/layout/GlyphRun.js
|
||||
|#
|
||||
|
||||
;; Represents a run of Glyph and GlyphPosition objects.
|
||||
;; Returned by the font layout method.
|
||||
(define-subclass object% (GlyphRun
|
||||
glyphs ; An array of Glyph objects in the run
|
||||
positions) ; An array of GlyphPosition objects for each glyph in the run
|
||||
|
||||
|
||||
(define/public (advanceWidth)
|
||||
(for/sum ([pos (in-list positions)])
|
||||
(· pos xAdvance))))
|
@ -0,0 +1,31 @@
|
||||
#lang fontkit/racket
|
||||
(require fontkit "gpos-processor.rkt" rackunit xenomorph racket/serialize describe)
|
||||
|
||||
(define fira-path "../pitfall/test/assets/fira.ttf")
|
||||
(define f (openSync fira-path))
|
||||
(define gpos (· f GPOS))
|
||||
|
||||
(define proc (+GPOSProcessor f gpos))
|
||||
|
||||
(check-equal? (dump (· proc features))
|
||||
'((cpsp (lookupCount . 1) (lookupListIndexes 0) (featureParams . 0))
|
||||
(mkmk (lookupCount . 5) (lookupListIndexes 8 9 10 11 12) (featureParams . 0))
|
||||
(mark (lookupCount . 3) (lookupListIndexes 5 6 7) (featureParams . 0))
|
||||
(kern (lookupCount . 4) (lookupListIndexes 1 2 3 4) (featureParams . 0))))
|
||||
|
||||
(check-equal? (dump (· proc script))
|
||||
'((count . 0)
|
||||
(defaultLangSys (featureIndexes 0 14 28 42)
|
||||
(reserved . 0)
|
||||
(reqFeatureIndex . 65535)
|
||||
(featureCount . 4))
|
||||
(langSysRecords)))
|
||||
(check-equal? (dump (· proc scriptTag)) 'DFLT)
|
||||
(check-equal? (dump (· proc language))
|
||||
'((featureIndexes 0 14 28 42)
|
||||
(reserved . 0)
|
||||
(reqFeatureIndex . 65535)
|
||||
(featureCount . 4)))
|
||||
(check-equal? (dump (· proc languageTag)) #f)
|
||||
(check-equal? (dump (· proc lookups)) empty)
|
||||
(check-equal? (dump (· proc direction)) 'ltr)
|
@ -0,0 +1,24 @@
|
||||
#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))
|
||||
|
||||
(check-equal? (map car (dump (· proc features)))
|
||||
'(c2sc pnum liga tnum onum ss01 dlig lnum sups zero ss02 aalt subs ss03 ordn calt dnom smcp salt case numr frac mgrk))
|
||||
|
||||
(check-equal? (dict-ref (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))
|
||||
|
||||
(check-equal? (dump (· proc scriptTag)) 'DFLT)
|
||||
|
||||
(check-equal? (dict-ref (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))
|
||||
|
||||
(check-equal? (dump (· proc languageTag)) #f)
|
||||
(check-equal? (dump (· proc lookups)) empty)
|
||||
(check-equal? (dump (· proc direction)) 'ltr)
|
||||
|
@ -0,0 +1,148 @@
|
||||
#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 lookupType '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))
|
||||
(send glyph id
|
||||
(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))
|
||||
(send (· this glyphIterator cur) id (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)
|
||||
(send (· this glyphIterator cur) id (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 (send (· table ligatureSets) get index))]
|
||||
[matched (in-value (send this sequenceMatchIndices 1 (· ligature components)))]
|
||||
#:when 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)])
|
||||
index
|
||||
(get-field codePoints (list-ref (· this glyphs) index))))))
|
||||
|
||||
characters
|
||||
;; Create the replacement ligature glyph
|
||||
(define ligatureGlyph (+GlyphInfo (· this font) (· ligature glyph) characters (· curGlyph features)))
|
||||
(· ligatureGlyph id)
|
||||
(set-field! shaperInfo ligatureGlyph (· curGlyph shaperInfo))
|
||||
(set-field! isLigated ligatureGlyph #t)
|
||||
(set-field! substituted ligatureGlyph #t)
|
||||
|
||||
(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
|
||||
#;(report (for/list ([g (· this glyphs)]) (· g id)) 'step-a)
|
||||
#;(report matched)
|
||||
#;(report (· this glyphIterator index))
|
||||
(set-field! glyphs this (for*/list ([(glyph idx) (in-indexed (· this glyphs))]
|
||||
[midx (in-list matched)]
|
||||
#:unless (= idx midx))
|
||||
(if (= idx (· this glyphIterator index))
|
||||
ligatureGlyph
|
||||
glyph)))
|
||||
(set-field! glyphs (· this glyphIterator) (· this glyphs)) ; update glyph iterator to keep it in sync <sigh>
|
||||
#;(report (for/list ([g (· this glyphs)]) (· g id)) 'step-c)
|
||||
#;(report (· this glyphIterator index))
|
||||
#t)]
|
||||
[else #f])]
|
||||
[(5) ;; Contextual Substitution
|
||||
(send this applyContext table)]
|
||||
[(6) ;; Chaining Contextual Substitution
|
||||
(send this applyChainingContext table)]
|
||||
[(7) ;; Extension Substitution
|
||||
(send this applyLookup (· table lookupType) (· table extension))]
|
||||
[else (error 'unimplemented-gsub-lookup)])))
|
||||
|
@ -0,0 +1,60 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/head.js
|
||||
|#
|
||||
|
||||
(define-subclass Struct (Rhead))
|
||||
|
||||
(define head (make-object Rhead
|
||||
(dictify
|
||||
'version int32be ;; 0x00010000 (version 1.0)
|
||||
'revision int32be ;; set by font manufacturer
|
||||
'checkSumAdjustment uint32be
|
||||
'magicNumber uint32be ;; set to 0x5F0F3CF5
|
||||
'flags uint16be
|
||||
'unitsPerEm uint16be ;; range from 64 to 16384
|
||||
'created (+Array int32be 2)
|
||||
'modified (+Array int32be 2)
|
||||
'xMin int16be ;; for all glyph bounding boxes
|
||||
'yMin int16be ;; for all glyph bounding boxes
|
||||
'xMax int16be ;; for all glyph bounding boxes
|
||||
'yMax int16be ;; for all glyph bounding boxes
|
||||
'macStyle (+Bitfield uint16be '(bold italic underline outline shadow condensed extended))
|
||||
'lowestRecPPEM uint16be ;; smallest readable size in pixels
|
||||
'fontDirectionHint int16be
|
||||
'indexToLocFormat int16be ;; 0 for short offsets 1 for long
|
||||
'glyphDataFormat int16be ;; 0 for current format
|
||||
)))
|
||||
|
||||
|
||||
(test-module
|
||||
(require racket/serialize)
|
||||
(define ip (open-input-file charter-italic-path))
|
||||
(define dir (deserialize (read (open-input-file charter-italic-directory-path))))
|
||||
(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.\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 table-bytes))
|
||||
(check-equal? (· table-data unitsPerEm) 1000)
|
||||
(check-equal? (· table-data yMin) -236)
|
||||
(check-equal? (· table-data yMax) 980)
|
||||
(check-equal? (· table-data xMax) 1174)
|
||||
(check-equal? (· table-data xMin) -226)
|
||||
(check-equal? (· table-data macStyle) (make-hasheq '((shadow . #f)
|
||||
(extended . #f)
|
||||
(condensed . #f)
|
||||
(underline . #f)
|
||||
(outline . #f)
|
||||
(bold . #f)
|
||||
(italic . #t))))
|
||||
(check-equal? (· table-data magicNumber) #x5F0F3CF5)
|
||||
(check-equal? (· table-data indexToLocFormat) 0) ; used in loca table
|
||||
(check-equal? (encode head table-data #f) table-bytes))
|
@ -0,0 +1,27 @@
|
||||
#lang racket/base
|
||||
(require (for-syntax racket/base) racket/runtime-path br/define)
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define index? (λ (x) (and (number? x) (integer? x) (not (negative? x)))))
|
||||
|
||||
(define-runtime-path charter-path "../ptest/assets/charter.ttf")
|
||||
(define-runtime-path charter-italic-path "../ptest/assets/charter-italic.ttf")
|
||||
(define-runtime-path fira-path "../ptest/assets/fira.ttf")
|
||||
(define-runtime-path fira-otf-path "../ptest/assets/fira.otf")
|
||||
(define-runtime-path charter-directory-path "charter-directory.rktd")
|
||||
(define-runtime-path charter-italic-directory-path "charter-italic-directory.rktd")
|
||||
|
||||
(define-macro (test-module . EXPRS)
|
||||
#`(module+ test
|
||||
(require #,(datum->syntax caller-stx 'rackunit) #,(datum->syntax caller-stx 'racket/serialize))
|
||||
. 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)))
|
@ -0,0 +1,39 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define-subclass Struct (Rhhea))
|
||||
|
||||
(define hhea (make-object Rhhea
|
||||
(dictify
|
||||
'version int32be
|
||||
'ascent int16be ;; Distance from baseline of highest ascender
|
||||
'descent int16be ;; Distance from baseline of lowest descender
|
||||
'lineGap int16be ;; Typographic line gap
|
||||
'advanceWidthMax uint16be ;; Maximum advance width value in 'hmtx' table
|
||||
'minLeftSideBearing int16be ;; Maximum advance width value in 'hmtx' table
|
||||
'minRightSideBearing int16be ;; Minimum right sidebearing value
|
||||
'xMaxExtent int16be
|
||||
'caretSlopeRise int16be ;; Used to calculate the slope of the cursor (rise/run); 1 for vertical
|
||||
'caretSlopeRun int16be ;; 0 for vertical
|
||||
'caretOffset int16be ;; Set to 0 for non-slanted fonts
|
||||
'reserved (+Array int16be 4)
|
||||
'metricDataFormat int16be ;; 0 for current format
|
||||
'numberOfMetrics uint16be ;; Number of advance widths in 'hmtx' table
|
||||
)))
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables hhea offset))
|
||||
(define length (· dir tables hhea length))
|
||||
(check-equal? offset 292)
|
||||
(check-equal? length 36)
|
||||
(define table-bytes #"\0\1\0\0\3\324\377\22\0\0\4\311\377_\377`\4\251\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\345")
|
||||
(set-port-position! ip 0)
|
||||
(check-equal? (peek-bytes length offset ip) table-bytes)
|
||||
(define table-data (decode hhea table-bytes))
|
||||
(check-equal? (· table-data ascent) 980)
|
||||
(check-equal? (· table-data descent) -238)
|
||||
(check-equal? (· table-data numberOfMetrics) 229))
|
||||
|
@ -0,0 +1 @@
|
||||
#lang info
|
@ -0,0 +1,129 @@
|
||||
#lang fontkit/racket
|
||||
(require (prefix-in Script- "script.rkt") "glyph.rkt" "glyphrun.rkt" "glyph-position.rkt" "ot-layout-engine.rkt")
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/layout/LayoutEngine.js
|
||||
|#
|
||||
|
||||
(define-subclass object% (LayoutEngine font)
|
||||
(field [unicodeLayoutEngine #f]
|
||||
[kernProcessor #f]
|
||||
[engine
|
||||
;; Choose an advanced layout engine.
|
||||
;; We try the AAT morx table first since more
|
||||
;; scripts are currently supported because
|
||||
;; the shaping logic is built into the font.
|
||||
(cond
|
||||
[(· this font has-morx-table?) (error 'morx-layout-unimplemented)]
|
||||
[(or (· this font has-gsub-table?) (· this font has-gpos-table?))
|
||||
#;(report/file 'starting-layout-engine)
|
||||
(+OTLayoutEngine (· this font))]
|
||||
[else #f])])
|
||||
|
||||
(as-methods
|
||||
layout
|
||||
substitute
|
||||
position
|
||||
hideDefaultIgnorables
|
||||
isDefaultIgnorable))
|
||||
|
||||
(define/contract (layout this str-or-glyphs [features #f]
|
||||
;; Attempt to detect the script if not provided.
|
||||
[script (if (string? str-or-glyphs)
|
||||
(Script-forString 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))
|
||||
|
||||
#;(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))
|
||||
|
||||
;; Substitute and position the glyphs
|
||||
(set! glyphs (send this substitute glyphs features script language))
|
||||
#;(report*/file 'end-sub glyphs)
|
||||
#;(error 'stop)
|
||||
#;(report/file 'ready-position)
|
||||
#;(report (for/list ((g (in-list glyphs))) (· g id)) 'shecky)
|
||||
(define positions (send this position glyphs features script language))
|
||||
#;(report (for/list ((p (in-list positions))) (list (· p xAdvance) (· p xOffset))))
|
||||
#;(report/file 'fired-position)
|
||||
|
||||
;; 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 (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))
|
||||
(set! glyphs (send (· this engine) substitute glyphs features script language)))
|
||||
#;(report/file glyphs)
|
||||
glyphs)
|
||||
|
||||
|
||||
(define/contract (position this glyphs features script language)
|
||||
((listof Glyph?) (option/c list?) (option/c symbol?) (option/c symbol?) . ->m . (listof GlyphPosition?))
|
||||
|
||||
(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 glyphs positions features script language)))
|
||||
|
||||
;; if there is no GPOS table, use unicode properties to position marks.
|
||||
;; todo: unicode layout
|
||||
#;(unless positioned
|
||||
(unless (· this unicodeLayoutEngine)
|
||||
(set! unicodeLayoutEngine (+UnicodeLayoutEngine (· this font))))
|
||||
(send unicodeLayoutEngine positionGlyphs glyphs positions))
|
||||
|
||||
;; if kerning is not supported by GPOS, do kerning with the TrueType/AAT kern table
|
||||
;; todo: old style kern table
|
||||
#;(when (and (or (not positioned) (not (· positioned kern))) (· this font kern))
|
||||
(unless kernProcessor
|
||||
(set! kernProcessor (+KernProcessor (· this font))))
|
||||
(send kernProcessor process glyphs positions))
|
||||
|
||||
positions
|
||||
)
|
||||
|
||||
|
||||
(define/contract (hideDefaultIgnorables this glyphRun)
|
||||
((is-a?/c GlyphRun) . ->m . void?)
|
||||
(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)])))
|
||||
(set-field! glyphs glyphRun new-glyphs)
|
||||
(set-field! positions glyphRun new-positions))
|
||||
|
||||
|
||||
(define/contract (isDefaultIgnorable this codepoint)
|
||||
(index? . ->m . boolean?)
|
||||
#f ; todo: everything
|
||||
)
|
@ -0,0 +1,62 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define 16bit-style 0)
|
||||
(define 32bit-style 1)
|
||||
(define max-32-bit-value #xffff)
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/loca.js
|
||||
|#
|
||||
|
||||
(define-subclass VersionedStruct (Rloca)
|
||||
(define/augride (post-decode res stream ctx)
|
||||
;; in `xenomorph` `process` method, `res` is aliased as `this`
|
||||
;;
|
||||
(when (= 16bit-style (· res version))
|
||||
;; in a 16bits-style loca table, actual 32bit offset values are divided by 2 (to fit into 16 bits)
|
||||
;; so we re-inflate them.
|
||||
(dict-update! res 'offsets (λ (offsets) (map (curry * 2) offsets))))
|
||||
res)
|
||||
|
||||
(define/augride (pre-encode this-val stream)
|
||||
;; this = val to be encoded
|
||||
(loca-pre-encode this-val stream)
|
||||
this-val))
|
||||
|
||||
;; make "static method"
|
||||
(define (loca-pre-encode this . args)
|
||||
;; this = val to be encoded
|
||||
(unless (dict-has-key? this 'version)
|
||||
(dict-set! this 'version (if (> (last (· this offsets)) max-32-bit-value)
|
||||
32bit-style
|
||||
16bit-style))
|
||||
(when (= 16bit-style (· this version))
|
||||
(dict-update! this 'offsets (λ (offsets) (map (curryr / 2) offsets))))))
|
||||
|
||||
(define loca (+Rloca
|
||||
(λ (o) (· o head indexToLocFormat))
|
||||
(dictify
|
||||
0 (dictify 'offsets (+Array uint16be))
|
||||
1 (dictify 'offsets (+Array uint32be)))))
|
||||
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables loca offset))
|
||||
(define len (· dir tables loca length))
|
||||
(check-equal? offset 38692)
|
||||
(check-equal? len 460)
|
||||
(define ds (peek-bytes len offset ip))
|
||||
(check-equal?
|
||||
(send loca encode #f (mhash 'version 0 'offsets '(0 76 156))) #"\0\0\0L\0\234")
|
||||
(check-equal?
|
||||
(send loca encode #f '#hash((version . 1) (offsets . (0 76 156)))) #"\0\0\0\0\0\0\0L\0\0\0\234")
|
||||
(send loca force-version! 0)
|
||||
(define table-data (send loca decode ds))
|
||||
(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))
|
||||
)
|
@ -0,0 +1,7 @@
|
||||
#lang fontkit/racket
|
||||
|
||||
(r+p "font.rkt"
|
||||
"glyph-position.rkt"
|
||||
"subset.rkt"
|
||||
"bbox.rkt"
|
||||
"tables.rkt")
|
@ -0,0 +1,38 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define-subclass Struct (Rmaxp))
|
||||
|
||||
(define maxp (make-object Rmaxp
|
||||
(dictify 'version int32be
|
||||
'numGlyphs uint16be ;; The number of glyphs in the font
|
||||
'maxPoints uint16be ;; Maximum points in a non-composite glyph
|
||||
'maxContours uint16be ;; Maximum contours in a non-composite glyph
|
||||
'maxComponentPoints uint16be ;; Maximum points in a composite glyph
|
||||
'maxComponentContours uint16be ;; Maximum contours in a composite glyph
|
||||
'maxZones uint16be ;; 1 if instructions do not use the twilight zone, 2 otherwise
|
||||
'maxTwilightPoints uint16be ;; Maximum points used in Z0
|
||||
'maxStorage uint16be ;; Number of Storage Area locations
|
||||
'maxFunctionDefs uint16be ;; Number of FDEFs
|
||||
'maxInstructionDefs uint16be ;; Number of IDEFs
|
||||
'maxStackElements uint16be ;; Maximum stack depth
|
||||
'maxSizeOfInstructions uint16be ;; Maximum byte count for glyph instructions
|
||||
'maxComponentElements uint16be ;; Maximum number of components referenced at “top level” for any composite glyph
|
||||
'maxComponentDepth uint16be ;; Maximum levels of recursion; 1 for simple components
|
||||
)))
|
||||
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define maxp-offset (· dir tables maxp offset))
|
||||
(define maxp-length (· dir tables maxp length))
|
||||
(check-equal? maxp-offset 328)
|
||||
(check-equal? maxp-length 32)
|
||||
(define maxp-bytes #"\0\1\0\0\0\345\0f\0\a\0O\0\4\0\1\0\0\0\0\0\n\0\0\2\0\1s\0\2\0\1")
|
||||
(set-port-position! ip 0)
|
||||
(check-equal? (peek-bytes maxp-length maxp-offset ip) maxp-bytes)
|
||||
(define maxp-data (send maxp decode maxp-bytes))
|
||||
(check-equal? (· maxp-data numGlyphs) 229)
|
||||
(check-equal? (· maxp-data version) 65536))
|
@ -0,0 +1,194 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/opentype.js
|
||||
|#
|
||||
|
||||
;;########################
|
||||
;; Scripts and Languages #
|
||||
;;########################
|
||||
|
||||
(define-subclass Array (FeatIdxArray))
|
||||
|
||||
(define LangSysTable (+Struct
|
||||
(dictify 'reserved uint16be
|
||||
'reqFeatureIndex uint16be
|
||||
'featureCount uint16be
|
||||
'featureIndexes (+FeatIdxArray uint16be 'featureCount))))
|
||||
|
||||
(define-subclass Pointer (LSR-Pointer))
|
||||
(define LangSysRecord (+Struct
|
||||
(dictify 'tag (+Symbol 4)
|
||||
'langSys (+LSR-Pointer uint16be LangSysTable (mhash 'type 'parent)))))
|
||||
|
||||
(define-subclass Pointer (DLS-Pointer))
|
||||
(define-subclass Array (DLS-Array))
|
||||
(define Script (+Struct
|
||||
(dictify 'defaultLangSys (+DLS-Pointer uint16be LangSysTable)
|
||||
'count uint16be
|
||||
'langSysRecords (+DLS-Array LangSysRecord 'count))))
|
||||
|
||||
(define-subclass Struct (ScriptRecord-Struct))
|
||||
(define-subclass Pointer (ScriptRecord-Pointer))
|
||||
(define ScriptRecord (+ScriptRecord-Struct
|
||||
(dictify 'tag (+Symbol 4)
|
||||
'script (+ScriptRecord-Pointer uint16be Script (mhash 'type 'parent)))))
|
||||
|
||||
(define ScriptList (+Array ScriptRecord uint16be))
|
||||
|
||||
;;#######################
|
||||
;; Features and Lookups #
|
||||
;;#######################
|
||||
|
||||
|
||||
(define Feature (+Struct (dictify
|
||||
'featureParams uint16be
|
||||
'lookupCount uint16be
|
||||
'lookupListIndexes (+Array uint16be 'lookupCount))))
|
||||
|
||||
(define-subclass Struct (FeatureRec))
|
||||
(define-subclass Pointer (FeatureRec-Pointer))
|
||||
(define FeatureRecord (+FeatureRec (dictify
|
||||
'tag (+Symbol 4)
|
||||
'feature (+FeatureRec-Pointer uint16be Feature (mhash 'type 'parent)))))
|
||||
|
||||
(define FeatureList (+Array FeatureRecord uint16be))
|
||||
|
||||
(define LookupFlags (+Bitfield uint16be '(rightToLeft ignoreBaseGlyphs ignoreLigatures ignoreMarks useMarkFilteringSet #f markAttachmentType)))
|
||||
|
||||
(define (LookupList SubTable)
|
||||
(define Lookup (+Struct
|
||||
(dictify
|
||||
'lookupType uint16be
|
||||
'flags LookupFlags
|
||||
'subTableCount uint16be
|
||||
'subTables (+Array (+Pointer uint16be SubTable) 'subTableCount)
|
||||
'markFilteringSet uint16be)))
|
||||
(+LazyArray (+Pointer uint16be Lookup) uint16be))
|
||||
|
||||
|
||||
;;#################
|
||||
;; Coverage Table #
|
||||
;;#################
|
||||
|
||||
(define RangeRecord
|
||||
(+Struct
|
||||
(dictify
|
||||
'start uint16be
|
||||
'end uint16be
|
||||
'startCoverageIndex uint16be)))
|
||||
|
||||
(define Coverage
|
||||
(+VersionedStruct uint16be
|
||||
(dictify
|
||||
1 (dictify
|
||||
'glyphCount uint16be
|
||||
'glyphs (+Array uint16be 'glyphCount))
|
||||
|
||||
2 (dictify
|
||||
'rangeCount uint16be
|
||||
'rangeRecords (+Array RangeRecord 'rangeCount)))))
|
||||
|
||||
;;#########################
|
||||
;; Class Definition Table #
|
||||
;;#########################
|
||||
|
||||
(define ClassRangeRecord (+Struct
|
||||
(dictify
|
||||
'start uint16be
|
||||
'end uint16be
|
||||
'class uint16be)))
|
||||
|
||||
(define ClassDef (+VersionedStruct uint16be
|
||||
(dictify
|
||||
1 ;; Class array
|
||||
(dictify
|
||||
'startGlyph uint16be
|
||||
'glyphCount uint16be
|
||||
'classValueArray (+Array uint16be 'glyphCount))
|
||||
2 ;; Class ranges
|
||||
(dictify
|
||||
'classRangeCount uint16be
|
||||
'classRangeRecord (+Array ClassRangeRecord 'classRangeCount)))))
|
||||
|
||||
|
||||
;;###############
|
||||
;; Device Table #
|
||||
;;###############
|
||||
|
||||
(define Device (+Struct
|
||||
(dictify
|
||||
'startSize uint16be
|
||||
'endSize uint16be
|
||||
'deltaFormat uint16be)))
|
||||
|
||||
;;#############################################
|
||||
;; Contextual Substitution/Positioning Tables #
|
||||
;;#############################################
|
||||
|
||||
|
||||
(define LookupRecord (+Struct
|
||||
(dictify
|
||||
'sequenceIndex uint16be
|
||||
'lookupListIndex uint16be)))
|
||||
|
||||
(define Context
|
||||
(+VersionedStruct
|
||||
uint16be
|
||||
(dictify
|
||||
;; Simple context
|
||||
1 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'ruleSetCount uint16be
|
||||
'ruleSets (+Array (+Pointer uint16be 'RuleSet) 'ruleSetCount))
|
||||
|
||||
;; Class-based context
|
||||
2 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'classDef (+Pointer uint16be 'ClassDef)
|
||||
'classSetCnt uint16be
|
||||
'classSet (+Array (+Pointer uint16be 'ClassSet) 'classSetCnt))
|
||||
|
||||
3 (dictify
|
||||
'glyphCount uint16be
|
||||
'lookupCount uint16be
|
||||
'coverages (+Array (+Pointer uint16be Coverage) 'glyphCount)
|
||||
'lookupRecords (+Array LookupRecord 'lookupCount)))))
|
||||
|
||||
|
||||
;;######################################################
|
||||
;; Chaining Contextual Substitution/Positioning Tables #
|
||||
;;######################################################
|
||||
|
||||
(define ChainingContext
|
||||
(+VersionedStruct
|
||||
uint16be
|
||||
(dictify
|
||||
;; Simple context glyph substitution
|
||||
1 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'chainCount uint16be
|
||||
'chainRuleSets (+Array (+Pointer uint16be 'ChainRuleSet) 'chainCount))
|
||||
|
||||
;; Class-based chaining context
|
||||
2 (dictify
|
||||
'coverage (+Pointer uint16be Coverage)
|
||||
'backtrackClassDef (+Pointer uint16be 'ClassDef)
|
||||
'inputClassDef (+Pointer uint16be 'ClassDef)
|
||||
'lookaheadClassDef (+Pointer uint16be 'ClassDef)
|
||||
'chainCount uint16be
|
||||
'chainClassSet (+Array (+Pointer uint16be 'ChainRuleSet) 'chainCount))
|
||||
|
||||
;; Coverage-based chaining context
|
||||
3 (dictify
|
||||
'backtrackGlyphCount uint16be
|
||||
'backtrackCoverage (+Array (+Pointer uint16be Coverage) 'backtrackGlyphCount)
|
||||
'inputGlyphCount uint16be
|
||||
'inputCoverage (+Array (+Pointer uint16be Coverage) 'inputGlyphCount)
|
||||
'lookaheadGlyphCount uint16be
|
||||
'lookaheadCoverage (+Array (+Pointer uint16be Coverage) 'lookaheadGlyphCount)
|
||||
'lookupCount uint16be
|
||||
'lookupRecords (+Array LookupRecord 'lookupCount)))))
|
@ -0,0 +1,84 @@
|
||||
#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))
|
||||
(define new-glyphinfos
|
||||
(send (· this plan) process (· this GSUBProcessor) (· this glyphInfos)))
|
||||
(set! glyphInfos new-glyphinfos) ; update OTLayoutEngine state for positioning pass
|
||||
#;(report/file new-glyphinfos)
|
||||
;; Map glyph infos back to normal Glyph objects
|
||||
#;(report/file (for/list ([glyphInfo (in-list new-glyphinfos)])
|
||||
(send (· this font) getGlyph (· glyphInfo id) (· glyphInfo codePoints))))
|
||||
(for/list ([glyphInfo (in-list new-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)))
|
||||
(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)))
|
||||
|
||||
)
|
@ -0,0 +1,48 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/post.js
|
||||
|#
|
||||
|
||||
|
||||
(define-subclass VersionedStruct (Rpost))
|
||||
|
||||
(define post (make-object Rpost
|
||||
fixed32be
|
||||
(dictify
|
||||
'header (dictify 'italicAngle fixed32be ;; Italic angle in counter-clockwise degrees from the vertical.
|
||||
'underlinePosition int16be ;; Suggested distance of the top of the underline from the baseline
|
||||
'underlineThickness int16be ;; Suggested values for the underline thickness
|
||||
'isFixedPitch uint32be ;; Whether the font is monospaced
|
||||
'minMemType42 uint32be ;; Minimum memory usage when a TrueType font is downloaded as a Type 42 font
|
||||
'maxMemType42 uint32be ;; Maximum memory usage when a TrueType font is downloaded as a Type 42 font
|
||||
'minMemType1 uint32be ;; Minimum memory usage when a TrueType font is downloaded as a Type 1 font
|
||||
'maxMemType1 uint32be) ;; Maximum memory usage when a TrueType font is downloaded as a Type 1 font
|
||||
|
||||
1 null
|
||||
2 (dictify 'numberOfGlyphs uint16be
|
||||
'glyphNameIndex (+Array uint16be 'numberOfGlyphs)
|
||||
'names (+Array (+String uint8))
|
||||
)
|
||||
2.5 (dictify 'numberOfGlyphs uint16be
|
||||
'offsets (+Array uint8))
|
||||
3 null
|
||||
4 (dictify 'map (+Array uint32be (λ (t) (· t parent maxp numGlyphs)))))))
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables post offset))
|
||||
(define len (· dir tables post length))
|
||||
(check-equal? offset 41520)
|
||||
(check-equal? len 514)
|
||||
(define ds (open-input-bytes (peek-bytes len offset ip)))
|
||||
(define version (decode fixed32be ds)) ; version = 2
|
||||
(send post force-version! version)
|
||||
(define table-data (decode post ds))
|
||||
(check-equal? (· table-data underlineThickness) 58)
|
||||
(check-equal? (· table-data underlinePosition) -178)
|
||||
(check-equal? (· table-data names) '("periodcentered" "macron")))
|
@ -0,0 +1,27 @@
|
||||
#lang fontkit/racket
|
||||
(require xenomorph)
|
||||
(provide (all-defined-out))
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/prep.js
|
||||
|#
|
||||
|
||||
(define-subclass Struct (Rprep))
|
||||
|
||||
(define prep (make-object Rprep
|
||||
(dictify
|
||||
'controlValueProgram (+Array uint8))))
|
||||
|
||||
|
||||
(test-module
|
||||
(define ip (open-input-file charter-path))
|
||||
(define dir (deserialize (read (open-input-file charter-directory-path))))
|
||||
(define offset (· dir tables prep offset))
|
||||
(define len (· dir tables prep length))
|
||||
(check-equal? offset 4512)
|
||||
(check-equal? len 78)
|
||||
(set-port-position! ip 0)
|
||||
(define table-bytes #"\270\0\0+\0\272\0\1\0\1\0\2+\1\272\0\2\0\1\0\2+\1\277\0\2\0C\0007\0+\0\37\0\23\0\0\0\b+\0\277\0\1\0\200\0i\0R\0;\0#\0\0\0\b+\0\272\0\3\0\5\0\a+\270\0\0 E}i\30D")
|
||||
(check-equal? table-bytes (peek-bytes len offset ip))
|
||||
(define ds (open-input-bytes (peek-bytes len offset ip)))
|
||||
(check-equal? (dict-ref (decode prep ds) 'controlValueProgram) '(184 0 0 43 0 186 0 1 0 1 0 2 43 1 186 0 2 0 1 0 2 43 1 191 0 2 0 67 0 55 0 43 0 31 0 19 0 0 0 8 43 0 191 0 1 0 128 0 105 0 82 0 59 0 35 0 0 0 8 43 0 186 0 3 0 5 0 7 43 184 0 0 32 69 125 105 24 68)))
|
@ -0,0 +1,34 @@
|
||||
#lang racket/base
|
||||
(require (for-syntax racket/base br/syntax))
|
||||
(provide (for-syntax (all-from-out racket/base br/syntax)))
|
||||
(provide (all-from-out racket/base) r+p (all-defined-out))
|
||||
|
||||
(define-syntax-rule (r+p id ...) (begin (require id ...) (provide (all-from-out id ...))))
|
||||
|
||||
(r+p "helper.rkt"
|
||||
sugar/debug
|
||||
racket/class
|
||||
racket/file
|
||||
racket/match
|
||||
racket/string
|
||||
racket/format
|
||||
racket/contract
|
||||
racket/dict
|
||||
racket/list
|
||||
racket/port
|
||||
racket/function
|
||||
br/define
|
||||
sugar/class
|
||||
sugar/js
|
||||
sugar/dict
|
||||
sugar/stub
|
||||
sugar/port
|
||||
sugar/contract
|
||||
describe)
|
||||
|
||||
|
||||
(module reader syntax/module-reader
|
||||
#:language 'fontkit/racket
|
||||
#:read @-read
|
||||
#:read-syntax @-read-syntax
|
||||
(require (prefix-in @- scribble/reader)))
|
@ -0,0 +1,182 @@
|
||||
#lang fontkit/racket
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/layout/Script.js
|
||||
|#
|
||||
|
||||
;; This maps the Unicode Script property to an OpenType script tag
|
||||
;; Data from http://www.microsoft.com/typography/otspec/scripttags.htm
|
||||
;; and http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt.
|
||||
|
||||
(define UNICODE_SCRIPTS
|
||||
(apply mhash
|
||||
'(Caucasian_Albanian aghb
|
||||
Arabic arab
|
||||
Imperial_Aramaic armi
|
||||
Armenian armn
|
||||
Avestan avst
|
||||
Balinese bali
|
||||
Bamum bamu
|
||||
Bassa_Vah bass
|
||||
Batak batk
|
||||
Bengali '(bng2 beng)
|
||||
Bopomofo bopo
|
||||
Brahmi brah
|
||||
Braille brai
|
||||
Buginese bugi
|
||||
Buhid buhd
|
||||
Chakma cakm
|
||||
Canadian_Aboriginal cans
|
||||
Carian cari
|
||||
Cham cham
|
||||
Cherokee cher
|
||||
Coptic copt
|
||||
Cypriot cprt
|
||||
Cyrillic cyrl
|
||||
Devanagari '(dev2 deva)
|
||||
Deseret dsrt
|
||||
Duployan dupl
|
||||
Egyptian_Hieroglyphs egyp
|
||||
Elbasan elba
|
||||
Ethiopic ethi
|
||||
Georgian geor
|
||||
Glagolitic glag
|
||||
Gothic goth
|
||||
Grantha gran
|
||||
Greek grek
|
||||
Gujarati '(gjr2 gujr)
|
||||
Gurmukhi '(gur2 guru)
|
||||
Hangul hang
|
||||
Han hani
|
||||
Hanunoo hano
|
||||
Hebrew hebr
|
||||
Hiragana hira
|
||||
Pahawh_Hmong hmng
|
||||
Katakana_Or_Hiragana hrkt
|
||||
Old_Italic ital
|
||||
Javanese java
|
||||
Kayah_Li kali
|
||||
Katakana kana
|
||||
Kharoshthi khar
|
||||
Khmer khmr
|
||||
Khojki khoj
|
||||
Kannada '(knd2 knda)
|
||||
Kaithi kthi
|
||||
Tai_Tham lana
|
||||
Lao lao
|
||||
Latin latn
|
||||
Lepcha lepc
|
||||
Limbu limb
|
||||
Linear_A lina
|
||||
Linear_B linb
|
||||
Lisu lisu
|
||||
Lycian lyci
|
||||
Lydian lydi
|
||||
Mahajani mahj
|
||||
Mandaic mand
|
||||
Manichaean mani
|
||||
Mende_Kikakui mend
|
||||
Meroitic_Cursive merc
|
||||
Meroitic_Hieroglyphs mero
|
||||
Malayalam '(mlm2 mlym)
|
||||
Modi modi
|
||||
Mongolian mong
|
||||
Mro mroo
|
||||
Meetei_Mayek mtei
|
||||
Myanmar '(mym2 mymr)
|
||||
Old_North_Arabian narb
|
||||
Nabataean nbat
|
||||
Nko nko
|
||||
Ogham ogam
|
||||
Ol_Chiki olck
|
||||
Old_Turkic orkh
|
||||
Oriya orya
|
||||
Osmanya osma
|
||||
Palmyrene palm
|
||||
Pau_Cin_Hau pauc
|
||||
Old_Permic perm
|
||||
Phags_Pa phag
|
||||
Inscriptional_Pahlavi phli
|
||||
Psalter_Pahlavi phlp
|
||||
Phoenician phnx
|
||||
Miao plrd
|
||||
Inscriptional_Parthian prti
|
||||
Rejang rjng
|
||||
Runic runr
|
||||
Samaritan samr
|
||||
Old_South_Arabian sarb
|
||||
Saurashtra saur
|
||||
Shavian shaw
|
||||
Sharada shrd
|
||||
Siddham sidd
|
||||
Khudawadi sind
|
||||
Sinhala sinh
|
||||
Sora_Sompeng sora
|
||||
Sundanese sund
|
||||
Syloti_Nagri sylo
|
||||
Syriac syrc
|
||||
Tagbanwa tagb
|
||||
Takri takr
|
||||
Tai_Le tale
|
||||
New_Tai_Lue talu
|
||||
Tamil taml
|
||||
Tai_Viet tavt
|
||||
Telugu '(tel2 telu)
|
||||
Tifinagh tfng
|
||||
Tagalog tglg
|
||||
Thaana thaa
|
||||
Thai thai
|
||||
Tibetan tibt
|
||||
Tirhuta tirh
|
||||
Ugaritic ugar
|
||||
Vai vai
|
||||
Warang_Citi wara
|
||||
Old_Persian xpeo
|
||||
Cuneiform xsux
|
||||
Yi yi
|
||||
Inherited zinh
|
||||
Common zyyy
|
||||
Unknown zzzz)))
|
||||
|
||||
|
||||
(define/contract (fromUnicode script)
|
||||
((option/c symbol?) . -> . symbol?)
|
||||
(hash-ref UNICODE_SCRIPTS script #f))
|
||||
|
||||
(define-stub-stop forString)
|
||||
|
||||
(define-stub-stop forCodePoints)
|
||||
|
||||
(define RTL '( arab ;; Arabic
|
||||
hebr ;; Hebrew
|
||||
syrc ;; Syriac
|
||||
thaa ;; Thaana
|
||||
cprt ;; Cypriot Syllabary
|
||||
khar ;; Kharosthi
|
||||
phnx ;; Phoenician
|
||||
|nko | ;; N'Ko
|
||||
lydi ;; Lydian
|
||||
avst ;; Avestan
|
||||
armi ;; Imperial Aramaic
|
||||
phli ;; Inscriptional Pahlavi
|
||||
prti ;; Inscriptional Parthian
|
||||
sarb ;; Old South Arabian
|
||||
orkh ;; Old Turkic, Orkhon Runic
|
||||
samr ;; Samaritan
|
||||
mand ;; Mandaic, Mandaean
|
||||
merc ;; Meroitic Cursive
|
||||
mero ;; Meroitic Hieroglyphs
|
||||
|
||||
;; Unicode 7.0 (not listed on http://www.microsoft.com/typography/otspec/scripttags.htm)
|
||||
mani ;; Manichaean
|
||||
mend ;; Mende Kikakui
|
||||
nbat ;; Nabataean
|
||||
narb ;; Old North Arabian
|
||||
palm ;; Palmyrene
|
||||
phlp ;; Psalter Pahlavi
|
||||
))
|
||||
|
||||
(define/contract (direction script)
|
||||
((option/c symbol?) . -> . (or/c 'rtl 'ltr))
|
||||
(if (memq script RTL) 'rtl 'ltr))
|
@ -0,0 +1,95 @@
|
||||
#lang fontkit/racket
|
||||
(require (prefix-in Script- "script.rkt"))
|
||||
(provide (all-defined-out))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/opentype/ShapingPlan.js
|
||||
|#
|
||||
|
||||
; * 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.
|
||||
|
||||
(define-subclass object% (ShapingPlan font script language)
|
||||
(field [direction (Script-direction script)]
|
||||
[stages empty]
|
||||
[globalFeatures (mhasheq)]
|
||||
[allFeatures (mhasheq)])
|
||||
|
||||
;; Adds the given features to the last stage.
|
||||
;; Ignores features that have already been applied.
|
||||
(define/public (_addFeatures features)
|
||||
#;(report*/file 'stages-before stages)
|
||||
(match-define (list head-stages ... last-stage) stages)
|
||||
(set! stages
|
||||
`(,@head-stages
|
||||
,(append last-stage
|
||||
(for/list ([feature (in-list features)]
|
||||
#:unless (dict-ref (· this allFeatures) feature #f))
|
||||
(dict-set! (· this allFeatures) feature #t)
|
||||
feature))))
|
||||
#;(report*/file 'stages-after stages)
|
||||
stages)
|
||||
|
||||
;; Adds the given features to the global list
|
||||
(define/public (_addGlobal features)
|
||||
(for ([feature (in-list features)])
|
||||
(dict-set! (· this globalFeatures) feature #t)))
|
||||
|
||||
;; Add features to the last stage
|
||||
(define/public (add arg [global #t])
|
||||
(when (zero? (length (· this stages)))
|
||||
(push-end-field! stages this empty))
|
||||
|
||||
(when (string? arg)
|
||||
(set! arg (list arg)))
|
||||
|
||||
(cond
|
||||
[(list? arg)
|
||||
(_addFeatures arg)
|
||||
(when global (_addGlobal arg))]
|
||||
[(dict? arg)
|
||||
(define features (append (or (· arg global) empty)
|
||||
(or (· arg local) empty)))
|
||||
(_addFeatures features)
|
||||
(when (· arg global)
|
||||
(_addGlobal (· arg global)))]
|
||||
[else (raise-argument-error 'ShapingPlan:add "valid arg" arg)]))
|
||||
|
||||
;; Add a new stage
|
||||
(define/public (addStage arg global)
|
||||
(cond
|
||||
[(procedure? arg)
|
||||
(push-end-field! stages this arg)
|
||||
(push-end-field! stages this empty)]
|
||||
[else (push-end-field! stages this empty)
|
||||
(add arg global)]))
|
||||
|
||||
;; Assigns the global features to the given glyphs
|
||||
(define/public (assignGlobalFeatures glyphs)
|
||||
#;(report*/file glyphs (· this globalFeatures))
|
||||
(for* ([glyph (in-list glyphs)]
|
||||
[feature (in-dict-keys (· this globalFeatures))])
|
||||
(dict-set! (· glyph features) feature #t)))
|
||||
|
||||
;; Executes the planned stages using the given OTProcessor
|
||||
(define/public (process processor glyphs [positions #f])
|
||||
#;(report*/file 'shaping-plan-process processor)
|
||||
(send processor selectScript (· this script) (· this language))
|
||||
|
||||
#;(report/file stages)
|
||||
(for/fold ([glyphs glyphs])
|
||||
([stage (in-list stages)])
|
||||
(cond
|
||||
[(and (procedure? stage) (not positions))
|
||||
(stage (· this font) glyphs positions)]
|
||||
[(> (length stage) 0)
|
||||
#;(report*/file 'shaping-plan:applying-features processor)
|
||||
#;(report/file positions)
|
||||
#;(report/file (send processor applyFeatures stage glyphs positions))
|
||||
(send processor applyFeatures stage glyphs positions)]))))
|
||||
|
||||
|
Binary file not shown.
@ -0,0 +1,26 @@
|
||||
#lang fontkit/racket
|
||||
(provide (all-defined-out))
|
||||
(require (for-syntax racket/string))
|
||||
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/tables/index.js
|
||||
|#
|
||||
|
||||
(define-macro (define-table-codecs ID TABLE-ID ...)
|
||||
(with-pattern ([(TABLE-ID-STRING ...) (pattern-case-filter #'(TABLE-ID ...)
|
||||
[STX (datum->syntax caller-stx (string-replace (format "~a.rkt" (syntax->datum #'STX)) "/" ""))])])
|
||||
#'(begin
|
||||
(r+p TABLE-ID-STRING ...)
|
||||
(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
|
||||
;; required tables
|
||||
#;cmap head hhea hmtx maxp #;name OS/2 post
|
||||
;; TrueType outlines
|
||||
cvt_ fpgm loca prep glyf
|
||||
;; PostScript outlines
|
||||
CFF_ CFF2 #;VORG
|
||||
;; Advanced OpenType Tables
|
||||
#;BASE #;GDEF GPOS GSUB #;JSTF)
|
@ -0,0 +1,179 @@
|
||||
#lang fontkit/racket
|
||||
(require "glyph.rkt" xenomorph)
|
||||
(provide (all-defined-out))
|
||||
#|
|
||||
approximates
|
||||
https://github.com/mbutterick/fontkit/blob/master/src/glyph/TTFGlyph.js
|
||||
|#
|
||||
|
||||
;; The header for both simple and composite glyphs
|
||||
(define-subclass Struct (RGlyfHeader))
|
||||
(define GlyfHeader (+RGlyfHeader
|
||||
(dictify 'numberOfContours int16be ;; if negative, this is a composite glyph
|
||||
'xMin int16be
|
||||
'yMin int16be
|
||||
'xMax int16be
|
||||
'yMax int16be)))
|
||||
|
||||
;; Flags for simple glyphs
|
||||
(define-macro (define-flag-series . IDS)
|
||||
#`(match-define (list . IDS) (map (curry expt 2) (range #,(length (syntax->list #'IDS))))))
|
||||
|
||||
;; Flags for simple glyphs
|
||||
(define-flag-series ON_CURVE
|
||||
X_SHORT_VECTOR
|
||||
Y_SHORT_VECTOR
|
||||
REPEAT
|
||||
SAME_X
|
||||
SAME_Y)
|
||||
|
||||
;; Flags for composite glyphs
|
||||
(define-flag-series ARG_1_AND_2_ARE_WORDS
|
||||
ARGS_ARE_XY_VALUES
|
||||
ROUND_XY_TO_GRID
|
||||
WE_HAVE_A_SCALE
|
||||
__EMPTY-FLAG___
|
||||
MORE_COMPONENTS
|
||||
WE_HAVE_AN_X_AND_Y_SCALE
|
||||
WE_HAVE_A_TWO_BY_TWO
|
||||
WE_HAVE_INSTRUCTIONS
|
||||
USE_MY_METRICS
|
||||
OVERLAP_COMPOUND
|
||||
SCALED_COMPONENT_OFFSET
|
||||
UNSCALED_COMPONENT_OFFSET)
|
||||
|
||||
;; Represents a point in a simple glyph
|
||||
(define-subclass object% (Point onCurve endContour [x 0] [y 0])
|
||||
|
||||
(define/public (copy)
|
||||
(+Point onCurve endContour x y)))
|
||||
|
||||
;; Represents a component in a composite glyph
|
||||
(define-subclass object% (Component glyphID dx dy)
|
||||
(field [pos 0]
|
||||
[scaleX 1]
|
||||
[scaleY 1]
|
||||
[scale01 0]
|
||||
[scale10 0]))
|
||||
|
||||
;; Represents a TrueType glyph.
|
||||
(define-subclass Glyph (TTFGlyph)
|
||||
(inherit-field _font id)
|
||||
|
||||
;; Parses just the glyph header and returns the bounding box
|
||||
(define/override (_getCBox internal)
|
||||
(unfinished))
|
||||
|
||||
;; Parses a single glyph coordinate
|
||||
(define/public (_parseGlyphCoord port prev short same)
|
||||
(unless (input-port? port)
|
||||
(raise-argument-error '_parseGlyphCoord "input port" port))
|
||||
(unless (number? prev)
|
||||
(raise-argument-error '_parseGlyphCoord "number" prev))
|
||||
(unless (and (boolean? short) (boolean? same))
|
||||
(raise-argument-error '_parseGlyphCoord "booleans" (list short same)))
|
||||
(+ prev (if short
|
||||
((if (not same) - +) (decode uint8 port))
|
||||
(if same 0 (decode int16be port)))))
|
||||
|
||||
|
||||
;; Decodes the glyph data into points for simple glyphs,
|
||||
;; or components for composite glyphs
|
||||
(define/public (_decode)
|
||||
(define offsets (· _font loca offsets))
|
||||
(match-define (list glyfPos nextPos) (take (drop offsets id) 2))
|
||||
|
||||
;; Nothing to do if there is no data for this glyph
|
||||
(and (not (= glyfPos nextPos))
|
||||
(let ()
|
||||
(define port (send _font _getTableStream 'glyf))
|
||||
(pos port (+ (pos port) glyfPos))
|
||||
(define startPos (pos port))
|
||||
(define glyph (decode GlyfHeader port))
|
||||
(match (· glyph numberOfContours)
|
||||
[(? positive?) (_decodeSimple glyph port)]
|
||||
[(? negative?) (_decodeComposite glyph port startPos)])
|
||||
glyph)))
|
||||
|
||||
(define/public (_decodeSimple glyph port)
|
||||
(unless (dict? glyph)
|
||||
(raise-argument-error 'TTFGlyph-_decodeSimple "decoded RGlyfHeader" glyph))
|
||||
|
||||
(unless (input-port? port)
|
||||
(raise-argument-error 'TTFGlyph-_decodeSimple "input port" port))
|
||||
|
||||
;; this is a simple glyph
|
||||
(dict-set! glyph 'points empty)
|
||||
(define endPtsOfContours (decode (+Array uint16be (· glyph numberOfContours)) port))
|
||||
(dict-set! glyph 'instructions (decode (+Array uint8be uint16be) port))
|
||||
(define numCoords (add1 (last endPtsOfContours)))
|
||||
|
||||
(define flags
|
||||
(for*/lists (flags)
|
||||
([i (in-naturals)]
|
||||
#:break (= (length flags) numCoords)
|
||||
[flag (in-value (decode uint8 port))]
|
||||
[count (in-range (add1 (if (not (zero? (bitwise-and flag REPEAT)))
|
||||
(decode uint8 port)
|
||||
0)))])
|
||||
flag))
|
||||
|
||||
(match-define-values
|
||||
(points _ _)
|
||||
(for/fold ([points empty] [px 0] [py 0])
|
||||
([(flag i) (in-indexed flags)])
|
||||
(define point (+Point (zero? (bitwise-and flag ON_CURVE)) (and (index-of endPtsOfContours i) #t) 0 0))
|
||||
(define next-px (_parseGlyphCoord port px (not (zero? (bitwise-and flag X_SHORT_VECTOR))) (not (zero? (bitwise-and flag SAME_X)))))
|
||||
(define next-py (_parseGlyphCoord port py (not (zero? (bitwise-and flag Y_SHORT_VECTOR))) (not (zero? (bitwise-and flag SAME_Y)))))
|
||||
(set-field! x point next-px)
|
||||
(set-field! y point next-py)
|
||||
(values (cons point points) next-px next-py)))
|
||||
(dict-set! glyph 'points (reverse points)))
|
||||
|
||||
(define/public (_decodeComposite glyph port [offset 0])
|
||||
;; this is a composite glyph
|
||||
(dict-set! glyph 'components empty)
|
||||
(define haveInstructions #f)
|
||||
(define flags MORE_COMPONENTS)
|
||||
|
||||
(dict-set! glyph 'components
|
||||
(for/list ([i (in-naturals)]
|
||||
#:break (zero? (bitwise-and flags MORE_COMPONENTS)))
|
||||
(set! flags (send uint16be decode port))
|
||||
(define gPos (- (pos port) offset))
|
||||
(define glyphID (send uint16be decode port))
|
||||
(unless haveInstructions
|
||||
(set! haveInstructions (not (zero? (bitwise-and flags WE_HAVE_INSTRUCTIONS)))))
|
||||
|
||||
(match-define
|
||||
(list dx dy)
|
||||
(let ([decoder (if (not (zero? (bitwise-and flags ARG_1_AND_2_ARE_WORDS))) int16be int8)])
|
||||
(list (send decoder decode port) (send decoder decode port))))
|
||||
|
||||
(define component (+Component glyphID dx dy))
|
||||
(set-field! pos component gPos)
|
||||
|
||||
(cond
|
||||
[(not (zero? (bitwise-and flags WE_HAVE_A_SCALE)))
|
||||
(define scale (read-fixed14 port))
|
||||
(set-field! scaleX component scale)
|
||||
(set-field! scaleY component scale)]
|
||||
[(not (zero? (bitwise-and flags WE_HAVE_AN_X_AND_Y_SCALE)))
|
||||
(set-field! scaleX component (read-fixed14 port))
|
||||
(set-field! scaleY component (read-fixed14 port))]
|
||||
[(not (zero? (bitwise-and flags WE_HAVE_A_TWO_BY_TWO)))
|
||||
(set-field! scaleX component (read-fixed14 port))
|
||||
(set-field! scale01 component (read-fixed14 port))
|
||||
(set-field! scale10 component (read-fixed14 port))
|
||||
(set-field! scaleY component (read-fixed14 port))])
|
||||
component))
|
||||
haveInstructions))
|
||||
|
||||
|
||||
(define (bytes->fixed14 b1 b2)
|
||||
(/ (+ (* b1 (expt 2 8)) b2) (expt 2 14) 1.0))
|
||||
|
||||
(define (read-fixed14 stream)
|
||||
(define b1 (send uint8 decode stream))
|
||||
(define b2 (send uint8 decode stream))
|
||||
(bytes->fixed14 b1 b2))
|
@ -0,0 +1,11 @@
|
||||
#lang fontkit/racket
|
||||
(provide TTFGlyphEncoder)
|
||||
|
||||
(define-subclass object% (TTFGlyphEncoder)
|
||||
(as-methods
|
||||
encodeSimple
|
||||
_encodePoint))
|
||||
|
||||
(define-stub encodeSimple)
|
||||
|
||||
(define-stub _encodePoint)
|
@ -0,0 +1,3 @@
|
||||
#lang info
|
||||
(define collection 'multi)
|
||||
(define version "0.0")
|
Loading…
Reference in New Issue