@@ -87,6 +87,11 @@ function compareFont(
87
87
return - 1
88
88
}
89
89
90
+ const cachedParsedFont = new WeakMap <
91
+ Buffer | ArrayBuffer ,
92
+ opentype . Font | null | undefined
93
+ > ( )
94
+
90
95
export default class FontLoader {
91
96
defaultFont : opentype . Font
92
97
fonts = new Map < string , [ opentype . Font , Weight ?, FontStyle ?] [ ] > ( )
@@ -143,30 +148,37 @@ export default class FontLoader {
143
148
)
144
149
}
145
150
const _lang = lang ?? SUFFIX_WHEN_LANG_NOT_SET
146
- const font = opentype . parse (
147
- // Buffer to ArrayBuffer.
148
- 'buffer' in data
149
- ? data . buffer . slice (
150
- data . byteOffset ,
151
- data . byteOffset + data . byteLength
152
- )
153
- : data ,
154
- // @ts -ignore
155
- { lowMemory : true }
156
- )
151
+ let font
157
152
158
- // Modify the `charToGlyphIndex` method, so we can know which char is
159
- // being mapped to which glyph.
160
- const originalCharToGlyphIndex = font . charToGlyphIndex
161
- font . charToGlyphIndex = ( char ) => {
162
- const index = originalCharToGlyphIndex . call ( font , char )
163
- if ( index === 0 ) {
164
- // The current requested char is missing a glyph.
165
- if ( ( font as any ) . _trackBrokenChars ) {
166
- ; ( font as any ) . _trackBrokenChars . push ( char )
153
+ if ( cachedParsedFont . has ( data ) ) {
154
+ font = cachedParsedFont . get ( data )
155
+ } else {
156
+ font = opentype . parse (
157
+ // Buffer to ArrayBuffer.
158
+ 'buffer' in data
159
+ ? data . buffer . slice (
160
+ data . byteOffset ,
161
+ data . byteOffset + data . byteLength
162
+ )
163
+ : data ,
164
+ // @ts -ignore
165
+ { lowMemory : true }
166
+ )
167
+ // Modify the `charToGlyphIndex` method, so we can know which char is
168
+ // being mapped to which glyph.
169
+ const originalCharToGlyphIndex = font . charToGlyphIndex
170
+ font . charToGlyphIndex = ( char ) => {
171
+ const index = originalCharToGlyphIndex . call ( font , char )
172
+ if ( index === 0 ) {
173
+ // The current requested char is missing a glyph.
174
+ if ( ( font as any ) . _trackBrokenChars ) {
175
+ ; ( font as any ) . _trackBrokenChars . push ( char )
176
+ }
167
177
}
178
+ return index
168
179
}
169
- return index
180
+
181
+ cachedParsedFont . set ( data , font )
170
182
}
171
183
172
184
// We use the first font as the default font fallback.
@@ -494,11 +506,53 @@ export default class FontLoader {
494
506
if ( fontSize === 0 ) {
495
507
return ''
496
508
}
497
- return font
498
- . getPath ( content . replace ( / \n / g, '' ) , left , top , fontSize , {
499
- letterSpacing : letterSpacing / fontSize ,
500
- } )
501
- . toPathData ( 1 )
509
+
510
+ const fullPath = new opentype . Path ( )
511
+
512
+ const options = {
513
+ letterSpacing : letterSpacing / fontSize ,
514
+ }
515
+
516
+ const cachedPath = new WeakMap <
517
+ opentype . Glyph ,
518
+ [ number , number , opentype . Path ]
519
+ > ( )
520
+
521
+ font . forEachGlyph (
522
+ content . replace ( / \n / g, '' ) ,
523
+ left ,
524
+ top ,
525
+ fontSize ,
526
+ options ,
527
+ function ( glyph , gX , gY , gFontSize ) {
528
+ let glyphPath : opentype . Path
529
+ if ( ! cachedPath . has ( glyph ) ) {
530
+ glyphPath = glyph . getPath ( gX , gY , gFontSize , options )
531
+ cachedPath . set ( glyph , [ gX , gY , glyphPath ] )
532
+ } else {
533
+ const [ _x , _y , _glyphPath ] = cachedPath . get ( glyph )
534
+ glyphPath = new opentype . Path ( )
535
+ glyphPath . commands = _glyphPath . commands . map ( ( command ) => {
536
+ const movedCommand = { ...command }
537
+ for ( let k in movedCommand ) {
538
+ if ( typeof movedCommand [ k ] === 'number' ) {
539
+ if ( k === 'x' || k === 'x1' || k === 'x2' ) {
540
+ movedCommand [ k ] += gX - _x
541
+ }
542
+ if ( k === 'y' || k === 'y1' || k === 'y2' ) {
543
+ movedCommand [ k ] += gY - _y
544
+ }
545
+ }
546
+ }
547
+ return movedCommand
548
+ } )
549
+ }
550
+
551
+ fullPath . extend ( glyphPath )
552
+ }
553
+ )
554
+
555
+ return fullPath . toPathData ( 1 )
502
556
} finally {
503
557
unpatch ( )
504
558
}
0 commit comments