1 | /**************************************************************************** |
2 | * |
3 | * otvgpos.c |
4 | * |
5 | * OpenType GPOS table validation (body). |
6 | * |
7 | * Copyright (C) 2002-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 | #include "otvgpos.h" |
22 | |
23 | |
24 | /************************************************************************** |
25 | * |
26 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
27 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
28 | * messages during execution. |
29 | */ |
30 | #undef FT_COMPONENT |
31 | #define FT_COMPONENT otvgpos |
32 | |
33 | |
34 | static void |
35 | otv_Anchor_validate( FT_Bytes table, |
36 | OTV_Validator valid ); |
37 | |
38 | static void |
39 | otv_MarkArray_validate( FT_Bytes table, |
40 | OTV_Validator valid ); |
41 | |
42 | |
43 | /*************************************************************************/ |
44 | /*************************************************************************/ |
45 | /***** *****/ |
46 | /***** UTILITY FUNCTIONS *****/ |
47 | /***** *****/ |
48 | /*************************************************************************/ |
49 | /*************************************************************************/ |
50 | |
51 | #define BaseArrayFunc otv_x_sxy |
52 | #define LigatureAttachFunc otv_x_sxy |
53 | #define Mark2ArrayFunc otv_x_sxy |
54 | |
55 | /* uses valid->extra1 (counter) */ |
56 | /* uses valid->extra2 (boolean to handle NULL anchor field) */ |
57 | |
58 | static void |
59 | otv_x_sxy( FT_Bytes table, |
60 | OTV_Validator otvalid ) |
61 | { |
62 | FT_Bytes p = table; |
63 | FT_UInt Count, count1, table_size; |
64 | |
65 | |
66 | OTV_ENTER; |
67 | |
68 | OTV_LIMIT_CHECK( 2 ); |
69 | |
70 | Count = FT_NEXT_USHORT( p ); |
71 | |
72 | OTV_TRACE(( " (Count = %d)\n" , Count )); |
73 | |
74 | OTV_LIMIT_CHECK( Count * otvalid->extra1 * 2 ); |
75 | |
76 | table_size = Count * otvalid->extra1 * 2 + 2; |
77 | |
78 | for ( ; Count > 0; Count-- ) |
79 | for ( count1 = otvalid->extra1; count1 > 0; count1-- ) |
80 | { |
81 | OTV_OPTIONAL_TABLE( anchor_offset ); |
82 | |
83 | |
84 | OTV_OPTIONAL_OFFSET( anchor_offset ); |
85 | |
86 | if ( otvalid->extra2 ) |
87 | { |
88 | OTV_SIZE_CHECK( anchor_offset ); |
89 | if ( anchor_offset ) |
90 | otv_Anchor_validate( table + anchor_offset, otvalid ); |
91 | } |
92 | else |
93 | otv_Anchor_validate( table + anchor_offset, otvalid ); |
94 | } |
95 | |
96 | OTV_EXIT; |
97 | } |
98 | |
99 | |
100 | #define MarkBasePosFormat1Func otv_u_O_O_u_O_O |
101 | #define MarkLigPosFormat1Func otv_u_O_O_u_O_O |
102 | #define MarkMarkPosFormat1Func otv_u_O_O_u_O_O |
103 | |
104 | /* sets otvalid->extra1 (class count) */ |
105 | |
106 | static void |
107 | otv_u_O_O_u_O_O( FT_Bytes table, |
108 | OTV_Validator otvalid ) |
109 | { |
110 | FT_Bytes p = table; |
111 | FT_UInt Coverage1, Coverage2, ClassCount; |
112 | FT_UInt Array1, Array2; |
113 | OTV_Validate_Func func; |
114 | |
115 | |
116 | OTV_ENTER; |
117 | |
118 | p += 2; /* skip PosFormat */ |
119 | |
120 | OTV_LIMIT_CHECK( 10 ); |
121 | Coverage1 = FT_NEXT_USHORT( p ); |
122 | Coverage2 = FT_NEXT_USHORT( p ); |
123 | ClassCount = FT_NEXT_USHORT( p ); |
124 | Array1 = FT_NEXT_USHORT( p ); |
125 | Array2 = FT_NEXT_USHORT( p ); |
126 | |
127 | otv_Coverage_validate( table + Coverage1, otvalid, -1 ); |
128 | otv_Coverage_validate( table + Coverage2, otvalid, -1 ); |
129 | |
130 | otv_MarkArray_validate( table + Array1, otvalid ); |
131 | |
132 | otvalid->nesting_level++; |
133 | func = otvalid->func[otvalid->nesting_level]; |
134 | otvalid->extra1 = ClassCount; |
135 | |
136 | func( table + Array2, otvalid ); |
137 | |
138 | otvalid->nesting_level--; |
139 | |
140 | OTV_EXIT; |
141 | } |
142 | |
143 | |
144 | /*************************************************************************/ |
145 | /*************************************************************************/ |
146 | /***** *****/ |
147 | /***** VALUE RECORDS *****/ |
148 | /***** *****/ |
149 | /*************************************************************************/ |
150 | /*************************************************************************/ |
151 | |
152 | static FT_UInt |
153 | otv_value_length( FT_UInt format ) |
154 | { |
155 | FT_UInt count; |
156 | |
157 | |
158 | count = ( ( format & 0xAA ) >> 1 ) + ( format & 0x55 ); |
159 | count = ( ( count & 0xCC ) >> 2 ) + ( count & 0x33 ); |
160 | count = ( ( count & 0xF0 ) >> 4 ) + ( count & 0x0F ); |
161 | |
162 | return count * 2; |
163 | } |
164 | |
165 | |
166 | /* uses otvalid->extra3 (pointer to base table) */ |
167 | |
168 | static void |
169 | otv_ValueRecord_validate( FT_Bytes table, |
170 | FT_UInt format, |
171 | OTV_Validator otvalid ) |
172 | { |
173 | FT_Bytes p = table; |
174 | FT_UInt count; |
175 | |
176 | #ifdef FT_DEBUG_LEVEL_TRACE |
177 | FT_Int loop; |
178 | FT_ULong res = 0; |
179 | |
180 | |
181 | OTV_NAME_ENTER( "ValueRecord" ); |
182 | |
183 | /* display `format' in dual representation */ |
184 | for ( loop = 7; loop >= 0; loop-- ) |
185 | { |
186 | res <<= 4; |
187 | res += ( format >> loop ) & 1; |
188 | } |
189 | |
190 | OTV_TRACE(( " (format 0b%08lx)\n" , res )); |
191 | #endif |
192 | |
193 | if ( format >= 0x100 ) |
194 | FT_INVALID_FORMAT; |
195 | |
196 | for ( count = 4; count > 0; count-- ) |
197 | { |
198 | if ( format & 1 ) |
199 | { |
200 | /* XPlacement, YPlacement, XAdvance, YAdvance */ |
201 | OTV_LIMIT_CHECK( 2 ); |
202 | p += 2; |
203 | } |
204 | |
205 | format >>= 1; |
206 | } |
207 | |
208 | for ( count = 4; count > 0; count-- ) |
209 | { |
210 | if ( format & 1 ) |
211 | { |
212 | FT_PtrDist table_size; |
213 | |
214 | OTV_OPTIONAL_TABLE( device ); |
215 | |
216 | |
217 | /* XPlaDevice, YPlaDevice, XAdvDevice, YAdvDevice */ |
218 | OTV_LIMIT_CHECK( 2 ); |
219 | OTV_OPTIONAL_OFFSET( device ); |
220 | |
221 | table_size = p - otvalid->extra3; |
222 | |
223 | OTV_SIZE_CHECK( device ); |
224 | if ( device ) |
225 | otv_Device_validate( otvalid->extra3 + device, otvalid ); |
226 | } |
227 | format >>= 1; |
228 | } |
229 | |
230 | OTV_EXIT; |
231 | } |
232 | |
233 | |
234 | /*************************************************************************/ |
235 | /*************************************************************************/ |
236 | /***** *****/ |
237 | /***** ANCHORS *****/ |
238 | /***** *****/ |
239 | /*************************************************************************/ |
240 | /*************************************************************************/ |
241 | |
242 | static void |
243 | otv_Anchor_validate( FT_Bytes table, |
244 | OTV_Validator otvalid ) |
245 | { |
246 | FT_Bytes p = table; |
247 | FT_UInt AnchorFormat; |
248 | |
249 | |
250 | OTV_NAME_ENTER( "Anchor" ); |
251 | |
252 | OTV_LIMIT_CHECK( 6 ); |
253 | AnchorFormat = FT_NEXT_USHORT( p ); |
254 | |
255 | OTV_TRACE(( " (format %d)\n" , AnchorFormat )); |
256 | |
257 | p += 4; /* skip XCoordinate and YCoordinate */ |
258 | |
259 | switch ( AnchorFormat ) |
260 | { |
261 | case 1: |
262 | break; |
263 | |
264 | case 2: |
265 | OTV_LIMIT_CHECK( 2 ); /* AnchorPoint */ |
266 | break; |
267 | |
268 | case 3: |
269 | { |
270 | FT_UInt table_size; |
271 | |
272 | OTV_OPTIONAL_TABLE( XDeviceTable ); |
273 | OTV_OPTIONAL_TABLE( YDeviceTable ); |
274 | |
275 | |
276 | OTV_LIMIT_CHECK( 4 ); |
277 | OTV_OPTIONAL_OFFSET( XDeviceTable ); |
278 | OTV_OPTIONAL_OFFSET( YDeviceTable ); |
279 | |
280 | table_size = 6 + 4; |
281 | |
282 | OTV_SIZE_CHECK( XDeviceTable ); |
283 | if ( XDeviceTable ) |
284 | otv_Device_validate( table + XDeviceTable, otvalid ); |
285 | |
286 | OTV_SIZE_CHECK( YDeviceTable ); |
287 | if ( YDeviceTable ) |
288 | otv_Device_validate( table + YDeviceTable, otvalid ); |
289 | } |
290 | break; |
291 | |
292 | default: |
293 | FT_INVALID_FORMAT; |
294 | } |
295 | |
296 | OTV_EXIT; |
297 | } |
298 | |
299 | |
300 | /*************************************************************************/ |
301 | /*************************************************************************/ |
302 | /***** *****/ |
303 | /***** MARK ARRAYS *****/ |
304 | /***** *****/ |
305 | /*************************************************************************/ |
306 | /*************************************************************************/ |
307 | |
308 | static void |
309 | otv_MarkArray_validate( FT_Bytes table, |
310 | OTV_Validator otvalid ) |
311 | { |
312 | FT_Bytes p = table; |
313 | FT_UInt MarkCount; |
314 | |
315 | |
316 | OTV_NAME_ENTER( "MarkArray" ); |
317 | |
318 | OTV_LIMIT_CHECK( 2 ); |
319 | MarkCount = FT_NEXT_USHORT( p ); |
320 | |
321 | OTV_TRACE(( " (MarkCount = %d)\n" , MarkCount )); |
322 | |
323 | OTV_LIMIT_CHECK( MarkCount * 4 ); |
324 | |
325 | /* MarkRecord */ |
326 | for ( ; MarkCount > 0; MarkCount-- ) |
327 | { |
328 | p += 2; /* skip Class */ |
329 | /* MarkAnchor */ |
330 | otv_Anchor_validate( table + FT_NEXT_USHORT( p ), otvalid ); |
331 | } |
332 | |
333 | OTV_EXIT; |
334 | } |
335 | |
336 | |
337 | /*************************************************************************/ |
338 | /*************************************************************************/ |
339 | /***** *****/ |
340 | /***** GPOS LOOKUP TYPE 1 *****/ |
341 | /***** *****/ |
342 | /*************************************************************************/ |
343 | /*************************************************************************/ |
344 | |
345 | /* sets otvalid->extra3 (pointer to base table) */ |
346 | |
347 | static void |
348 | otv_SinglePos_validate( FT_Bytes table, |
349 | OTV_Validator otvalid ) |
350 | { |
351 | FT_Bytes p = table; |
352 | FT_UInt PosFormat; |
353 | |
354 | |
355 | OTV_NAME_ENTER( "SinglePos" ); |
356 | |
357 | OTV_LIMIT_CHECK( 2 ); |
358 | PosFormat = FT_NEXT_USHORT( p ); |
359 | |
360 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
361 | |
362 | otvalid->extra3 = table; |
363 | |
364 | switch ( PosFormat ) |
365 | { |
366 | case 1: /* SinglePosFormat1 */ |
367 | { |
368 | FT_UInt Coverage, ValueFormat; |
369 | |
370 | |
371 | OTV_LIMIT_CHECK( 4 ); |
372 | Coverage = FT_NEXT_USHORT( p ); |
373 | ValueFormat = FT_NEXT_USHORT( p ); |
374 | |
375 | otv_Coverage_validate( table + Coverage, otvalid, -1 ); |
376 | otv_ValueRecord_validate( p, ValueFormat, otvalid ); /* Value */ |
377 | } |
378 | break; |
379 | |
380 | case 2: /* SinglePosFormat2 */ |
381 | { |
382 | FT_UInt Coverage, ValueFormat, ValueCount, len_value; |
383 | |
384 | |
385 | OTV_LIMIT_CHECK( 6 ); |
386 | Coverage = FT_NEXT_USHORT( p ); |
387 | ValueFormat = FT_NEXT_USHORT( p ); |
388 | ValueCount = FT_NEXT_USHORT( p ); |
389 | |
390 | OTV_TRACE(( " (ValueCount = %d)\n" , ValueCount )); |
391 | |
392 | len_value = otv_value_length( ValueFormat ); |
393 | |
394 | otv_Coverage_validate( table + Coverage, |
395 | otvalid, |
396 | (FT_Int)ValueCount ); |
397 | |
398 | OTV_LIMIT_CHECK( ValueCount * len_value ); |
399 | |
400 | /* Value */ |
401 | for ( ; ValueCount > 0; ValueCount-- ) |
402 | { |
403 | otv_ValueRecord_validate( p, ValueFormat, otvalid ); |
404 | p += len_value; |
405 | } |
406 | } |
407 | break; |
408 | |
409 | default: |
410 | FT_INVALID_FORMAT; |
411 | } |
412 | |
413 | OTV_EXIT; |
414 | } |
415 | |
416 | |
417 | /*************************************************************************/ |
418 | /*************************************************************************/ |
419 | /***** *****/ |
420 | /***** GPOS LOOKUP TYPE 2 *****/ |
421 | /***** *****/ |
422 | /*************************************************************************/ |
423 | /*************************************************************************/ |
424 | |
425 | /* sets otvalid->extra3 (pointer to base table) */ |
426 | |
427 | static void |
428 | otv_PairSet_validate( FT_Bytes table, |
429 | FT_UInt format1, |
430 | FT_UInt format2, |
431 | OTV_Validator otvalid ) |
432 | { |
433 | FT_Bytes p = table; |
434 | FT_UInt value_len1, value_len2, PairValueCount; |
435 | |
436 | |
437 | OTV_NAME_ENTER( "PairSet" ); |
438 | |
439 | otvalid->extra3 = table; |
440 | |
441 | OTV_LIMIT_CHECK( 2 ); |
442 | PairValueCount = FT_NEXT_USHORT( p ); |
443 | |
444 | OTV_TRACE(( " (PairValueCount = %d)\n" , PairValueCount )); |
445 | |
446 | value_len1 = otv_value_length( format1 ); |
447 | value_len2 = otv_value_length( format2 ); |
448 | |
449 | OTV_LIMIT_CHECK( PairValueCount * ( value_len1 + value_len2 + 2 ) ); |
450 | |
451 | /* PairValueRecord */ |
452 | for ( ; PairValueCount > 0; PairValueCount-- ) |
453 | { |
454 | p += 2; /* skip SecondGlyph */ |
455 | |
456 | if ( format1 ) |
457 | otv_ValueRecord_validate( p, format1, otvalid ); /* Value1 */ |
458 | p += value_len1; |
459 | |
460 | if ( format2 ) |
461 | otv_ValueRecord_validate( p, format2, otvalid ); /* Value2 */ |
462 | p += value_len2; |
463 | } |
464 | |
465 | OTV_EXIT; |
466 | } |
467 | |
468 | |
469 | /* sets otvalid->extra3 (pointer to base table) */ |
470 | |
471 | static void |
472 | otv_PairPos_validate( FT_Bytes table, |
473 | OTV_Validator otvalid ) |
474 | { |
475 | FT_Bytes p = table; |
476 | FT_UInt PosFormat; |
477 | |
478 | |
479 | OTV_NAME_ENTER( "PairPos" ); |
480 | |
481 | OTV_LIMIT_CHECK( 2 ); |
482 | PosFormat = FT_NEXT_USHORT( p ); |
483 | |
484 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
485 | |
486 | switch ( PosFormat ) |
487 | { |
488 | case 1: /* PairPosFormat1 */ |
489 | { |
490 | FT_UInt Coverage, ValueFormat1, ValueFormat2, PairSetCount; |
491 | |
492 | |
493 | OTV_LIMIT_CHECK( 8 ); |
494 | Coverage = FT_NEXT_USHORT( p ); |
495 | ValueFormat1 = FT_NEXT_USHORT( p ); |
496 | ValueFormat2 = FT_NEXT_USHORT( p ); |
497 | PairSetCount = FT_NEXT_USHORT( p ); |
498 | |
499 | OTV_TRACE(( " (PairSetCount = %d)\n" , PairSetCount )); |
500 | |
501 | otv_Coverage_validate( table + Coverage, otvalid, -1 ); |
502 | |
503 | OTV_LIMIT_CHECK( PairSetCount * 2 ); |
504 | |
505 | /* PairSetOffset */ |
506 | for ( ; PairSetCount > 0; PairSetCount-- ) |
507 | otv_PairSet_validate( table + FT_NEXT_USHORT( p ), |
508 | ValueFormat1, ValueFormat2, otvalid ); |
509 | } |
510 | break; |
511 | |
512 | case 2: /* PairPosFormat2 */ |
513 | { |
514 | FT_UInt Coverage, ValueFormat1, ValueFormat2, ClassDef1, ClassDef2; |
515 | FT_UInt ClassCount1, ClassCount2, len_value1, len_value2, count; |
516 | |
517 | |
518 | OTV_LIMIT_CHECK( 14 ); |
519 | Coverage = FT_NEXT_USHORT( p ); |
520 | ValueFormat1 = FT_NEXT_USHORT( p ); |
521 | ValueFormat2 = FT_NEXT_USHORT( p ); |
522 | ClassDef1 = FT_NEXT_USHORT( p ); |
523 | ClassDef2 = FT_NEXT_USHORT( p ); |
524 | ClassCount1 = FT_NEXT_USHORT( p ); |
525 | ClassCount2 = FT_NEXT_USHORT( p ); |
526 | |
527 | OTV_TRACE(( " (ClassCount1 = %d)\n" , ClassCount1 )); |
528 | OTV_TRACE(( " (ClassCount2 = %d)\n" , ClassCount2 )); |
529 | |
530 | len_value1 = otv_value_length( ValueFormat1 ); |
531 | len_value2 = otv_value_length( ValueFormat2 ); |
532 | |
533 | otv_Coverage_validate( table + Coverage, otvalid, -1 ); |
534 | otv_ClassDef_validate( table + ClassDef1, otvalid ); |
535 | otv_ClassDef_validate( table + ClassDef2, otvalid ); |
536 | |
537 | OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 * |
538 | ( len_value1 + len_value2 ) ); |
539 | |
540 | otvalid->extra3 = table; |
541 | |
542 | /* Class1Record */ |
543 | for ( ; ClassCount1 > 0; ClassCount1-- ) |
544 | { |
545 | /* Class2Record */ |
546 | for ( count = ClassCount2; count > 0; count-- ) |
547 | { |
548 | if ( ValueFormat1 ) |
549 | /* Value1 */ |
550 | otv_ValueRecord_validate( p, ValueFormat1, otvalid ); |
551 | p += len_value1; |
552 | |
553 | if ( ValueFormat2 ) |
554 | /* Value2 */ |
555 | otv_ValueRecord_validate( p, ValueFormat2, otvalid ); |
556 | p += len_value2; |
557 | } |
558 | } |
559 | } |
560 | break; |
561 | |
562 | default: |
563 | FT_INVALID_FORMAT; |
564 | } |
565 | |
566 | OTV_EXIT; |
567 | } |
568 | |
569 | |
570 | /*************************************************************************/ |
571 | /*************************************************************************/ |
572 | /***** *****/ |
573 | /***** GPOS LOOKUP TYPE 3 *****/ |
574 | /***** *****/ |
575 | /*************************************************************************/ |
576 | /*************************************************************************/ |
577 | |
578 | static void |
579 | otv_CursivePos_validate( FT_Bytes table, |
580 | OTV_Validator otvalid ) |
581 | { |
582 | FT_Bytes p = table; |
583 | FT_UInt PosFormat; |
584 | |
585 | |
586 | OTV_NAME_ENTER( "CursivePos" ); |
587 | |
588 | OTV_LIMIT_CHECK( 2 ); |
589 | PosFormat = FT_NEXT_USHORT( p ); |
590 | |
591 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
592 | |
593 | switch ( PosFormat ) |
594 | { |
595 | case 1: /* CursivePosFormat1 */ |
596 | { |
597 | FT_UInt table_size; |
598 | FT_UInt Coverage, EntryExitCount; |
599 | |
600 | OTV_OPTIONAL_TABLE( EntryAnchor ); |
601 | OTV_OPTIONAL_TABLE( ExitAnchor ); |
602 | |
603 | |
604 | OTV_LIMIT_CHECK( 4 ); |
605 | Coverage = FT_NEXT_USHORT( p ); |
606 | EntryExitCount = FT_NEXT_USHORT( p ); |
607 | |
608 | OTV_TRACE(( " (EntryExitCount = %d)\n" , EntryExitCount )); |
609 | |
610 | otv_Coverage_validate( table + Coverage, |
611 | otvalid, |
612 | (FT_Int)EntryExitCount ); |
613 | |
614 | OTV_LIMIT_CHECK( EntryExitCount * 4 ); |
615 | |
616 | table_size = EntryExitCount * 4 + 4; |
617 | |
618 | /* EntryExitRecord */ |
619 | for ( ; EntryExitCount > 0; EntryExitCount-- ) |
620 | { |
621 | OTV_OPTIONAL_OFFSET( EntryAnchor ); |
622 | OTV_OPTIONAL_OFFSET( ExitAnchor ); |
623 | |
624 | OTV_SIZE_CHECK( EntryAnchor ); |
625 | if ( EntryAnchor ) |
626 | otv_Anchor_validate( table + EntryAnchor, otvalid ); |
627 | |
628 | OTV_SIZE_CHECK( ExitAnchor ); |
629 | if ( ExitAnchor ) |
630 | otv_Anchor_validate( table + ExitAnchor, otvalid ); |
631 | } |
632 | } |
633 | break; |
634 | |
635 | default: |
636 | FT_INVALID_FORMAT; |
637 | } |
638 | |
639 | OTV_EXIT; |
640 | } |
641 | |
642 | |
643 | /*************************************************************************/ |
644 | /*************************************************************************/ |
645 | /***** *****/ |
646 | /***** GPOS LOOKUP TYPE 4 *****/ |
647 | /***** *****/ |
648 | /*************************************************************************/ |
649 | /*************************************************************************/ |
650 | |
651 | /* UNDOCUMENTED (in OpenType 1.5): */ |
652 | /* BaseRecord tables can contain NULL pointers. */ |
653 | |
654 | /* sets otvalid->extra2 (1) */ |
655 | |
656 | static void |
657 | otv_MarkBasePos_validate( FT_Bytes table, |
658 | OTV_Validator otvalid ) |
659 | { |
660 | FT_Bytes p = table; |
661 | FT_UInt PosFormat; |
662 | |
663 | |
664 | OTV_NAME_ENTER( "MarkBasePos" ); |
665 | |
666 | OTV_LIMIT_CHECK( 2 ); |
667 | PosFormat = FT_NEXT_USHORT( p ); |
668 | |
669 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
670 | |
671 | switch ( PosFormat ) |
672 | { |
673 | case 1: |
674 | otvalid->extra2 = 1; |
675 | OTV_NEST2( MarkBasePosFormat1, BaseArray ); |
676 | OTV_RUN( table, otvalid ); |
677 | break; |
678 | |
679 | default: |
680 | FT_INVALID_FORMAT; |
681 | } |
682 | |
683 | OTV_EXIT; |
684 | } |
685 | |
686 | |
687 | /*************************************************************************/ |
688 | /*************************************************************************/ |
689 | /***** *****/ |
690 | /***** GPOS LOOKUP TYPE 5 *****/ |
691 | /***** *****/ |
692 | /*************************************************************************/ |
693 | /*************************************************************************/ |
694 | |
695 | /* sets otvalid->extra2 (1) */ |
696 | |
697 | static void |
698 | otv_MarkLigPos_validate( FT_Bytes table, |
699 | OTV_Validator otvalid ) |
700 | { |
701 | FT_Bytes p = table; |
702 | FT_UInt PosFormat; |
703 | |
704 | |
705 | OTV_NAME_ENTER( "MarkLigPos" ); |
706 | |
707 | OTV_LIMIT_CHECK( 2 ); |
708 | PosFormat = FT_NEXT_USHORT( p ); |
709 | |
710 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
711 | |
712 | switch ( PosFormat ) |
713 | { |
714 | case 1: |
715 | otvalid->extra2 = 1; |
716 | OTV_NEST3( MarkLigPosFormat1, LigatureArray, LigatureAttach ); |
717 | OTV_RUN( table, otvalid ); |
718 | break; |
719 | |
720 | default: |
721 | FT_INVALID_FORMAT; |
722 | } |
723 | |
724 | OTV_EXIT; |
725 | } |
726 | |
727 | |
728 | /*************************************************************************/ |
729 | /*************************************************************************/ |
730 | /***** *****/ |
731 | /***** GPOS LOOKUP TYPE 6 *****/ |
732 | /***** *****/ |
733 | /*************************************************************************/ |
734 | /*************************************************************************/ |
735 | |
736 | /* sets otvalid->extra2 (0) */ |
737 | |
738 | static void |
739 | otv_MarkMarkPos_validate( FT_Bytes table, |
740 | OTV_Validator otvalid ) |
741 | { |
742 | FT_Bytes p = table; |
743 | FT_UInt PosFormat; |
744 | |
745 | |
746 | OTV_NAME_ENTER( "MarkMarkPos" ); |
747 | |
748 | OTV_LIMIT_CHECK( 2 ); |
749 | PosFormat = FT_NEXT_USHORT( p ); |
750 | |
751 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
752 | |
753 | switch ( PosFormat ) |
754 | { |
755 | case 1: |
756 | otvalid->extra2 = 0; |
757 | OTV_NEST2( MarkMarkPosFormat1, Mark2Array ); |
758 | OTV_RUN( table, otvalid ); |
759 | break; |
760 | |
761 | default: |
762 | FT_INVALID_FORMAT; |
763 | } |
764 | |
765 | OTV_EXIT; |
766 | } |
767 | |
768 | |
769 | /*************************************************************************/ |
770 | /*************************************************************************/ |
771 | /***** *****/ |
772 | /***** GPOS LOOKUP TYPE 7 *****/ |
773 | /***** *****/ |
774 | /*************************************************************************/ |
775 | /*************************************************************************/ |
776 | |
777 | /* sets otvalid->extra1 (lookup count) */ |
778 | |
779 | static void |
780 | otv_ContextPos_validate( FT_Bytes table, |
781 | OTV_Validator otvalid ) |
782 | { |
783 | FT_Bytes p = table; |
784 | FT_UInt PosFormat; |
785 | |
786 | |
787 | OTV_NAME_ENTER( "ContextPos" ); |
788 | |
789 | OTV_LIMIT_CHECK( 2 ); |
790 | PosFormat = FT_NEXT_USHORT( p ); |
791 | |
792 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
793 | |
794 | switch ( PosFormat ) |
795 | { |
796 | case 1: |
797 | /* no need to check glyph indices/classes used as input for these */ |
798 | /* context rules since even invalid glyph indices/classes return */ |
799 | /* meaningful results */ |
800 | |
801 | otvalid->extra1 = otvalid->lookup_count; |
802 | OTV_NEST3( ContextPosFormat1, PosRuleSet, PosRule ); |
803 | OTV_RUN( table, otvalid ); |
804 | break; |
805 | |
806 | case 2: |
807 | /* no need to check glyph indices/classes used as input for these */ |
808 | /* context rules since even invalid glyph indices/classes return */ |
809 | /* meaningful results */ |
810 | |
811 | OTV_NEST3( ContextPosFormat2, PosClassSet, PosClassRule ); |
812 | OTV_RUN( table, otvalid ); |
813 | break; |
814 | |
815 | case 3: |
816 | OTV_NEST1( ContextPosFormat3 ); |
817 | OTV_RUN( table, otvalid ); |
818 | break; |
819 | |
820 | default: |
821 | FT_INVALID_FORMAT; |
822 | } |
823 | |
824 | OTV_EXIT; |
825 | } |
826 | |
827 | |
828 | /*************************************************************************/ |
829 | /*************************************************************************/ |
830 | /***** *****/ |
831 | /***** GPOS LOOKUP TYPE 8 *****/ |
832 | /***** *****/ |
833 | /*************************************************************************/ |
834 | /*************************************************************************/ |
835 | |
836 | /* sets otvalid->extra1 (lookup count) */ |
837 | |
838 | static void |
839 | otv_ChainContextPos_validate( FT_Bytes table, |
840 | OTV_Validator otvalid ) |
841 | { |
842 | FT_Bytes p = table; |
843 | FT_UInt PosFormat; |
844 | |
845 | |
846 | OTV_NAME_ENTER( "ChainContextPos" ); |
847 | |
848 | OTV_LIMIT_CHECK( 2 ); |
849 | PosFormat = FT_NEXT_USHORT( p ); |
850 | |
851 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
852 | |
853 | switch ( PosFormat ) |
854 | { |
855 | case 1: |
856 | /* no need to check glyph indices/classes used as input for these */ |
857 | /* context rules since even invalid glyph indices/classes return */ |
858 | /* meaningful results */ |
859 | |
860 | otvalid->extra1 = otvalid->lookup_count; |
861 | OTV_NEST3( ChainContextPosFormat1, |
862 | ChainPosRuleSet, ChainPosRule ); |
863 | OTV_RUN( table, otvalid ); |
864 | break; |
865 | |
866 | case 2: |
867 | /* no need to check glyph indices/classes used as input for these */ |
868 | /* context rules since even invalid glyph indices/classes return */ |
869 | /* meaningful results */ |
870 | |
871 | OTV_NEST3( ChainContextPosFormat2, |
872 | ChainPosClassSet, ChainPosClassRule ); |
873 | OTV_RUN( table, otvalid ); |
874 | break; |
875 | |
876 | case 3: |
877 | OTV_NEST1( ChainContextPosFormat3 ); |
878 | OTV_RUN( table, otvalid ); |
879 | break; |
880 | |
881 | default: |
882 | FT_INVALID_FORMAT; |
883 | } |
884 | |
885 | OTV_EXIT; |
886 | } |
887 | |
888 | |
889 | /*************************************************************************/ |
890 | /*************************************************************************/ |
891 | /***** *****/ |
892 | /***** GPOS LOOKUP TYPE 9 *****/ |
893 | /***** *****/ |
894 | /*************************************************************************/ |
895 | /*************************************************************************/ |
896 | |
897 | /* uses otvalid->type_funcs */ |
898 | |
899 | static void |
900 | otv_ExtensionPos_validate( FT_Bytes table, |
901 | OTV_Validator otvalid ) |
902 | { |
903 | FT_Bytes p = table; |
904 | FT_UInt PosFormat; |
905 | |
906 | |
907 | OTV_NAME_ENTER( "ExtensionPos" ); |
908 | |
909 | OTV_LIMIT_CHECK( 2 ); |
910 | PosFormat = FT_NEXT_USHORT( p ); |
911 | |
912 | OTV_TRACE(( " (format %d)\n" , PosFormat )); |
913 | |
914 | switch ( PosFormat ) |
915 | { |
916 | case 1: /* ExtensionPosFormat1 */ |
917 | { |
918 | FT_UInt ExtensionLookupType; |
919 | FT_ULong ExtensionOffset; |
920 | OTV_Validate_Func validate; |
921 | |
922 | |
923 | OTV_LIMIT_CHECK( 6 ); |
924 | ExtensionLookupType = FT_NEXT_USHORT( p ); |
925 | ExtensionOffset = FT_NEXT_ULONG( p ); |
926 | |
927 | if ( ExtensionLookupType == 0 || ExtensionLookupType >= 9 ) |
928 | FT_INVALID_DATA; |
929 | |
930 | validate = otvalid->type_funcs[ExtensionLookupType - 1]; |
931 | validate( table + ExtensionOffset, otvalid ); |
932 | } |
933 | break; |
934 | |
935 | default: |
936 | FT_INVALID_FORMAT; |
937 | } |
938 | |
939 | OTV_EXIT; |
940 | } |
941 | |
942 | |
943 | static const OTV_Validate_Func otv_gpos_validate_funcs[9] = |
944 | { |
945 | otv_SinglePos_validate, |
946 | otv_PairPos_validate, |
947 | otv_CursivePos_validate, |
948 | otv_MarkBasePos_validate, |
949 | otv_MarkLigPos_validate, |
950 | otv_MarkMarkPos_validate, |
951 | otv_ContextPos_validate, |
952 | otv_ChainContextPos_validate, |
953 | otv_ExtensionPos_validate |
954 | }; |
955 | |
956 | |
957 | /* sets otvalid->type_count */ |
958 | /* sets otvalid->type_funcs */ |
959 | |
960 | FT_LOCAL_DEF( void ) |
961 | otv_GPOS_subtable_validate( FT_Bytes table, |
962 | OTV_Validator otvalid ) |
963 | { |
964 | otvalid->type_count = 9; |
965 | otvalid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs; |
966 | |
967 | otv_Lookup_validate( table, otvalid ); |
968 | } |
969 | |
970 | |
971 | /*************************************************************************/ |
972 | /*************************************************************************/ |
973 | /***** *****/ |
974 | /***** GPOS TABLE *****/ |
975 | /***** *****/ |
976 | /*************************************************************************/ |
977 | /*************************************************************************/ |
978 | |
979 | /* sets otvalid->glyph_count */ |
980 | |
981 | FT_LOCAL_DEF( void ) |
982 | otv_GPOS_validate( FT_Bytes table, |
983 | FT_UInt glyph_count, |
984 | FT_Validator ftvalid ) |
985 | { |
986 | OTV_ValidatorRec validrec; |
987 | OTV_Validator otvalid = &validrec; |
988 | FT_Bytes p = table; |
989 | FT_UInt table_size; |
990 | FT_UShort version; |
991 | FT_UInt ScriptList, FeatureList, LookupList; |
992 | |
993 | OTV_OPTIONAL_TABLE32( featureVariations ); |
994 | |
995 | |
996 | otvalid->root = ftvalid; |
997 | |
998 | FT_TRACE3(( "validating GPOS table\n" )); |
999 | OTV_INIT; |
1000 | |
1001 | OTV_LIMIT_CHECK( 4 ); |
1002 | |
1003 | if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */ |
1004 | FT_INVALID_FORMAT; |
1005 | |
1006 | version = FT_NEXT_USHORT( p ); /* minorVersion */ |
1007 | |
1008 | table_size = 10; |
1009 | switch ( version ) |
1010 | { |
1011 | case 0: |
1012 | OTV_LIMIT_CHECK( 6 ); |
1013 | break; |
1014 | |
1015 | case 1: |
1016 | OTV_LIMIT_CHECK( 10 ); |
1017 | table_size += 4; |
1018 | break; |
1019 | |
1020 | default: |
1021 | FT_INVALID_FORMAT; |
1022 | } |
1023 | |
1024 | ScriptList = FT_NEXT_USHORT( p ); |
1025 | FeatureList = FT_NEXT_USHORT( p ); |
1026 | LookupList = FT_NEXT_USHORT( p ); |
1027 | |
1028 | otvalid->type_count = 9; |
1029 | otvalid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs; |
1030 | otvalid->glyph_count = glyph_count; |
1031 | |
1032 | otv_LookupList_validate( table + LookupList, |
1033 | otvalid ); |
1034 | otv_FeatureList_validate( table + FeatureList, table + LookupList, |
1035 | otvalid ); |
1036 | otv_ScriptList_validate( table + ScriptList, table + FeatureList, |
1037 | otvalid ); |
1038 | |
1039 | if ( version > 0 ) |
1040 | { |
1041 | OTV_OPTIONAL_OFFSET32( featureVariations ); |
1042 | OTV_SIZE_CHECK32( featureVariations ); |
1043 | if ( featureVariations ) |
1044 | OTV_TRACE(( " [omitting featureVariations validation]\n" )); /* XXX */ |
1045 | } |
1046 | |
1047 | FT_TRACE4(( "\n" )); |
1048 | } |
1049 | |
1050 | |
1051 | /* END */ |
1052 | |