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