1 | /**************************************************************************** |
2 | * |
3 | * otvgsub.c |
4 | * |
5 | * OpenType GSUB table validation (body). |
6 | * |
7 | * Copyright (C) 2004-2023 by |
8 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
9 | * |
10 | * This file is part of the FreeType project, and may only be used, |
11 | * modified, and distributed under the terms of the FreeType project |
12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
13 | * this file you indicate that you have read the license and |
14 | * understand and accept it fully. |
15 | * |
16 | */ |
17 | |
18 | |
19 | #include "otvalid.h" |
20 | #include "otvcommn.h" |
21 | |
22 | |
23 | /************************************************************************** |
24 | * |
25 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
26 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
27 | * messages during execution. |
28 | */ |
29 | #undef FT_COMPONENT |
30 | #define FT_COMPONENT otvgsub |
31 | |
32 | |
33 | /*************************************************************************/ |
34 | /*************************************************************************/ |
35 | /***** *****/ |
36 | /***** GSUB LOOKUP TYPE 1 *****/ |
37 | /***** *****/ |
38 | /*************************************************************************/ |
39 | /*************************************************************************/ |
40 | |
41 | /* uses otvalid->glyph_count */ |
42 | |
43 | static void |
44 | otv_SingleSubst_validate( FT_Bytes table, |
45 | OTV_Validator otvalid ) |
46 | { |
47 | FT_Bytes p = table; |
48 | FT_UInt SubstFormat; |
49 | |
50 | |
51 | OTV_NAME_ENTER( "SingleSubst" ); |
52 | |
53 | OTV_LIMIT_CHECK( 2 ); |
54 | SubstFormat = FT_NEXT_USHORT( p ); |
55 | |
56 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
57 | |
58 | switch ( SubstFormat ) |
59 | { |
60 | case 1: /* SingleSubstFormat1 */ |
61 | { |
62 | FT_Bytes Coverage; |
63 | FT_Int DeltaGlyphID; |
64 | FT_UInt first_cov, last_cov; |
65 | FT_UInt first_idx, last_idx; |
66 | |
67 | |
68 | OTV_LIMIT_CHECK( 4 ); |
69 | Coverage = table + FT_NEXT_USHORT( p ); |
70 | DeltaGlyphID = FT_NEXT_SHORT( p ); |
71 | |
72 | otv_Coverage_validate( Coverage, otvalid, -1 ); |
73 | |
74 | first_cov = otv_Coverage_get_first( Coverage ); |
75 | last_cov = otv_Coverage_get_last( Coverage ); |
76 | |
77 | /* These additions are modulo 65536. */ |
78 | first_idx = (FT_UInt)( (FT_Int)first_cov + DeltaGlyphID ) & 0xFFFFU; |
79 | last_idx = (FT_UInt)( (FT_Int)last_cov + DeltaGlyphID ) & 0xFFFFU; |
80 | |
81 | /* Since the maximum number of glyphs is 2^16 - 1 = 65535, */ |
82 | /* the largest possible glyph index is 65534. For this */ |
83 | /* reason there can't be a wrap-around region, which would */ |
84 | /* imply the use of the invalid glyph index 65535. */ |
85 | if ( first_idx > last_idx ) |
86 | FT_INVALID_DATA; |
87 | |
88 | if ( last_idx >= otvalid->glyph_count ) |
89 | FT_INVALID_DATA; |
90 | } |
91 | break; |
92 | |
93 | case 2: /* SingleSubstFormat2 */ |
94 | { |
95 | FT_UInt Coverage, GlyphCount; |
96 | |
97 | |
98 | OTV_LIMIT_CHECK( 4 ); |
99 | Coverage = FT_NEXT_USHORT( p ); |
100 | GlyphCount = FT_NEXT_USHORT( p ); |
101 | |
102 | OTV_TRACE(( " (GlyphCount = %d)\n" , GlyphCount )); |
103 | |
104 | otv_Coverage_validate( table + Coverage, |
105 | otvalid, |
106 | (FT_Int)GlyphCount ); |
107 | |
108 | OTV_LIMIT_CHECK( GlyphCount * 2 ); |
109 | |
110 | /* Substitute */ |
111 | for ( ; GlyphCount > 0; GlyphCount-- ) |
112 | if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) |
113 | FT_INVALID_GLYPH_ID; |
114 | } |
115 | break; |
116 | |
117 | default: |
118 | FT_INVALID_FORMAT; |
119 | } |
120 | |
121 | OTV_EXIT; |
122 | } |
123 | |
124 | |
125 | /*************************************************************************/ |
126 | /*************************************************************************/ |
127 | /***** *****/ |
128 | /***** GSUB LOOKUP TYPE 2 *****/ |
129 | /***** *****/ |
130 | /*************************************************************************/ |
131 | /*************************************************************************/ |
132 | |
133 | /* sets otvalid->extra1 (glyph count) */ |
134 | |
135 | static void |
136 | otv_MultipleSubst_validate( FT_Bytes table, |
137 | OTV_Validator otvalid ) |
138 | { |
139 | FT_Bytes p = table; |
140 | FT_UInt SubstFormat; |
141 | |
142 | |
143 | OTV_NAME_ENTER( "MultipleSubst" ); |
144 | |
145 | OTV_LIMIT_CHECK( 2 ); |
146 | SubstFormat = FT_NEXT_USHORT( p ); |
147 | |
148 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
149 | |
150 | switch ( SubstFormat ) |
151 | { |
152 | case 1: |
153 | otvalid->extra1 = otvalid->glyph_count; |
154 | OTV_NEST2( MultipleSubstFormat1, Sequence ); |
155 | OTV_RUN( table, otvalid ); |
156 | break; |
157 | |
158 | default: |
159 | FT_INVALID_FORMAT; |
160 | } |
161 | |
162 | OTV_EXIT; |
163 | } |
164 | |
165 | |
166 | /*************************************************************************/ |
167 | /*************************************************************************/ |
168 | /***** *****/ |
169 | /***** GSUB LOOKUP TYPE 3 *****/ |
170 | /***** *****/ |
171 | /*************************************************************************/ |
172 | /*************************************************************************/ |
173 | |
174 | /* sets otvalid->extra1 (glyph count) */ |
175 | |
176 | static void |
177 | otv_AlternateSubst_validate( FT_Bytes table, |
178 | OTV_Validator otvalid ) |
179 | { |
180 | FT_Bytes p = table; |
181 | FT_UInt SubstFormat; |
182 | |
183 | |
184 | OTV_NAME_ENTER( "AlternateSubst" ); |
185 | |
186 | OTV_LIMIT_CHECK( 2 ); |
187 | SubstFormat = FT_NEXT_USHORT( p ); |
188 | |
189 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
190 | |
191 | switch ( SubstFormat ) |
192 | { |
193 | case 1: |
194 | otvalid->extra1 = otvalid->glyph_count; |
195 | OTV_NEST2( AlternateSubstFormat1, AlternateSet ); |
196 | OTV_RUN( table, otvalid ); |
197 | break; |
198 | |
199 | default: |
200 | FT_INVALID_FORMAT; |
201 | } |
202 | |
203 | OTV_EXIT; |
204 | } |
205 | |
206 | |
207 | /*************************************************************************/ |
208 | /*************************************************************************/ |
209 | /***** *****/ |
210 | /***** GSUB LOOKUP TYPE 4 *****/ |
211 | /***** *****/ |
212 | /*************************************************************************/ |
213 | /*************************************************************************/ |
214 | |
215 | #define LigatureFunc otv_Ligature_validate |
216 | |
217 | /* uses otvalid->glyph_count */ |
218 | |
219 | static void |
220 | otv_Ligature_validate( FT_Bytes table, |
221 | OTV_Validator otvalid ) |
222 | { |
223 | FT_Bytes p = table; |
224 | FT_UInt LigatureGlyph, CompCount; |
225 | |
226 | |
227 | OTV_ENTER; |
228 | |
229 | OTV_LIMIT_CHECK( 4 ); |
230 | LigatureGlyph = FT_NEXT_USHORT( p ); |
231 | if ( LigatureGlyph >= otvalid->glyph_count ) |
232 | FT_INVALID_DATA; |
233 | |
234 | CompCount = FT_NEXT_USHORT( p ); |
235 | |
236 | OTV_TRACE(( " (CompCount = %d)\n" , CompCount )); |
237 | |
238 | if ( CompCount == 0 ) |
239 | FT_INVALID_DATA; |
240 | |
241 | CompCount--; |
242 | |
243 | OTV_LIMIT_CHECK( CompCount * 2 ); /* Component */ |
244 | |
245 | /* no need to check the Component glyph indices */ |
246 | |
247 | OTV_EXIT; |
248 | } |
249 | |
250 | |
251 | static void |
252 | otv_LigatureSubst_validate( FT_Bytes table, |
253 | OTV_Validator otvalid ) |
254 | { |
255 | FT_Bytes p = table; |
256 | FT_UInt SubstFormat; |
257 | |
258 | |
259 | OTV_NAME_ENTER( "LigatureSubst" ); |
260 | |
261 | OTV_LIMIT_CHECK( 2 ); |
262 | SubstFormat = FT_NEXT_USHORT( p ); |
263 | |
264 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
265 | |
266 | switch ( SubstFormat ) |
267 | { |
268 | case 1: |
269 | OTV_NEST3( LigatureSubstFormat1, LigatureSet, Ligature ); |
270 | OTV_RUN( table, otvalid ); |
271 | break; |
272 | |
273 | default: |
274 | FT_INVALID_FORMAT; |
275 | } |
276 | |
277 | OTV_EXIT; |
278 | } |
279 | |
280 | |
281 | /*************************************************************************/ |
282 | /*************************************************************************/ |
283 | /***** *****/ |
284 | /***** GSUB LOOKUP TYPE 5 *****/ |
285 | /***** *****/ |
286 | /*************************************************************************/ |
287 | /*************************************************************************/ |
288 | |
289 | /* sets otvalid->extra1 (lookup count) */ |
290 | |
291 | static void |
292 | otv_ContextSubst_validate( FT_Bytes table, |
293 | OTV_Validator otvalid ) |
294 | { |
295 | FT_Bytes p = table; |
296 | FT_UInt SubstFormat; |
297 | |
298 | |
299 | OTV_NAME_ENTER( "ContextSubst" ); |
300 | |
301 | OTV_LIMIT_CHECK( 2 ); |
302 | SubstFormat = FT_NEXT_USHORT( p ); |
303 | |
304 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
305 | |
306 | switch ( SubstFormat ) |
307 | { |
308 | case 1: |
309 | /* no need to check glyph indices/classes used as input for these */ |
310 | /* context rules since even invalid glyph indices/classes return */ |
311 | /* meaningful results */ |
312 | |
313 | otvalid->extra1 = otvalid->lookup_count; |
314 | OTV_NEST3( ContextSubstFormat1, SubRuleSet, SubRule ); |
315 | OTV_RUN( table, otvalid ); |
316 | break; |
317 | |
318 | case 2: |
319 | /* no need to check glyph indices/classes used as input for these */ |
320 | /* context rules since even invalid glyph indices/classes return */ |
321 | /* meaningful results */ |
322 | |
323 | OTV_NEST3( ContextSubstFormat2, SubClassSet, SubClassRule ); |
324 | OTV_RUN( table, otvalid ); |
325 | break; |
326 | |
327 | case 3: |
328 | OTV_NEST1( ContextSubstFormat3 ); |
329 | OTV_RUN( table, otvalid ); |
330 | break; |
331 | |
332 | default: |
333 | FT_INVALID_FORMAT; |
334 | } |
335 | |
336 | OTV_EXIT; |
337 | } |
338 | |
339 | |
340 | /*************************************************************************/ |
341 | /*************************************************************************/ |
342 | /***** *****/ |
343 | /***** GSUB LOOKUP TYPE 6 *****/ |
344 | /***** *****/ |
345 | /*************************************************************************/ |
346 | /*************************************************************************/ |
347 | |
348 | /* sets otvalid->extra1 (lookup count) */ |
349 | |
350 | static void |
351 | otv_ChainContextSubst_validate( FT_Bytes table, |
352 | OTV_Validator otvalid ) |
353 | { |
354 | FT_Bytes p = table; |
355 | FT_UInt SubstFormat; |
356 | |
357 | |
358 | OTV_NAME_ENTER( "ChainContextSubst" ); |
359 | |
360 | OTV_LIMIT_CHECK( 2 ); |
361 | SubstFormat = FT_NEXT_USHORT( p ); |
362 | |
363 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
364 | |
365 | switch ( SubstFormat ) |
366 | { |
367 | case 1: |
368 | /* no need to check glyph indices/classes used as input for these */ |
369 | /* context rules since even invalid glyph indices/classes return */ |
370 | /* meaningful results */ |
371 | |
372 | otvalid->extra1 = otvalid->lookup_count; |
373 | OTV_NEST3( ChainContextSubstFormat1, |
374 | ChainSubRuleSet, ChainSubRule ); |
375 | OTV_RUN( table, otvalid ); |
376 | break; |
377 | |
378 | case 2: |
379 | /* no need to check glyph indices/classes used as input for these */ |
380 | /* context rules since even invalid glyph indices/classes return */ |
381 | /* meaningful results */ |
382 | |
383 | OTV_NEST3( ChainContextSubstFormat2, |
384 | ChainSubClassSet, ChainSubClassRule ); |
385 | OTV_RUN( table, otvalid ); |
386 | break; |
387 | |
388 | case 3: |
389 | OTV_NEST1( ChainContextSubstFormat3 ); |
390 | OTV_RUN( table, otvalid ); |
391 | break; |
392 | |
393 | default: |
394 | FT_INVALID_FORMAT; |
395 | } |
396 | |
397 | OTV_EXIT; |
398 | } |
399 | |
400 | |
401 | /*************************************************************************/ |
402 | /*************************************************************************/ |
403 | /***** *****/ |
404 | /***** GSUB LOOKUP TYPE 7 *****/ |
405 | /***** *****/ |
406 | /*************************************************************************/ |
407 | /*************************************************************************/ |
408 | |
409 | /* uses otvalid->type_funcs */ |
410 | |
411 | static void |
412 | otv_ExtensionSubst_validate( FT_Bytes table, |
413 | OTV_Validator otvalid ) |
414 | { |
415 | FT_Bytes p = table; |
416 | FT_UInt SubstFormat; |
417 | |
418 | |
419 | OTV_NAME_ENTER( "ExtensionSubst" ); |
420 | |
421 | OTV_LIMIT_CHECK( 2 ); |
422 | SubstFormat = FT_NEXT_USHORT( p ); |
423 | |
424 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
425 | |
426 | switch ( SubstFormat ) |
427 | { |
428 | case 1: /* ExtensionSubstFormat1 */ |
429 | { |
430 | FT_UInt ExtensionLookupType; |
431 | FT_ULong ExtensionOffset; |
432 | OTV_Validate_Func validate; |
433 | |
434 | |
435 | OTV_LIMIT_CHECK( 6 ); |
436 | ExtensionLookupType = FT_NEXT_USHORT( p ); |
437 | ExtensionOffset = FT_NEXT_ULONG( p ); |
438 | |
439 | if ( ExtensionLookupType == 0 || |
440 | ExtensionLookupType == 7 || |
441 | ExtensionLookupType > 8 ) |
442 | FT_INVALID_DATA; |
443 | |
444 | validate = otvalid->type_funcs[ExtensionLookupType - 1]; |
445 | validate( table + ExtensionOffset, otvalid ); |
446 | } |
447 | break; |
448 | |
449 | default: |
450 | FT_INVALID_FORMAT; |
451 | } |
452 | |
453 | OTV_EXIT; |
454 | } |
455 | |
456 | |
457 | /*************************************************************************/ |
458 | /*************************************************************************/ |
459 | /***** *****/ |
460 | /***** GSUB LOOKUP TYPE 8 *****/ |
461 | /***** *****/ |
462 | /*************************************************************************/ |
463 | /*************************************************************************/ |
464 | |
465 | /* uses otvalid->glyph_count */ |
466 | |
467 | static void |
468 | otv_ReverseChainSingleSubst_validate( FT_Bytes table, |
469 | OTV_Validator otvalid ) |
470 | { |
471 | FT_Bytes p = table, Coverage; |
472 | FT_UInt SubstFormat; |
473 | FT_UInt BacktrackGlyphCount, LookaheadGlyphCount, GlyphCount; |
474 | |
475 | |
476 | OTV_NAME_ENTER( "ReverseChainSingleSubst" ); |
477 | |
478 | OTV_LIMIT_CHECK( 2 ); |
479 | SubstFormat = FT_NEXT_USHORT( p ); |
480 | |
481 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
482 | |
483 | switch ( SubstFormat ) |
484 | { |
485 | case 1: /* ReverseChainSingleSubstFormat1 */ |
486 | OTV_LIMIT_CHECK( 4 ); |
487 | Coverage = table + FT_NEXT_USHORT( p ); |
488 | BacktrackGlyphCount = FT_NEXT_USHORT( p ); |
489 | |
490 | OTV_TRACE(( " (BacktrackGlyphCount = %d)\n" , BacktrackGlyphCount )); |
491 | |
492 | otv_Coverage_validate( Coverage, otvalid, -1 ); |
493 | |
494 | OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 ); |
495 | |
496 | for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- ) |
497 | otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); |
498 | |
499 | LookaheadGlyphCount = FT_NEXT_USHORT( p ); |
500 | |
501 | OTV_TRACE(( " (LookaheadGlyphCount = %d)\n" , LookaheadGlyphCount )); |
502 | |
503 | OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 ); |
504 | |
505 | for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- ) |
506 | otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); |
507 | |
508 | GlyphCount = FT_NEXT_USHORT( p ); |
509 | |
510 | OTV_TRACE(( " (GlyphCount = %d)\n" , GlyphCount )); |
511 | |
512 | if ( GlyphCount != otv_Coverage_get_count( Coverage ) ) |
513 | FT_INVALID_DATA; |
514 | |
515 | OTV_LIMIT_CHECK( GlyphCount * 2 ); |
516 | |
517 | /* Substitute */ |
518 | for ( ; GlyphCount > 0; GlyphCount-- ) |
519 | if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) |
520 | FT_INVALID_DATA; |
521 | |
522 | break; |
523 | |
524 | default: |
525 | FT_INVALID_FORMAT; |
526 | } |
527 | |
528 | OTV_EXIT; |
529 | } |
530 | |
531 | |
532 | static const OTV_Validate_Func otv_gsub_validate_funcs[8] = |
533 | { |
534 | otv_SingleSubst_validate, |
535 | otv_MultipleSubst_validate, |
536 | otv_AlternateSubst_validate, |
537 | otv_LigatureSubst_validate, |
538 | otv_ContextSubst_validate, |
539 | otv_ChainContextSubst_validate, |
540 | otv_ExtensionSubst_validate, |
541 | otv_ReverseChainSingleSubst_validate |
542 | }; |
543 | |
544 | |
545 | /*************************************************************************/ |
546 | /*************************************************************************/ |
547 | /***** *****/ |
548 | /***** GSUB TABLE *****/ |
549 | /***** *****/ |
550 | /*************************************************************************/ |
551 | /*************************************************************************/ |
552 | |
553 | /* sets otvalid->type_count */ |
554 | /* sets otvalid->type_funcs */ |
555 | /* sets otvalid->glyph_count */ |
556 | |
557 | FT_LOCAL_DEF( void ) |
558 | otv_GSUB_validate( FT_Bytes table, |
559 | FT_UInt glyph_count, |
560 | FT_Validator ftvalid ) |
561 | { |
562 | OTV_ValidatorRec otvalidrec; |
563 | OTV_Validator otvalid = &otvalidrec; |
564 | FT_Bytes p = table; |
565 | FT_UInt table_size; |
566 | FT_UShort version; |
567 | FT_UInt ScriptList, FeatureList, LookupList; |
568 | |
569 | OTV_OPTIONAL_TABLE32( featureVariations ); |
570 | |
571 | |
572 | otvalid->root = ftvalid; |
573 | |
574 | FT_TRACE3(( "validating GSUB table\n" )); |
575 | OTV_INIT; |
576 | |
577 | OTV_LIMIT_CHECK( 4 ); |
578 | |
579 | if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */ |
580 | FT_INVALID_FORMAT; |
581 | |
582 | version = FT_NEXT_USHORT( p ); /* minorVersion */ |
583 | |
584 | table_size = 10; |
585 | switch ( version ) |
586 | { |
587 | case 0: |
588 | OTV_LIMIT_CHECK( 6 ); |
589 | break; |
590 | |
591 | case 1: |
592 | OTV_LIMIT_CHECK( 10 ); |
593 | table_size += 4; |
594 | break; |
595 | |
596 | default: |
597 | FT_INVALID_FORMAT; |
598 | } |
599 | |
600 | ScriptList = FT_NEXT_USHORT( p ); |
601 | FeatureList = FT_NEXT_USHORT( p ); |
602 | LookupList = FT_NEXT_USHORT( p ); |
603 | |
604 | otvalid->type_count = 8; |
605 | otvalid->type_funcs = (OTV_Validate_Func*)otv_gsub_validate_funcs; |
606 | otvalid->glyph_count = glyph_count; |
607 | |
608 | otv_LookupList_validate( table + LookupList, |
609 | otvalid ); |
610 | otv_FeatureList_validate( table + FeatureList, table + LookupList, |
611 | otvalid ); |
612 | otv_ScriptList_validate( table + ScriptList, table + FeatureList, |
613 | otvalid ); |
614 | |
615 | if ( version > 0 ) |
616 | { |
617 | OTV_OPTIONAL_OFFSET32( featureVariations ); |
618 | OTV_SIZE_CHECK32( featureVariations ); |
619 | if ( featureVariations ) |
620 | OTV_TRACE(( " [omitting featureVariations validation]\n" )); /* XXX */ |
621 | } |
622 | |
623 | FT_TRACE4(( "\n" )); |
624 | } |
625 | |
626 | |
627 | /* END */ |
628 | |