1 | /**************************************************************************** |
2 | * |
3 | * ttpload.c |
4 | * |
5 | * TrueType-specific tables loader (body). |
6 | * |
7 | * Copyright (C) 1996-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 <freetype/internal/ftdebug.h> |
20 | #include <freetype/internal/ftobjs.h> |
21 | #include <freetype/internal/ftstream.h> |
22 | #include <freetype/tttags.h> |
23 | |
24 | #include "ttpload.h" |
25 | |
26 | #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
27 | #include "ttgxvar.h" |
28 | #endif |
29 | |
30 | #include "tterrors.h" |
31 | |
32 | |
33 | /************************************************************************** |
34 | * |
35 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
36 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
37 | * messages during execution. |
38 | */ |
39 | #undef FT_COMPONENT |
40 | #define FT_COMPONENT ttpload |
41 | |
42 | |
43 | /************************************************************************** |
44 | * |
45 | * @Function: |
46 | * tt_face_load_loca |
47 | * |
48 | * @Description: |
49 | * Load the locations table. |
50 | * |
51 | * @InOut: |
52 | * face :: |
53 | * A handle to the target face object. |
54 | * |
55 | * @Input: |
56 | * stream :: |
57 | * The input stream. |
58 | * |
59 | * @Return: |
60 | * FreeType error code. 0 means success. |
61 | */ |
62 | FT_LOCAL_DEF( FT_Error ) |
63 | tt_face_load_loca( TT_Face face, |
64 | FT_Stream stream ) |
65 | { |
66 | FT_Error error; |
67 | FT_ULong table_len; |
68 | FT_Int shift; |
69 | |
70 | |
71 | /* we need the size of the `glyf' table for malformed `loca' tables */ |
72 | error = face->goto_table( face, TTAG_glyf, stream, &face->glyf_len ); |
73 | |
74 | /* it is possible that a font doesn't have a glyf table at all */ |
75 | /* or its size is zero */ |
76 | if ( FT_ERR_EQ( error, Table_Missing ) ) |
77 | { |
78 | face->glyf_len = 0; |
79 | face->glyf_offset = 0; |
80 | } |
81 | else if ( error ) |
82 | goto Exit; |
83 | else |
84 | { |
85 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
86 | if ( face->root.internal->incremental_interface ) |
87 | face->glyf_offset = 0; |
88 | else |
89 | #endif |
90 | face->glyf_offset = FT_STREAM_POS(); |
91 | } |
92 | |
93 | FT_TRACE2(( "Locations " )); |
94 | error = face->goto_table( face, TTAG_loca, stream, &table_len ); |
95 | if ( error ) |
96 | { |
97 | error = FT_THROW( Locations_Missing ); |
98 | goto Exit; |
99 | } |
100 | |
101 | shift = face->header.Index_To_Loc_Format != 0 ? 2 : 1; |
102 | |
103 | if ( table_len > 0x10000UL << shift ) |
104 | { |
105 | FT_TRACE2(( "table too large\n" )); |
106 | table_len = 0x10000UL << shift; |
107 | } |
108 | |
109 | face->num_locations = table_len >> shift; |
110 | |
111 | if ( face->num_locations != (FT_ULong)face->root.num_glyphs + 1 ) |
112 | { |
113 | FT_TRACE2(( "glyph count mismatch! loca: %ld, maxp: %ld\n" , |
114 | face->num_locations - 1, face->root.num_glyphs )); |
115 | |
116 | /* we only handle the case where `maxp' gives a larger value */ |
117 | if ( face->num_locations < (FT_ULong)face->root.num_glyphs + 1 ) |
118 | { |
119 | FT_ULong new_loca_len = |
120 | ( (FT_ULong)face->root.num_glyphs + 1 ) << shift; |
121 | |
122 | TT_Table entry = face->dir_tables; |
123 | TT_Table limit = entry + face->num_tables; |
124 | |
125 | FT_Long pos = (FT_Long)FT_STREAM_POS(); |
126 | FT_Long dist = 0x7FFFFFFFL; |
127 | FT_Bool found = 0; |
128 | |
129 | |
130 | /* compute the distance to next table in font file */ |
131 | for ( ; entry < limit; entry++ ) |
132 | { |
133 | FT_Long diff = (FT_Long)entry->Offset - pos; |
134 | |
135 | |
136 | if ( diff > 0 && diff < dist ) |
137 | { |
138 | dist = diff; |
139 | found = 1; |
140 | } |
141 | } |
142 | |
143 | if ( !found ) |
144 | { |
145 | /* `loca' is the last table */ |
146 | dist = (FT_Long)stream->size - pos; |
147 | } |
148 | |
149 | if ( new_loca_len <= (FT_ULong)dist ) |
150 | { |
151 | face->num_locations = (FT_ULong)face->root.num_glyphs + 1; |
152 | table_len = new_loca_len; |
153 | |
154 | FT_TRACE2(( "adjusting num_locations to %ld\n" , |
155 | face->num_locations )); |
156 | } |
157 | else |
158 | { |
159 | face->root.num_glyphs = face->num_locations |
160 | ? (FT_Long)face->num_locations - 1 : 0; |
161 | |
162 | FT_TRACE2(( "adjusting num_glyphs to %ld\n" , |
163 | face->root.num_glyphs )); |
164 | } |
165 | } |
166 | } |
167 | |
168 | /* |
169 | * Extract the frame. We don't need to decompress it since |
170 | * we are able to parse it directly. |
171 | */ |
172 | if ( FT_FRAME_EXTRACT( table_len, face->glyph_locations ) ) |
173 | goto Exit; |
174 | |
175 | FT_TRACE2(( "loaded\n" )); |
176 | |
177 | Exit: |
178 | return error; |
179 | } |
180 | |
181 | |
182 | FT_LOCAL_DEF( FT_ULong ) |
183 | tt_face_get_location( FT_Face face, /* TT_Face */ |
184 | FT_UInt gindex, |
185 | FT_ULong *asize ) |
186 | { |
187 | TT_Face ttface = (TT_Face)face; |
188 | FT_ULong pos1, pos2; |
189 | FT_Byte* p; |
190 | FT_Byte* p_limit; |
191 | |
192 | |
193 | pos1 = pos2 = 0; |
194 | |
195 | if ( gindex < ttface->num_locations ) |
196 | { |
197 | if ( ttface->header.Index_To_Loc_Format != 0 ) |
198 | { |
199 | p = ttface->glyph_locations + gindex * 4; |
200 | p_limit = ttface->glyph_locations + ttface->num_locations * 4; |
201 | |
202 | pos1 = FT_NEXT_ULONG( p ); |
203 | pos2 = pos1; |
204 | |
205 | if ( p + 4 <= p_limit ) |
206 | pos2 = FT_NEXT_ULONG( p ); |
207 | } |
208 | else |
209 | { |
210 | p = ttface->glyph_locations + gindex * 2; |
211 | p_limit = ttface->glyph_locations + ttface->num_locations * 2; |
212 | |
213 | pos1 = FT_NEXT_USHORT( p ); |
214 | pos2 = pos1; |
215 | |
216 | if ( p + 2 <= p_limit ) |
217 | pos2 = FT_NEXT_USHORT( p ); |
218 | |
219 | pos1 <<= 1; |
220 | pos2 <<= 1; |
221 | } |
222 | } |
223 | |
224 | /* Check broken location data. */ |
225 | if ( pos1 > ttface->glyf_len ) |
226 | { |
227 | FT_TRACE1(( "tt_face_get_location:" |
228 | " too large offset (0x%08lx) found for glyph index %d,\n" , |
229 | pos1, gindex )); |
230 | FT_TRACE1(( " " |
231 | " exceeding the end of `glyf' table (0x%08lx)\n" , |
232 | ttface->glyf_len )); |
233 | *asize = 0; |
234 | return 0; |
235 | } |
236 | |
237 | if ( pos2 > ttface->glyf_len ) |
238 | { |
239 | /* We try to sanitize the last `loca' entry. */ |
240 | if ( gindex == ttface->num_locations - 2 ) |
241 | { |
242 | FT_TRACE1(( "tt_face_get_location:" |
243 | " too large size (%ld bytes) found for glyph index %d,\n" , |
244 | pos2 - pos1, gindex )); |
245 | FT_TRACE1(( " " |
246 | " truncating at the end of `glyf' table to %ld bytes\n" , |
247 | ttface->glyf_len - pos1 )); |
248 | pos2 = ttface->glyf_len; |
249 | } |
250 | else |
251 | { |
252 | FT_TRACE1(( "tt_face_get_location:" |
253 | " too large offset (0x%08lx) found for glyph index %d,\n" , |
254 | pos2, gindex + 1 )); |
255 | FT_TRACE1(( " " |
256 | " exceeding the end of `glyf' table (0x%08lx)\n" , |
257 | ttface->glyf_len )); |
258 | *asize = 0; |
259 | return 0; |
260 | } |
261 | } |
262 | |
263 | /* The `loca' table must be ordered; it refers to the length of */ |
264 | /* an entry as the difference between the current and the next */ |
265 | /* position. However, there do exist (malformed) fonts which */ |
266 | /* don't obey this rule, so we are only able to provide an */ |
267 | /* upper bound for the size. */ |
268 | /* */ |
269 | /* We get (intentionally) a wrong, non-zero result in case the */ |
270 | /* `glyf' table is missing. */ |
271 | if ( pos2 >= pos1 ) |
272 | *asize = (FT_ULong)( pos2 - pos1 ); |
273 | else |
274 | *asize = (FT_ULong)( ttface->glyf_len - pos1 ); |
275 | |
276 | return pos1; |
277 | } |
278 | |
279 | |
280 | FT_LOCAL_DEF( void ) |
281 | tt_face_done_loca( TT_Face face ) |
282 | { |
283 | FT_Stream stream = face->root.stream; |
284 | |
285 | |
286 | FT_FRAME_RELEASE( face->glyph_locations ); |
287 | face->num_locations = 0; |
288 | } |
289 | |
290 | |
291 | |
292 | /************************************************************************** |
293 | * |
294 | * @Function: |
295 | * tt_face_load_cvt |
296 | * |
297 | * @Description: |
298 | * Load the control value table into a face object. |
299 | * |
300 | * @InOut: |
301 | * face :: |
302 | * A handle to the target face object. |
303 | * |
304 | * @Input: |
305 | * stream :: |
306 | * A handle to the input stream. |
307 | * |
308 | * @Return: |
309 | * FreeType error code. 0 means success. |
310 | */ |
311 | FT_LOCAL_DEF( FT_Error ) |
312 | tt_face_load_cvt( TT_Face face, |
313 | FT_Stream stream ) |
314 | { |
315 | #ifdef TT_USE_BYTECODE_INTERPRETER |
316 | |
317 | FT_Error error; |
318 | FT_Memory memory = stream->memory; |
319 | FT_ULong table_len; |
320 | |
321 | |
322 | FT_TRACE2(( "CVT " )); |
323 | |
324 | error = face->goto_table( face, TTAG_cvt, stream, &table_len ); |
325 | if ( error ) |
326 | { |
327 | FT_TRACE2(( "is missing\n" )); |
328 | |
329 | face->cvt_size = 0; |
330 | face->cvt = NULL; |
331 | error = FT_Err_Ok; |
332 | |
333 | goto Exit; |
334 | } |
335 | |
336 | face->cvt_size = table_len / 2; |
337 | |
338 | if ( FT_QNEW_ARRAY( face->cvt, face->cvt_size ) ) |
339 | goto Exit; |
340 | |
341 | if ( FT_FRAME_ENTER( face->cvt_size * 2L ) ) |
342 | goto Exit; |
343 | |
344 | { |
345 | FT_Int32* cur = face->cvt; |
346 | FT_Int32* limit = cur + face->cvt_size; |
347 | |
348 | |
349 | for ( ; cur < limit; cur++ ) |
350 | *cur = FT_GET_SHORT() * 64; |
351 | } |
352 | |
353 | FT_FRAME_EXIT(); |
354 | FT_TRACE2(( "loaded\n" )); |
355 | |
356 | #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
357 | if ( face->doblend ) |
358 | error = tt_face_vary_cvt( face, stream ); |
359 | #endif |
360 | |
361 | Exit: |
362 | return error; |
363 | |
364 | #else /* !TT_USE_BYTECODE_INTERPRETER */ |
365 | |
366 | FT_UNUSED( face ); |
367 | FT_UNUSED( stream ); |
368 | |
369 | return FT_Err_Ok; |
370 | |
371 | #endif |
372 | } |
373 | |
374 | |
375 | /************************************************************************** |
376 | * |
377 | * @Function: |
378 | * tt_face_load_fpgm |
379 | * |
380 | * @Description: |
381 | * Load the font program. |
382 | * |
383 | * @InOut: |
384 | * face :: |
385 | * A handle to the target face object. |
386 | * |
387 | * @Input: |
388 | * stream :: |
389 | * A handle to the input stream. |
390 | * |
391 | * @Return: |
392 | * FreeType error code. 0 means success. |
393 | */ |
394 | FT_LOCAL_DEF( FT_Error ) |
395 | tt_face_load_fpgm( TT_Face face, |
396 | FT_Stream stream ) |
397 | { |
398 | #ifdef TT_USE_BYTECODE_INTERPRETER |
399 | |
400 | FT_Error error; |
401 | FT_ULong table_len; |
402 | |
403 | |
404 | FT_TRACE2(( "Font program " )); |
405 | |
406 | /* The font program is optional */ |
407 | error = face->goto_table( face, TTAG_fpgm, stream, &table_len ); |
408 | if ( error ) |
409 | { |
410 | face->font_program = NULL; |
411 | face->font_program_size = 0; |
412 | error = FT_Err_Ok; |
413 | |
414 | FT_TRACE2(( "is missing\n" )); |
415 | } |
416 | else |
417 | { |
418 | face->font_program_size = table_len; |
419 | if ( FT_FRAME_EXTRACT( table_len, face->font_program ) ) |
420 | goto Exit; |
421 | |
422 | FT_TRACE2(( "loaded, %12ld bytes\n" , face->font_program_size )); |
423 | } |
424 | |
425 | Exit: |
426 | return error; |
427 | |
428 | #else /* !TT_USE_BYTECODE_INTERPRETER */ |
429 | |
430 | FT_UNUSED( face ); |
431 | FT_UNUSED( stream ); |
432 | |
433 | return FT_Err_Ok; |
434 | |
435 | #endif |
436 | } |
437 | |
438 | |
439 | /************************************************************************** |
440 | * |
441 | * @Function: |
442 | * tt_face_load_prep |
443 | * |
444 | * @Description: |
445 | * Load the cvt program. |
446 | * |
447 | * @InOut: |
448 | * face :: |
449 | * A handle to the target face object. |
450 | * |
451 | * @Input: |
452 | * stream :: |
453 | * A handle to the input stream. |
454 | * |
455 | * @Return: |
456 | * FreeType error code. 0 means success. |
457 | */ |
458 | FT_LOCAL_DEF( FT_Error ) |
459 | tt_face_load_prep( TT_Face face, |
460 | FT_Stream stream ) |
461 | { |
462 | #ifdef TT_USE_BYTECODE_INTERPRETER |
463 | |
464 | FT_Error error; |
465 | FT_ULong table_len; |
466 | |
467 | |
468 | FT_TRACE2(( "Prep program " )); |
469 | |
470 | error = face->goto_table( face, TTAG_prep, stream, &table_len ); |
471 | if ( error ) |
472 | { |
473 | face->cvt_program = NULL; |
474 | face->cvt_program_size = 0; |
475 | error = FT_Err_Ok; |
476 | |
477 | FT_TRACE2(( "is missing\n" )); |
478 | } |
479 | else |
480 | { |
481 | face->cvt_program_size = table_len; |
482 | if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) ) |
483 | goto Exit; |
484 | |
485 | FT_TRACE2(( "loaded, %12ld bytes\n" , face->cvt_program_size )); |
486 | } |
487 | |
488 | Exit: |
489 | return error; |
490 | |
491 | #else /* !TT_USE_BYTECODE_INTERPRETER */ |
492 | |
493 | FT_UNUSED( face ); |
494 | FT_UNUSED( stream ); |
495 | |
496 | return FT_Err_Ok; |
497 | |
498 | #endif |
499 | } |
500 | |
501 | |
502 | FT_COMPARE_DEF( int ) |
503 | compare_ppem( const void* a, |
504 | const void* b ) |
505 | { |
506 | return **(FT_Byte**)a - **(FT_Byte**)b; |
507 | } |
508 | |
509 | |
510 | /************************************************************************** |
511 | * |
512 | * @Function: |
513 | * tt_face_load_hdmx |
514 | * |
515 | * @Description: |
516 | * Load the `hdmx' table into the face object. |
517 | * |
518 | * @Input: |
519 | * face :: |
520 | * A handle to the target face object. |
521 | * |
522 | * stream :: |
523 | * A handle to the input stream. |
524 | * |
525 | * @Return: |
526 | * FreeType error code. 0 means success. |
527 | */ |
528 | |
529 | FT_LOCAL_DEF( FT_Error ) |
530 | tt_face_load_hdmx( TT_Face face, |
531 | FT_Stream stream ) |
532 | { |
533 | FT_Error error; |
534 | FT_Memory memory = stream->memory; |
535 | FT_UInt nn, num_records; |
536 | FT_ULong table_size, record_size; |
537 | FT_Byte* p; |
538 | FT_Byte* limit; |
539 | |
540 | |
541 | /* this table is optional */ |
542 | error = face->goto_table( face, TTAG_hdmx, stream, &table_size ); |
543 | if ( error || table_size < 8 ) |
544 | return FT_Err_Ok; |
545 | |
546 | if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) ) |
547 | goto Exit; |
548 | |
549 | p = face->hdmx_table; |
550 | limit = p + table_size; |
551 | |
552 | /* Given that `hdmx' tables are losing its importance (for example, */ |
553 | /* variation fonts introduced in OpenType 1.8 must not have this */ |
554 | /* table) we no longer test for a correct `version' field. */ |
555 | p += 2; |
556 | num_records = FT_NEXT_USHORT( p ); |
557 | record_size = FT_NEXT_ULONG( p ); |
558 | |
559 | /* There are at least two fonts, HANNOM-A and HANNOM-B version */ |
560 | /* 2.0 (2005), which get this wrong: The upper two bytes of */ |
561 | /* the size value are set to 0xFF instead of 0x00. We catch */ |
562 | /* and fix this. */ |
563 | |
564 | if ( record_size >= 0xFFFF0000UL ) |
565 | record_size &= 0xFFFFU; |
566 | |
567 | FT_TRACE2(( "Hdmx " )); |
568 | |
569 | /* The limit for `num_records' is a heuristic value. */ |
570 | if ( num_records > 255 || num_records == 0 ) |
571 | { |
572 | FT_TRACE2(( "with unreasonable %u records rejected\n" , num_records )); |
573 | goto Fail; |
574 | } |
575 | |
576 | /* Out-of-spec tables are rejected. The record size must be */ |
577 | /* equal to the number of glyphs + 2 + 32-bit padding. */ |
578 | if ( (FT_Long)record_size != ( ( face->root.num_glyphs + 2 + 3 ) & ~3 ) ) |
579 | { |
580 | FT_TRACE2(( "with record size off by %ld bytes rejected\n" , |
581 | (FT_Long)record_size - |
582 | ( ( face->root.num_glyphs + 2 + 3 ) & ~3 ) )); |
583 | goto Fail; |
584 | } |
585 | |
586 | if ( FT_QNEW_ARRAY( face->hdmx_records, num_records ) ) |
587 | goto Fail; |
588 | |
589 | for ( nn = 0; nn < num_records; nn++ ) |
590 | { |
591 | if ( p + record_size > limit ) |
592 | break; |
593 | face->hdmx_records[nn] = p; |
594 | p += record_size; |
595 | } |
596 | |
597 | /* The records must be already sorted by ppem but it does not */ |
598 | /* hurt to make sure so that the binary search works later. */ |
599 | ft_qsort( face->hdmx_records, nn, sizeof ( FT_Byte* ), compare_ppem ); |
600 | |
601 | face->hdmx_record_count = nn; |
602 | face->hdmx_table_size = table_size; |
603 | face->hdmx_record_size = record_size; |
604 | |
605 | FT_TRACE2(( "%ux%lu loaded\n" , num_records, record_size )); |
606 | |
607 | Exit: |
608 | return error; |
609 | |
610 | Fail: |
611 | FT_FRAME_RELEASE( face->hdmx_table ); |
612 | face->hdmx_table_size = 0; |
613 | goto Exit; |
614 | } |
615 | |
616 | |
617 | FT_LOCAL_DEF( void ) |
618 | tt_face_free_hdmx( TT_Face face ) |
619 | { |
620 | FT_Stream stream = face->root.stream; |
621 | FT_Memory memory = stream->memory; |
622 | |
623 | |
624 | FT_FREE( face->hdmx_records ); |
625 | FT_FRAME_RELEASE( face->hdmx_table ); |
626 | } |
627 | |
628 | |
629 | /************************************************************************** |
630 | * |
631 | * Return the advance width table for a given pixel size if it is found |
632 | * in the font's `hdmx' table (if any). The records must be sorted for |
633 | * the binary search to work properly. |
634 | */ |
635 | FT_LOCAL_DEF( FT_Byte* ) |
636 | tt_face_get_device_metrics( TT_Face face, |
637 | FT_UInt ppem, |
638 | FT_UInt gindex ) |
639 | { |
640 | FT_UInt min = 0; |
641 | FT_UInt max = face->hdmx_record_count; |
642 | FT_UInt mid; |
643 | FT_Byte* result = NULL; |
644 | |
645 | |
646 | while ( min < max ) |
647 | { |
648 | mid = ( min + max ) >> 1; |
649 | |
650 | if ( face->hdmx_records[mid][0] > ppem ) |
651 | max = mid; |
652 | else if ( face->hdmx_records[mid][0] < ppem ) |
653 | min = mid + 1; |
654 | else |
655 | { |
656 | result = face->hdmx_records[mid] + 2 + gindex; |
657 | break; |
658 | } |
659 | } |
660 | |
661 | return result; |
662 | } |
663 | |
664 | |
665 | /* END */ |
666 | |