-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathot_shape_fallback.go
416 lines (360 loc) · 11.4 KB
/
ot_shape_fallback.go
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
package harfbuzz
import "fmt"
// ported from harfbuzz/src/hb-ot-shape-fallback.cc Copyright © 2011,2012 Google, Inc. Behdad Esfahbod
const (
combiningClassAttachedBelowLeft = 200
combiningClassAttachedBelow = 202
combiningClassAttachedAbove = 214
combiningClassAttachedAboveRight = 216
combiningClassBelowLeft = 218
combiningClassBelow = 220
combiningClassBelowRight = 222
combiningClassLeft = 224
combiningClassRight = 226
combiningClassAboveLeft = 228
combiningClassAbove = 230
combiningClassAboveRight = 232
combiningClassDoubleBelow = 233
combiningClassDoubleAbove = 234
)
func recategorizeCombiningClass(u rune, klass uint8) uint8 {
if klass >= 200 {
return klass
}
/* Thai / Lao need some per-character work. */
if (u & ^0xFF) == 0x0E00 {
if klass == 0 {
switch u {
case 0x0E31, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E47, 0x0E4C, 0x0E4D, 0x0E4E:
klass = combiningClassAboveRight
case 0x0EB1, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, 0x0EBB, 0x0ECC, 0x0ECD:
klass = combiningClassAbove
case 0x0EBC:
klass = combiningClassBelow
}
} else {
/* Thai virama is below-right */
if u == 0x0E3A {
klass = combiningClassBelowRight
}
}
}
switch klass {
/* Hebrew */
case mcc10, /* sheva */
mcc11, /* hataf segol */
mcc12, /* hataf patah */
mcc13, /* hataf qamats */
mcc14, /* hiriq */
mcc15, /* tsere */
mcc16, /* segol */
mcc17, /* patah */
mcc18, /* qamats & qamats qatan */
mcc20, /* qubuts */
mcc22: /* meteg */
return combiningClassBelow
case mcc23: /* rafe */
return combiningClassAttachedAbove
case mcc24: /* shin dot */
return combiningClassAboveRight
case mcc25, /* sin dot */
mcc19: /* holam & holam haser for vav*/
return combiningClassAboveLeft
case mcc26: /* point varika */
return combiningClassAbove
case mcc21: /* dagesh */
/* Arabic and Syriac */
case mcc27, /* fathatan */
mcc28, /* dammatan */
mcc30, /* fatha */
mcc31, /* damma */
mcc33, /* shadda */
mcc34, /* sukun */
mcc35, /* superscript alef */
mcc36: /* superscript alaph */
return combiningClassAbove
case mcc29, /* kasratan */
mcc32: /* kasra */
return combiningClassBelow
/* Thai */
case mcc103: /* sara u / sara uu */
return combiningClassBelowRight
case mcc107: /* mai */
return combiningClassAboveRight
/* Lao */
case mcc118: /* sign u / sign uu */
return combiningClassBelow
case mcc122: /* mai */
return combiningClassAbove
/* Tibetan */
case mcc129: /* sign aa */
return combiningClassBelow
case mcc130: /* sign i*/
return combiningClassAbove
case mcc132: /* sign u */
return combiningClassBelow
}
return klass
}
func fallbackMarkPositionRecategorizeMarks(buffer *Buffer) {
for i, info := range buffer.Info {
if info.unicode.generalCategory() == nonSpacingMark {
combiningClass := info.getModifiedCombiningClass()
combiningClass = recategorizeCombiningClass(info.codepoint, combiningClass)
buffer.Info[i].setModifiedCombiningClass(combiningClass)
}
}
}
func zeroMarkAdvances(buffer *Buffer, start, end int, adjustOffsetsWhenZeroing bool) {
info := buffer.Info
for i := start; i < end; i++ {
if info[i].unicode.generalCategory() != nonSpacingMark {
continue
}
if adjustOffsetsWhenZeroing {
buffer.Pos[i].XOffset -= buffer.Pos[i].XAdvance
buffer.Pos[i].YOffset -= buffer.Pos[i].YAdvance
}
buffer.Pos[i].XAdvance = 0
buffer.Pos[i].YAdvance = 0
}
}
func positionMark(font *Font, buffer *Buffer, baseExtents *GlyphExtents,
i int, combiningClass uint8) {
markExtents, ok := font.GlyphExtents(buffer.Info[i].Glyph)
if !ok {
return
}
yGap := font.YScale / 16
pos := &buffer.Pos[i]
pos.XOffset = 0
pos.YOffset = 0
// we don't position LEFT and RIGHT marks.
// X positioning
switch combiningClass {
case combiningClassAttachedBelowLeft, combiningClassBelowLeft, combiningClassAboveLeft:
/* Left align. */
pos.XOffset += baseExtents.XBearing - markExtents.XBearing
case combiningClassAttachedAboveRight, combiningClassBelowRight, combiningClassAboveRight:
/* Right align. */
pos.XOffset += baseExtents.XBearing + baseExtents.Width - markExtents.Width - markExtents.XBearing
case combiningClassDoubleBelow, combiningClassDoubleAbove:
if buffer.Props.Direction == LeftToRight {
pos.XOffset += baseExtents.XBearing + baseExtents.Width - markExtents.Width/2 - markExtents.XBearing
break
} else if buffer.Props.Direction == RightToLeft {
pos.XOffset += baseExtents.XBearing - markExtents.Width/2 - markExtents.XBearing
break
}
fallthrough
case combiningClassAttachedBelow, combiningClassAttachedAbove, combiningClassBelow, combiningClassAbove:
fallthrough
default:
/* Center align. */
pos.XOffset += baseExtents.XBearing + (baseExtents.Width-markExtents.Width)/2 - markExtents.XBearing
}
/* Y positioning */
switch combiningClass {
case combiningClassDoubleBelow, combiningClassBelowLeft, combiningClassBelow, combiningClassBelowRight:
/* Add gap, fall-through. */
baseExtents.Height -= yGap
fallthrough
case combiningClassAttachedBelowLeft, combiningClassAttachedBelow:
pos.YOffset = baseExtents.YBearing + baseExtents.Height - markExtents.YBearing
/* Never shift up "below" marks. */
if (yGap > 0) == (pos.YOffset > 0) {
baseExtents.Height -= pos.YOffset
pos.YOffset = 0
}
baseExtents.Height += markExtents.Height
case combiningClassDoubleAbove, combiningClassAboveLeft, combiningClassAbove, combiningClassAboveRight:
/* Add gap, fall-through. */
baseExtents.YBearing += yGap
baseExtents.Height -= yGap
fallthrough
case combiningClassAttachedAbove, combiningClassAttachedAboveRight:
pos.YOffset = baseExtents.YBearing - (markExtents.YBearing + markExtents.Height)
/* Don't shift down "above" marks too much. */
if (yGap > 0) != (pos.YOffset > 0) {
correction := -pos.YOffset / 2
baseExtents.YBearing += correction
baseExtents.Height -= correction
pos.YOffset += correction
}
baseExtents.YBearing -= markExtents.Height
baseExtents.Height += markExtents.Height
}
}
func positionAroundBase(plan *otShapePlan, font *Font, buffer *Buffer,
base, end int, adjustOffsetsWhenZeroing bool) {
buffer.unsafeToBreak(base, end)
baseExtents, ok := font.GlyphExtents(buffer.Info[base].Glyph)
if !ok {
// if extents don't work, zero marks and go home.
zeroMarkAdvances(buffer, base+1, end, adjustOffsetsWhenZeroing)
return
}
baseExtents.YBearing += buffer.Pos[base].YOffset
/* Use horizontal advance for horizontal positioning.
* Generally a better idea. Also works for zero-ink glyphs. See:
* https://github.com/harfbuzz/harfbuzz/issues/1532 */
baseExtents.XBearing = 0
baseExtents.Width = font.GlyphHAdvance(buffer.Info[base].Glyph)
ligID := buffer.Info[base].getLigID()
numLigComponents := int32(buffer.Info[base].getLigNumComps())
var xOffset, yOffset Position
if buffer.Props.Direction.isForward() {
xOffset -= buffer.Pos[base].XAdvance
yOffset -= buffer.Pos[base].YAdvance
}
var horizDir Direction
componentExtents := baseExtents
lastLigComponent := int32(-1)
lastCombiningClass := uint8(255)
clusterExtents := baseExtents
info := buffer.Info
for i := base + 1; i < end; i++ {
thisCombiningClass := info[i].getModifiedCombiningClass()
if thisCombiningClass != 0 {
if numLigComponents > 1 {
thisLigID := info[i].getLigID()
thisLigComponent := int32(info[i].getLigComp() - 1)
// conditions for attaching to the last component.
if ligID == 0 || ligID != thisLigID || thisLigComponent >= numLigComponents {
thisLigComponent = numLigComponents - 1
}
if lastLigComponent != thisLigComponent {
lastLigComponent = thisLigComponent
lastCombiningClass = 255
componentExtents = baseExtents
if horizDir == 0 {
if plan.props.Direction.isHorizontal() {
horizDir = plan.props.Direction
} else {
horizDir = getHorizontalDirection(plan.props.Script)
}
}
if horizDir == LeftToRight {
componentExtents.XBearing += (thisLigComponent * componentExtents.Width) / numLigComponents
} else {
componentExtents.XBearing += ((numLigComponents - 1 - thisLigComponent) * componentExtents.Width) / numLigComponents
}
componentExtents.Width /= numLigComponents
}
}
if lastCombiningClass != thisCombiningClass {
lastCombiningClass = thisCombiningClass
clusterExtents = componentExtents
}
positionMark(font, buffer, &clusterExtents, i, thisCombiningClass)
buffer.Pos[i].XAdvance = 0
buffer.Pos[i].YAdvance = 0
buffer.Pos[i].XOffset += xOffset
buffer.Pos[i].YOffset += yOffset
} else {
if buffer.Props.Direction.isForward() {
xOffset -= buffer.Pos[i].XAdvance
yOffset -= buffer.Pos[i].YAdvance
} else {
xOffset += buffer.Pos[i].XAdvance
yOffset += buffer.Pos[i].YAdvance
}
}
}
}
func positionCluster(plan *otShapePlan, font *Font, buffer *Buffer,
start, end int, adjustOffsetsWhenZeroing bool) {
if end-start < 2 {
return
}
// find the base glyph
info := buffer.Info
for i := start; i < end; i++ {
if !info[i].isUnicodeMark() {
// find mark glyphs
var j int
for j = i + 1; j < end; j++ {
if !info[j].isUnicodeMark() {
break
}
}
positionAroundBase(plan, font, buffer, i, j, adjustOffsetsWhenZeroing)
i = j - 1
}
}
}
func fallbackMarkPosition(plan *otShapePlan, font *Font, buffer *Buffer,
adjustOffsetsWhenZeroing bool) {
var start int
info := buffer.Info
for i := 1; i < len(info); i++ {
if !info[i].isUnicodeMark() {
positionCluster(plan, font, buffer, start, i, adjustOffsetsWhenZeroing)
start = i
}
}
positionCluster(plan, font, buffer, start, len(info), adjustOffsetsWhenZeroing)
}
// adjusts width of various spaces.
func fallbackSpaces(font *Font, buffer *Buffer) {
if debugMode >= 1 {
fmt.Println("POSITION - applying fallback spaces")
}
info := buffer.Info
pos := buffer.Pos
horizontal := buffer.Props.Direction.isHorizontal()
for i, inf := range info {
if !inf.isUnicodeSpace() || inf.ligated() {
continue
}
spaceType := inf.getUnicodeSpaceFallbackType()
switch spaceType {
case notSpace, space: // shouldn't happen
case spaceEM, spaceEM2, spaceEM3, spaceEM4, spaceEM5, spaceEM6, spaceEM16:
if horizontal {
pos[i].XAdvance = +(font.XScale + int32(spaceType)/2) / int32(spaceType)
} else {
pos[i].YAdvance = -(font.YScale + int32(spaceType)/2) / int32(spaceType)
}
case space4EM18:
if horizontal {
pos[i].XAdvance = +font.XScale * 4 / 18
} else {
pos[i].YAdvance = -font.YScale * 4 / 18
}
case spaceFigure:
for u := '0'; u <= '9'; u++ {
if glyph, ok := font.face.NominalGlyph(u); ok {
if horizontal {
pos[i].XAdvance = font.GlyphHAdvance(glyph)
} else {
pos[i].YAdvance = font.getGlyphVAdvance(glyph)
}
}
}
case spacePunctuation:
glyph, ok := font.face.NominalGlyph('.')
if !ok {
glyph, ok = font.face.NominalGlyph(',')
}
if ok {
if horizontal {
pos[i].XAdvance = font.GlyphHAdvance(glyph)
} else {
pos[i].YAdvance = font.getGlyphVAdvance(glyph)
}
}
case spaceNarrow:
/* Half-space?
* Unicode doc https://unicode.org/charts/PDF/U2000.pdf says ~1/4 or 1/5 of EM.
* However, in my testing, many fonts have their regular space being about that
* size. To me, a percentage of the space width makes more sense. Half is as
* good as any. */
if horizontal {
pos[i].XAdvance /= 2
} else {
pos[i].YAdvance /= 2
}
}
}
}