1 // [Blend2D]
2 // 2D Vector Graphics Powered by a JIT Compiler.
3 //
4 // [License]
5 // Zlib - See LICENSE.md file in the package.
6
7 #include "../api-build_p.h"
8 #include "../bitops_p.h"
9 #include "../font_p.h"
10 #include "../glyphbuffer_p.h"
11 #include "../support_p.h"
12 #include "../tables_p.h"
13 #include "../trace_p.h"
14 #include "../opentype/otface_p.h"
15 #include "../opentype/otlayout_p.h"
16
17 // TODO: This is not complete so we had to disable some warnings here...
18 BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS)
19
20 namespace BLOpenType {
21 namespace LayoutImpl {
22
23 // ============================================================================
24 // [BLOpenType::LayoutImpl - Tracing]
25 // ============================================================================
26
27 #if defined(BL_TRACE_OT_ALL) || defined(BL_TRACE_OT_LAYOUT)
28 #define Trace BLDebugTrace
29 #else
30 #define Trace BLDummyTrace
31 #endif
32
33 // ============================================================================
34 // [BLOpenType::LayoutImpl - Debugging]
35 // ============================================================================
36
37 #if 0
38 static void dumpGPosValue(const Int16* p, uint32_t index, uint32_t valueFormat) noexcept {
39 int32_t v;
40 if (valueFormat & GPosTable::kValueXPlacement ) { v = p->value(); p++; printf("#%d ValueXPlacement: %d\n", index, v); }
41 if (valueFormat & GPosTable::kValueYPlacement ) { v = p->value(); p++; printf("#%d ValueYPlacement: %d\n", index, v); }
42 if (valueFormat & GPosTable::kValueXAdvance ) { v = p->value(); p++; printf("#%d ValueXAdvance: %d\n", index, v); }
43 if (valueFormat & GPosTable::kValueYAdvance ) { v = p->value(); p++; printf("#%d ValueYAdvance: %d\n", index, v); }
44 if (valueFormat & GPosTable::kValueXPlacementDevice) { v = p->value(); p++; printf("#%d ValueXPlacementDevice: %d\n", index, v); }
45 if (valueFormat & GPosTable::kValueYPlacementDevice) { v = p->value(); p++; printf("#%d ValueYPlacementDevice: %d\n", index, v); }
46 if (valueFormat & GPosTable::kValueXAdvanceDevice ) { v = p->value(); p++; printf("#%d ValueXAdvanceDevice: %d\n", index, v); }
47 if (valueFormat & GPosTable::kValueYAdvanceDevice ) { v = p->value(); p++; printf("#%d ValueYAdvanceDevice: %d\n", index, v); }
48 }
49 #endif
50
51 // ============================================================================
52 // [BLOpenType::LayoutImpl - Validator]
53 // ============================================================================
54
55 class Validator {
56 public:
57 BLOTFaceImpl* faceI;
58
59 union {
60 BLFontTable tables[3];
61 struct {
62 BLFontTable gsub;
63 BLFontTable gpos;
64 BLFontTable gdef;
65 };
66 };
67
68 BLArray<BLTag> scriptTags;
69 BLArray<BLTag> featureTags;
70
Validator(BLOTFaceImpl * faceI)71 BL_INLINE Validator(BLOTFaceImpl* faceI) noexcept
72 : faceI(faceI),
73 tables {},
74 scriptTags(),
75 featureTags() {}
76 };
77
78 // ============================================================================
79 // [BLOpenType::LayoutImpl - LookupInfo]
80 // ============================================================================
81
82 static const LookupInfo gLookupInfo[2] = {
83 // GSUB:
84 {
85 // LookupCount & ExtensionType:
86 GSubTable::kLookupCount,
87 GSubTable::kLookupExtension,
88
89 // LookupTypeInfo:
90 {
91 { 0, LookupInfo::kGSubNone }, // GSUB Lookup Type #0 - Invalid.
92 { 2, LookupInfo::kGSubType1Format1 }, // GSUB Lookup Type #1 - Single Substitution.
93 { 1, LookupInfo::kGSubType2Format1 }, // GSUB Lookup Type #2 - Multiple Substitution.
94 { 1, LookupInfo::kGSubType3Format1 }, // GSUB Lookup Type #3 - Alternate Substitution.
95 { 1, LookupInfo::kGSubType4Format1 }, // GSUB Lookup Type #4 - Ligature Substitution.
96 { 3, LookupInfo::kGSubType5Format1 }, // GSUB Lookup Type #5 - Contextual Substitution.
97 { 3, LookupInfo::kGSubType6Format1 }, // GSUB Lookup Type #6 - Chained Context.
98 { 1, LookupInfo::kGSubNone }, // GSUB Lookup Type #7 - Extension.
99 { 1, LookupInfo::kGSubType8Format1 } // GSUB Lookup Type #8 - Reverse Chained Substitution.
100 },
101
102 // LookupIdInfo:
103 {
104 { uint8_t(0) },
105 { uint8_t(6) }, // Lookup Type #1 - Format #1.
106 { uint8_t(6) }, // Lookup Type #1 - Format #2.
107 { uint8_t(6) }, // Lookup Type #2 - Format #1.
108 { uint8_t(6) }, // Lookup Type #3 - Format #1.
109 { uint8_t(6) }, // Lookup Type #4 - Format #1.
110 { uint8_t(6) }, // Lookup Type #5 - Format #1.
111 { uint8_t(8) }, // Lookup Type #5 - Format #2.
112 { uint8_t(6) }, // Lookup Type #5 - Format #3.
113 { uint8_t(6) }, // Lookup Type #6 - Format #1.
114 { uint8_t(12) }, // Lookup Type #6 - Format #2.
115 { uint8_t(10) }, // Lookup Type #6 - Format #3.
116 { uint8_t(10) } // Lookup Type #8 - Format #1.
117 }
118 },
119
120 // GPOS:
121 {
122 // LookupCount & ExtensionType:
123 GPosTable::kLookupCount,
124 GPosTable::kLookupExtension,
125
126 // LookupTypeInfo:
127 {
128 { 0, LookupInfo::kGPosNone }, // GPOS Lookup Type #0 - Invalid.
129 { 2, LookupInfo::kGPosType1Format1 }, // GPOS Lookup Type #1 - Single Adjustment.
130 { 2, LookupInfo::kGPosType2Format1 }, // GPOS Lookup Type #2 - Pair Adjustment.
131 { 1, LookupInfo::kGPosType3Format1 }, // GPOS Lookup Type #3 - Cursive Attachment.
132 { 1, LookupInfo::kGPosType4Format1 }, // GPOS Lookup Type #4 - MarkToBase Attachment.
133 { 1, LookupInfo::kGPosType5Format1 }, // GPOS Lookup Type #5 - MarkToLigature Attachment.
134 { 1, LookupInfo::kGPosType6Format1 }, // GPOS Lookup Type #6 - MarkToMark Attachment.
135 { 3, LookupInfo::kGPosType7Format1 }, // GPOS Lookup Type #7 - Context Positioning.
136 { 3, LookupInfo::kGPosType8Format1 }, // GPOS Lookup Type #8 - Chained Contextual Positioning
137 { 1, LookupInfo::kGPosNone } // GPOS Lookup Type #9 - Extension.
138 },
139
140 // LookupIdInfo:
141 {
142 { uint8_t(0) },
143 { uint8_t(6) }, // Lookup Type #1 - Format #1.
144 { uint8_t(8) }, // Lookup Type #1 - Format #2.
145 { uint8_t(10) }, // Lookup Type #2 - Format #1.
146 { uint8_t(16) }, // Lookup Type #2 - Format #2.
147 { uint8_t(6) }, // Lookup Type #3 - Format #1.
148 { uint8_t(12) }, // Lookup Type #4 - Format #1.
149 { uint8_t(12) }, // Lookup Type #5 - Format #1.
150 { uint8_t(12) }, // Lookup Type #6 - Format #1.
151 { uint8_t(6) }, // Lookup Type #7 - Format #1.
152 { uint8_t(8) }, // Lookup Type #7 - Format #2.
153 { uint8_t(6) }, // Lookup Type #7 - Format #3.
154 // TODO: [OPENTYPE GPOS]
155 { uint8_t(2) }, // Lookup Type #8 - Format #1.
156 { uint8_t(2) }, // Lookup Type #8 - Format #2.
157 { uint8_t(2) } // Lookup Type #8 - Format #3.
158 }
159 }
160 };
161
162 // ============================================================================
163 // [BLOpenType::LayoutImpl - ValueRecord]
164 // ============================================================================
165
166 // struct ValueRecords {
167 // ?[Int16 xPlacement]
168 // ?[Int16 yPlacement]
169 // ?[Int16 xAdvance]
170 // ?[Int16 yAdvance]
171 // ?[UInt16 xPlacementDeviceOffset]
172 // ?[UInt16 yPlacementDeviceOffset]
173 // ?[UInt16 xAdvanceDeviceOffset]
174 // ?[UInt16 yAdvanceDeviceOffset]
175 // }
sizeOfValueRecordByFormat(uint32_t valueFormat)176 static BL_INLINE uint32_t sizeOfValueRecordByFormat(uint32_t valueFormat) noexcept {
177 return uint32_t(blBitCountOfByteTable[valueFormat & 0xFFu]) * 2u;
178 }
179
180 // ============================================================================
181 // [BLOpenType::LayoutImpl - Offsets]
182 // ============================================================================
183
checkRawOffsetArray(Validator * self,Trace trace,BLFontTable data,const char * tableName)184 static bool checkRawOffsetArray(Validator* self, Trace trace, BLFontTable data, const char* tableName) noexcept {
185 BL_UNUSED(self);
186
187 if (BL_UNLIKELY(data.size < 2u))
188 return trace.fail("%s: Table is too small [Size=%zu]\n", tableName, data.size);
189
190 uint32_t count = data.dataAs<Array16<UInt16>>()->count();
191 size_t headerSize = 2u + count * 2u;
192
193 if (BL_UNLIKELY(data.size < headerSize))
194 return trace.fail("%s: Table is truncated [Size=%zu RequiredSize=%zu]\n", tableName, data.size, headerSize);
195
196 const UInt16* array = data.dataAs<Array16<Offset16>>()->array();
197 for (uint32_t i = 0; i < count; i++) {
198 uint32_t subOffset = array[i].value();
199 if (BL_UNLIKELY(subOffset < headerSize || subOffset >= data.size))
200 return trace.fail("%s: Invalid offset at #%u [%u], valid range [%zu:%zu]\n", tableName, i, subOffset, headerSize, data.size);
201 }
202
203 return true;
204 }
205
checkTagRef16Array(Validator * self,Trace trace,BLFontTable data,const char * tableName)206 static bool checkTagRef16Array(Validator* self, Trace trace, BLFontTable data, const char* tableName) noexcept {
207 BL_UNUSED(self);
208
209 if (BL_UNLIKELY(data.size < 2u))
210 return trace.fail("%s is too small [Size=%zu]\n", tableName, data.size);
211
212 uint32_t count = data.dataAs<Array16<UInt16>>()->count();
213 size_t headerSize = 2u + count * uint32_t(sizeof(TagRef16));
214
215 if (BL_UNLIKELY(data.size < headerSize))
216 return trace.fail("%s is truncated [Size=%zu RequiredSize=%zu]\n", tableName, data.size, headerSize);
217
218 const TagRef16* array = data.dataAs<Array16<TagRef16>>()->array();
219 for (uint32_t i = 0; i < count; i++) {
220 uint32_t subOffset = array[i].offset.value();
221 if (BL_UNLIKELY(subOffset < headerSize || subOffset >= data.size))
222 return trace.fail("%s has invalid offset at #%u [%u], valid range [%zu:%zu]\n", tableName, i, subOffset, headerSize, data.size);
223 }
224
225 return true;
226 }
227
228 // ============================================================================
229 // [BLOpenType::LayoutImpl - ClassDefTable]
230 // ============================================================================
231
checkClassDefTable(Validator * self,Trace trace,BLFontTable data,const char * tableName)232 static bool checkClassDefTable(Validator* self, Trace trace, BLFontTable data, const char* tableName) noexcept {
233 trace.info("%s\n", tableName);
234 trace.indent();
235
236 // Ignore if it doesn't fit.
237 if (BL_UNLIKELY(!blFontTableFitsT<ClassDefTable>(data)))
238 return trace.fail("Table is too small [Size=%zu]\n", data.size);
239
240 uint32_t format = data.dataAs<ClassDefTable>()->format();
241 trace.info("Format: %u\n", format);
242
243 switch (format) {
244 case 1: {
245 size_t headerSize = ClassDefTable::Format1::kMinSize;
246 if (BL_UNLIKELY(data.size < headerSize))
247 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", data.size, headerSize);
248
249 const ClassDefTable::Format1* f = data.dataAs<ClassDefTable>()->format1();
250 uint32_t first = f->firstGlyph();
251 uint32_t count = f->classValues.count();
252
253 trace.info("FirstGlyph: %u\n", first);
254 trace.info("GlyphCount: %u\n", count);
255
256 // We won't fail, but we won't consider we have a ClassDef either.
257 // If the ClassDef is required by other tables then we will fail later.
258 if (BL_UNLIKELY(!count))
259 return trace.warn("No glyph ids specified, ignoring...\n");
260
261 headerSize += count * 2u;
262 if (BL_UNLIKELY(data.size < headerSize))
263 return trace.fail("Table is truncated [Size=%zu RequiredSize=%zu]\n", data.size, headerSize);
264
265 return true;
266 }
267
268 case 2: {
269 size_t headerSize = ClassDefTable::Format2::kMinSize;
270 if (BL_UNLIKELY(data.size < headerSize))
271 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", data.size, headerSize);
272
273 const ClassDefTable::Format2* f = data.dataAs<ClassDefTable>()->format2();
274 uint32_t count = f->ranges.count();
275
276 trace.info("RangeCount: %u\n", count);
277
278 // We won't fail, but we won't consider we have a class definition either.
279 if (BL_UNLIKELY(!count))
280 return trace.warn("No range specified, ignoring...\n");
281
282 headerSize = ClassDefTable::Format2::kMinSize + count * sizeof(ClassDefTable::Range);
283 if (BL_UNLIKELY(data.size < headerSize))
284 return trace.fail("Table is truncated [Size=%zu RequiredSize=%zu]\n", data.size, headerSize);
285
286 const ClassDefTable::Range* rangeArray = f->ranges.array();
287 uint32_t lastGlyph = rangeArray[0].lastGlyph();
288
289 if (BL_UNLIKELY(rangeArray[0].firstGlyph() > lastGlyph))
290 return trace.fail("Table is invalid\n");
291
292 for (uint32_t i = 1; i < count; i++) {
293 const ClassDefTable::Range& range = rangeArray[i];
294 uint32_t firstGlyph = range.firstGlyph();
295
296 if (BL_UNLIKELY(firstGlyph <= lastGlyph))
297 return trace.fail("Range #%u: FirstGlyph [%u] not greater than previous LastGlyph [%u] \n", i, firstGlyph, lastGlyph);
298
299 lastGlyph = range.lastGlyph();
300 if (BL_UNLIKELY(firstGlyph > lastGlyph))
301 return trace.fail("Range #%u: FirstGlyph [%u] greater than LastGlyph [%u]\n", i, firstGlyph, lastGlyph);
302 }
303
304 return true;
305 }
306
307 default:
308 return trace.fail("ClassDefTable format %u is invalid\n", format);
309 }
310 }
311
312 // ============================================================================
313 // [BLOpenType::LayoutImpl - CoverageTable]
314 // ============================================================================
315
checkCoverageTable(Validator * self,Trace trace,BLFontTable data,uint32_t & countCoverageEntries)316 static bool checkCoverageTable(Validator* self, Trace trace, BLFontTable data, uint32_t& countCoverageEntries) noexcept {
317 countCoverageEntries = 0;
318 if (!blFontTableFitsT<CoverageTable>(data))
319 return trace.fail("CoverageTable is too small [Size=%zu]\n", data.size);
320
321 uint32_t format = data.dataAs<CoverageTable>()->format();
322 switch (format) {
323 case 1: {
324 const CoverageTable::Format1* table = data.dataAs<CoverageTable::Format1>();
325 uint32_t glyphCount = table->glyphs.count();
326
327 trace.info("CoverageTable::Format1\n");
328 trace.indent();
329
330 size_t headerSize = CoverageTable::Format1::kMinSize + glyphCount * 2u;
331 if (BL_UNLIKELY(data.size < headerSize))
332 return trace.fail("Table is truncated [Size=%zu RequiredSize=%zu]\n", data.size, headerSize);
333
334 if (BL_UNLIKELY(!glyphCount))
335 return trace.fail("GlyphCount cannot be zero\n");
336
337 countCoverageEntries = glyphCount;
338 return true;
339 }
340
341 case 2: {
342 const CoverageTable::Format2* table = data.dataAs<CoverageTable::Format2>();
343 uint32_t rangeCount = table->ranges.count();
344
345 trace.info("CoverageTable::Format2\n");
346 trace.indent();
347
348 size_t headerSize = CoverageTable::Format2::kMinSize + rangeCount * sizeof(CoverageTable::Range);
349 if (BL_UNLIKELY(data.size < headerSize))
350 return trace.fail("Table is truncated [Size=%zu RequiredSize=%zu]\n", data.size, headerSize);
351
352 if (BL_UNLIKELY(!rangeCount))
353 return trace.fail("RangeCount cannot be zero\n");
354
355 const CoverageTable::Range* rangeArray = table->ranges.array();
356
357 uint32_t firstGlyph = rangeArray[0].firstGlyph();
358 uint32_t lastGlyph = rangeArray[0].lastGlyph();
359 uint32_t currentCoverageIndex = rangeArray[0].startCoverageIndex();
360
361 if (BL_UNLIKELY(firstGlyph > lastGlyph))
362 return trace.fail("Range[%u]: FirstGlyph [%u] is greater than LastGlyph [%u]\n", 0, firstGlyph, lastGlyph);
363
364 if (BL_UNLIKELY(currentCoverageIndex))
365 return trace.fail("Range[%u]: Initial StartCoverageIndex [%u] must be zero\n", 0, currentCoverageIndex);
366 currentCoverageIndex += lastGlyph - firstGlyph + 1u;
367
368 for (uint32_t i = 1; i < rangeCount; i++) {
369 const CoverageTable::Range& range = rangeArray[i];
370
371 firstGlyph = range.firstGlyph();
372 if (BL_UNLIKELY(firstGlyph <= lastGlyph))
373 return trace.fail("Range[%u]: FirstGlyph [%u] is not greater than previous LastGlyph [%u]\n", i, firstGlyph, lastGlyph);
374
375 lastGlyph = range.lastGlyph();
376 if (BL_UNLIKELY(firstGlyph > lastGlyph))
377 return trace.fail("Range[%u]: FirstGlyph [%u] is greater than LastGlyph [%u]\n", i, firstGlyph, lastGlyph);
378
379 uint32_t startCoverageIndex = range.startCoverageIndex();
380 if (BL_UNLIKELY(startCoverageIndex != currentCoverageIndex))
381 return trace.fail("Range[%u]: StartCoverageIndex [%u] doesnt' match CurrentCoverageIndex [%u]\n", i, startCoverageIndex, currentCoverageIndex);
382
383 currentCoverageIndex += lastGlyph - firstGlyph + 1u;
384 }
385
386 countCoverageEntries = currentCoverageIndex;
387 return true;
388 }
389
390 default:
391 return trace.fail("Invalid CoverageTable format [%u]\n", format);
392 }
393 }
394
checkLookupWithCoverage(Validator * self,Trace trace,BLFontTable data,size_t headerSize,uint32_t & countCoverageEntries)395 static bool checkLookupWithCoverage(Validator* self, Trace trace, BLFontTable data, size_t headerSize, uint32_t& countCoverageEntries) noexcept {
396 if (BL_UNLIKELY(data.size < headerSize))
397 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", data.size, headerSize);
398
399 uint32_t coverageOffset = data.dataAs<GAnyTable::LookupHeaderWithCoverage>()->coverageOffset();
400 if (BL_UNLIKELY(coverageOffset < headerSize || coverageOffset >= data.size))
401 return trace.fail("Coverage offset [%u] is out of range [%zu:%zu]\n", coverageOffset, headerSize, data.size);
402
403 return checkCoverageTable(self, trace, blFontSubTable(data, coverageOffset), countCoverageEntries);
404 }
405
406 // ============================================================================
407 // [BLOpenType::LayoutImpl - GDEF - Init]
408 // ============================================================================
409
checkGDefTable(Validator * self,Trace trace)410 static bool checkGDefTable(Validator* self, Trace trace) noexcept {
411 BLOTFaceImpl* faceI = self->faceI;
412 BLFontTableT<GDefTable> gdef = self->gdef;
413
414 trace.info("OpenType::Init 'GDEF' [Size=%zu]\n", gdef.size);
415 trace.indent();
416
417 if (!blFontTableFitsT<GDefTable>(gdef))
418 return trace.fail("Table too small [Size=%zu Required: %zu]\n", gdef.size, size_t(GDefTable::kMinSize));
419
420 uint32_t version = gdef->v1_0()->version();
421 size_t headerSize = GDefTable::HeaderV1_0::kMinSize;
422
423 if (version >= 0x00010002u) headerSize = GDefTable::HeaderV1_2::kMinSize;
424 if (version >= 0x00010003u) headerSize = GDefTable::HeaderV1_3::kMinSize;
425
426 if (BL_UNLIKELY(version < 0x00010000u || version > 0x00010003u))
427 return trace.fail("Invalid version [%u.%u]\n", version >> 16, version & 0xFFFFu);
428
429 if (BL_UNLIKELY(gdef.size < headerSize))
430 return trace.fail("Table is too small [Size=%zu Required=%zu]\n", gdef.size, headerSize);
431
432 uint32_t glyphClassDefOffset = gdef->v1_0()->glyphClassDefOffset();
433 uint32_t attachListOffset = gdef->v1_0()->attachListOffset();
434 uint32_t ligCaretListOffset = gdef->v1_0()->ligCaretListOffset();
435 uint32_t markAttachClassDefOffset = gdef->v1_0()->markAttachClassDefOffset();
436 uint32_t markGlyphSetsDefOffset = version >= 0x00010002u ? uint32_t(gdef->v1_2()->markGlyphSetsDefOffset()) : uint32_t(0);
437 uint32_t itemVarStoreOffset = version >= 0x00010003u ? uint32_t(gdef->v1_3()->itemVarStoreOffset() ) : uint32_t(0);
438
439 // Some fonts have incorrect value of `GlyphClassDefOffset` set to 10. This
440 // collides with the header which is 12 bytes. It's probably a result of some
441 // broken tool used to write such fonts in the past. We simply fix this issue
442 // by changing the `headerSize` to 10.
443 if (glyphClassDefOffset == 10 && version == 0x00010000u) {
444 trace.warn("Fixing header size from 12 to 10 because of GlyphClassDefOffset\n");
445 headerSize = 10;
446 markAttachClassDefOffset = 0;
447 }
448
449 if (glyphClassDefOffset) {
450 const char* name = "GlyphClassDef";
451 if (glyphClassDefOffset < headerSize || glyphClassDefOffset >= gdef.size)
452 return trace.fail("%s offset [%u] out of range [%zu:%zu]\n", name, glyphClassDefOffset, headerSize, gdef.size);
453
454 if (!checkClassDefTable(self, trace, blFontSubTable(gdef, glyphClassDefOffset), name)) {
455 faceI->faceInfo.diagFlags |= BL_FONT_FACE_DIAG_WRONG_GDEF_DATA;
456 }
457 else {
458 faceI->otFlags |= BL_OT_FACE_FLAG_GLYPH_CLASS_DEF;
459 }
460 }
461
462 if (markAttachClassDefOffset) {
463 const char* name = "MatchAttachClassDef";
464 if (markAttachClassDefOffset < headerSize || markAttachClassDefOffset >= gdef.size)
465 return trace.fail("%s offset [%u] out of range [%zu:%zu]\n", name, markAttachClassDefOffset, headerSize, gdef.size);
466
467 if (!checkClassDefTable(self, trace, blFontSubTable(gdef, markAttachClassDefOffset), name)) {
468 faceI->faceInfo.diagFlags |= BL_FONT_FACE_DIAG_WRONG_GDEF_DATA;
469 }
470 else {
471 faceI->otFlags |= BL_OT_FACE_FLAG_MARK_ATTACH_CLASS_DEF;
472 }
473 }
474
475 return true;
476 }
477
478 // ============================================================================
479 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #1]
480 // ============================================================================
481
482 // Single Substitution
483 // -------------------
484 //
485 // Replace a single glyph with another glyph.
486
checkGSubLookupType1Format1(Validator * self,Trace trace,BLFontTable table)487 static BL_INLINE bool checkGSubLookupType1Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
488 uint32_t countCoverageEntries;
489 return checkLookupWithCoverage(self, trace, table, sizeof(GSubTable::SingleSubst1), countCoverageEntries);
490 }
491
checkGSubLookupType1Format2(Validator * self,Trace trace,BLFontTable table)492 static BL_INLINE bool checkGSubLookupType1Format2(Validator* self, Trace trace, BLFontTable table) noexcept {
493 uint32_t countCoverageEntries;
494 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GSubTable::SingleSubst2), countCoverageEntries)))
495 return false;
496
497 const GSubTable::SingleSubst2* lookup = table.dataAs<GSubTable::SingleSubst2>();
498 uint32_t glyphCount = lookup->glyphs.count();
499
500 size_t headerSize = sizeof(GSubTable::SingleSubst2) + glyphCount * 2u;
501 if (BL_UNLIKELY(table.size < headerSize))
502 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
503
504 return true;
505 }
506
507 template<uint32_t CoverageFormat>
applyGSubLookupType1Format1(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTableT<GSubTable::SingleSubst1> table,uint32_t lookupFlags,CoverageIterator & covIt)508 static BL_INLINE BLResult applyGSubLookupType1Format1(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTableT<GSubTable::SingleSubst1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
509 size_t itemCount = ctx.in.end - ctx.in.index;
510
511 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
512 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
513 uint32_t glyphDelta = uint16_t(table->deltaGlyphId());
514
515 if (ctx.inPlace() && ctx.isSameIndex()) {
516 uint32_t* ptr = ctx.in.glyphData + ctx.in.index;
517 uint32_t* end = ctx.in.glyphData + ctx.in.end;
518
519 while (ptr != end) {
520 uint32_t glyphId = ptr[0];
521 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
522 uint32_t coverageIndex;
523 if (covIt.find<CoverageFormat>(glyphId, coverageIndex))
524 ptr[0] = (glyphId + glyphDelta) & 0xFFFFu;
525 }
526 ptr++;
527 }
528 }
529 else {
530 if (!ctx.inPlace())
531 BL_PROPAGATE(ctx.prepareOut(itemCount));
532
533 uint32_t* inPtr = ctx.in.glyphData + ctx.in.index;
534 uint32_t* inEnd = ctx.in.glyphData + ctx.in.end;
535 BLGlyphInfo* inInfo = ctx.in.infoData + ctx.in.index;
536
537 uint32_t* outPtr = ctx.out.glyphData + ctx.out.index;
538 BLGlyphInfo* outInfo = ctx.out.infoData + ctx.out.index;
539
540 while (inPtr != inEnd) {
541 uint32_t glyphId = inPtr[0];
542 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
543 uint32_t coverageIndex;
544 if (covIt.find<CoverageFormat>(glyphId, coverageIndex))
545 glyphId = (glyphId + glyphDelta) & 0xFFFFu;
546 }
547
548 *outPtr++ = glyphId;
549 *outInfo++ = *inInfo;
550
551 inPtr++;
552 inInfo++;
553 }
554 }
555
556 ctx.in.index += itemCount;
557 ctx.out.index += itemCount;
558 return BL_SUCCESS;
559 }
560
561 template<uint32_t CoverageFormat>
applyGSubLookupType1Format2(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTableT<GSubTable::SingleSubst2> table,uint32_t lookupFlags,CoverageIterator & covIt)562 static BL_INLINE BLResult applyGSubLookupType1Format2(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTableT<GSubTable::SingleSubst2> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
563 size_t itemCount = ctx.in.end - ctx.in.index;
564
565 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
566 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
567 uint32_t substCount = table->glyphs.count();
568
569 if (BL_UNLIKELY(table.size < GSubTable::SingleSubst2::kMinSize + substCount * 2u))
570 return ctx.advance(itemCount);
571
572 if (ctx.inPlace() && ctx.isSameIndex()) {
573 uint32_t* ptr = ctx.in.glyphData + ctx.in.index;
574 uint32_t* end = ctx.in.glyphData + ctx.in.end;
575
576 while (ptr != end) {
577 uint32_t glyphId = ptr[0];
578 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
579 uint32_t coverageIndex;
580 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= substCount)
581 ptr[0] = table->glyphs.array()[coverageIndex].value();
582 }
583
584 ptr++;
585 }
586 }
587 else {
588 if (!ctx.inPlace())
589 BL_PROPAGATE(ctx.prepareOut(itemCount));
590
591 uint32_t* inPtr = ctx.in.glyphData + ctx.in.index;
592 uint32_t* inEnd = ctx.in.glyphData + ctx.in.end;
593 BLGlyphInfo* inInfo = ctx.in.infoData + ctx.in.index;
594
595 uint32_t* outPtr = ctx.out.glyphData + ctx.out.index;
596 BLGlyphInfo* outInfo = ctx.out.infoData + ctx.out.index;
597
598 while (inPtr != inEnd) {
599 uint32_t glyphId = inPtr[0];
600 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
601 uint32_t coverageIndex;
602 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= substCount)
603 glyphId = table->glyphs.array()[coverageIndex].value();
604 }
605
606 *outPtr++ = glyphId;
607 *outInfo++ = *inInfo;
608
609 inPtr++;
610 inInfo++;
611 }
612 }
613
614 ctx.in.index += itemCount;
615 ctx.out.index += itemCount;
616 return BL_SUCCESS;
617 }
618
619 // ============================================================================
620 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #2]
621 // ============================================================================
622
623 // Multiple Substitution
624 // ---------------------
625 //
626 // Replace a single glyph with more than one glyph. The replacement sequence
627 // cannot be empty, it's explicitly forbidden by the specification.
628
checkGSubLookupType2Format1(Validator * self,Trace trace,BLFontTable table)629 static BL_INLINE bool checkGSubLookupType2Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
630 uint32_t countCoverageEntries;
631 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GSubTable::MultipleSubst1), countCoverageEntries)))
632 return false;
633
634 const GSubTable::MultipleSubst1* lookup = table.dataAs<GSubTable::MultipleSubst1>();
635 uint32_t seqSetCount = lookup->sequenceOffsets.count();
636
637 size_t headerSize = sizeof(GSubTable::MultipleSubst1) + seqSetCount * 2u;
638 if (BL_UNLIKELY(table.size < headerSize))
639 return trace.fail("Table is too small [Size=%zu Required=%zu]\n", table.size, headerSize);
640
641 // Offsets to glyph sequences.
642 const Offset16* offsetArray = lookup->sequenceOffsets.array();
643 size_t endOffset = table.size - 4u;
644
645 for (uint32_t i = 0; i < seqSetCount; i++) {
646 uint32_t seqOffset = offsetArray[i].value();
647
648 if (BL_UNLIKELY(seqOffset < headerSize || seqOffset > endOffset))
649 return trace.fail("Sequence #%u [%u] is out of range [%zu:%zu]\n", i, seqOffset, headerSize, endOffset);
650
651 const Array16<UInt16>* sequence = blOffsetPtr<const Array16<UInt16>>(table.data, seqOffset);
652 uint32_t seqLength = sequence->count();
653
654 // Specification forbids an empty Sequence.
655 if (BL_UNLIKELY(!seqLength))
656 return trace.fail("Sequence #%u [%u] is empty, which is not allowed\n", i);
657
658 uint32_t seqEnd = seqOffset + 2u + seqLength * 2u;
659 if (BL_UNLIKELY(seqEnd > table.size))
660 return trace.fail("Sequence #%u [%u] length [%u] overflows the table size by [%zu] bytes\n", i, seqLength, size_t(table.size - seqEnd));
661 }
662
663 return true;
664 }
665
666 template<uint32_t CoverageFormat>
applyGSubLookupType2Format1(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTableT<GSubTable::MultipleSubst1> table,uint32_t lookupFlags,CoverageIterator & covIt)667 static BL_INLINE BLResult applyGSubLookupType2Format1(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTableT<GSubTable::MultipleSubst1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
668 size_t itemCount = ctx.in.end - ctx.in.index;
669
670 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
671 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
672 uint32_t substSeqCount = table->sequenceOffsets.count();
673 uint32_t maxSeqOffset = uint32_t(table.size) - 2u;
674
675 if (BL_UNLIKELY(table.size < GSubTable::MultipleSubst1::kMinSize + substSeqCount * 2u))
676 return ctx.advance(itemCount);
677
678 uint32_t* inPtr = ctx.in.glyphData + ctx.in.index;
679 uint32_t* inEnd = ctx.in.glyphData + ctx.in.end;
680
681 // Used to mark the first unmatched glyph that will be copied to output buffer.
682 size_t unmatchedStart = ctx.in.index;
683
684 // Required for match.
685 uint32_t glyphId;
686 uint32_t coverageIndex;
687 uint32_t seqOffset;
688 uint32_t seqLength;
689
690 // Detects the first substitution to be done. If there is no substitution to
691 // be done then we won't force the context to allocate the output buffer.
692 while (inPtr != inEnd) {
693 glyphId = inPtr[0];
694 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
695 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= substSeqCount) {
696 seqOffset = table->sequenceOffsets.array()[coverageIndex].value();
697 if (BL_LIKELY(seqOffset <= maxSeqOffset)) {
698 seqLength = blMemReadU16uBE(table.data + seqOffset);
699 if (BL_LIKELY(seqLength && seqOffset + seqLength * 2u <= maxSeqOffset)) {
700 // This makes sure we have the output buffer allocated.
701 BL_PROPAGATE(ctx.prepareOut(itemCount + seqLength));
702 goto HaveMatch;
703 }
704 }
705 }
706 }
707
708 inPtr++;
709 }
710
711 // No match at all.
712 return ctx.advance(itemCount);
713
714 // Second loop - only executed if there is at least one match.
715 while (inPtr != inEnd) {
716 glyphId = inPtr[0];
717 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
718 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= substSeqCount) {
719 seqOffset = table->sequenceOffsets.array()[coverageIndex].value();
720 if (BL_LIKELY(seqOffset <= maxSeqOffset)) {
721 seqLength = blMemReadU16uBE(table.data + seqOffset);
722 if (BL_LIKELY(seqLength && seqOffset + seqLength * 2u <= maxSeqOffset)) {
723 HaveMatch:
724 size_t unmatchedSize = (size_t)(inPtr - ctx.in.glyphData) - unmatchedStart;
725 size_t requiredSize = unmatchedSize + seqLength;
726
727 if (ctx.outRemaining() < requiredSize)
728 BL_PROPAGATE(ctx.prepareOut(requiredSize));
729
730 const BLGlyphInfo* inInfo = ctx.in.infoData + unmatchedStart;
731 uint32_t* outPtr = ctx.out.glyphData + ctx.out.index;
732 BLGlyphInfo* outInfo = ctx.out.infoData + ctx.out.index;
733
734 // Copy the unmatched data.
735 blCopyGlyphData(outPtr, outInfo, ctx.in.glyphData + unmatchedStart, inInfo, unmatchedSize);
736
737 inInfo += unmatchedSize;
738 outPtr += unmatchedSize;
739 outInfo += unmatchedSize;
740 ctx.out.index += unmatchedSize;
741
742 // Copy the substitution.
743 const UInt16* seq = reinterpret_cast<const UInt16*>(table.data + seqOffset + 2u);
744 for (uint32_t i = 0; i < seqLength; i++) {
745 uint32_t value = seq->value();
746
747 *outPtr++ = value;
748 *outInfo++ = *inInfo;
749 }
750
751 unmatchedStart += unmatchedSize + 1;
752 }
753 }
754 }
755 }
756
757 inPtr++;
758 }
759
760 return BL_SUCCESS;
761 }
762
763 // ============================================================================
764 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #3]
765 // ============================================================================
766
767 // Alternate Substitution
768 // ----------------------
769 //
770 // Replace a single glyph by an alternative glyph. The 'cmap' table contains
771 // the default mapping, which is then changed by alternate substitution based
772 // on features selected by the user.
773
checkGSubLookupType3Format1(Validator * self,Trace trace,BLFontTable table)774 static BL_INLINE bool checkGSubLookupType3Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
775 uint32_t countCoverageEntries;
776 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GSubTable::AlternateSubst1), countCoverageEntries)))
777 return false;
778
779 const GSubTable::AlternateSubst1* lookup = table.dataAs<GSubTable::AlternateSubst1>();
780 uint32_t altSetCount = lookup->altSetOffsets.count();
781 size_t headerSize = sizeof(GSubTable::AlternateSubst1) + altSetCount * 2u;
782
783 if (BL_UNLIKELY(table.size < headerSize))
784 return trace.fail("Table is too small [Size=%zu Required=%zu]\n", table.size, headerSize);
785
786 // Offsets to AlternateSet tables.
787 const Offset16* offsetArray = lookup->altSetOffsets.array();
788 size_t endOffset = table.size - 4u;
789
790 for (uint32_t i = 0; i < altSetCount; i++) {
791 uint32_t alternateSetOffset = offsetArray[i].value();
792
793 if (BL_UNLIKELY(alternateSetOffset < headerSize || alternateSetOffset > endOffset))
794 return trace.fail("AlternateSet #%u [%u] is out of range [%zu:%zu]\n", i, alternateSetOffset, headerSize, endOffset);
795
796 const Array16<UInt16>* alternateSet = blOffsetPtr<const Array16<UInt16>>(table.data, alternateSetOffset);
797 uint32_t alternateSetLength = alternateSet->count();
798
799 // Specification forbids an empty AlternateSet.
800 if (BL_UNLIKELY(!alternateSetLength))
801 return trace.fail("AlternateSet #%u [%u] is empty, which is not allowed\n", i);
802
803 uint32_t alternateSetEnd = alternateSetOffset + 2u + alternateSetLength * 2u;
804 if (BL_UNLIKELY(alternateSetEnd > table.size))
805 return trace.fail("AlternateSet #%u [%u] requires [%u] bytes of data, but only [%zu] bytes are available\n", i, alternateSetLength, size_t(table.size - alternateSetOffset));
806 }
807
808 return true;
809 }
810
811 template<uint32_t CoverageFormat>
applyGSubLookupType3Format1(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTableT<GSubTable::AlternateSubst1> table,uint32_t lookupFlags,CoverageIterator & covIt)812 static BL_INLINE BLResult applyGSubLookupType3Format1(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTableT<GSubTable::AlternateSubst1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
813 size_t itemCount = ctx.in.end - ctx.in.index;
814
815 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
816 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
817 uint32_t altSetCount = table->altSetOffsets.count();
818 uint32_t maxAltSetOffset = uint32_t(table.size) - 2u;
819
820 // TODO: [OPENTYPE GSUB] Not sure how the index should be selected.
821 uint32_t selectedIndex = 0u;
822
823 if (BL_UNLIKELY(table.size < GSubTable::AlternateSubst1::kMinSize + altSetCount * 2u))
824 return ctx.advance(itemCount);
825
826 if (ctx.inPlace() && ctx.isSameIndex()) {
827 uint32_t* ptr = ctx.in.glyphData + ctx.in.index;
828 uint32_t* end = ctx.in.glyphData + ctx.in.end;
829
830 while (ptr != end) {
831 uint32_t glyphId = ptr[0];
832
833 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
834 uint32_t coverageIndex;
835 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= altSetCount) {
836 uint32_t altSetOffset = table->altSetOffsets.array()[coverageIndex].value();
837 if (BL_LIKELY(altSetOffset <= maxAltSetOffset)) {
838 const UInt16* alts = reinterpret_cast<const UInt16*>(table.data + altSetOffset + 2u);
839 uint32_t altGlyphsCount = alts[-1].value();
840 if (BL_LIKELY(altGlyphsCount && altSetOffset + altGlyphsCount * 2u <= maxAltSetOffset)) {
841 uint32_t altGlyphIndex = (selectedIndex % altGlyphsCount);
842 ptr[0] = alts[altGlyphIndex].value();
843 }
844 }
845 }
846 }
847
848 ptr++;
849 }
850 }
851 else {
852 if (!ctx.inPlace())
853 BL_PROPAGATE(ctx.prepareOut(itemCount));
854
855 uint32_t* inPtr = ctx.in.glyphData + ctx.in.index;
856 uint32_t* inEnd = ctx.in.glyphData + ctx.in.end;
857 BLGlyphInfo* inInfo = ctx.in.infoData + ctx.in.index;
858
859 uint32_t* outPtr = ctx.out.glyphData + ctx.out.index;
860 BLGlyphInfo* outInfo = ctx.out.infoData + ctx.out.index;
861
862 while (inPtr != inEnd) {
863 uint32_t glyphId = inPtr[0];
864
865 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
866 uint32_t coverageIndex;
867 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= altSetCount) {
868 uint32_t altSetOffset = table->altSetOffsets.array()[coverageIndex].value();
869 if (BL_LIKELY(altSetOffset <= maxAltSetOffset)) {
870 const UInt16* alts = reinterpret_cast<const UInt16*>(table.data + altSetOffset + 2u);
871 uint32_t altGlyphsCount = alts[-1].value();
872 if (BL_LIKELY(altGlyphsCount && altSetOffset + altGlyphsCount * 2u <= maxAltSetOffset)) {
873 uint32_t altGlyphIndex = (selectedIndex % altGlyphsCount);
874 glyphId = alts[altGlyphIndex].value();
875 }
876 }
877 }
878 }
879
880 *outPtr++ = glyphId;
881 *outInfo++ = *inInfo;
882
883 inPtr++;
884 inInfo++;
885 }
886 }
887
888 ctx.in.index += itemCount;
889 ctx.out.index += itemCount;
890 return BL_SUCCESS;
891 }
892
893 // ============================================================================
894 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #4]
895 // ============================================================================
896
897 // Ligature Substitution
898 // ---------------------
899 //
900 // Replace multiple glyphs by a single glyph.
901
checkGSubLookupType4Format1(Validator * self,Trace trace,BLFontTable table)902 static BL_INLINE bool checkGSubLookupType4Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
903 uint32_t countCoverageEntries;
904 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GSubTable::LigatureSubst1), countCoverageEntries)))
905 return false;
906
907 const GSubTable::LigatureSubst1* lookup = table.dataAs<GSubTable::LigatureSubst1>();
908 uint32_t ligatureSetCount = lookup->ligSetOffsets.count();
909 size_t headerSize = sizeof(GSubTable::LigatureSubst1) + ligatureSetCount * 2u;
910
911 if (BL_UNLIKELY(table.size < headerSize))
912 return trace.fail("Table is too small [Size=%zu Required=%zu]\n", table.size, headerSize);
913
914 // Offsets to LigatureSet tables.
915 const Offset16* ligatureSetOffsetArray = lookup->ligSetOffsets.array();
916 size_t ligatureSetOffsetEnd = table.size - 4u;
917
918 for (uint32_t i = 0; i < ligatureSetCount; i++) {
919 uint32_t ligatureSetOffset = ligatureSetOffsetArray[i].value();
920
921 if (BL_UNLIKELY(ligatureSetOffset < headerSize || ligatureSetOffset > ligatureSetOffsetEnd))
922 return trace.fail("LigatureSet #%u [%u] is out of range [%zu:%zu]\n", i, ligatureSetOffset, headerSize, ligatureSetOffsetEnd);
923
924 const Array16<UInt16>* ligatureSet = blOffsetPtr<const Array16<UInt16>>(table.data, ligatureSetOffset);
925 uint32_t ligatureCount = ligatureSet->count();
926
927 // Specification forbids an empty LigatureSet.
928 if (BL_UNLIKELY(!ligatureCount))
929 return trace.fail("LigatureSet #%u [%u] is empty, which is not allowed\n", i);
930
931 uint32_t ligatureSetEnd = ligatureSetOffset + 2u + ligatureCount * 2u;
932 if (BL_UNLIKELY(ligatureSetEnd > table.size))
933 return trace.fail("LigatureSet #%u [%u] count of Ligatures [%u] overflows the table size by [%zu] bytes\n", i, ligatureCount, size_t(table.size - ligatureSetEnd));
934
935 const Offset16* ligatureOffsetArray = ligatureSet->array();
936 for (uint32_t ligatureIndex = 0; ligatureIndex < ligatureCount; ligatureIndex++) {
937 uint32_t ligatureOffset = ligatureSetOffset + ligatureOffsetArray[ligatureIndex].value();
938
939 if (BL_UNLIKELY(ligatureOffset < ligatureSetEnd || ligatureOffset > ligatureSetOffsetEnd))
940 return trace.fail("LigatureSet #%u: Ligature #%u [%u] is out of range [%zu:%zu]\n", i, ligatureIndex, ligatureOffset, headerSize, table.size);
941
942 const GSubTable::Ligature* ligature = blOffsetPtr<const GSubTable::Ligature>(table.data, ligatureOffset);
943 uint32_t componentCount = ligature->glyphs.count();
944 if (BL_UNLIKELY(!componentCount))
945 return trace.fail("LigatureSet #%u: Ligature #%u is empty\n", i, ligatureIndex);
946
947 uint32_t ligatureDataEnd = ligatureSetOffset + 2u + componentCount * 2u;
948 if (BL_UNLIKELY(ligatureDataEnd > table.size))
949 return trace.fail("LigatureSet #%u: Ligature #%u overflows the table size by [%zu] bytes\n", i, ligatureIndex, size_t(table.size - ligatureDataEnd));
950 }
951 }
952
953 return true;
954 }
955
matchLigature(BLFontTableT<Array16<Offset16>> ligOffsets,uint32_t ligCount,const uint32_t * inGlyphData,size_t maxGlyphCount,uint32_t & ligGlyphIdOut,uint32_t & ligGlyphCount)956 static BL_INLINE bool matchLigature(
957 BLFontTableT<Array16<Offset16>> ligOffsets,
958 uint32_t ligCount,
959 const uint32_t* inGlyphData,
960 size_t maxGlyphCount,
961 uint32_t& ligGlyphIdOut,
962 uint32_t& ligGlyphCount) noexcept {
963
964 // Ligatures are ordered by preference. This means we have to go one by one.
965 uint32_t maxLigOffset = uint32_t(ligOffsets.size) - 4u;
966 size_t maxGlyphCountMinusOne = maxGlyphCount - 1u;
967
968 for (uint32_t ligIndex = 0; ligIndex < ligCount; ligIndex++) {
969 uint32_t ligOffset = ligOffsets->array()[ligIndex].value();
970 if (BL_UNLIKELY(ligOffset > maxLigOffset))
971 break;
972
973 const GSubTable::Ligature* lig = blOffsetPtr<const GSubTable::Ligature>(ligOffsets.data, ligOffset);
974 ligGlyphCount = uint32_t(lig->glyphs.count()) - 1u;
975 if (ligGlyphCount > maxGlyphCountMinusOne)
976 continue;
977
978 // This is safe - a single Ligature is 4 bytes + GlyphId[ligGlyphCount - 1].
979 // MaxLigOffset is 4 bytes less than the end to include the header, so we
980 // only have to include `ligGlyphCount * 2u` to verify we won't read beyond.
981 if (BL_UNLIKELY(ligOffset + ligGlyphCount * 2u > maxLigOffset))
982 continue;
983
984 uint32_t glyphIndex = 0;
985 for (;;) {
986 uint32_t glyphA = lig->glyphs.array()[glyphIndex++].value();
987 uint32_t glyphB = inGlyphData[glyphIndex];
988
989 if (glyphA != glyphB)
990 break;
991
992 if (glyphIndex < ligGlyphCount)
993 continue;
994
995 ligGlyphIdOut = lig->ligatureGlyphId();
996 return true;
997 }
998 }
999
1000 return false;
1001 }
1002
1003 template<uint32_t CoverageFormat>
applyGSubLookupType4Format1(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTableT<GSubTable::LigatureSubst1> table,uint32_t lookupFlags,CoverageIterator & covIt)1004 static BL_INLINE BLResult applyGSubLookupType4Format1(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTableT<GSubTable::LigatureSubst1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
1005 size_t itemCount = ctx.in.end - ctx.in.index;
1006
1007 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
1008 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
1009 uint32_t ligSetCount = table->ligSetOffsets.count();
1010 uint32_t maxLigSetOffset = uint32_t(table.size) - 2u;
1011
1012 if (BL_UNLIKELY(table.size < GSubTable::LigatureSubst1::kMinSize + ligSetCount * 2u))
1013 return ctx.advance(itemCount);
1014
1015 uint32_t* inPtr = ctx.in.glyphData + ctx.in.index;
1016 uint32_t* inEnd = ctx.in.glyphData + ctx.in.end;
1017
1018 if (ctx.inPlace() && ctx.isSameIndex()) {
1019 while (inPtr != inEnd) {
1020 uint32_t glyphId = inPtr[0];
1021
1022 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
1023 uint32_t coverageIndex;
1024 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= ligSetCount) {
1025 uint32_t ligSetOffset = table->ligSetOffsets.array()[coverageIndex].value();
1026 if (BL_LIKELY(ligSetOffset <= maxLigSetOffset)) {
1027 BLFontTableT<Array16<Offset16>> ligOffsets { blFontSubTable(table, ligSetOffset) };
1028 uint32_t ligCount = ligOffsets->count();
1029 if (BL_LIKELY(ligCount && ligSetOffset + ligCount * 2u <= maxLigSetOffset)) {
1030 uint32_t ligGlyphId;
1031 uint32_t ligGlyphCount;
1032 if (matchLigature(ligOffsets, ligCount, inPtr, (size_t)(inEnd - inPtr), ligGlyphId, ligGlyphCount)) {
1033 *inPtr = ligGlyphId;
1034 inPtr += ligGlyphCount;
1035 ctx.in.index = (size_t)(inPtr - ctx.in.glyphData);
1036 ctx.out.index = ctx.in.index;
1037 goto OutPlace;
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 inPtr++;
1045 }
1046
1047 ctx.in.index += itemCount;
1048 ctx.out.index += itemCount;
1049 return BL_SUCCESS;
1050 }
1051 else {
1052 OutPlace:
1053 uint32_t* outPtr = ctx.out.glyphData + ctx.out.index;
1054
1055 BLGlyphInfo* inInfo = ctx.in.infoData + ctx.in.index;
1056 BLGlyphInfo* outInfo = ctx.out.infoData + ctx.out.index;
1057
1058 while (inPtr != inEnd) {
1059 uint32_t glyphId = inPtr[0];
1060
1061 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
1062 uint32_t coverageIndex;
1063 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= ligSetCount) {
1064 uint32_t ligSetOffset = table->ligSetOffsets.array()[coverageIndex].value();
1065 if (BL_LIKELY(ligSetOffset <= maxLigSetOffset)) {
1066 BLFontTableT<Array16<Offset16>> ligOffsets { blFontSubTable(table, ligSetOffset) };
1067 uint32_t ligCount = ligOffsets->count();
1068 if (BL_LIKELY(ligCount && ligSetOffset + ligCount * 2u <= maxLigSetOffset)) {
1069 uint32_t ligGlyphId;
1070 uint32_t ligGlyphCount;
1071 if (matchLigature(ligOffsets, ligCount, inPtr, (size_t)(inEnd - inPtr), ligGlyphId, ligGlyphCount)) {
1072 *outPtr++ = ligGlyphId;
1073 *outInfo++ = *inInfo;
1074
1075 inPtr += ligGlyphCount;
1076 inInfo += ligGlyphCount;
1077 }
1078 }
1079 }
1080 }
1081 }
1082
1083 *outPtr++ = glyphId;
1084 *outInfo++ = *inInfo;
1085
1086 inPtr++;
1087 inInfo++;
1088 }
1089
1090 ctx.in.index = ctx.in.end;
1091 ctx.out.index = (size_t)(outPtr - ctx.out.glyphData);
1092 return BL_SUCCESS;
1093 }
1094 }
1095
1096 // ============================================================================
1097 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #5]
1098 // ============================================================================
1099
1100 // Contextual Substitution
1101 // -----------------------
1102
1103 template<typename SubstTable>
checkGSubLookupType5Format1_2(Validator * self,Trace trace,BLFontTable table)1104 static BL_INLINE bool checkGSubLookupType5Format1_2(Validator* self, Trace trace, BLFontTable table) noexcept {
1105 typedef GSubTable::SubRule SubRule;
1106 typedef GSubTable::SubRuleSet SubRuleSet;
1107
1108 uint32_t countCoverageEntries;
1109 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(SubstTable), countCoverageEntries)))
1110 return false;
1111
1112 const SubstTable* lookup = table.dataAs<SubstTable>();
1113 uint32_t subRuleSetCount = lookup->subRuleSetOffsets.count();
1114 size_t headerSize = sizeof(SubstTable) + subRuleSetCount * 2u;
1115
1116 if (BL_UNLIKELY(table.size < headerSize))
1117 return trace.fail("Table is too small [Size=%zu Required=%zu]\n", table.size, headerSize);
1118
1119 // Offsets to SubRuleSet tables.
1120 const Offset16* subRuleSetOffsetArray = lookup->subRuleSetOffsets.array();
1121 size_t subRuleSetOffsetEnd = table.size - 4u;
1122
1123 for (uint32_t i = 0; i < subRuleSetCount; i++) {
1124 uint32_t subRuleSetOffset = subRuleSetOffsetArray[i].value();
1125
1126 if (BL_UNLIKELY(subRuleSetOffset < headerSize || subRuleSetOffset > subRuleSetOffsetEnd))
1127 return trace.fail("SubRuleSet #%u [%u] is out of range [%zu:%zu]\n", i, subRuleSetOffset, headerSize, subRuleSetOffsetEnd);
1128
1129 const SubRuleSet* subRuleSet = blOffsetPtr<const Array16<UInt16>>(table.data, subRuleSetOffset);
1130 uint32_t subRuleCount = subRuleSet->count();
1131
1132 // Specification forbids an empty SubRuleSet.
1133 if (BL_UNLIKELY(!subRuleCount))
1134 return trace.fail("SubRuleSet #%u [%u] is empty, which is not allowed\n", i);
1135
1136 uint32_t subRuleSetEnd = subRuleSetOffset + 2u + subRuleCount * 2u;
1137 if (BL_UNLIKELY(subRuleSetOffset > table.size))
1138 return trace.fail("SubRuleSet #%u [%u] count of SubRules [%u] overflows the table size by [%zu] bytes\n", i, subRuleCount, size_t(table.size - subRuleSetEnd));
1139
1140 const Offset16* subRuleOffsetArray = subRuleSet->array();
1141 for (uint32_t subRuleIndex = 0; subRuleIndex < subRuleCount; subRuleIndex++) {
1142 uint32_t subRuleOffset = subRuleSetOffset + subRuleOffsetArray[subRuleIndex].value();
1143
1144 if (BL_UNLIKELY(subRuleOffset < subRuleSetEnd || subRuleOffset > subRuleSetOffsetEnd))
1145 return trace.fail("SubRuleSet #%u: SubRule #%u [%u] is out of range [%zu:%zu]\n", i, subRuleIndex, subRuleOffset, headerSize, table.size);
1146
1147 const SubRule* subRule = blOffsetPtr<const SubRule>(table.data, subRuleOffset);
1148 uint32_t glyphCount = subRule->glyphCount();
1149 uint32_t substCount = subRule->substCount();
1150
1151 if (BL_UNLIKELY(glyphCount < 2))
1152 return trace.fail("SubRuleSet #%u: SubRule #%u has no InputSequence\n", i, subRuleIndex);
1153
1154 if (BL_UNLIKELY(substCount < 1))
1155 return trace.fail("SubRuleSet #%u: SubRule #%u has no LookupRecords\n", i, subRuleIndex);
1156
1157 uint32_t subRuleDataEnd = subRuleSetOffset + 4u + (substCount + glyphCount - 1) * 2u;
1158 if (BL_UNLIKELY(subRuleDataEnd > table.size))
1159 return trace.fail("SubRuleSet #%u: SubRule #%u overflows the table size by [%zu] bytes\n", i, subRuleIndex, size_t(table.size - subRuleDataEnd));
1160 }
1161 }
1162
1163 return true;
1164 }
1165
checkGSubLookupType5Format1(Validator * self,Trace trace,BLFontTable table)1166 static BL_INLINE bool checkGSubLookupType5Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1167 return checkGSubLookupType5Format1_2<GSubTable::ContextSubst1>(self, trace, table);
1168 }
1169
checkGSubLookupType5Format2(Validator * self,Trace trace,BLFontTable table)1170 static BL_INLINE bool checkGSubLookupType5Format2(Validator* self, Trace trace, BLFontTable table) noexcept {
1171 // This is essentially the same as Format1 except that it also provides `ClassDefTable`.
1172 size_t headerSize = sizeof(GSubTable::ContextSubst2);
1173
1174 // If the size is smaller it would fail in `checkGSubLookupType5Format1_2()`.
1175 if (table.size >= headerSize) {
1176 uint32_t classDefOffset = table.dataAs<GSubTable::ContextSubst2>()->classDefOffset();
1177 if (classDefOffset < headerSize || classDefOffset > table.size)
1178 return trace.fail("ClassDefOffset [%u] out of range [%zu:%zu]\n", classDefOffset, headerSize, table.size);
1179
1180 if (!checkClassDefTable(self, trace, blFontSubTable(table, classDefOffset), "ClassDef"))
1181 return false;
1182 }
1183
1184 return checkGSubLookupType5Format1_2<GSubTable::ContextSubst2>(self, trace, table);
1185 }
1186
checkGSubLookupType5Format3(Validator * self,Trace trace,BLFontTable table)1187 static BL_INLINE bool checkGSubLookupType5Format3(Validator* self, Trace trace, BLFontTable table) noexcept {
1188 // TODO: [OPENTYPE GSUB]
1189 return true;
1190 }
1191
matchSubRule(BLFontTableT<Array16<Offset16>> subRuleOffsets,uint32_t subRuleCount,const uint32_t * glyphData,uint32_t maxGlyphCount,const GSubTable::SubRule ** out)1192 static BL_INLINE bool matchSubRule(
1193 BLFontTableT<Array16<Offset16>> subRuleOffsets,
1194 uint32_t subRuleCount,
1195 const uint32_t* glyphData,
1196 uint32_t maxGlyphCount,
1197 const GSubTable::SubRule** out) noexcept {
1198
1199 // Ligatures are ordered by preference. This means we have to go one by one.
1200 uint32_t maxLigOffset = uint32_t(subRuleOffsets.size) - 4u;
1201 uint32_t maxGlyphCountMinusOne = maxGlyphCount - 1u;
1202
1203 for (uint32_t subRuleIndex = 0; subRuleIndex < subRuleCount; subRuleIndex++) {
1204 uint32_t subRuleOffset = subRuleOffsets->array()[subRuleIndex].value();
1205 if (BL_UNLIKELY(subRuleOffset > maxLigOffset))
1206 break;
1207
1208 const GSubTable::SubRule* subRule = blOffsetPtr<const GSubTable::SubRule>(subRuleOffsets.data, subRuleOffset);
1209 uint32_t glyphCount = uint32_t(subRule->glyphCount()) - 1u;
1210 if (glyphCount > maxGlyphCountMinusOne)
1211 continue;
1212
1213 // This is safe - a single SubRule is 4 bytes that is followed by
1214 // `GlyphId[glyphCount - 1]` and then by `SubstLookupRecord[substCount]`.
1215 // Since we don't know whether we have a match or not we will only check
1216 // bounds required by matching postponing `substCount` until we have
1217 // an actual match.
1218 if (BL_UNLIKELY(subRuleOffset + glyphCount * 2u > maxLigOffset))
1219 continue;
1220
1221 uint32_t glyphIndex = 0;
1222 for (;;) {
1223 uint32_t glyphA = subRule->glyphArray()[glyphIndex++].value();
1224 uint32_t glyphB = glyphData[glyphIndex];
1225
1226 if (glyphA != glyphB)
1227 break;
1228
1229 if (glyphIndex < glyphCount)
1230 continue;
1231
1232 // Now check whether the `subRule` is not out of bounds.
1233 uint32_t substCount = subRule->substCount();
1234 if (BL_UNLIKELY(!substCount || subRuleOffset + glyphCount * 2u + substCount * 4u > maxLigOffset))
1235 return false;
1236
1237 *out = subRule;
1238 return true;
1239 }
1240 }
1241
1242 return false;
1243 }
1244
1245 template<uint32_t CoverageFormat>
applyGSubLookupType5Format1(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTableT<GSubTable::ContextSubst1> table,uint32_t lookupFlags,CoverageIterator & covIt)1246 static BL_INLINE BLResult applyGSubLookupType5Format1(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTableT<GSubTable::ContextSubst1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
1247 size_t itemCount = ctx.in.end - ctx.in.index;
1248 return ctx.advance(itemCount);
1249
1250 /*
1251 // TODO: [OPENTYPE GSUB]
1252 uint32_t* glyphData = buf->glyphData;
1253 uint32_t size = buf->size;
1254
1255 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
1256 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
1257 uint32_t subRuleSetCount = table->subRuleSetOffsets.count();
1258
1259 if (BL_UNLIKELY(table.size < GSubTable::LigatureSubst1::kMinSize + subRuleSetCount * 2u))
1260 return BL_SUCCESS;
1261
1262 uint32_t i;
1263 uint32_t maxSubRuleSetOffset = uint32_t(table.size) - 2u;
1264
1265 for (i = 0; i < size; i++) {
1266 uint32_t glyphId = glyphData[i];
1267
1268 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
1269 uint32_t coverageIndex;
1270 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) || coverageIndex >= subRuleSetCount) {
1271 uint32_t subRuleSetOffset = table->subRuleSetOffsets.array()[coverageIndex].value();
1272 if (BL_LIKELY(subRuleSetOffset <= maxSubRuleSetOffset)) {
1273 BLFontTableT<Array16<Offset16>> subRuleOffsets { blFontSubTable(table, subRuleSetOffset) };
1274 uint32_t subRuleCount = subRuleOffsets->count();
1275 if (BL_LIKELY(subRuleCount && subRuleSetOffset + subRuleCount * 2u <= maxSubRuleSetOffset)) {
1276 const GSubTable::SubRule* subRule;
1277 if (matchSubRule(subRuleOffsets, subRuleCount, glyphData + i, size - i, &subRule)) {
1278 continue;
1279 }
1280 }
1281 }
1282 }
1283 }
1284 }
1285 */
1286 }
1287
1288 // ============================================================================
1289 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #6]
1290 // ============================================================================
1291
1292 // Chained Contextual Substitution
1293 // -------------------------------
1294
checkGSubLookupType6Format1(Validator * self,Trace trace,BLFontTable table)1295 static BL_INLINE bool checkGSubLookupType6Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1296 // TODO: [OPENTYPE GSUB]
1297 return true;
1298 }
1299
checkGSubLookupType6Format2(Validator * self,Trace trace,BLFontTable table)1300 static BL_INLINE bool checkGSubLookupType6Format2(Validator* self, Trace trace, BLFontTable table) noexcept {
1301 // TODO: [OPENTYPE GSUB]
1302 return true;
1303 }
1304
checkGSubLookupType6Format3(Validator * self,Trace trace,BLFontTable table)1305 static BL_INLINE bool checkGSubLookupType6Format3(Validator* self, Trace trace, BLFontTable table) noexcept {
1306 // TODO: [OPENTYPE GSUB]
1307 return true;
1308 }
1309
1310 // ============================================================================
1311 // [BLOpenType::LayoutImpl - GSUB - Lookup Type #8]
1312 // ============================================================================
1313
1314 // Reverse Chained Substitution
1315 // ----------------------------
1316 //
1317 // Similar to "Chained Contextual Substitution", but processed in reverse order.
1318
checkGSubLookupType8Format1(Validator * self,Trace trace,BLFontTable table)1319 static BL_INLINE bool checkGSubLookupType8Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1320 // TODO: [OPENTYPE GSUB]
1321 return true;
1322 }
1323
1324 // ============================================================================
1325 // [BLOpenType::LayoutImpl - GSUB - Lookup Common]
1326 // ============================================================================
1327
checkGSubLookup(Validator * self,Trace trace,BLFontTable table,uint32_t lookupId)1328 static bool checkGSubLookup(Validator* self, Trace trace, BLFontTable table, uint32_t lookupId) noexcept {
1329 switch (lookupId) {
1330 case LookupInfo::kGSubType1Format1: return checkGSubLookupType1Format1(self, trace, table);
1331 case LookupInfo::kGSubType1Format2: return checkGSubLookupType1Format2(self, trace, table);
1332 case LookupInfo::kGSubType2Format1: return checkGSubLookupType2Format1(self, trace, table);
1333 case LookupInfo::kGSubType3Format1: return checkGSubLookupType3Format1(self, trace, table);
1334 case LookupInfo::kGSubType4Format1: return checkGSubLookupType4Format1(self, trace, table);
1335 case LookupInfo::kGSubType5Format1: return checkGSubLookupType5Format1(self, trace, table);
1336 case LookupInfo::kGSubType5Format2: return checkGSubLookupType5Format2(self, trace, table);
1337 case LookupInfo::kGSubType5Format3: return checkGSubLookupType5Format3(self, trace, table);
1338 case LookupInfo::kGSubType6Format1: return checkGSubLookupType6Format1(self, trace, table);
1339 case LookupInfo::kGSubType6Format2: return checkGSubLookupType6Format2(self, trace, table);
1340 case LookupInfo::kGSubType6Format3: return checkGSubLookupType6Format3(self, trace, table);
1341 case LookupInfo::kGSubType8Format1: return checkGSubLookupType8Format1(self, trace, table);
1342
1343 default:
1344 // Invalid LookupType & Format combination should never pass checks that use LookupInfo.
1345 BL_NOT_REACHED();
1346 }
1347 }
1348
applyGSubLookup(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTable table,uint32_t lookupId,uint32_t lookupFlags)1349 static BLResult applyGSubLookup(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTable table, uint32_t lookupId, uint32_t lookupFlags) noexcept {
1350 #define GSUB_APPLY_COMMON(FN, TABLE) \
1351 CoverageIterator covIt; \
1352 switch (covIt.init(blFontSubTableChecked(table, table.dataAs<TABLE>()->coverageOffset()))) { \
1353 case 1: return FN<1>(faceI, ctx, table, lookupFlags, covIt); \
1354 case 2: return FN<2>(faceI, ctx, table, lookupFlags, covIt); \
1355 }
1356
1357 if (BL_LIKELY(table.size >= gLookupInfo[LookupInfo::kKindGSub].idEntries[lookupId].headerSize)) {
1358 switch (lookupId) {
1359 case LookupInfo::kGSubType1Format1: {
1360 GSUB_APPLY_COMMON(applyGSubLookupType1Format1, GSubTable::LookupHeaderWithCoverage)
1361 break;
1362 }
1363
1364 case LookupInfo::kGSubType1Format2: {
1365 GSUB_APPLY_COMMON(applyGSubLookupType1Format2, GSubTable::LookupHeaderWithCoverage)
1366 break;
1367 }
1368
1369 case LookupInfo::kGSubType2Format1: {
1370 GSUB_APPLY_COMMON(applyGSubLookupType2Format1, GSubTable::LookupHeaderWithCoverage)
1371 break;
1372 }
1373
1374 case LookupInfo::kGSubType3Format1: {
1375 GSUB_APPLY_COMMON(applyGSubLookupType3Format1, GSubTable::LookupHeaderWithCoverage)
1376 break;
1377 }
1378
1379 case LookupInfo::kGSubType4Format1: {
1380 GSUB_APPLY_COMMON(applyGSubLookupType4Format1, GSubTable::LookupHeaderWithCoverage)
1381 break;
1382 }
1383
1384 /*
1385 case LookupInfo::kGSubType5Format1: {
1386 GSUB_APPLY_COMMON(applyGSubLookupType5Format1, GSubTable::LookupHeaderWithCoverage)
1387 break;
1388 }
1389 */
1390 }
1391 }
1392
1393 #undef GSUB_APPLY_COMMON
1394
1395 ctx.advance(ctx.inRemaining());
1396 return BL_SUCCESS;
1397 }
1398
1399 // ============================================================================
1400 // [BLOpenType::LayoutImpl - GPOS - Utilities]
1401 // ============================================================================
1402
1403 template<typename T>
binarySearchGlyphIdInVarStruct(const uint8_t * array,size_t itemSize,size_t arraySize,uint32_t glyphId,size_t offset=0)1404 static BL_INLINE const uint8_t* binarySearchGlyphIdInVarStruct(const uint8_t* array, size_t itemSize, size_t arraySize, uint32_t glyphId, size_t offset = 0) noexcept {
1405 if (!arraySize)
1406 return nullptr;
1407
1408 const uint8_t* ptr = array;
1409 while (size_t half = arraySize / 2u) {
1410 const uint8_t* middlePtr = ptr + half * itemSize;
1411 arraySize -= half;
1412 if (glyphId >= reinterpret_cast<const T*>(middlePtr + offset)->value())
1413 ptr = middlePtr;
1414 }
1415
1416 if (glyphId != reinterpret_cast<const T*>(ptr + offset)->value())
1417 ptr = nullptr;
1418 return ptr;
1419 }
1420
applyGPosValue(const Int16 * p,uint32_t valueFormat,BLGlyphPlacement * glyphPlacement)1421 static BL_INLINE const Int16* applyGPosValue(const Int16* p, uint32_t valueFormat, BLGlyphPlacement* glyphPlacement) noexcept {
1422 int32_t v;
1423 if (valueFormat & GPosTable::kValueXPlacement ) { v = p->value(); p++; glyphPlacement->placement.x += v; }
1424 if (valueFormat & GPosTable::kValueYPlacement ) { v = p->value(); p++; glyphPlacement->placement.y += v; }
1425 if (valueFormat & GPosTable::kValueXAdvance ) { v = p->value(); p++; glyphPlacement->advance.x += v; }
1426 if (valueFormat & GPosTable::kValueYAdvance ) { v = p->value(); p++; glyphPlacement->advance.y += v; }
1427 if (valueFormat & GPosTable::kValueXPlacementDevice) { v = p->value(); p++; }
1428 if (valueFormat & GPosTable::kValueYPlacementDevice) { v = p->value(); p++; }
1429 if (valueFormat & GPosTable::kValueXAdvanceDevice ) { v = p->value(); p++; }
1430 if (valueFormat & GPosTable::kValueYAdvanceDevice ) { v = p->value(); p++; }
1431 return p;
1432 }
1433
1434 // ============================================================================
1435 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #1]
1436 // ============================================================================
1437
1438 // Single Adjustment
1439 // -----------------
1440
checkGPosLookupType1Format1(Validator * self,Trace trace,BLFontTable table)1441 static BL_INLINE bool checkGPosLookupType1Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1442 uint32_t countCoverageEntries;
1443 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GPosTable::SingleAdjustment1), countCoverageEntries)))
1444 return false;
1445
1446 const GPosTable::SingleAdjustment1* lookup = table.dataAs<GPosTable::SingleAdjustment1>();
1447 uint32_t valueRecordSize = sizeOfValueRecordByFormat(lookup->valueFormat());
1448
1449 size_t headerSize = sizeof(GPosTable::SingleAdjustment1) + valueRecordSize;
1450 if (BL_UNLIKELY(table.size < headerSize))
1451 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
1452
1453 return true;
1454 }
1455
1456 template<uint32_t CoverageFormat>
applyGPosLookupType1Format1(const BLOTFaceImpl * faceI,GPosContext & ctx,BLFontTableT<GPosTable::SingleAdjustment1> table,uint32_t lookupFlags,CoverageIterator & covIt)1457 static BL_INLINE BLResult applyGPosLookupType1Format1(const BLOTFaceImpl* faceI, GPosContext& ctx, BLFontTableT<GPosTable::SingleAdjustment1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
1458 size_t index = ctx.index;
1459 size_t end = ctx.end;
1460
1461 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
1462 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
1463
1464 uint32_t valueFormat = table->valueFormat();
1465 if (BL_UNLIKELY(!valueFormat))
1466 return BL_SUCCESS;
1467
1468 uint32_t valueRecordSize = sizeOfValueRecordByFormat(valueFormat);
1469 if (BL_UNLIKELY(table.size < GPosTable::PairAdjustment1::kMinSize + valueRecordSize))
1470 return BL_SUCCESS;
1471
1472 uint32_t* glyphData = ctx.glyphData;
1473 BLGlyphPlacement* placementData = ctx.placementData;
1474
1475 for (; index < end; index++) {
1476 uint32_t glyphId = glyphData[index];
1477 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
1478 uint32_t coverageIndex;
1479 if (covIt.find<CoverageFormat>(glyphId, coverageIndex)) {
1480 const Int16* p = reinterpret_cast<const Int16*>(table.data + GPosTable::PairAdjustment1::kMinSize);
1481 applyGPosValue(p, valueFormat, &placementData[index]);
1482 }
1483 }
1484 }
1485
1486 return BL_SUCCESS;
1487 }
1488
checkGPosLookupType1Format2(Validator * self,Trace trace,BLFontTable table)1489 static BL_INLINE bool checkGPosLookupType1Format2(Validator* self, Trace trace, BLFontTable table) noexcept {
1490 uint32_t countCoverageEntries;
1491 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GPosTable::SingleAdjustment2), countCoverageEntries)))
1492 return false;
1493
1494 const GPosTable::SingleAdjustment2* lookup = table.dataAs<GPosTable::SingleAdjustment2>();
1495 uint32_t valueCount = lookup->valueCount();
1496 uint32_t valueRecordSize = sizeOfValueRecordByFormat(lookup->valueFormat());
1497
1498 size_t headerSize = sizeof(GPosTable::SingleAdjustment2) + valueRecordSize * valueCount;
1499 if (BL_UNLIKELY(table.size < headerSize))
1500 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
1501
1502 return true;
1503 }
1504
1505 template<uint32_t CoverageFormat>
applyGPosLookupType1Format2(const BLOTFaceImpl * faceI,GPosContext & ctx,BLFontTableT<GPosTable::SingleAdjustment2> table,uint32_t lookupFlags,CoverageIterator & covIt)1506 static BL_INLINE BLResult applyGPosLookupType1Format2(const BLOTFaceImpl* faceI, GPosContext& ctx, BLFontTableT<GPosTable::SingleAdjustment2> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
1507 size_t index = ctx.index;
1508 size_t end = ctx.end;
1509
1510 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
1511 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
1512
1513 uint32_t valueFormat = table->valueFormat();
1514 if (BL_UNLIKELY(!valueFormat))
1515 return BL_SUCCESS;
1516
1517 uint32_t valueRecordCount = table->valueCount();
1518 uint32_t valueRecordSize = sizeOfValueRecordByFormat(valueFormat);
1519 if (BL_UNLIKELY(table.size < GPosTable::PairAdjustment1::kMinSize + valueRecordCount * valueRecordSize))
1520 return BL_SUCCESS;
1521
1522 uint32_t* glyphData = ctx.glyphData;
1523 BLGlyphPlacement* placementData = ctx.placementData;
1524
1525 for (; index < end; index++) {
1526 uint32_t glyphId = glyphData[index];
1527 if (glyphId >= minGlyphId && glyphId <= maxGlyphId) {
1528 uint32_t coverageIndex;
1529 if (covIt.find<CoverageFormat>(glyphId, coverageIndex) && coverageIndex < valueRecordCount) {
1530 const Int16* p = reinterpret_cast<const Int16*>(table.data + GPosTable::PairAdjustment1::kMinSize + coverageIndex * valueRecordSize);
1531 applyGPosValue(p, valueFormat, &placementData[index]);
1532 }
1533 }
1534 }
1535
1536 return BL_SUCCESS;
1537 }
1538
1539 // ============================================================================
1540 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #2]
1541 // ============================================================================
1542
1543 // Pair Adjustment
1544 // ---------------
1545
checkGPosLookupType2Format1(Validator * self,Trace trace,BLFontTable table)1546 static BL_INLINE bool checkGPosLookupType2Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1547 uint32_t countCoverageEntries;
1548 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GPosTable::PairAdjustment1), countCoverageEntries)))
1549 return false;
1550
1551 const GPosTable::PairAdjustment1* lookup = table.dataAs<GPosTable::PairAdjustment1>();
1552 uint32_t pairSetCount = lookup->pairSetOffsets.count();
1553 uint32_t valueRecordSize = 2u + sizeOfValueRecordByFormat(lookup->valueFormat1()) +
1554 sizeOfValueRecordByFormat(lookup->valueFormat2()) ;
1555
1556 size_t headerSize = sizeof(GPosTable::PairAdjustment1) + pairSetCount * 2u;
1557 if (BL_UNLIKELY(table.size < headerSize))
1558 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
1559
1560 const Offset16* pairSetOffsetArray = lookup->pairSetOffsets.array();
1561 size_t offsetRangeEnd = table.size - 2u;
1562
1563 for (uint32_t i = 0; i < pairSetCount; i++) {
1564 size_t pairSetOffset = pairSetOffsetArray[i].value();
1565 if (BL_UNLIKELY(pairSetOffset < headerSize || pairSetOffset > offsetRangeEnd))
1566 return trace.fail("Pair %u: Offset [%zu] is out of range [%zu:%zu]\n", i, pairSetOffset, headerSize, offsetRangeEnd);
1567
1568 uint32_t valueCount = blMemReadU16uBE(table.data + pairSetOffset);
1569 uint32_t pairSetSize = valueCount * valueRecordSize;
1570
1571 if (pairSetSize > table.size - pairSetOffset)
1572 return trace.fail("Pair #%u of ValueCount [%u] requires [%zu] bytes of data, but only [%zu] bytes are available\n", i, valueCount, pairSetSize, table.size - pairSetOffset);
1573 }
1574
1575 return true;
1576 }
1577
1578 template<uint32_t CoverageFormat>
applyGPosLookupType2Format1(const BLOTFaceImpl * faceI,GPosContext & ctx,BLFontTableT<GPosTable::PairAdjustment1> table,uint32_t lookupFlags,CoverageIterator & covIt)1579 static BL_INLINE BLResult applyGPosLookupType2Format1(const BLOTFaceImpl* faceI, GPosContext& ctx, BLFontTableT<GPosTable::PairAdjustment1> table, uint32_t lookupFlags, CoverageIterator& covIt) noexcept {
1580 size_t index = ctx.index;
1581 size_t end = ctx.end;
1582
1583 if (end <= 1)
1584 return BL_SUCCESS;
1585 end--;
1586
1587 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
1588 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
1589
1590 uint32_t valueFormat1 = table->valueFormat1();
1591 uint32_t valueFormat2 = table->valueFormat2();
1592 uint32_t pairSetOffsetsCount = table->pairSetOffsets.count();
1593
1594 if (BL_UNLIKELY(table.size < GPosTable::PairAdjustment1::kMinSize + pairSetOffsetsCount * 2u))
1595 return BL_SUCCESS;
1596
1597 size_t valueRecordSize = 2u + sizeOfValueRecordByFormat(valueFormat1) +
1598 sizeOfValueRecordByFormat(valueFormat2) ;
1599
1600 uint32_t* glyphData = ctx.glyphData;
1601 BLGlyphPlacement* placementData = ctx.placementData;
1602
1603 uint32_t leftGlyphId = glyphData[index];
1604 uint32_t rightGlyphId = 0;
1605
1606 for (; index < end; index++, leftGlyphId = rightGlyphId) {
1607 rightGlyphId = glyphData[index + 1];
1608 if (leftGlyphId >= minGlyphId && leftGlyphId <= maxGlyphId) {
1609 uint32_t coverageIndex;
1610 if (covIt.find<CoverageFormat>(leftGlyphId, coverageIndex) && coverageIndex < pairSetOffsetsCount) {
1611 uint32_t pairSetOffset = table->pairSetOffsets.array()[coverageIndex].value();
1612 if (pairSetOffset > table.size - 2u)
1613 continue;
1614
1615 const uint8_t* pairSetData = table.data + pairSetOffset;
1616 uint32_t pairSetCount = reinterpret_cast<const UInt16*>(pairSetData)->value();
1617
1618 if (pairSetOffset + pairSetCount * valueRecordSize > table.size)
1619 continue;
1620
1621 const Int16* p = reinterpret_cast<const Int16*>(
1622 binarySearchGlyphIdInVarStruct<UInt16>(pairSetData, valueRecordSize, pairSetCount, rightGlyphId));
1623
1624 if (p) {
1625 if (valueFormat1) p = applyGPosValue(p + 1, valueFormat1, &placementData[index + 0]);
1626 if (valueFormat2) p = applyGPosValue(p + 0, valueFormat2, &placementData[index + 1]);
1627 }
1628 }
1629 }
1630 }
1631
1632 return BL_SUCCESS;
1633 }
1634
checkGPosLookupType2Format2(Validator * self,Trace trace,BLFontTableT<GPosTable::PairAdjustment2> table)1635 static BL_INLINE bool checkGPosLookupType2Format2(Validator* self, Trace trace, BLFontTableT<GPosTable::PairAdjustment2> table) noexcept {
1636 uint32_t countCoverageEntries;
1637 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GPosTable::PairAdjustment2), countCoverageEntries)))
1638 return false;
1639
1640 uint32_t class1Count = table->class1Count();
1641 uint32_t class2Count = table->class2Count();
1642 uint32_t valueRecordCount = class1Count * class2Count;
1643
1644 uint32_t valueFormat1 = table->valueFormat1();
1645 uint32_t valueFormat2 = table->valueFormat2();
1646 uint32_t valueRecordSize = sizeOfValueRecordByFormat(valueFormat1) + sizeOfValueRecordByFormat(valueFormat2);
1647
1648 BLOverflowFlag of = 0;
1649 size_t headerSize = blAddOverflow(uint32_t(sizeof(GPosTable::PairAdjustment2)),
1650 blMulOverflow(valueRecordCount, valueRecordSize, &of), &of);
1651
1652 if (BL_UNLIKELY(of))
1653 return trace.fail("Overflow detected when calculating header size [Class1Count=%u Class2Count=%u]\n", class1Count, class2Count);
1654
1655 if (BL_UNLIKELY(table.size < headerSize))
1656 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
1657
1658 return true;
1659 }
1660
1661 template<uint32_t CoverageFormat, uint32_t ClassDef1Format, uint32_t ClassDef2Format>
applyGPosLookupType2Format2(const BLOTFaceImpl * faceI,GPosContext & ctx,BLFontTableT<GPosTable::PairAdjustment2> table,uint32_t lookupFlags,CoverageIterator & covIt,ClassDefIterator & cd1It,ClassDefIterator & cd2It)1662 static BL_INLINE BLResult applyGPosLookupType2Format2(const BLOTFaceImpl* faceI, GPosContext& ctx, BLFontTableT<GPosTable::PairAdjustment2> table, uint32_t lookupFlags, CoverageIterator& covIt, ClassDefIterator& cd1It, ClassDefIterator& cd2It) noexcept {
1663 size_t index = ctx.index;
1664 size_t end = ctx.end;
1665
1666 if (end <= 1)
1667 return BL_SUCCESS;
1668 end--;
1669
1670 uint32_t minGlyphId = covIt.minGlyphId<CoverageFormat>();
1671 uint32_t maxGlyphId = covIt.maxGlyphId<CoverageFormat>();
1672
1673 uint32_t valueFormat1 = table->valueFormat1();
1674 uint32_t valueFormat2 = table->valueFormat2();
1675 uint32_t valueRecordSize = sizeOfValueRecordByFormat(valueFormat1) + sizeOfValueRecordByFormat(valueFormat2);
1676
1677 uint32_t class1Count = table->class1Count();
1678 uint32_t class2Count = table->class2Count();
1679 uint32_t valueRecordCount = class1Count * class2Count;
1680
1681 BLOverflowFlag of = 0;
1682 size_t requiredDataSize = blAddOverflow(uint32_t(sizeof(GPosTable::PairAdjustment2)),
1683 blMulOverflow(valueRecordCount, valueRecordSize, &of), &of);
1684
1685 if (BL_UNLIKELY(table.size < requiredDataSize || of))
1686 return BL_SUCCESS;
1687
1688 const uint8_t* valueBasePtr = table.data + sizeof(GPosTable::PairAdjustment2);
1689
1690 uint32_t* glyphData = ctx.glyphData;
1691 BLGlyphPlacement* placementData = ctx.placementData;
1692
1693 uint32_t leftGlyphId = glyphData[index];
1694 uint32_t rightGlyphId = 0;
1695
1696 for (; index < end; index++, leftGlyphId = rightGlyphId) {
1697 rightGlyphId = glyphData[index + 1];
1698 if (leftGlyphId >= minGlyphId && leftGlyphId <= maxGlyphId) {
1699 uint32_t coverageIndex;
1700 if (covIt.find<CoverageFormat>(leftGlyphId, coverageIndex)) {
1701 uint32_t c1;
1702 uint32_t c2;
1703
1704 if (cd1It.find<ClassDef1Format>(leftGlyphId, c1) && cd2It.find<ClassDef2Format>(rightGlyphId, c2)) {
1705 uint32_t cIndex = c1 * class2Count + c2;
1706 if (cIndex < valueRecordCount) {
1707 const Int16* p = blOffsetPtr<const Int16>(valueBasePtr, cIndex * valueRecordSize);
1708 if (valueFormat1) p = applyGPosValue(p, valueFormat1, &placementData[index + 0]);
1709 if (valueFormat2) p = applyGPosValue(p, valueFormat2, &placementData[index + 1]);
1710 }
1711 }
1712 }
1713 }
1714 }
1715
1716 return BL_SUCCESS;
1717 }
1718
1719 // ============================================================================
1720 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #3]
1721 // ============================================================================
1722
1723 // Cursive Attachment
1724 // ------------------
1725
checkGPosLookupType3Format1(Validator * self,Trace trace,BLFontTable table)1726 static BL_INLINE bool checkGPosLookupType3Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1727 uint32_t countCoverageEntries;
1728 if (BL_UNLIKELY(!checkLookupWithCoverage(self, trace, table, sizeof(GPosTable::CursiveAttachment1), countCoverageEntries)))
1729 return false;
1730
1731 const GPosTable::CursiveAttachment1* lookup = table.dataAs<GPosTable::CursiveAttachment1>();
1732 uint32_t entryExitCount = lookup->entryExits.count();
1733
1734 size_t headerSize = sizeof(GPosTable::CursiveAttachment1) + entryExitCount * sizeof(GPosTable::EntryExit);
1735 if (BL_UNLIKELY(table.size < headerSize))
1736 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
1737
1738 return true;
1739 }
1740
1741 // ============================================================================
1742 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #4]
1743 // ============================================================================
1744
1745 // MarkToBase Attachment
1746 // ---------------------
1747
checkGPosLookupType4Format1(Validator * self,Trace trace,BLFontTable table)1748 static BL_INLINE bool checkGPosLookupType4Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1749 // TODO: [OPENTYPE GPOS]
1750 return true;
1751 }
1752
1753 // ============================================================================
1754 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #5]
1755 // ============================================================================
1756
1757 // MarkToLigature Attachment
1758 // -------------------------
1759
checkGPosLookupType5Format1(Validator * self,Trace trace,BLFontTable table)1760 static BL_INLINE bool checkGPosLookupType5Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1761 // TODO: [OPENTYPE GPOS]
1762 return true;
1763 }
1764
1765 // ============================================================================
1766 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #6]
1767 // ============================================================================
1768
1769 // MarkToMark Attachment
1770 // ---------------------
1771
checkGPosLookupType6Format1(Validator * self,Trace trace,BLFontTable table)1772 static BL_INLINE bool checkGPosLookupType6Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1773 // TODO: [OPENTYPE GPOS]
1774 return true;
1775 }
1776
1777 // ============================================================================
1778 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #7]
1779 // ============================================================================
1780
1781 // Contextual Positioning
1782 // ----------------------
1783
checkGPosLookupType7Format1(Validator * self,Trace trace,BLFontTable table)1784 static BL_INLINE bool checkGPosLookupType7Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1785 // TODO: [OPENTYPE GPOS]
1786 return true;
1787 }
1788
checkGPosLookupType7Format2(Validator * self,Trace trace,BLFontTable table)1789 static BL_INLINE bool checkGPosLookupType7Format2(Validator* self, Trace trace, BLFontTable table) noexcept {
1790 // TODO: [OPENTYPE GPOS]
1791 return true;
1792 }
1793
checkGPosLookupType7Format3(Validator * self,Trace trace,BLFontTable table)1794 static BL_INLINE bool checkGPosLookupType7Format3(Validator* self, Trace trace, BLFontTable table) noexcept {
1795 // TODO: [OPENTYPE GPOS]
1796 return true;
1797 }
1798
1799 // ============================================================================
1800 // [BLOpenType::LayoutImpl - GPOS - Lookup Type #8]
1801 // ============================================================================
1802
1803 // Chained Contextual Positioning
1804 // ------------------------------
1805
checkGPosLookupType8Format1(Validator * self,Trace trace,BLFontTable table)1806 static BL_INLINE bool checkGPosLookupType8Format1(Validator* self, Trace trace, BLFontTable table) noexcept {
1807 // TODO: [OPENTYPE GPOS]
1808 return true;
1809 }
1810
checkGPosLookupType8Format2(Validator * self,Trace trace,BLFontTable table)1811 static BL_INLINE bool checkGPosLookupType8Format2(Validator* self, Trace trace, BLFontTable table) noexcept {
1812 // TODO: [OPENTYPE GPOS]
1813 return true;
1814 }
1815
checkGPosLookupType8Format3(Validator * self,Trace trace,BLFontTable table)1816 static BL_INLINE bool checkGPosLookupType8Format3(Validator* self, Trace trace, BLFontTable table) noexcept {
1817 // TODO: [OPENTYPE GPOS]
1818 return true;
1819 }
1820
1821 // ============================================================================
1822 // [BLOpenType::LayoutImpl - GPOS - Lookup Common]
1823 // ============================================================================
1824
checkGPosLookup(Validator * self,Trace trace,BLFontTable table,uint32_t lookupId)1825 static BL_INLINE bool checkGPosLookup(Validator* self, Trace trace, BLFontTable table, uint32_t lookupId) noexcept {
1826 switch (lookupId) {
1827 case LookupInfo::kGPosType1Format1: return checkGPosLookupType1Format1(self, trace, table);
1828 case LookupInfo::kGPosType1Format2: return checkGPosLookupType1Format2(self, trace, table);
1829 case LookupInfo::kGPosType2Format1: return checkGPosLookupType2Format1(self, trace, table);
1830 case LookupInfo::kGPosType2Format2: return checkGPosLookupType2Format2(self, trace, table);
1831 case LookupInfo::kGPosType3Format1: return checkGPosLookupType3Format1(self, trace, table);
1832 case LookupInfo::kGPosType4Format1: return checkGPosLookupType4Format1(self, trace, table);
1833 case LookupInfo::kGPosType5Format1: return checkGPosLookupType5Format1(self, trace, table);
1834 case LookupInfo::kGPosType6Format1: return checkGPosLookupType6Format1(self, trace, table);
1835 case LookupInfo::kGPosType7Format1: return checkGPosLookupType7Format1(self, trace, table);
1836 case LookupInfo::kGPosType7Format2: return checkGPosLookupType7Format2(self, trace, table);
1837 case LookupInfo::kGPosType7Format3: return checkGPosLookupType7Format3(self, trace, table);
1838 case LookupInfo::kGPosType8Format1: return checkGPosLookupType8Format1(self, trace, table);
1839 case LookupInfo::kGPosType8Format2: return checkGPosLookupType8Format2(self, trace, table);
1840 case LookupInfo::kGPosType8Format3: return checkGPosLookupType8Format3(self, trace, table);
1841
1842 default:
1843 // Invalid LookupType & Format combination should never pass checks that use LookupInfo.
1844 BL_NOT_REACHED();
1845 }
1846 }
1847
1848 // TODO: [OPENTYPE GPOS]
applyGPosLookup(const BLOTFaceImpl * faceI,GPosContext & ctx,BLFontTable table,uint32_t lookupId,uint32_t lookupFlags)1849 static BLResult applyGPosLookup(const BLOTFaceImpl* faceI, GPosContext& ctx, BLFontTable table, uint32_t lookupId, uint32_t lookupFlags) noexcept {
1850 // Artificial format that we use here, instead of using 1-2 we use 0-1 as it's easier to write
1851 // a switch in case that multiple tables with different format options can be used by a lookup.
1852 enum Format : uint32_t {
1853 kFmt1 = 0,
1854 kFmt2 = 1
1855 };
1856
1857 #define GPOS_APPLY_COMMON(FN, TABLE) \
1858 CoverageIterator covIt; \
1859 switch (covIt.init(blFontSubTableChecked(table, table.dataAs<TABLE>()->coverageOffset()))) { \
1860 case 1: return FN<1>(faceI, ctx, table, lookupFlags, covIt); \
1861 case 2: return FN<2>(faceI, ctx, table, lookupFlags, covIt); \
1862 }
1863
1864 if (BL_LIKELY(table.size >= gLookupInfo[LookupInfo::kKindGPos].idEntries[lookupId].headerSize)) {
1865 switch (lookupId) {
1866 case LookupInfo::kGPosType1Format1: {
1867 GPOS_APPLY_COMMON(applyGPosLookupType1Format1, GPosTable::SingleAdjustment1)
1868 break;
1869 }
1870
1871 case LookupInfo::kGPosType1Format2: {
1872 GPOS_APPLY_COMMON(applyGPosLookupType1Format2, GPosTable::SingleAdjustment2)
1873 break;
1874 }
1875
1876 case LookupInfo::kGPosType2Format1: {
1877 GPOS_APPLY_COMMON(applyGPosLookupType2Format1, GPosTable::PairAdjustment1)
1878 break;
1879 }
1880
1881 case LookupInfo::kGPosType2Format2: {
1882 CoverageIterator covIt;
1883 ClassDefIterator cd1It;
1884 ClassDefIterator cd2It;
1885
1886 uint32_t bits = ((covIt.init(blFontSubTableChecked(table, table.dataAs<GPosTable::PairAdjustment2>()->coverageOffset())) - 1u) << 2) |
1887 ((cd1It.init(blFontSubTableChecked(table, table.dataAs<GPosTable::PairAdjustment2>()->classDef1Offset())) - 1u) << 1) |
1888 ((cd2It.init(blFontSubTableChecked(table, table.dataAs<GPosTable::PairAdjustment2>()->classDef2Offset())) - 1u) << 0);
1889
1890 switch (bits) {
1891 case ((kFmt1 << 2) | (kFmt1 << 1) | (kFmt1 << 0)): return applyGPosLookupType2Format2<1, 1, 1>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1892 case ((kFmt1 << 2) | (kFmt1 << 1) | (kFmt2 << 0)): return applyGPosLookupType2Format2<1, 1, 2>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1893 case ((kFmt1 << 2) | (kFmt2 << 1) | (kFmt1 << 0)): return applyGPosLookupType2Format2<1, 2, 1>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1894 case ((kFmt1 << 2) | (kFmt2 << 1) | (kFmt2 << 0)): return applyGPosLookupType2Format2<1, 2, 2>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1895 case ((kFmt2 << 2) | (kFmt1 << 1) | (kFmt1 << 0)): return applyGPosLookupType2Format2<2, 1, 1>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1896 case ((kFmt2 << 2) | (kFmt1 << 1) | (kFmt2 << 0)): return applyGPosLookupType2Format2<2, 1, 2>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1897 case ((kFmt2 << 2) | (kFmt2 << 1) | (kFmt1 << 0)): return applyGPosLookupType2Format2<2, 2, 1>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1898 case ((kFmt2 << 2) | (kFmt2 << 1) | (kFmt2 << 0)): return applyGPosLookupType2Format2<2, 2, 2>(faceI, ctx, table, lookupFlags, covIt, cd1It, cd2It);
1899 }
1900 break;
1901 }
1902 }
1903
1904 // TODO: [OPENTYPE GPOS]
1905 }
1906
1907 #undef GPOS_APPLY_COMMON
1908
1909 return BL_SUCCESS;
1910 }
1911
1912 // ============================================================================
1913 // [BLOpenType::LayoutImpl - GPOS / GSUB - Init]
1914 // ============================================================================
1915
lookupTypeAsString(uint32_t kind,uint32_t lookupType)1916 static const char* lookupTypeAsString(uint32_t kind, uint32_t lookupType) noexcept {
1917 if (kind == LookupInfo::kKindGPos) {
1918 switch (lookupType) {
1919 case 1: return "SingleAdjustment";
1920 case 2: return "PairAdjustment";
1921 case 3: return "CursiveAdjustment";
1922 case 4: return "MarkToBaseAttachment";
1923 case 5: return "MarkToLigatureAttachment";
1924 case 6: return "MarkToMarkAttachment";
1925 case 7: return "ContextPositioning";
1926 case 8: return "ChainedContextPositioning";
1927 case 9: return "Extension";
1928 }
1929 }
1930 else {
1931 switch (lookupType) {
1932 case 1: return "SingleSubstitution";
1933 case 2: return "MultipleSubstitution";
1934 case 3: return "AlternateSubstitution";
1935 case 4: return "LigatureSubstitution";
1936 case 5: return "ContextSubstitution";
1937 case 6: return "ChainedContextSubstitution";
1938 case 7: return "Extension";
1939 case 8: return "ReverseChainedContextSubstitution";
1940 }
1941 }
1942 return "Unknown";
1943 }
1944
checkLookupTable(Validator * self,Trace trace,uint32_t kind,BLFontTableT<GAnyTable::LookupTable> table,uint32_t lookupIndex)1945 static bool checkLookupTable(Validator* self, Trace trace, uint32_t kind, BLFontTableT<GAnyTable::LookupTable> table, uint32_t lookupIndex) noexcept {
1946 trace.info("LookupTable #%u\n", lookupIndex);
1947 trace.indent();
1948
1949 if (BL_UNLIKELY(!blFontTableFitsT<GAnyTable::LookupTable>(table)))
1950 return trace.fail("Table is too small [Size=%zu]\n", table.size);
1951
1952 uint32_t lookupType = table->lookupType();
1953 uint32_t lookupFlags = table->lookupFlags();
1954
1955 uint32_t offsetCount = table->lookupOffsets.count();
1956 size_t headerSize = 6u + offsetCount * 2u + (lookupFlags & GAnyTable::LookupTable::kFlagUseMarkFilteringSet ? 2u : 0u);
1957
1958 trace.info("LookupType: %u (%s)\n", lookupType, lookupTypeAsString(kind, lookupType));
1959 trace.info("LookupFlags: 0x%02X\n", lookupFlags & 0xFFu);
1960 trace.info("MarkAttachmentType: %u\n", lookupFlags >> 8);
1961
1962 bool isExtension = (lookupType == gLookupInfo[kind].extensionType);
1963 if (BL_UNLIKELY(lookupType - 1u >= gLookupInfo[kind].lookupCount))
1964 return trace.fail("Invalid lookup type [%u]\n", lookupType);
1965
1966 if (BL_UNLIKELY(table.size < headerSize))
1967 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
1968
1969 const Offset16* offsetArray = table->lookupOffsets.array();
1970 uint32_t& lookupTypes = self->faceI->layout.kinds[kind].lookupTypes;
1971
1972 // TODO: WHY?
1973 // BLArray<LayoutData::LookupEntry>& lookupEntries = self->faceI->layout.lookupEntries[kind];
1974
1975 const LookupInfo::TypeEntry& lookupTypeInfo = gLookupInfo[kind].typeEntries[lookupType];
1976 size_t lookupTableEnd = table.size - 2u;
1977
1978 for (uint32_t i = 0; i < offsetCount; i++) {
1979 uint32_t offset = offsetArray[i].value();
1980
1981 trace.info("Lookup #%u [%u]\n", i, offset);
1982 trace.indent();
1983
1984 if (BL_UNLIKELY(offset < headerSize || offset > lookupTableEnd))
1985 return trace.fail("Invalid offset #%u [%u], valid range [%zu:%zu]\n", i, offset, headerSize, lookupTableEnd);
1986
1987 BLFontTableT<GSubTable::LookupHeader> header { blFontSubTable(table, offset) };
1988 uint32_t lookupFormat = header->format();
1989
1990 trace.info("LookupFormat: %u\n", lookupFormat);
1991 if (BL_UNLIKELY(lookupFormat - 1u >= lookupTypeInfo.formatCount))
1992 return trace.fail("Invalid format [%u]\n", lookupFormat);
1993
1994 uint32_t lookupId = lookupTypeInfo.lookupIdIndex + lookupFormat - 1u;
1995 if (isExtension) {
1996 if (BL_UNLIKELY(header.size < sizeof(GAnyTable::ExtensionLookup)))
1997 return trace.fail("Extension data too small [%zu]\n", header.size);
1998
1999 uint32_t extensionLookupType = header.dataAs<GAnyTable::ExtensionLookup>()->lookupType();
2000 trace.info("ExtensionLookupType: %u (%s)\n", extensionLookupType, lookupTypeAsString(kind, extensionLookupType));
2001
2002 if (BL_UNLIKELY(extensionLookupType - 1u >= gLookupInfo[kind].lookupCount))
2003 return trace.fail("Invalid extension LookupType [%u]\n", extensionLookupType);
2004
2005 if (BL_UNLIKELY(extensionLookupType == gLookupInfo[kind].extensionType))
2006 return trace.fail("Extension's LookupType cannot be Extension\n");
2007
2008 const LookupInfo::TypeEntry& extensionLookupTypeInfo = gLookupInfo[kind].typeEntries[extensionLookupType];
2009 uint32_t extensionOffset = header.dataAs<GAnyTable::ExtensionLookup>()->offset();
2010
2011 if (extensionOffset > header.size - 2u)
2012 return trace.fail("Invalid extension offset [%u], data ends at [%zu]\n", extensionOffset, header.size);
2013
2014 header = blFontSubTable(header, extensionOffset);
2015 lookupFormat = header->format();
2016
2017 if (BL_UNLIKELY(lookupFormat - 1u >= extensionLookupTypeInfo.formatCount))
2018 return trace.fail("Invalid extension format [%u]\n", lookupFormat);
2019
2020 lookupId = extensionLookupTypeInfo.lookupIdIndex + lookupFormat - 1u;
2021 }
2022
2023 bool result = (kind == LookupInfo::kKindGSub) ? checkGSubLookup(self, trace, header, lookupId)
2024 : checkGPosLookup(self, trace, header, lookupId);
2025 if (BL_UNLIKELY(!result))
2026 return false;
2027
2028 lookupTypes |= 1u << lookupType;
2029 trace.deindent();
2030 }
2031
2032 return true;
2033 }
2034
checkFeatureTable(Validator * self,Trace trace,uint32_t kind,BLFontTableT<GAnyTable::FeatureTable> table,uint32_t index,uint32_t tag)2035 static bool checkFeatureTable(Validator* self, Trace trace, uint32_t kind, BLFontTableT<GAnyTable::FeatureTable> table, uint32_t index, uint32_t tag) noexcept {
2036 char tagString[5];
2037 blFontTagToAscii(tagString, tag);
2038
2039 trace.info("FeatureTable #%u '%s'\n", index, tagString);
2040 trace.indent();
2041
2042 if (BL_UNLIKELY(!blFontTableFitsT<GAnyTable::FeatureTable>(table)))
2043 return trace.fail("Table is too small [Size=%zu]\n", table.size);
2044
2045 uint32_t featureParamsOffset = table->featureParamsOffset();
2046 uint32_t lookupListCount = table->lookupListIndexes.count();
2047
2048 size_t headerSize = 4u + lookupListCount * 2u;
2049 if (BL_UNLIKELY(table.size < headerSize))
2050 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
2051
2052 const UInt16* lookupListIndexes = table->lookupListIndexes.array();
2053 uint32_t totalLookupCount = self->faceI->layout.kinds[kind].lookupCount;
2054
2055 for (uint32_t i = 0; i < lookupListCount; i++) {
2056 uint32_t lookupListIndex = lookupListIndexes[i].value();
2057 trace.info("Entry #%u -> LookupTable #%u\n", i, lookupListIndex);
2058
2059 if (lookupListIndex >= totalLookupCount)
2060 return trace.fail("LookupTable #%u is out of bounds [Count=%u]\n", lookupListIndex, totalLookupCount);
2061 }
2062
2063 return true;
2064 }
2065
checkScriptTable(Validator * self,Trace trace,uint32_t kind,BLFontTableT<GAnyTable::ScriptTable> table,uint32_t index,uint32_t tag)2066 static bool checkScriptTable(Validator* self, Trace trace, uint32_t kind, BLFontTableT<GAnyTable::ScriptTable> table, uint32_t index, uint32_t tag) noexcept {
2067 char tagString[5];
2068 blFontTagToAscii(tagString, tag);
2069
2070 trace.info("ScriptTable #%u '%s'\n", index, tagString);
2071 trace.indent();
2072
2073 if (BL_UNLIKELY(!blFontTableFitsT<GAnyTable::ScriptTable>(table)))
2074 return trace.fail("Table is too small [Size=%zu]\n", table.size);
2075
2076 uint32_t langSysCount = table->langSysOffsets.count();
2077 uint32_t langSysDefault = table->langSysDefault();
2078
2079 size_t headerSize = 4u + langSysCount * 2u;
2080 if (BL_UNLIKELY(table.size < headerSize))
2081 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, headerSize);
2082
2083 const TagRef16* langSysOffsetArray = table->langSysOffsets.array();
2084 uint32_t totalFeatureCount = self->faceI->layout.kinds[kind].featureCount;
2085
2086 for (uint32_t i = 0; i < langSysCount; i++) {
2087 uint32_t langSysTag = langSysOffsetArray[i].tag();
2088 uint32_t langSysOffset = langSysOffsetArray[i].offset();
2089
2090 blFontTagToAscii(tagString, langSysTag);
2091 trace.info("LangSys #%u '%s' [%u]\n", i, tagString, langSysOffset);
2092 trace.indent();
2093
2094 if (langSysOffset < headerSize || langSysOffset > table.size - GAnyTable::LangSysTable::kMinSize)
2095 return trace.fail("Offset [%u] out of range [%zu:%zu]\n", langSysOffset, headerSize, table.size);
2096
2097 BLFontTableT<GAnyTable::LangSysTable> langSys { blFontSubTable(table, langSysOffset) };
2098
2099 uint32_t lookupOrderOffset = langSys->lookupOrderOffset();
2100 uint32_t requiredFeatureIndex = langSys->requiredFeatureIndex();
2101 uint32_t featureIndexCount = langSys->featureIndexes.count();
2102
2103 size_t langSysTableSize = (GAnyTable::LangSysTable::kMinSize) + featureIndexCount * 2u;
2104 if (langSys.size < langSysTableSize)
2105 return trace.fail("Table is truncated [Size=%zu Required=%zu]\n", table.size, langSysTableSize);
2106
2107 if (requiredFeatureIndex != GAnyTable::kFeatureNotRequired && requiredFeatureIndex >= totalFeatureCount)
2108 return trace.fail("Required Feature Index [%u] is out of bounds [Count=%u]\n", requiredFeatureIndex, totalFeatureCount);
2109
2110 const UInt16* featureIndexArray = langSys->featureIndexes.array();
2111 for (uint32_t j = 0; j < featureIndexCount; j++) {
2112 uint32_t featureIndex = featureIndexArray[j].value();
2113 if (featureIndex >= totalFeatureCount)
2114 return trace.fail("Feature #%u index [%u] is out of bounds [Count=%u]\n", j, featureIndex, totalFeatureCount);
2115
2116 trace.info("Entry #%u -> FeatureIndex #%u\n", j, featureIndex);
2117 }
2118
2119 trace.deindent();
2120 }
2121
2122 return true;
2123 }
2124
checkGPosGSubTable(Validator * self,Trace trace,uint32_t kind)2125 static bool checkGPosGSubTable(Validator* self, Trace trace, uint32_t kind) noexcept {
2126 BLOTFaceImpl* faceI = self->faceI;
2127
2128 BLFontTableT<GAnyTable> table;
2129 const char* tableTypeAsString;
2130
2131 if (kind == LookupInfo::kKindGPos) {
2132 table = self->gpos;
2133 tableTypeAsString = "GPOS";
2134 }
2135 else {
2136 table = self->gsub;
2137 tableTypeAsString = "GSUB";
2138 }
2139
2140 trace.info("OpenType::Init '%s' [Size=%zu]\n", tableTypeAsString, table.size);
2141 trace.indent();
2142
2143 if (BL_UNLIKELY(!blFontTableFitsT<GAnyTable>(table)))
2144 return trace.fail("Table too small [Size=%zu Required: %zu]\n", table.size, size_t(GAnyTable::kMinSize));
2145
2146 uint32_t version = table->v1_0()->version();
2147 size_t headerSize = GPosTable::HeaderV1_0::kMinSize;
2148
2149 if (version >= 0x00010001u)
2150 headerSize = GPosTable::HeaderV1_1::kMinSize;
2151
2152 if (BL_UNLIKELY(version < 0x00010000u || version > 0x00010001u))
2153 return trace.fail("Invalid version [%u.%u]\n", version >> 16, version & 0xFFFFu);
2154
2155 if (BL_UNLIKELY(table.size < headerSize))
2156 return trace.fail("Table is too small [Size=%zu Required=%zu]\n", table.size, headerSize);
2157
2158 // --------------------------------------------------------------------------
2159 // [Validate Offsets]
2160 // --------------------------------------------------------------------------
2161
2162 uint32_t scriptListOffset = table->v1_0()->scriptListOffset();
2163 uint32_t featureListOffset = table->v1_0()->featureListOffset();
2164 uint32_t lookupListOffset = table->v1_0()->lookupListOffset();
2165
2166 if (scriptListOffset == table.size) scriptListOffset = 0;
2167 if (featureListOffset == table.size) featureListOffset = 0;
2168 if (lookupListOffset == table.size) lookupListOffset = 0;
2169
2170 if (lookupListOffset) {
2171 if (BL_UNLIKELY(lookupListOffset < headerSize || lookupListOffset >= table.size))
2172 return trace.fail("Invalid LookupList offset [%u]\n", lookupListOffset);
2173
2174 if (BL_UNLIKELY(!checkRawOffsetArray(self, trace, blFontSubTable(table, lookupListOffset), "LookupList")))
2175 return false;
2176 }
2177
2178 if (featureListOffset) {
2179 if (BL_UNLIKELY(featureListOffset < headerSize || featureListOffset >= table.size))
2180 return trace.fail("Invalid FeatureList offset [%u]\n", featureListOffset);
2181
2182 if (BL_UNLIKELY(!checkTagRef16Array(self, trace, blFontSubTable(table, featureListOffset), "FeatureList")))
2183 return false;
2184 }
2185
2186 if (scriptListOffset) {
2187 if (BL_UNLIKELY(scriptListOffset < headerSize || scriptListOffset >= table.size))
2188 return trace.fail("Invalid ScriptList offset [%u]\n", scriptListOffset);
2189
2190 if (BL_UNLIKELY(!checkTagRef16Array(self, trace, blFontSubTable(table, scriptListOffset), "ScriptList")))
2191 return false;
2192 }
2193
2194 // --------------------------------------------------------------------------
2195 // [Validate Tables]
2196 // --------------------------------------------------------------------------
2197
2198 if (lookupListOffset) {
2199 BLFontTableT<Array16<Offset16>> lookupListOffsets { blFontSubTable(table, lookupListOffset) };
2200 uint32_t count = lookupListOffsets->count();
2201
2202 if (count) {
2203 const Offset16* array = lookupListOffsets->array();
2204 for (uint32_t i = 0; i < count; i++) {
2205 BLFontTableT<GAnyTable::LookupTable> lookupTable { blFontSubTable(lookupListOffsets, array[i].value()) };
2206 if (!checkLookupTable(self, trace, kind, lookupTable, i))
2207 return false;
2208 }
2209
2210 faceI->layout.kinds[kind].lookupCount = uint16_t(count);
2211 faceI->layout.kinds[kind].lookupListOffset = uint16_t(lookupListOffset);
2212 }
2213 }
2214
2215 if (featureListOffset) {
2216 BLFontTableT<Array16<TagRef16>> featureListOffsets { blFontSubTable(table, featureListOffset) };
2217 uint32_t count = featureListOffsets->count();
2218
2219 if (count) {
2220 const TagRef16* array = featureListOffsets->array();
2221 for (uint32_t i = 0; i < count; i++) {
2222 uint32_t featureTag = array[i].tag();
2223 BLFontTableT<GAnyTable::FeatureTable> featureTable { blFontSubTable(featureListOffsets, array[i].offset()) };
2224
2225 if (!checkFeatureTable(self, trace, kind, featureTable, i, featureTag))
2226 return false;
2227
2228 if (self->featureTags.append(featureTag) != BL_SUCCESS)
2229 return false;
2230 }
2231
2232 faceI->layout.kinds[kind].featureCount = uint16_t(count);
2233 faceI->layout.kinds[kind].featureListOffset = uint16_t(featureListOffset);
2234 }
2235 }
2236
2237 if (scriptListOffset) {
2238 BLFontTableT<Array16<TagRef16>> scriptListOffsets { blFontSubTable(table, scriptListOffset) };
2239 uint32_t count = scriptListOffsets->count();
2240
2241 if (count) {
2242 const TagRef16* array = scriptListOffsets->array();
2243 for (uint32_t i = 0; i < count; i++) {
2244 uint32_t scriptTag = array[i].tag();
2245 BLFontTableT<GAnyTable::ScriptTable> scriptTable { blFontSubTable(scriptListOffsets, array[i].offset()) };
2246
2247 if (!checkScriptTable(self, trace, kind, scriptTable, i, scriptTag))
2248 return false;
2249
2250 if (self->scriptTags.append(scriptTag) != BL_SUCCESS)
2251 return false;
2252 }
2253
2254 faceI->layout.kinds[kind].scriptListOffset = uint16_t(scriptListOffset);
2255 }
2256 }
2257
2258 return true;
2259 }
2260
2261 // ============================================================================
2262 // [BLOpenType::LayoutImpl - Apply]
2263 // ============================================================================
2264
applyLookup(const BLOTFaceImpl * faceI,GSubContext & ctx,BLFontTable table,uint32_t lookupId,uint32_t lookupFlags)2265 static BL_INLINE BLResult applyLookup(const BLOTFaceImpl* faceI, GSubContext& ctx, BLFontTable table, uint32_t lookupId, uint32_t lookupFlags) noexcept {
2266 return applyGSubLookup(faceI, ctx, table, lookupId, lookupFlags);
2267 }
2268
applyLookup(const BLOTFaceImpl * faceI,GPosContext & ctx,BLFontTable table,uint32_t lookupId,uint32_t lookupFlags)2269 static BL_INLINE BLResult applyLookup(const BLOTFaceImpl* faceI, GPosContext& ctx, BLFontTable table, uint32_t lookupId, uint32_t lookupFlags) noexcept {
2270 return applyGPosLookup(faceI, ctx, table, lookupId, lookupFlags);
2271 }
2272
2273 template<uint32_t Kind, typename Context>
applyLookups(const BLFontFaceImpl * faceI_,BLGlyphBuffer * gb,size_t index,BLBitWord lookups)2274 static BLResult BL_CDECL applyLookups(const BLFontFaceImpl* faceI_, BLGlyphBuffer* gb, size_t index, BLBitWord lookups) noexcept {
2275 constexpr bool kIsGSub = (Kind == LookupInfo::kKindGSub);
2276
2277 constexpr uint32_t kLookupCount = kIsGSub ? uint32_t(GSubTable::kLookupCount ) : uint32_t(GPosTable::kLookupCount );
2278 constexpr uint32_t kLookupExtension = kIsGSub ? uint32_t(GSubTable::kLookupExtension) : uint32_t(GPosTable::kLookupExtension);
2279
2280 const BLOTFaceImpl* faceI = static_cast<const BLOTFaceImpl*>(faceI_);
2281 BLFontTable table = faceI->layout.tables[Kind];
2282 size_t lookupListOffset = faceI->layout.kinds[Kind].lookupListOffset;
2283
2284 BLFontTableT<Array16<UInt16>> lookupListTable { blFontSubTable(table, lookupListOffset) };
2285 size_t lookupListEndMinus6 = lookupListTable.size - 6u;
2286 size_t lookupListTableCount = faceI->layout.kinds[Kind].lookupCount;
2287
2288 Context ctx;
2289 ctx.init(blInternalCast(gb->impl));
2290
2291 BLBitWordIterator<BLBitWord> it(lookups);
2292 while (it.hasNext()) {
2293 size_t lookupTableIndex = it.next() + index;
2294 if (BL_UNLIKELY(lookupTableIndex >= lookupListTableCount))
2295 return blTraceError(BL_ERROR_INVALID_VALUE);
2296
2297 size_t lookupTableOffset = lookupListTable->array()[lookupTableIndex].value();
2298 if (BL_UNLIKELY(lookupTableOffset > lookupListEndMinus6))
2299 continue;
2300
2301 BLFontTableT<GAnyTable::LookupTable> lookupTable { blFontSubTable(lookupListTable, lookupTableOffset) };
2302 uint32_t lookupType = lookupTable->lookupType();
2303 uint32_t lookupFlags = lookupTable->lookupFlags();
2304
2305 if (BL_UNLIKELY(lookupType - 1u >= kLookupCount))
2306 continue;
2307
2308 uint32_t lookupEntryCount = lookupTable->lookupOffsets.count();
2309 const Offset16* lookupEntryOffsets = lookupTable->lookupOffsets.array();
2310
2311 const LookupInfo::TypeEntry& lookupTypeInfo = gLookupInfo[Kind].typeEntries[lookupType];
2312 size_t lookupTableMinSize = lookupType == kLookupExtension ? 8u : 6u;
2313 size_t lookupTableEnd = lookupTable.size - lookupTableMinSize;
2314
2315 // If this doesn't pass it means that the index is out of range.
2316 if (BL_UNLIKELY(lookupTable.size < lookupTableMinSize + lookupEntryCount * 2u))
2317 continue;
2318
2319 for (uint32_t j = 0; j < lookupEntryCount; j++) {
2320 uint32_t lookupOffset = lookupEntryOffsets[j].value();
2321 if (BL_UNLIKELY(lookupOffset > lookupTableEnd))
2322 continue;
2323
2324 BLFontTableT<GAnyTable::LookupHeader> lookupHeader { blFontSubTable(lookupTable, lookupOffset) };
2325 uint32_t lookupFormat = lookupHeader->format();
2326
2327 if (BL_UNLIKELY(lookupFormat - 1u >= lookupTypeInfo.formatCount))
2328 continue;
2329
2330 uint32_t lookupId = lookupTypeInfo.lookupIdIndex + lookupFormat - 1u;
2331 if (lookupType == kLookupExtension) {
2332 BLFontTableT<GAnyTable::ExtensionLookup> extensionTable { blFontSubTable(lookupTable, lookupOffset) };
2333
2334 uint32_t extensionLookupType = extensionTable->lookupType();
2335 uint32_t extensionOffset = extensionTable->offset();
2336
2337 if (BL_UNLIKELY(extensionLookupType - 1u >= kLookupCount || extensionOffset > extensionTable.size - 6u))
2338 continue;
2339
2340 lookupHeader = blFontSubTable(extensionTable, extensionOffset);
2341 lookupFormat = lookupHeader->format();
2342 const LookupInfo::TypeEntry& extensionLookupTypeInfo = gLookupInfo[Kind].typeEntries[extensionLookupType];
2343
2344 if (BL_UNLIKELY(lookupFormat - 1u >= extensionLookupTypeInfo.formatCount))
2345 continue;
2346
2347 lookupId = extensionLookupTypeInfo.lookupIdIndex + lookupFormat - 1u;
2348 }
2349
2350 BL_PROPAGATE(applyLookup(faceI, ctx, lookupHeader, lookupId, lookupFlags));
2351 }
2352 }
2353
2354 ctx.done();
2355
2356 return BL_SUCCESS;
2357 }
2358
2359 // ============================================================================
2360 // [BLOpenType::LayoutImpl - Init]
2361 // ============================================================================
2362
init(BLOTFaceImpl * faceI,const BLFontData * fontData)2363 BLResult init(BLOTFaceImpl* faceI, const BLFontData* fontData) noexcept {
2364 Trace trace;
2365 Validator validator(faceI);
2366
2367 static const BLTag tableTags[3] = {
2368 BL_MAKE_TAG('G', 'S', 'U', 'B'),
2369 BL_MAKE_TAG('G', 'P', 'O', 'S'),
2370 BL_MAKE_TAG('G', 'D', 'E', 'F')
2371 };
2372
2373 if (!fontData->queryTables(faceI->faceInfo.faceIndex, validator.tables, tableTags, 3))
2374 return BL_SUCCESS;
2375
2376 if (validator.gdef.data) {
2377 if (BL_UNLIKELY(!checkGDefTable(&validator, trace))) {
2378 faceI->faceInfo.diagFlags |= BL_FONT_FACE_DIAG_WRONG_GDEF_DATA;
2379 return BL_SUCCESS;
2380 }
2381 faceI->layout.tables[2] = validator.tables[2];
2382 }
2383
2384 if (validator.gsub.data) {
2385 if (BL_UNLIKELY(!checkGPosGSubTable(&validator, trace, LookupInfo::kKindGSub))) {
2386 faceI->faceInfo.diagFlags |= BL_FONT_FACE_DIAG_WRONG_GSUB_DATA;
2387 return BL_SUCCESS;
2388 }
2389
2390 if (faceI->layout.gsub.lookupCount)
2391 faceI->funcs.applyGSub = applyLookups<LookupInfo::kKindGSub, GSubContext>;
2392 faceI->layout.tables[0] = validator.tables[0];
2393 }
2394
2395 if (validator.gpos.data) {
2396 if (BL_UNLIKELY(!checkGPosGSubTable(&validator, trace, LookupInfo::kKindGPos))) {
2397 faceI->faceInfo.diagFlags |= BL_FONT_FACE_DIAG_WRONG_GPOS_DATA;
2398 return BL_SUCCESS;
2399 }
2400
2401 if (faceI->layout.gpos.lookupCount)
2402 faceI->funcs.applyGPos = applyLookups<LookupInfo::kKindGPos, GPosContext>;
2403 faceI->layout.tables[1] = validator.tables[1];
2404 }
2405
2406 faceI->scriptTags = validator.scriptTags;
2407 faceI->featureTags = validator.featureTags;
2408 return BL_SUCCESS;
2409 }
2410
2411 } // {LayoutImpl}
2412 } // {BLOpenType}
2413
2414 BL_DIAGNOSTIC_POP
2415