1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 | 1
1
1
1
1
1
1
1
12
3
9
9
23
23
23
23
23
23
23
23
23
23
23
23
23
1
23
23
239
23
23
6
6
23
3
3
23
2
2
2
2
1
2
1
| KernProcessor = require './KernProcessor'
UnicodeLayoutEngine = require './UnicodeLayoutEngine'
GlyphRun = require './GlyphRun'
Script = require './Script'
unicode = require 'unicode-properties'
AATLayoutEngine = require '../aat/AATLayoutEngine'
OTLayoutEngine = require '../opentype/OTLayoutEngine'
class LayoutEngine
constructor: (@font) ->
# 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.
@engine = if @font.morx
new AATLayoutEngine @font
else Eif @font.GSUB or @font.GPOS
new OTLayoutEngine @font
layout: (string, features = [], script, language) ->
# Make the userFeatures parameter optional
Iif typeof features is 'string'
script = features
language = script
features = []
# Map string to glyphs if needed
Eif typeof string is 'string'
# Attempt to detect the script from the string if not provided.
script ?= Script.forString string
glyphs = @font.glyphsForString string
else
# Attempt to detect the script from the glyph code points if not provided.
unless script?
codePoints = []
for glyph in string
codePoints.push glyph.codePoints...
script = Script.forCodePoints codePoints
glyphs = string
# Return early if there are no glyphs
Iif glyphs.length is 0
return new GlyphRun glyphs, []
# Setup the advanced layout engine
@engine?.setup?(glyphs, features, script, language)
# Substitute and position the glyphs
glyphs = @substitute glyphs, features, script, language
positions = @position glyphs, features, script, language
# Let the layout engine clean up any state it might have
@engine?.cleanup?()
return new GlyphRun glyphs, positions
substitute: (glyphs, features, script, language) ->
# Call the advanced layout engine to make substitutions
Eif @engine?.substitute
glyphs = @engine.substitute(glyphs, features, script, language)
return glyphs
class GlyphPosition
constructor: (@xAdvance = 0, @yAdvance = 0, @xOffset = 0, @yOffset = 0) ->
position: (glyphs, features, script, language) ->
# Get initial glyph positions
positions = []
for glyph, i in glyphs
positions.push new GlyphPosition glyph.advanceWidth
# Call the advanced layout engine. Returns the features applied.
positioned = @engine?.position?(glyphs, positions, features, script, language)
# if there is no GPOS table, use unicode properties to position marks.
unless positioned
@unicodeLayoutEngine ?= new UnicodeLayoutEngine @font
@unicodeLayoutEngine.positionGlyphs glyphs, positions
# if kerning is not supported by GPOS, do kerning with the TrueType/AAT kern table
if not positioned?.kern and @font.kern
@kernProcessor ?= new KernProcessor @font
@kernProcessor.process glyphs, positions
return positions
getAvailableFeatures: (script, language) ->
features = []
Eif @engine
features.push @engine.getAvailableFeatures(script, language)...
if @font.kern and 'kern' not in features
features.push 'kern'
return features
module.exports = LayoutEngine
|