1 | /**************************************************************************** |
2 | * |
3 | * gxvkern.c |
4 | * |
5 | * TrueTypeGX/AAT kern table validation (body). |
6 | * |
7 | * Copyright (C) 2004-2023 by |
8 | * suzuki toshiya, Masatake YAMATO, Red Hat K.K., |
9 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
10 | * |
11 | * This file is part of the FreeType project, and may only be used, |
12 | * modified, and distributed under the terms of the FreeType project |
13 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
14 | * this file you indicate that you have read the license and |
15 | * understand and accept it fully. |
16 | * |
17 | */ |
18 | |
19 | /**************************************************************************** |
20 | * |
21 | * gxvalid is derived from both gxlayout module and otvalid module. |
22 | * Development of gxlayout is supported by the Information-technology |
23 | * Promotion Agency(IPA), Japan. |
24 | * |
25 | */ |
26 | |
27 | |
28 | #include "gxvalid.h" |
29 | #include "gxvcommn.h" |
30 | |
31 | #include <freetype/ftsnames.h> |
32 | #include <freetype/internal/services/svgxval.h> |
33 | |
34 | |
35 | /************************************************************************** |
36 | * |
37 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
38 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
39 | * messages during execution. |
40 | */ |
41 | #undef FT_COMPONENT |
42 | #define FT_COMPONENT gxvkern |
43 | |
44 | |
45 | /*************************************************************************/ |
46 | /*************************************************************************/ |
47 | /***** *****/ |
48 | /***** Data and Types *****/ |
49 | /***** *****/ |
50 | /*************************************************************************/ |
51 | /*************************************************************************/ |
52 | |
53 | typedef enum GXV_kern_Version_ |
54 | { |
55 | KERN_VERSION_CLASSIC = 0x0000, |
56 | KERN_VERSION_NEW = 0x0001 |
57 | |
58 | } GXV_kern_Version; |
59 | |
60 | |
61 | typedef enum GXV_kern_Dialect_ |
62 | { |
63 | KERN_DIALECT_UNKNOWN = 0, |
64 | KERN_DIALECT_MS = FT_VALIDATE_MS, |
65 | KERN_DIALECT_APPLE = FT_VALIDATE_APPLE, |
66 | KERN_DIALECT_ANY = FT_VALIDATE_CKERN |
67 | |
68 | } GXV_kern_Dialect; |
69 | |
70 | |
71 | typedef struct GXV_kern_DataRec_ |
72 | { |
73 | GXV_kern_Version version; |
74 | void *subtable_data; |
75 | GXV_kern_Dialect dialect_request; |
76 | |
77 | } GXV_kern_DataRec, *GXV_kern_Data; |
78 | |
79 | |
80 | #define GXV_KERN_DATA( field ) GXV_TABLE_DATA( kern, field ) |
81 | |
82 | #define KERN_IS_CLASSIC( gxvalid ) \ |
83 | ( KERN_VERSION_CLASSIC == GXV_KERN_DATA( version ) ) |
84 | #define KERN_IS_NEW( gxvalid ) \ |
85 | ( KERN_VERSION_NEW == GXV_KERN_DATA( version ) ) |
86 | |
87 | #define KERN_DIALECT( gxvalid ) \ |
88 | GXV_KERN_DATA( dialect_request ) |
89 | #define KERN_ALLOWS_MS( gxvalid ) \ |
90 | ( KERN_DIALECT( gxvalid ) & KERN_DIALECT_MS ) |
91 | #define KERN_ALLOWS_APPLE( gxvalid ) \ |
92 | ( KERN_DIALECT( gxvalid ) & KERN_DIALECT_APPLE ) |
93 | |
94 | #define ( KERN_IS_NEW( gxvalid ) ? 8 : 4 ) |
95 | #define ( KERN_IS_NEW( gxvalid ) ? 8 : 6 ) |
96 | |
97 | |
98 | /*************************************************************************/ |
99 | /*************************************************************************/ |
100 | /***** *****/ |
101 | /***** SUBTABLE VALIDATORS *****/ |
102 | /***** *****/ |
103 | /*************************************************************************/ |
104 | /*************************************************************************/ |
105 | |
106 | |
107 | /* ============================= format 0 ============================== */ |
108 | |
109 | static void |
110 | gxv_kern_subtable_fmt0_pairs_validate( FT_Bytes table, |
111 | FT_Bytes limit, |
112 | FT_UShort nPairs, |
113 | GXV_Validator gxvalid ) |
114 | { |
115 | FT_Bytes p = table; |
116 | FT_UShort i; |
117 | |
118 | FT_UShort last_gid_left = 0; |
119 | FT_UShort last_gid_right = 0; |
120 | |
121 | FT_UNUSED( limit ); |
122 | |
123 | |
124 | GXV_NAME_ENTER( "kern format 0 pairs" ); |
125 | |
126 | for ( i = 0; i < nPairs; i++ ) |
127 | { |
128 | FT_UShort gid_left; |
129 | FT_UShort gid_right; |
130 | #ifdef GXV_LOAD_UNUSED_VARS |
131 | FT_Short kernValue; |
132 | #endif |
133 | |
134 | |
135 | /* left */ |
136 | gid_left = FT_NEXT_USHORT( p ); |
137 | gxv_glyphid_validate( gid_left, gxvalid ); |
138 | |
139 | /* right */ |
140 | gid_right = FT_NEXT_USHORT( p ); |
141 | gxv_glyphid_validate( gid_right, gxvalid ); |
142 | |
143 | /* Pairs of left and right GIDs must be unique and sorted. */ |
144 | GXV_TRACE(( "left gid = %u, right gid = %u\n" , gid_left, gid_right )); |
145 | if ( gid_left == last_gid_left ) |
146 | { |
147 | if ( last_gid_right < gid_right ) |
148 | last_gid_right = gid_right; |
149 | else |
150 | FT_INVALID_DATA; |
151 | } |
152 | else if ( last_gid_left < gid_left ) |
153 | { |
154 | last_gid_left = gid_left; |
155 | last_gid_right = gid_right; |
156 | } |
157 | else |
158 | FT_INVALID_DATA; |
159 | |
160 | /* skip the kern value */ |
161 | #ifdef GXV_LOAD_UNUSED_VARS |
162 | kernValue = FT_NEXT_SHORT( p ); |
163 | #else |
164 | p += 2; |
165 | #endif |
166 | } |
167 | |
168 | GXV_EXIT; |
169 | } |
170 | |
171 | static void |
172 | gxv_kern_subtable_fmt0_validate( FT_Bytes table, |
173 | FT_Bytes limit, |
174 | GXV_Validator gxvalid ) |
175 | { |
176 | FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; |
177 | |
178 | FT_UShort nPairs; |
179 | FT_UShort unitSize; |
180 | |
181 | |
182 | GXV_NAME_ENTER( "kern subtable format 0" ); |
183 | |
184 | unitSize = 2 + 2 + 2; |
185 | nPairs = 0; |
186 | |
187 | /* nPairs, searchRange, entrySelector, rangeShift */ |
188 | GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 ); |
189 | gxv_BinSrchHeader_validate( p, limit, &unitSize, &nPairs, gxvalid ); |
190 | p += 2 + 2 + 2 + 2; |
191 | |
192 | gxv_kern_subtable_fmt0_pairs_validate( p, limit, nPairs, gxvalid ); |
193 | |
194 | GXV_EXIT; |
195 | } |
196 | |
197 | |
198 | /* ============================= format 1 ============================== */ |
199 | |
200 | |
201 | typedef struct GXV_kern_fmt1_StateOptRec_ |
202 | { |
203 | FT_UShort valueTable; |
204 | FT_UShort valueTable_length; |
205 | |
206 | } GXV_kern_fmt1_StateOptRec, *GXV_kern_fmt1_StateOptRecData; |
207 | |
208 | |
209 | static void |
210 | gxv_kern_subtable_fmt1_valueTable_load( FT_Bytes table, |
211 | FT_Bytes limit, |
212 | GXV_Validator gxvalid ) |
213 | { |
214 | FT_Bytes p = table; |
215 | GXV_kern_fmt1_StateOptRecData optdata = |
216 | (GXV_kern_fmt1_StateOptRecData)gxvalid->statetable.optdata; |
217 | |
218 | |
219 | GXV_LIMIT_CHECK( 2 ); |
220 | optdata->valueTable = FT_NEXT_USHORT( p ); |
221 | } |
222 | |
223 | |
224 | /* |
225 | * passed tables_size covers whole StateTable, including kern fmt1 header |
226 | */ |
227 | static void |
228 | gxv_kern_subtable_fmt1_subtable_setup( FT_UShort table_size, |
229 | FT_UShort classTable, |
230 | FT_UShort stateArray, |
231 | FT_UShort entryTable, |
232 | FT_UShort* classTable_length_p, |
233 | FT_UShort* stateArray_length_p, |
234 | FT_UShort* entryTable_length_p, |
235 | GXV_Validator gxvalid ) |
236 | { |
237 | FT_UShort o[4]; |
238 | FT_UShort *l[4]; |
239 | FT_UShort buff[5]; |
240 | |
241 | GXV_kern_fmt1_StateOptRecData optdata = |
242 | (GXV_kern_fmt1_StateOptRecData)gxvalid->statetable.optdata; |
243 | |
244 | |
245 | o[0] = classTable; |
246 | o[1] = stateArray; |
247 | o[2] = entryTable; |
248 | o[3] = optdata->valueTable; |
249 | l[0] = classTable_length_p; |
250 | l[1] = stateArray_length_p; |
251 | l[2] = entryTable_length_p; |
252 | l[3] = &(optdata->valueTable_length); |
253 | |
254 | gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, gxvalid ); |
255 | } |
256 | |
257 | |
258 | /* |
259 | * passed table & limit are of whole StateTable, not including subtables |
260 | */ |
261 | static void |
262 | gxv_kern_subtable_fmt1_entry_validate( |
263 | FT_Byte state, |
264 | FT_UShort flags, |
265 | GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, |
266 | FT_Bytes table, |
267 | FT_Bytes limit, |
268 | GXV_Validator gxvalid ) |
269 | { |
270 | #ifdef GXV_LOAD_UNUSED_VARS |
271 | FT_UShort push; |
272 | FT_UShort dontAdvance; |
273 | #endif |
274 | FT_UShort valueOffset; |
275 | #ifdef GXV_LOAD_UNUSED_VARS |
276 | FT_UShort kernAction; |
277 | FT_UShort kernValue; |
278 | #endif |
279 | |
280 | FT_UNUSED( state ); |
281 | FT_UNUSED( glyphOffset_p ); |
282 | |
283 | |
284 | #ifdef GXV_LOAD_UNUSED_VARS |
285 | push = (FT_UShort)( ( flags >> 15 ) & 1 ); |
286 | dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); |
287 | #endif |
288 | valueOffset = (FT_UShort)( flags & 0x3FFF ); |
289 | |
290 | { |
291 | GXV_kern_fmt1_StateOptRecData vt_rec = |
292 | (GXV_kern_fmt1_StateOptRecData)gxvalid->statetable.optdata; |
293 | FT_Bytes p; |
294 | |
295 | |
296 | if ( valueOffset < vt_rec->valueTable ) |
297 | FT_INVALID_OFFSET; |
298 | |
299 | p = table + valueOffset; |
300 | limit = table + vt_rec->valueTable + vt_rec->valueTable_length; |
301 | |
302 | GXV_LIMIT_CHECK( 2 + 2 ); |
303 | #ifdef GXV_LOAD_UNUSED_VARS |
304 | kernAction = FT_NEXT_USHORT( p ); |
305 | kernValue = FT_NEXT_USHORT( p ); |
306 | #endif |
307 | } |
308 | } |
309 | |
310 | |
311 | static void |
312 | gxv_kern_subtable_fmt1_validate( FT_Bytes table, |
313 | FT_Bytes limit, |
314 | GXV_Validator gxvalid ) |
315 | { |
316 | FT_Bytes p = table; |
317 | GXV_kern_fmt1_StateOptRec vt_rec; |
318 | |
319 | |
320 | GXV_NAME_ENTER( "kern subtable format 1" ); |
321 | |
322 | gxvalid->statetable.optdata = |
323 | &vt_rec; |
324 | gxvalid->statetable.optdata_load_func = |
325 | gxv_kern_subtable_fmt1_valueTable_load; |
326 | gxvalid->statetable.subtable_setup_func = |
327 | gxv_kern_subtable_fmt1_subtable_setup; |
328 | gxvalid->statetable.entry_glyphoffset_fmt = |
329 | GXV_GLYPHOFFSET_NONE; |
330 | gxvalid->statetable.entry_validate_func = |
331 | gxv_kern_subtable_fmt1_entry_validate; |
332 | |
333 | gxv_StateTable_validate( p, limit, gxvalid ); |
334 | |
335 | GXV_EXIT; |
336 | } |
337 | |
338 | |
339 | /* ================ Data for Class-Based Subtables 2, 3 ================ */ |
340 | |
341 | typedef enum GXV_kern_ClassSpec_ |
342 | { |
343 | GXV_KERN_CLS_L = 0, |
344 | GXV_KERN_CLS_R |
345 | |
346 | } GXV_kern_ClassSpec; |
347 | |
348 | |
349 | /* ============================= format 2 ============================== */ |
350 | |
351 | /* ---------------------- format 2 specific data ----------------------- */ |
352 | |
353 | typedef struct GXV_kern_subtable_fmt2_DataRec_ |
354 | { |
355 | FT_UShort rowWidth; |
356 | FT_UShort array; |
357 | FT_UShort offset_min[2]; |
358 | FT_UShort offset_max[2]; |
359 | const FT_String* class_tag[2]; |
360 | GXV_odtect_Range odtect; |
361 | |
362 | } GXV_kern_subtable_fmt2_DataRec, *GXV_kern_subtable_fmt2_Data; |
363 | |
364 | |
365 | #define GXV_KERN_FMT2_DATA( field ) \ |
366 | ( ( (GXV_kern_subtable_fmt2_DataRec *) \ |
367 | ( GXV_KERN_DATA( subtable_data ) ) )->field ) |
368 | |
369 | |
370 | /* -------------------------- utility functions ----------------------- */ |
371 | |
372 | static void |
373 | gxv_kern_subtable_fmt2_clstbl_validate( FT_Bytes table, |
374 | FT_Bytes limit, |
375 | GXV_kern_ClassSpec spec, |
376 | GXV_Validator gxvalid ) |
377 | { |
378 | const FT_String* tag = GXV_KERN_FMT2_DATA( class_tag[spec] ); |
379 | GXV_odtect_Range odtect = GXV_KERN_FMT2_DATA( odtect ); |
380 | |
381 | FT_Bytes p = table; |
382 | FT_UShort firstGlyph; |
383 | FT_UShort nGlyphs; |
384 | |
385 | |
386 | GXV_NAME_ENTER( "kern format 2 classTable" ); |
387 | |
388 | GXV_LIMIT_CHECK( 2 + 2 ); |
389 | firstGlyph = FT_NEXT_USHORT( p ); |
390 | nGlyphs = FT_NEXT_USHORT( p ); |
391 | GXV_TRACE(( " %s firstGlyph=%d, nGlyphs=%d\n" , |
392 | tag, firstGlyph, nGlyphs )); |
393 | |
394 | gxv_glyphid_validate( firstGlyph, gxvalid ); |
395 | gxv_glyphid_validate( (FT_UShort)( firstGlyph + nGlyphs - 1 ), gxvalid ); |
396 | |
397 | gxv_array_getlimits_ushort( p, p + ( 2 * nGlyphs ), |
398 | &( GXV_KERN_FMT2_DATA( offset_min[spec] ) ), |
399 | &( GXV_KERN_FMT2_DATA( offset_max[spec] ) ), |
400 | gxvalid ); |
401 | |
402 | gxv_odtect_add_range( table, 2 * nGlyphs, tag, odtect ); |
403 | |
404 | GXV_EXIT; |
405 | } |
406 | |
407 | |
408 | static void |
409 | gxv_kern_subtable_fmt2_validate( FT_Bytes table, |
410 | FT_Bytes limit, |
411 | GXV_Validator gxvalid ) |
412 | { |
413 | GXV_ODTECT( 3, odtect ); |
414 | GXV_kern_subtable_fmt2_DataRec fmt2_rec = |
415 | { 0, 0, { 0, 0 }, { 0, 0 }, { "leftClass" , "rightClass" }, NULL }; |
416 | |
417 | FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; |
418 | FT_UShort leftOffsetTable; |
419 | FT_UShort rightOffsetTable; |
420 | |
421 | |
422 | GXV_NAME_ENTER( "kern subtable format 2" ); |
423 | |
424 | GXV_ODTECT_INIT( odtect ); |
425 | fmt2_rec.odtect = odtect; |
426 | GXV_KERN_DATA( subtable_data ) = &fmt2_rec; |
427 | |
428 | GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 ); |
429 | GXV_KERN_FMT2_DATA( rowWidth ) = FT_NEXT_USHORT( p ); |
430 | leftOffsetTable = FT_NEXT_USHORT( p ); |
431 | rightOffsetTable = FT_NEXT_USHORT( p ); |
432 | GXV_KERN_FMT2_DATA( array ) = FT_NEXT_USHORT( p ); |
433 | |
434 | GXV_TRACE(( "rowWidth = %d\n" , GXV_KERN_FMT2_DATA( rowWidth ) )); |
435 | |
436 | |
437 | GXV_LIMIT_CHECK( leftOffsetTable ); |
438 | GXV_LIMIT_CHECK( rightOffsetTable ); |
439 | GXV_LIMIT_CHECK( GXV_KERN_FMT2_DATA( array ) ); |
440 | |
441 | gxv_kern_subtable_fmt2_clstbl_validate( table + leftOffsetTable, limit, |
442 | GXV_KERN_CLS_L, gxvalid ); |
443 | |
444 | gxv_kern_subtable_fmt2_clstbl_validate( table + rightOffsetTable, limit, |
445 | GXV_KERN_CLS_R, gxvalid ); |
446 | |
447 | if ( GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_L] ) + |
448 | GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_R] ) |
449 | < GXV_KERN_FMT2_DATA( array ) ) |
450 | FT_INVALID_OFFSET; |
451 | |
452 | gxv_odtect_add_range( table + GXV_KERN_FMT2_DATA( array ), |
453 | GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_L] ) |
454 | + GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_R] ) |
455 | - GXV_KERN_FMT2_DATA( array ), |
456 | "array" , odtect ); |
457 | |
458 | gxv_odtect_validate( odtect, gxvalid ); |
459 | |
460 | GXV_EXIT; |
461 | } |
462 | |
463 | |
464 | /* ============================= format 3 ============================== */ |
465 | |
466 | static void |
467 | gxv_kern_subtable_fmt3_validate( FT_Bytes table, |
468 | FT_Bytes limit, |
469 | GXV_Validator gxvalid ) |
470 | { |
471 | FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; |
472 | FT_UShort glyphCount; |
473 | FT_Byte kernValueCount; |
474 | FT_Byte leftClassCount; |
475 | FT_Byte rightClassCount; |
476 | FT_Byte flags; |
477 | |
478 | |
479 | GXV_NAME_ENTER( "kern subtable format 3" ); |
480 | |
481 | GXV_LIMIT_CHECK( 2 + 1 + 1 + 1 + 1 ); |
482 | glyphCount = FT_NEXT_USHORT( p ); |
483 | kernValueCount = FT_NEXT_BYTE( p ); |
484 | leftClassCount = FT_NEXT_BYTE( p ); |
485 | rightClassCount = FT_NEXT_BYTE( p ); |
486 | flags = FT_NEXT_BYTE( p ); |
487 | |
488 | if ( gxvalid->face->num_glyphs != glyphCount ) |
489 | { |
490 | GXV_TRACE(( "maxGID=%ld, but glyphCount=%d\n" , |
491 | gxvalid->face->num_glyphs, glyphCount )); |
492 | GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); |
493 | } |
494 | |
495 | if ( flags != 0 ) |
496 | GXV_TRACE(( "kern subtable fmt3 has nonzero value" |
497 | " (%d) in unused flag\n" , flags )); |
498 | /* |
499 | * just skip kernValue[kernValueCount] |
500 | */ |
501 | GXV_LIMIT_CHECK( 2 * kernValueCount ); |
502 | p += 2 * kernValueCount; |
503 | |
504 | /* |
505 | * check leftClass[gid] < leftClassCount |
506 | */ |
507 | { |
508 | FT_Byte min, max; |
509 | |
510 | |
511 | GXV_LIMIT_CHECK( glyphCount ); |
512 | gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, gxvalid ); |
513 | p += gxvalid->subtable_length; |
514 | |
515 | if ( leftClassCount < max ) |
516 | FT_INVALID_DATA; |
517 | } |
518 | |
519 | /* |
520 | * check rightClass[gid] < rightClassCount |
521 | */ |
522 | { |
523 | FT_Byte min, max; |
524 | |
525 | |
526 | GXV_LIMIT_CHECK( glyphCount ); |
527 | gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, gxvalid ); |
528 | p += gxvalid->subtable_length; |
529 | |
530 | if ( rightClassCount < max ) |
531 | FT_INVALID_DATA; |
532 | } |
533 | |
534 | /* |
535 | * check kernIndex[i, j] < kernValueCount |
536 | */ |
537 | { |
538 | FT_UShort i, j; |
539 | |
540 | |
541 | for ( i = 0; i < leftClassCount; i++ ) |
542 | { |
543 | for ( j = 0; j < rightClassCount; j++ ) |
544 | { |
545 | GXV_LIMIT_CHECK( 1 ); |
546 | if ( kernValueCount < FT_NEXT_BYTE( p ) ) |
547 | FT_INVALID_OFFSET; |
548 | } |
549 | } |
550 | } |
551 | |
552 | gxvalid->subtable_length = (FT_ULong)( p - table ); |
553 | |
554 | GXV_EXIT; |
555 | } |
556 | |
557 | |
558 | static FT_Bool |
559 | gxv_kern_coverage_new_apple_validate( FT_UShort coverage, |
560 | FT_UShort* format, |
561 | GXV_Validator gxvalid ) |
562 | { |
563 | /* new Apple-dialect */ |
564 | #ifdef GXV_LOAD_TRACE_VARS |
565 | FT_Bool kernVertical; |
566 | FT_Bool kernCrossStream; |
567 | FT_Bool kernVariation; |
568 | #endif |
569 | |
570 | FT_UNUSED( gxvalid ); |
571 | |
572 | |
573 | /* reserved bits = 0 */ |
574 | if ( coverage & 0x1FFC ) |
575 | return FALSE; |
576 | |
577 | #ifdef GXV_LOAD_TRACE_VARS |
578 | kernVertical = FT_BOOL( ( coverage >> 15 ) & 1 ); |
579 | kernCrossStream = FT_BOOL( ( coverage >> 14 ) & 1 ); |
580 | kernVariation = FT_BOOL( ( coverage >> 13 ) & 1 ); |
581 | #endif |
582 | |
583 | *format = (FT_UShort)( coverage & 0x0003 ); |
584 | |
585 | GXV_TRACE(( "new Apple-dialect: " |
586 | "horizontal=%d, cross-stream=%d, variation=%d, format=%d\n" , |
587 | !kernVertical, kernCrossStream, kernVariation, *format )); |
588 | |
589 | GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" )); |
590 | |
591 | return TRUE; |
592 | } |
593 | |
594 | |
595 | static FT_Bool |
596 | gxv_kern_coverage_classic_apple_validate( FT_UShort coverage, |
597 | FT_UShort* format, |
598 | GXV_Validator gxvalid ) |
599 | { |
600 | /* classic Apple-dialect */ |
601 | #ifdef GXV_LOAD_TRACE_VARS |
602 | FT_Bool horizontal; |
603 | FT_Bool cross_stream; |
604 | #endif |
605 | |
606 | |
607 | /* check expected flags, but don't check if MS-dialect is impossible */ |
608 | if ( !( coverage & 0xFD00 ) && KERN_ALLOWS_MS( gxvalid ) ) |
609 | return FALSE; |
610 | |
611 | /* reserved bits = 0 */ |
612 | if ( coverage & 0x02FC ) |
613 | return FALSE; |
614 | |
615 | #ifdef GXV_LOAD_TRACE_VARS |
616 | horizontal = FT_BOOL( ( coverage >> 15 ) & 1 ); |
617 | cross_stream = FT_BOOL( ( coverage >> 13 ) & 1 ); |
618 | #endif |
619 | |
620 | *format = (FT_UShort)( coverage & 0x0003 ); |
621 | |
622 | GXV_TRACE(( "classic Apple-dialect: " |
623 | "horizontal=%d, cross-stream=%d, format=%d\n" , |
624 | horizontal, cross_stream, *format )); |
625 | |
626 | /* format 1 requires GX State Machine, too new for classic */ |
627 | if ( *format == 1 ) |
628 | return FALSE; |
629 | |
630 | GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" )); |
631 | |
632 | return TRUE; |
633 | } |
634 | |
635 | |
636 | static FT_Bool |
637 | gxv_kern_coverage_classic_microsoft_validate( FT_UShort coverage, |
638 | FT_UShort* format, |
639 | GXV_Validator gxvalid ) |
640 | { |
641 | /* classic Microsoft-dialect */ |
642 | #ifdef GXV_LOAD_TRACE_VARS |
643 | FT_Bool horizontal; |
644 | FT_Bool minimum; |
645 | FT_Bool cross_stream; |
646 | FT_Bool override; |
647 | #endif |
648 | |
649 | FT_UNUSED( gxvalid ); |
650 | |
651 | |
652 | /* reserved bits = 0 */ |
653 | if ( coverage & 0xFDF0 ) |
654 | return FALSE; |
655 | |
656 | #ifdef GXV_LOAD_TRACE_VARS |
657 | horizontal = FT_BOOL( coverage & 1 ); |
658 | minimum = FT_BOOL( ( coverage >> 1 ) & 1 ); |
659 | cross_stream = FT_BOOL( ( coverage >> 2 ) & 1 ); |
660 | override = FT_BOOL( ( coverage >> 3 ) & 1 ); |
661 | #endif |
662 | |
663 | *format = (FT_UShort)( ( coverage >> 8 ) & 0x0003 ); |
664 | |
665 | GXV_TRACE(( "classic Microsoft-dialect: " |
666 | "horizontal=%d, minimum=%d, cross-stream=%d, " |
667 | "override=%d, format=%d\n" , |
668 | horizontal, minimum, cross_stream, override, *format )); |
669 | |
670 | if ( *format == 2 ) |
671 | GXV_TRACE(( |
672 | "kerning values in Microsoft format 2 subtable are ignored\n" )); |
673 | |
674 | return TRUE; |
675 | } |
676 | |
677 | |
678 | /*************************************************************************/ |
679 | /*************************************************************************/ |
680 | /***** *****/ |
681 | /***** MAIN *****/ |
682 | /***** *****/ |
683 | /*************************************************************************/ |
684 | /*************************************************************************/ |
685 | |
686 | static GXV_kern_Dialect |
687 | gxv_kern_coverage_validate( FT_UShort coverage, |
688 | FT_UShort* format, |
689 | GXV_Validator gxvalid ) |
690 | { |
691 | GXV_kern_Dialect result = KERN_DIALECT_UNKNOWN; |
692 | |
693 | |
694 | GXV_NAME_ENTER( "validating coverage" ); |
695 | |
696 | GXV_TRACE(( "interpret coverage 0x%04x by Apple style\n" , coverage )); |
697 | |
698 | if ( KERN_IS_NEW( gxvalid ) ) |
699 | { |
700 | if ( gxv_kern_coverage_new_apple_validate( coverage, |
701 | format, |
702 | gxvalid ) ) |
703 | { |
704 | result = KERN_DIALECT_APPLE; |
705 | goto Exit; |
706 | } |
707 | } |
708 | |
709 | if ( KERN_IS_CLASSIC( gxvalid ) && KERN_ALLOWS_APPLE( gxvalid ) ) |
710 | { |
711 | if ( gxv_kern_coverage_classic_apple_validate( coverage, |
712 | format, |
713 | gxvalid ) ) |
714 | { |
715 | result = KERN_DIALECT_APPLE; |
716 | goto Exit; |
717 | } |
718 | } |
719 | |
720 | if ( KERN_IS_CLASSIC( gxvalid ) && KERN_ALLOWS_MS( gxvalid ) ) |
721 | { |
722 | if ( gxv_kern_coverage_classic_microsoft_validate( coverage, |
723 | format, |
724 | gxvalid ) ) |
725 | { |
726 | result = KERN_DIALECT_MS; |
727 | goto Exit; |
728 | } |
729 | } |
730 | |
731 | GXV_TRACE(( "cannot interpret coverage, broken kern subtable\n" )); |
732 | |
733 | Exit: |
734 | GXV_EXIT; |
735 | return result; |
736 | } |
737 | |
738 | |
739 | static void |
740 | gxv_kern_subtable_validate( FT_Bytes table, |
741 | FT_Bytes limit, |
742 | GXV_Validator gxvalid ) |
743 | { |
744 | FT_Bytes p = table; |
745 | #ifdef GXV_LOAD_TRACE_VARS |
746 | FT_UShort version = 0; /* MS only: subtable version, unused */ |
747 | #endif |
748 | FT_ULong length; /* MS: 16bit, Apple: 32bit */ |
749 | FT_UShort coverage; |
750 | #ifdef GXV_LOAD_TRACE_VARS |
751 | FT_UShort tupleIndex = 0; /* Apple only */ |
752 | #endif |
753 | FT_UShort u16[2]; |
754 | FT_UShort format = 255; /* subtable format */ |
755 | |
756 | |
757 | GXV_NAME_ENTER( "kern subtable" ); |
758 | |
759 | GXV_LIMIT_CHECK( 2 + 2 + 2 ); |
760 | u16[0] = FT_NEXT_USHORT( p ); /* Apple: length_hi MS: version */ |
761 | u16[1] = FT_NEXT_USHORT( p ); /* Apple: length_lo MS: length */ |
762 | coverage = FT_NEXT_USHORT( p ); |
763 | |
764 | switch ( gxv_kern_coverage_validate( coverage, &format, gxvalid ) ) |
765 | { |
766 | case KERN_DIALECT_MS: |
767 | #ifdef GXV_LOAD_TRACE_VARS |
768 | version = u16[0]; |
769 | #endif |
770 | length = u16[1]; |
771 | #ifdef GXV_LOAD_TRACE_VARS |
772 | tupleIndex = 0; |
773 | #endif |
774 | GXV_TRACE(( "Subtable version = %d\n" , version )); |
775 | GXV_TRACE(( "Subtable length = %lu\n" , length )); |
776 | break; |
777 | |
778 | case KERN_DIALECT_APPLE: |
779 | #ifdef GXV_LOAD_TRACE_VARS |
780 | version = 0; |
781 | #endif |
782 | length = ( (FT_ULong)u16[0] << 16 ) + u16[1]; |
783 | #ifdef GXV_LOAD_TRACE_VARS |
784 | tupleIndex = 0; |
785 | #endif |
786 | GXV_TRACE(( "Subtable length = %lu\n" , length )); |
787 | |
788 | if ( KERN_IS_NEW( gxvalid ) ) |
789 | { |
790 | GXV_LIMIT_CHECK( 2 ); |
791 | #ifdef GXV_LOAD_TRACE_VARS |
792 | tupleIndex = FT_NEXT_USHORT( p ); |
793 | #else |
794 | p += 2; |
795 | #endif |
796 | GXV_TRACE(( "Subtable tupleIndex = %d\n" , tupleIndex )); |
797 | } |
798 | break; |
799 | |
800 | default: |
801 | length = u16[1]; |
802 | GXV_TRACE(( "cannot detect subtable dialect, " |
803 | "just skip %lu byte\n" , length )); |
804 | goto Exit; |
805 | } |
806 | |
807 | /* formats 1, 2, 3 require the position of the start of this subtable */ |
808 | if ( format == 0 ) |
809 | gxv_kern_subtable_fmt0_validate( table, table + length, gxvalid ); |
810 | else if ( format == 1 ) |
811 | gxv_kern_subtable_fmt1_validate( table, table + length, gxvalid ); |
812 | else if ( format == 2 ) |
813 | gxv_kern_subtable_fmt2_validate( table, table + length, gxvalid ); |
814 | else if ( format == 3 ) |
815 | gxv_kern_subtable_fmt3_validate( table, table + length, gxvalid ); |
816 | else |
817 | FT_INVALID_DATA; |
818 | |
819 | Exit: |
820 | gxvalid->subtable_length = length; |
821 | GXV_EXIT; |
822 | } |
823 | |
824 | |
825 | /*************************************************************************/ |
826 | /*************************************************************************/ |
827 | /***** *****/ |
828 | /***** kern TABLE *****/ |
829 | /***** *****/ |
830 | /*************************************************************************/ |
831 | /*************************************************************************/ |
832 | |
833 | static void |
834 | gxv_kern_validate_generic( FT_Bytes table, |
835 | FT_Face face, |
836 | FT_Bool classic_only, |
837 | GXV_kern_Dialect dialect_request, |
838 | FT_Validator ftvalid ) |
839 | { |
840 | GXV_ValidatorRec gxvalidrec; |
841 | GXV_Validator gxvalid = &gxvalidrec; |
842 | |
843 | GXV_kern_DataRec kernrec; |
844 | GXV_kern_Data kern = &kernrec; |
845 | |
846 | FT_Bytes p = table; |
847 | FT_Bytes limit = 0; |
848 | |
849 | FT_ULong nTables = 0; |
850 | FT_UInt i; |
851 | |
852 | |
853 | gxvalid->root = ftvalid; |
854 | gxvalid->table_data = kern; |
855 | gxvalid->face = face; |
856 | |
857 | FT_TRACE3(( "validating `kern' table\n" )); |
858 | GXV_INIT; |
859 | KERN_DIALECT( gxvalid ) = dialect_request; |
860 | |
861 | GXV_LIMIT_CHECK( 2 ); |
862 | GXV_KERN_DATA( version ) = (GXV_kern_Version)FT_NEXT_USHORT( p ); |
863 | GXV_TRACE(( "version 0x%04x (higher 16bit)\n" , |
864 | GXV_KERN_DATA( version ) )); |
865 | |
866 | if ( 0x0001 < GXV_KERN_DATA( version ) ) |
867 | FT_INVALID_FORMAT; |
868 | else if ( KERN_IS_CLASSIC( gxvalid ) ) |
869 | { |
870 | GXV_LIMIT_CHECK( 2 ); |
871 | nTables = FT_NEXT_USHORT( p ); |
872 | } |
873 | else if ( KERN_IS_NEW( gxvalid ) ) |
874 | { |
875 | if ( classic_only ) |
876 | FT_INVALID_FORMAT; |
877 | |
878 | if ( 0x0000 != FT_NEXT_USHORT( p ) ) |
879 | FT_INVALID_FORMAT; |
880 | |
881 | GXV_LIMIT_CHECK( 4 ); |
882 | nTables = FT_NEXT_ULONG( p ); |
883 | } |
884 | |
885 | for ( i = 0; i < nTables; i++ ) |
886 | { |
887 | GXV_TRACE(( "validating subtable %d/%lu\n" , i, nTables )); |
888 | /* p should be 32bit-aligned? */ |
889 | gxv_kern_subtable_validate( p, 0, gxvalid ); |
890 | p += gxvalid->subtable_length; |
891 | } |
892 | |
893 | FT_TRACE4(( "\n" )); |
894 | } |
895 | |
896 | |
897 | FT_LOCAL_DEF( void ) |
898 | gxv_kern_validate( FT_Bytes table, |
899 | FT_Face face, |
900 | FT_Validator ftvalid ) |
901 | { |
902 | gxv_kern_validate_generic( table, face, 0, KERN_DIALECT_ANY, ftvalid ); |
903 | } |
904 | |
905 | |
906 | FT_LOCAL_DEF( void ) |
907 | gxv_kern_validate_classic( FT_Bytes table, |
908 | FT_Face face, |
909 | FT_Int dialect_flags, |
910 | FT_Validator ftvalid ) |
911 | { |
912 | GXV_kern_Dialect dialect_request; |
913 | |
914 | |
915 | dialect_request = (GXV_kern_Dialect)dialect_flags; |
916 | gxv_kern_validate_generic( table, face, 1, dialect_request, ftvalid ); |
917 | } |
918 | |
919 | |
920 | /* END */ |
921 | |