sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
ttload.c
1/****************************************************************************
2 *
3 * ttload.c
4 *
5 * Load the basic TrueType tables, i.e., tables that can be either in
6 * TTF or OTF fonts (body).
7 *
8 * Copyright (C) 1996-2025 by
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
22#include <freetype/tttags.h>
23#include "ttload.h"
24
25#include "sferrors.h"
26
27
28 /**************************************************************************
29 *
30 * The macro FT_COMPONENT is used in trace mode. It is an implicit
31 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
32 * messages during execution.
33 */
34#undef FT_COMPONENT
35#define FT_COMPONENT ttload
36
37
38 /**************************************************************************
39 *
40 * @Function:
41 * tt_face_lookup_table
42 *
43 * @Description:
44 * Looks for a TrueType table by name.
45 *
46 * @Input:
47 * face ::
48 * A face object handle.
49 *
50 * tag ::
51 * The searched tag.
52 *
53 * @Return:
54 * A pointer to the table directory entry. 0 if not found.
55 */
56 FT_LOCAL_DEF( TT_Table )
57 tt_face_lookup_table( TT_Face face,
58 FT_ULong tag )
59 {
60 TT_Table entry;
61 TT_Table limit;
62#ifdef FT_DEBUG_LEVEL_TRACE
63 FT_Bool zero_length = FALSE;
64#endif
65
66
67 FT_TRACE4(( "tt_face_lookup_table: %p, `%c%c%c%c' -- ",
68 (void *)face,
69 (FT_Char)( tag >> 24 ),
70 (FT_Char)( tag >> 16 ),
71 (FT_Char)( tag >> 8 ),
72 (FT_Char)( tag ) ));
73
74 entry = face->dir_tables;
75 limit = entry + face->num_tables;
76
77 for ( ; entry < limit; entry++ )
78 {
79 /* For compatibility with Windows, we consider */
80 /* zero-length tables the same as missing tables. */
81 if ( entry->Tag == tag )
82 {
83 if ( entry->Length != 0 )
84 {
85 FT_TRACE4(( "found table.\n" ));
86 return entry;
87 }
88#ifdef FT_DEBUG_LEVEL_TRACE
89 zero_length = TRUE;
90#endif
91 }
92 }
93
94#ifdef FT_DEBUG_LEVEL_TRACE
95 if ( zero_length )
96 FT_TRACE4(( "ignoring empty table\n" ));
97 else
98 FT_TRACE4(( "could not find table\n" ));
99#endif
100
101 return NULL;
102 }
103
104
105 /**************************************************************************
106 *
107 * @Function:
108 * tt_face_goto_table
109 *
110 * @Description:
111 * Looks for a TrueType table by name, then seek a stream to it.
112 *
113 * @Input:
114 * face ::
115 * A face object handle.
116 *
117 * tag ::
118 * The searched tag.
119 *
120 * stream ::
121 * The stream to seek when the table is found.
122 *
123 * @Output:
124 * length ::
125 * The length of the table if found, undefined otherwise.
126 *
127 * @Return:
128 * FreeType error code. 0 means success.
129 */
130 FT_LOCAL_DEF( FT_Error )
131 tt_face_goto_table( TT_Face face,
132 FT_ULong tag,
133 FT_Stream stream,
134 FT_ULong* length )
135 {
136 TT_Table table;
137 FT_Error error;
138
139
140 table = tt_face_lookup_table( face, tag );
141 if ( table )
142 {
143 if ( length )
144 *length = table->Length;
145
146 if ( FT_STREAM_SEEK( table->Offset ) )
147 goto Exit;
148 }
149 else
150 error = FT_THROW( Table_Missing );
151
152 Exit:
153 return error;
154 }
155
156
157 /* Here, we */
158 /* */
159 /* - check that `num_tables' is valid (and adjust it if necessary); */
160 /* also return the number of valid table entries */
161 /* */
162 /* - look for a `head' table, check its size, and parse it to check */
163 /* whether its `magic' field is correctly set */
164 /* */
165 /* - errors (except errors returned by stream handling) */
166 /* */
167 /* SFNT_Err_Unknown_File_Format: */
168 /* no table is defined in directory, it is not sfnt-wrapped */
169 /* data */
170 /* SFNT_Err_Table_Missing: */
171 /* table directory is valid, but essential tables */
172 /* (head/bhed/SING) are missing */
173 /* */
174 static FT_Error
175 check_table_dir( SFNT_Header sfnt,
176 FT_Stream stream,
177 FT_UShort* valid )
178 {
179 FT_Error error;
180 FT_UShort nn, valid_entries = 0;
181 FT_UInt has_head = 0, has_sing = 0, has_meta = 0;
182 FT_ULong offset = sfnt->offset + 12;
183
184 static const FT_Frame_Field table_dir_entry_fields[] =
185 {
186#undef FT_STRUCTURE
187#define FT_STRUCTURE TT_TableRec
188
189 FT_FRAME_START( 16 ),
190 FT_FRAME_ULONG( Tag ),
191 FT_FRAME_ULONG( CheckSum ),
192 FT_FRAME_ULONG( Offset ),
193 FT_FRAME_ULONG( Length ),
194 FT_FRAME_END
195 };
196
197
198 if ( FT_STREAM_SEEK( offset ) )
199 goto Exit;
200
201 for ( nn = 0; nn < sfnt->num_tables; nn++ )
202 {
203 TT_TableRec table;
204
205
206 if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) )
207 {
208 FT_TRACE2(( "check_table_dir:"
209 " can read only %hu table%s in font (instead of %hu)\n",
210 nn, nn == 1 ? "" : "s", sfnt->num_tables ));
211 sfnt->num_tables = nn;
212 break;
213 }
214
215 /* we ignore invalid tables */
216
217 if ( table.Offset > stream->size )
218 {
219 FT_TRACE2(( "check_table_dir: table entry %hu invalid\n", nn ));
220 continue;
221 }
222 else if ( table.Length > stream->size - table.Offset )
223 {
224 /* Some tables have such a simple structure that clipping its */
225 /* contents is harmless. This also makes FreeType less sensitive */
226 /* to invalid table lengths (which programs like Acroread seem to */
227 /* ignore in general). */
228
229 if ( table.Tag == TTAG_hmtx ||
230 table.Tag == TTAG_vmtx )
231 valid_entries++;
232 else
233 {
234 FT_TRACE2(( "check_table_dir: table entry %hu invalid\n", nn ));
235 continue;
236 }
237 }
238 else
239 valid_entries++;
240
241 if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed )
242 {
243 FT_UInt32 magic;
244
245
246#ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
247 if ( table.Tag == TTAG_head )
248#endif
249 has_head = 1;
250
251 /*
252 * The table length should be 0x36, but certain font tools make it
253 * 0x38, so we will just check that it is greater.
254 *
255 * Note that according to the specification, the table must be
256 * padded to 32-bit lengths, but this doesn't apply to the value of
257 * its `Length' field!
258 *
259 */
260 if ( table.Length < 0x36 )
261 {
262 FT_TRACE2(( "check_table_dir:"
263 " `head' or `bhed' table too small\n" ));
264 error = FT_THROW( Table_Missing );
265 goto Exit;
266 }
267
268 if ( FT_STREAM_SEEK( table.Offset + 12 ) ||
269 FT_READ_ULONG( magic ) )
270 goto Exit;
271
272 if ( magic != 0x5F0F3CF5UL )
273 FT_TRACE2(( "check_table_dir:"
274 " invalid magic number in `head' or `bhed' table\n"));
275
276 if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) )
277 goto Exit;
278 }
279 else if ( table.Tag == TTAG_SING )
280 has_sing = 1;
281 else if ( table.Tag == TTAG_META )
282 has_meta = 1;
283 }
284
285 *valid = valid_entries;
286
287 if ( !valid_entries )
288 {
289 FT_TRACE2(( "check_table_dir: no valid tables found\n" ));
290 error = FT_THROW( Unknown_File_Format );
291 goto Exit;
292 }
293
294 /* if `sing' and `meta' tables are present, there is no `head' table */
295 if ( has_head || ( has_sing && has_meta ) )
296 {
297 error = FT_Err_Ok;
298 goto Exit;
299 }
300 else
301 {
302 FT_TRACE2(( "check_table_dir:" ));
303#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
304 FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" ));
305#else
306 FT_TRACE2(( " neither `head' nor `sing' table found\n" ));
307#endif
308 error = FT_THROW( Table_Missing );
309 }
310
311 Exit:
312 return error;
313 }
314
315
316 /**************************************************************************
317 *
318 * @Function:
319 * tt_face_load_font_dir
320 *
321 * @Description:
322 * Loads the header of a SFNT font file.
323 *
324 * @Input:
325 * face ::
326 * A handle to the target face object.
327 *
328 * stream ::
329 * The input stream.
330 *
331 * @Output:
332 * sfnt ::
333 * The SFNT header.
334 *
335 * @Return:
336 * FreeType error code. 0 means success.
337 *
338 * @Note:
339 * The stream cursor must be at the beginning of the font directory.
340 */
341 FT_LOCAL_DEF( FT_Error )
342 tt_face_load_font_dir( TT_Face face,
343 FT_Stream stream )
344 {
345 SFNT_HeaderRec sfnt;
346 FT_Error error;
347 FT_Memory memory = stream->memory;
348 FT_UShort nn, valid_entries = 0;
349
350 static const FT_Frame_Field offset_table_fields[] =
351 {
352#undef FT_STRUCTURE
353#define FT_STRUCTURE SFNT_HeaderRec
354
355 FT_FRAME_START( 8 ),
356 FT_FRAME_USHORT( num_tables ),
357 FT_FRAME_USHORT( search_range ),
358 FT_FRAME_USHORT( entry_selector ),
359 FT_FRAME_USHORT( range_shift ),
360 FT_FRAME_END
361 };
362
363
364 FT_TRACE2(( "tt_face_load_font_dir: %p\n", (void *)face ));
365
366 /* read the offset table */
367
368 sfnt.offset = FT_STREAM_POS();
369
370 if ( FT_READ_ULONG( sfnt.format_tag ) ||
371 FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) )
372 goto Exit;
373
374 /* many fonts don't have these fields set correctly */
375#if 0
376 if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 ) ||
377 sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 )
378 return FT_THROW( Unknown_File_Format );
379#endif
380
381 /* load the table directory */
382
383 FT_TRACE2(( "-- Number of tables: %10hu\n", sfnt.num_tables ));
384 FT_TRACE2(( "-- Format version: 0x%08lx\n", sfnt.format_tag ));
385
386 if ( sfnt.format_tag != TTAG_OTTO )
387 {
388 /* check first */
389 error = check_table_dir( &sfnt, stream, &valid_entries );
390 if ( error )
391 {
392 FT_TRACE2(( "tt_face_load_font_dir:"
393 " invalid table directory for TrueType\n" ));
394 goto Exit;
395 }
396 }
397 else
398 {
399 valid_entries = sfnt.num_tables;
400 if ( !valid_entries )
401 {
402 FT_TRACE2(( "tt_face_load_font_dir: no valid tables found\n" ));
403 error = FT_THROW( Unknown_File_Format );
404 goto Exit;
405 }
406 }
407
408 face->num_tables = valid_entries;
409 face->format_tag = sfnt.format_tag;
410
411 if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) )
412 goto Exit;
413
414 if ( FT_STREAM_SEEK( sfnt.offset + 12 ) ||
415 FT_FRAME_ENTER( sfnt.num_tables * 16L ) )
416 goto Exit;
417
418 FT_TRACE2(( "\n" ));
419 FT_TRACE2(( " tag offset length checksum\n" ));
420 FT_TRACE2(( " ----------------------------------\n" ));
421
422 valid_entries = 0;
423 for ( nn = 0; nn < sfnt.num_tables; nn++ )
424 {
425 TT_TableRec entry;
426 FT_UShort i;
427 FT_Bool duplicate;
428
429
430 entry.Tag = FT_GET_TAG4();
431 entry.CheckSum = FT_GET_ULONG();
432 entry.Offset = FT_GET_ULONG();
433 entry.Length = FT_GET_ULONG();
434
435 /* ignore invalid tables that can't be sanitized */
436
437 if ( entry.Offset > stream->size )
438 continue;
439 else if ( entry.Length > stream->size - entry.Offset )
440 {
441 if ( entry.Tag == TTAG_hmtx ||
442 entry.Tag == TTAG_vmtx )
443 {
444#ifdef FT_DEBUG_LEVEL_TRACE
445 FT_ULong old_length = entry.Length;
446#endif
447
448
449 /* make metrics table length a multiple of 4 */
450 entry.Length = ( stream->size - entry.Offset ) & ~3U;
451
452 FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx"
453 " (sanitized; original length %08lx)",
454 (FT_Char)( entry.Tag >> 24 ),
455 (FT_Char)( entry.Tag >> 16 ),
456 (FT_Char)( entry.Tag >> 8 ),
457 (FT_Char)( entry.Tag ),
458 entry.Offset,
459 entry.Length,
460 entry.CheckSum,
461 old_length ));
462 }
463 else
464 continue;
465 }
466#ifdef FT_DEBUG_LEVEL_TRACE
467 else
468 FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx",
469 (FT_Char)( entry.Tag >> 24 ),
470 (FT_Char)( entry.Tag >> 16 ),
471 (FT_Char)( entry.Tag >> 8 ),
472 (FT_Char)( entry.Tag ),
473 entry.Offset,
474 entry.Length,
475 entry.CheckSum ));
476#endif
477
478 /* ignore duplicate tables – the first one wins */
479 duplicate = 0;
480 for ( i = 0; i < valid_entries; i++ )
481 {
482 if ( face->dir_tables[i].Tag == entry.Tag )
483 {
484 duplicate = 1;
485 break;
486 }
487 }
488 if ( duplicate )
489 {
490 FT_TRACE2(( " (duplicate, ignored)\n" ));
491 continue;
492 }
493 else
494 {
495 FT_TRACE2(( "\n" ));
496
497 /* we finally have a valid entry */
498 face->dir_tables[valid_entries++] = entry;
499 }
500 }
501
502 /* final adjustment to number of tables */
503 face->num_tables = valid_entries;
504
505 FT_FRAME_EXIT();
506
507 if ( !valid_entries )
508 {
509 FT_TRACE2(( "tt_face_load_font_dir: no valid tables found\n" ));
510 error = FT_THROW( Unknown_File_Format );
511 goto Exit;
512 }
513
514 FT_TRACE2(( "table directory loaded\n" ));
515 FT_TRACE2(( "\n" ));
516
517 Exit:
518 return error;
519 }
520
521
522 /**************************************************************************
523 *
524 * @Function:
525 * tt_face_load_any
526 *
527 * @Description:
528 * Loads any font table into client memory.
529 *
530 * @Input:
531 * face ::
532 * The face object to look for.
533 *
534 * tag ::
535 * The tag of table to load. Use the value 0 if you want
536 * to access the whole font file, else set this parameter
537 * to a valid TrueType table tag that you can forge with
538 * the MAKE_TT_TAG macro. Use value 1 to access the table
539 * directory.
540 *
541 * offset ::
542 * The starting offset in the table (or the file if
543 * tag == 0).
544 *
545 * length ::
546 * The address of the decision variable:
547 *
548 * If length == NULL:
549 * Loads the whole table. Returns an error if
550 * `offset' == 0!
551 *
552 * If *length == 0:
553 * Exits immediately; returning the length of the given
554 * table or of the font file, depending on the value of
555 * `tag'.
556 *
557 * If *length != 0:
558 * Loads the next `length' bytes of table or font,
559 * starting at offset `offset' (in table or font too).
560 *
561 * @Output:
562 * buffer ::
563 * The address of target buffer.
564 *
565 * @Return:
566 * FreeType error code. 0 means success.
567 */
568 FT_LOCAL_DEF( FT_Error )
569 tt_face_load_any( TT_Face face,
570 FT_ULong tag,
571 FT_Long offset,
572 FT_Byte* buffer,
573 FT_ULong* length )
574 {
575 FT_Error error;
576 FT_Stream stream;
577 TT_Table table;
578 FT_ULong size;
579
580
581 if ( tag == 0 )
582 {
583 /* The whole font file. */
584 size = face->root.stream->size;
585 }
586 else if ( tag == 1 )
587 {
588 /* The currently selected font's table directory. */
589 /* */
590 /* Note that `face_index` is also used to enumerate elements */
591 /* of containers like a Mac Resource; this means we must */
592 /* check whether we actually have a TTC (with multiple table */
593 /* directories). */
594 FT_Long idx = face->root.face_index & 0xFFFF;
595
596
597 if ( idx >= face->ttc_header.count )
598 idx = 0;
599
600 offset += face->ttc_header.offsets[idx];
601 size = 4 + 8 + 16 * face->num_tables;
602 }
603 else
604 {
605 /* look for tag in font directory */
606 table = tt_face_lookup_table( face, tag );
607 if ( !table )
608 {
609 error = FT_THROW( Table_Missing );
610 goto Exit;
611 }
612
613 offset += table->Offset;
614 size = table->Length;
615 }
616
617 if ( length && *length == 0 )
618 {
619 *length = size;
620
621 return FT_Err_Ok;
622 }
623
624 if ( length )
625 size = *length;
626
627 stream = face->root.stream;
628 /* the `if' is syntactic sugar for picky compilers */
629 if ( FT_STREAM_READ_AT( offset, buffer, size ) )
630 goto Exit;
631
632 Exit:
633 return error;
634 }
635
636
637 /**************************************************************************
638 *
639 * @Function:
640 * tt_face_load_generic_header
641 *
642 * @Description:
643 * Loads the TrueType table `head' or `bhed'.
644 *
645 * @Input:
646 * face ::
647 * A handle to the target face object.
648 *
649 * stream ::
650 * The input stream.
651 *
652 * @Return:
653 * FreeType error code. 0 means success.
654 */
655 static FT_Error
656 tt_face_load_generic_header( TT_Face face,
657 FT_Stream stream,
658 FT_ULong tag )
659 {
660 FT_Error error;
661 TT_Header* header;
662
663 static const FT_Frame_Field header_fields[] =
664 {
665#undef FT_STRUCTURE
666#define FT_STRUCTURE TT_Header
667
668 FT_FRAME_START( 54 ),
669 FT_FRAME_ULONG ( Table_Version ),
670 FT_FRAME_ULONG ( Font_Revision ),
671 FT_FRAME_LONG ( CheckSum_Adjust ),
672 FT_FRAME_LONG ( Magic_Number ),
673 FT_FRAME_USHORT( Flags ),
674 FT_FRAME_USHORT( Units_Per_EM ),
675 FT_FRAME_ULONG ( Created[0] ),
676 FT_FRAME_ULONG ( Created[1] ),
677 FT_FRAME_ULONG ( Modified[0] ),
678 FT_FRAME_ULONG ( Modified[1] ),
679 FT_FRAME_SHORT ( xMin ),
680 FT_FRAME_SHORT ( yMin ),
681 FT_FRAME_SHORT ( xMax ),
682 FT_FRAME_SHORT ( yMax ),
683 FT_FRAME_USHORT( Mac_Style ),
684 FT_FRAME_USHORT( Lowest_Rec_PPEM ),
685 FT_FRAME_SHORT ( Font_Direction ),
686 FT_FRAME_SHORT ( Index_To_Loc_Format ),
687 FT_FRAME_SHORT ( Glyph_Data_Format ),
688 FT_FRAME_END
689 };
690
691
692 error = face->goto_table( face, tag, stream, 0 );
693 if ( error )
694 goto Exit;
695
696 header = &face->header;
697
698 if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
699 goto Exit;
700
701 FT_TRACE3(( "Units per EM: %4hu\n", header->Units_Per_EM ));
702 FT_TRACE3(( "IndexToLoc: %4hd\n", header->Index_To_Loc_Format ));
703
704 Exit:
705 return error;
706 }
707
708
709 FT_LOCAL_DEF( FT_Error )
710 tt_face_load_head( TT_Face face,
711 FT_Stream stream )
712 {
713 return tt_face_load_generic_header( face, stream, TTAG_head );
714 }
715
716
717#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
718
719 FT_LOCAL_DEF( FT_Error )
720 tt_face_load_bhed( TT_Face face,
721 FT_Stream stream )
722 {
723 return tt_face_load_generic_header( face, stream, TTAG_bhed );
724 }
725
726#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
727
728
729 /**************************************************************************
730 *
731 * @Function:
732 * tt_face_load_maxp
733 *
734 * @Description:
735 * Loads the maximum profile into a face object.
736 *
737 * @Input:
738 * face ::
739 * A handle to the target face object.
740 *
741 * stream ::
742 * The input stream.
743 *
744 * @Return:
745 * FreeType error code. 0 means success.
746 */
747 FT_LOCAL_DEF( FT_Error )
748 tt_face_load_maxp( TT_Face face,
749 FT_Stream stream )
750 {
751 FT_Error error;
752 TT_MaxProfile* maxProfile = &face->max_profile;
753
754 static const FT_Frame_Field maxp_fields[] =
755 {
756#undef FT_STRUCTURE
757#define FT_STRUCTURE TT_MaxProfile
758
759 FT_FRAME_START( 6 ),
760 FT_FRAME_LONG ( version ),
761 FT_FRAME_USHORT( numGlyphs ),
762 FT_FRAME_END
763 };
764
765 static const FT_Frame_Field maxp_fields_extra[] =
766 {
767 FT_FRAME_START( 26 ),
768 FT_FRAME_USHORT( maxPoints ),
769 FT_FRAME_USHORT( maxContours ),
770 FT_FRAME_USHORT( maxCompositePoints ),
771 FT_FRAME_USHORT( maxCompositeContours ),
772 FT_FRAME_USHORT( maxZones ),
773 FT_FRAME_USHORT( maxTwilightPoints ),
774 FT_FRAME_USHORT( maxStorage ),
775 FT_FRAME_USHORT( maxFunctionDefs ),
776 FT_FRAME_USHORT( maxInstructionDefs ),
777 FT_FRAME_USHORT( maxStackElements ),
778 FT_FRAME_USHORT( maxSizeOfInstructions ),
779 FT_FRAME_USHORT( maxComponentElements ),
780 FT_FRAME_USHORT( maxComponentDepth ),
781 FT_FRAME_END
782 };
783
784
785 error = face->goto_table( face, TTAG_maxp, stream, 0 );
786 if ( error )
787 goto Exit;
788
789 if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
790 goto Exit;
791
792 maxProfile->maxPoints = 0;
793 maxProfile->maxContours = 0;
794 maxProfile->maxCompositePoints = 0;
795 maxProfile->maxCompositeContours = 0;
796 maxProfile->maxZones = 0;
797 maxProfile->maxTwilightPoints = 0;
798 maxProfile->maxStorage = 0;
799 maxProfile->maxFunctionDefs = 0;
800 maxProfile->maxInstructionDefs = 0;
801 maxProfile->maxStackElements = 0;
802 maxProfile->maxSizeOfInstructions = 0;
803 maxProfile->maxComponentElements = 0;
804 maxProfile->maxComponentDepth = 0;
805
806 if ( maxProfile->version >= 0x10000L )
807 {
808 if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
809 goto Exit;
810
811 /* XXX: an adjustment that is necessary to load certain */
812 /* broken fonts like `Keystrokes MT' :-( */
813 /* */
814 /* We allocate 64 function entries by default when */
815 /* the maxFunctionDefs value is smaller. */
816
817 if ( maxProfile->maxFunctionDefs < 64 )
818 maxProfile->maxFunctionDefs = 64;
819
820 /* we add 4 phantom points later */
821 if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) )
822 {
823 FT_TRACE0(( "tt_face_load_maxp:"
824 " too much twilight points in `maxp' table;\n" ));
825 FT_TRACE0(( " "
826 " some glyphs might be rendered incorrectly\n" ));
827
828 maxProfile->maxTwilightPoints = 0xFFFFU - 4;
829 }
830 }
831
832 FT_TRACE3(( "numGlyphs: %hu\n", maxProfile->numGlyphs ));
833
834 Exit:
835 return error;
836 }
837
838
839 /**************************************************************************
840 *
841 * @Function:
842 * tt_face_load_name
843 *
844 * @Description:
845 * Loads the name records.
846 *
847 * @Input:
848 * face ::
849 * A handle to the target face object.
850 *
851 * stream ::
852 * The input stream.
853 *
854 * @Return:
855 * FreeType error code. 0 means success.
856 */
857 FT_LOCAL_DEF( FT_Error )
858 tt_face_load_name( TT_Face face,
859 FT_Stream stream )
860 {
861 FT_Error error;
862 FT_Memory memory = stream->memory;
863 FT_ULong table_pos, table_len;
864 FT_ULong storage_start, storage_limit;
865 TT_NameTable table;
866 TT_Name names = NULL;
867 TT_LangTag langTags = NULL;
868
869 static const FT_Frame_Field name_table_fields[] =
870 {
871#undef FT_STRUCTURE
872#define FT_STRUCTURE TT_NameTableRec
873
874 FT_FRAME_START( 6 ),
875 FT_FRAME_USHORT( format ),
876 FT_FRAME_USHORT( numNameRecords ),
877 FT_FRAME_USHORT( storageOffset ),
878 FT_FRAME_END
879 };
880
881 static const FT_Frame_Field name_record_fields[] =
882 {
883#undef FT_STRUCTURE
884#define FT_STRUCTURE TT_NameRec
885
886 /* no FT_FRAME_START */
887 FT_FRAME_USHORT( platformID ),
888 FT_FRAME_USHORT( encodingID ),
889 FT_FRAME_USHORT( languageID ),
890 FT_FRAME_USHORT( nameID ),
891 FT_FRAME_USHORT( stringLength ),
892 FT_FRAME_USHORT( stringOffset ),
893 FT_FRAME_END
894 };
895
896 static const FT_Frame_Field langTag_record_fields[] =
897 {
898#undef FT_STRUCTURE
899#define FT_STRUCTURE TT_LangTagRec
900
901 /* no FT_FRAME_START */
902 FT_FRAME_USHORT( stringLength ),
903 FT_FRAME_USHORT( stringOffset ),
904 FT_FRAME_END
905 };
906
907
908 table = &face->name_table;
909 table->stream = stream;
910
911 error = face->goto_table( face, TTAG_name, stream, &table_len );
912 if ( error )
913 goto Exit;
914
915 table_pos = FT_STREAM_POS();
916
917 if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
918 goto Exit;
919
920 /* Some popular Asian fonts have an invalid `storageOffset' value (it */
921 /* should be at least `6 + 12*numNameRecords'). However, the string */
922 /* offsets, computed as `storageOffset + entry->stringOffset', are */
923 /* valid pointers within the name table... */
924 /* */
925 /* We thus can't check `storageOffset' right now. */
926 /* */
927 storage_start = table_pos + 6 + 12 * table->numNameRecords;
928 storage_limit = table_pos + table_len;
929
930 if ( storage_start > storage_limit )
931 {
932 FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
933 error = FT_THROW( Name_Table_Missing );
934 goto Exit;
935 }
936
937 /* `name' format 1 contains additional language tag records, */
938 /* which we load first */
939 if ( table->format == 1 )
940 {
941 if ( FT_STREAM_SEEK( storage_start ) ||
942 FT_READ_USHORT( table->numLangTagRecords ) )
943 goto Exit;
944
945 storage_start += 2 + 4 * table->numLangTagRecords;
946
947 /* allocate language tag records array */
948 if ( FT_QNEW_ARRAY( langTags, table->numLangTagRecords ) ||
949 FT_FRAME_ENTER( table->numLangTagRecords * 4 ) )
950 goto Exit;
951
952 /* load language tags */
953 {
954 TT_LangTag entry = langTags;
955 TT_LangTag limit = FT_OFFSET( entry, table->numLangTagRecords );
956
957
958 for ( ; entry < limit; entry++ )
959 {
960 (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
961
962 /* check that the langTag string is within the table */
963 entry->stringOffset += table_pos + table->storageOffset;
964 if ( entry->stringOffset < storage_start ||
965 entry->stringOffset + entry->stringLength > storage_limit )
966 {
967 /* invalid entry; ignore it */
968 entry->stringLength = 0;
969 }
970
971 /* mark the string as not yet loaded */
972 entry->string = NULL;
973 }
974
975 table->langTags = langTags;
976 langTags = NULL;
977 }
978
979 FT_FRAME_EXIT();
980
981 (void)FT_STREAM_SEEK( table_pos + 6 );
982 }
983
984 /* allocate name records array */
985 if ( FT_QNEW_ARRAY( names, table->numNameRecords ) ||
986 FT_FRAME_ENTER( table->numNameRecords * 12 ) )
987 goto Exit;
988
989 /* load name records */
990 {
991 TT_Name entry = names;
992 FT_UInt count = table->numNameRecords;
993 FT_UInt valid = 0;
994
995
996 for ( ; count > 0; count-- )
997 {
998 if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
999 continue;
1000
1001 /* check that the name is not empty */
1002 if ( entry->stringLength == 0 )
1003 continue;
1004
1005 /* check that the name string is within the table */
1006 entry->stringOffset += table_pos + table->storageOffset;
1007 if ( entry->stringOffset < storage_start ||
1008 entry->stringOffset + entry->stringLength > storage_limit )
1009 {
1010 /* invalid entry; ignore it */
1011 continue;
1012 }
1013
1014 /* assure that we have a valid language tag ID, and */
1015 /* that the corresponding langTag entry is valid, too */
1016 if ( table->format == 1 && entry->languageID >= 0x8000U )
1017 {
1018 if ( entry->languageID - 0x8000U >= table->numLangTagRecords ||
1019 !table->langTags[entry->languageID - 0x8000U].stringLength )
1020 {
1021 /* invalid entry; ignore it */
1022 continue;
1023 }
1024 }
1025
1026 /* mark the string as not yet converted */
1027 entry->string = NULL;
1028
1029 valid++;
1030 entry++;
1031 }
1032
1033 /* reduce array size to the actually used elements */
1034 FT_MEM_QRENEW_ARRAY( names,
1035 table->numNameRecords,
1036 valid );
1037 table->names = names;
1038 names = NULL;
1039 table->numNameRecords = valid;
1040 }
1041
1042 FT_FRAME_EXIT();
1043
1044 /* everything went well, update face->num_names */
1045 face->num_names = (FT_UShort)table->numNameRecords;
1046
1047 Exit:
1048 FT_FREE( names );
1049 FT_FREE( langTags );
1050 return error;
1051 }
1052
1053
1054 /**************************************************************************
1055 *
1056 * @Function:
1057 * tt_face_free_name
1058 *
1059 * @Description:
1060 * Frees the name records.
1061 *
1062 * @Input:
1063 * face ::
1064 * A handle to the target face object.
1065 */
1066 FT_LOCAL_DEF( void )
1067 tt_face_free_name( TT_Face face )
1068 {
1069 FT_Memory memory = face->root.memory;
1070 TT_NameTable table = &face->name_table;
1071
1072
1073 if ( table->names )
1074 {
1075 TT_Name entry = table->names;
1076 TT_Name limit = entry + table->numNameRecords;
1077
1078
1079 for ( ; entry < limit; entry++ )
1080 FT_FREE( entry->string );
1081
1082 FT_FREE( table->names );
1083 }
1084
1085 if ( table->langTags )
1086 {
1087 TT_LangTag entry = table->langTags;
1088 TT_LangTag limit = entry + table->numLangTagRecords;
1089
1090
1091 for ( ; entry < limit; entry++ )
1092 FT_FREE( entry->string );
1093
1094 FT_FREE( table->langTags );
1095 }
1096
1097 table->numNameRecords = 0;
1098 table->numLangTagRecords = 0;
1099 table->format = 0;
1100 table->storageOffset = 0;
1101 }
1102
1103
1104 /**************************************************************************
1105 *
1106 * @Function:
1107 * tt_face_load_cmap
1108 *
1109 * @Description:
1110 * Loads the cmap directory in a face object. The cmaps themselves
1111 * are loaded on demand in the `ttcmap.c' module.
1112 *
1113 * @Input:
1114 * face ::
1115 * A handle to the target face object.
1116 *
1117 * stream ::
1118 * A handle to the input stream.
1119 *
1120 * @Return:
1121 * FreeType error code. 0 means success.
1122 */
1123
1124 FT_LOCAL_DEF( FT_Error )
1125 tt_face_load_cmap( TT_Face face,
1126 FT_Stream stream )
1127 {
1128 FT_Error error;
1129
1130
1131 error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
1132 if ( error )
1133 goto Exit;
1134
1135 if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
1136 face->cmap_size = 0;
1137
1138 Exit:
1139 return error;
1140 }
1141
1142
1143
1144 /**************************************************************************
1145 *
1146 * @Function:
1147 * tt_face_load_os2
1148 *
1149 * @Description:
1150 * Loads the OS2 table.
1151 *
1152 * @Input:
1153 * face ::
1154 * A handle to the target face object.
1155 *
1156 * stream ::
1157 * A handle to the input stream.
1158 *
1159 * @Return:
1160 * FreeType error code. 0 means success.
1161 */
1162 FT_LOCAL_DEF( FT_Error )
1163 tt_face_load_os2( TT_Face face,
1164 FT_Stream stream )
1165 {
1166 FT_Error error;
1167 TT_OS2* os2;
1168
1169 static const FT_Frame_Field os2_fields[] =
1170 {
1171#undef FT_STRUCTURE
1172#define FT_STRUCTURE TT_OS2
1173
1174 FT_FRAME_START( 78 ),
1175 FT_FRAME_USHORT( version ),
1176 FT_FRAME_SHORT ( xAvgCharWidth ),
1177 FT_FRAME_USHORT( usWeightClass ),
1178 FT_FRAME_USHORT( usWidthClass ),
1179 FT_FRAME_SHORT ( fsType ),
1180 FT_FRAME_SHORT ( ySubscriptXSize ),
1181 FT_FRAME_SHORT ( ySubscriptYSize ),
1182 FT_FRAME_SHORT ( ySubscriptXOffset ),
1183 FT_FRAME_SHORT ( ySubscriptYOffset ),
1184 FT_FRAME_SHORT ( ySuperscriptXSize ),
1185 FT_FRAME_SHORT ( ySuperscriptYSize ),
1186 FT_FRAME_SHORT ( ySuperscriptXOffset ),
1187 FT_FRAME_SHORT ( ySuperscriptYOffset ),
1188 FT_FRAME_SHORT ( yStrikeoutSize ),
1189 FT_FRAME_SHORT ( yStrikeoutPosition ),
1190 FT_FRAME_SHORT ( sFamilyClass ),
1191 FT_FRAME_BYTE ( panose[0] ),
1192 FT_FRAME_BYTE ( panose[1] ),
1193 FT_FRAME_BYTE ( panose[2] ),
1194 FT_FRAME_BYTE ( panose[3] ),
1195 FT_FRAME_BYTE ( panose[4] ),
1196 FT_FRAME_BYTE ( panose[5] ),
1197 FT_FRAME_BYTE ( panose[6] ),
1198 FT_FRAME_BYTE ( panose[7] ),
1199 FT_FRAME_BYTE ( panose[8] ),
1200 FT_FRAME_BYTE ( panose[9] ),
1201 FT_FRAME_ULONG ( ulUnicodeRange1 ),
1202 FT_FRAME_ULONG ( ulUnicodeRange2 ),
1203 FT_FRAME_ULONG ( ulUnicodeRange3 ),
1204 FT_FRAME_ULONG ( ulUnicodeRange4 ),
1205 FT_FRAME_BYTE ( achVendID[0] ),
1206 FT_FRAME_BYTE ( achVendID[1] ),
1207 FT_FRAME_BYTE ( achVendID[2] ),
1208 FT_FRAME_BYTE ( achVendID[3] ),
1209
1210 FT_FRAME_USHORT( fsSelection ),
1211 FT_FRAME_USHORT( usFirstCharIndex ),
1212 FT_FRAME_USHORT( usLastCharIndex ),
1213 FT_FRAME_SHORT ( sTypoAscender ),
1214 FT_FRAME_SHORT ( sTypoDescender ),
1215 FT_FRAME_SHORT ( sTypoLineGap ),
1216 FT_FRAME_USHORT( usWinAscent ),
1217 FT_FRAME_USHORT( usWinDescent ),
1218 FT_FRAME_END
1219 };
1220
1221 /* `OS/2' version 1 and newer */
1222 static const FT_Frame_Field os2_fields_extra1[] =
1223 {
1224 FT_FRAME_START( 8 ),
1225 FT_FRAME_ULONG( ulCodePageRange1 ),
1226 FT_FRAME_ULONG( ulCodePageRange2 ),
1227 FT_FRAME_END
1228 };
1229
1230 /* `OS/2' version 2 and newer */
1231 static const FT_Frame_Field os2_fields_extra2[] =
1232 {
1233 FT_FRAME_START( 10 ),
1234 FT_FRAME_SHORT ( sxHeight ),
1235 FT_FRAME_SHORT ( sCapHeight ),
1236 FT_FRAME_USHORT( usDefaultChar ),
1237 FT_FRAME_USHORT( usBreakChar ),
1238 FT_FRAME_USHORT( usMaxContext ),
1239 FT_FRAME_END
1240 };
1241
1242 /* `OS/2' version 5 and newer */
1243 static const FT_Frame_Field os2_fields_extra5[] =
1244 {
1245 FT_FRAME_START( 4 ),
1246 FT_FRAME_USHORT( usLowerOpticalPointSize ),
1247 FT_FRAME_USHORT( usUpperOpticalPointSize ),
1248 FT_FRAME_END
1249 };
1250
1251
1252 /* We now support old Mac fonts where the OS/2 table doesn't */
1253 /* exist. Simply put, we set the `version' field to 0xFFFF */
1254 /* and test this value each time we need to access the table. */
1255 error = face->goto_table( face, TTAG_OS2, stream, 0 );
1256 if ( error )
1257 goto Exit;
1258
1259 os2 = &face->os2;
1260
1261 if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
1262 goto Exit;
1263
1264 os2->ulCodePageRange1 = 0;
1265 os2->ulCodePageRange2 = 0;
1266 os2->sxHeight = 0;
1267 os2->sCapHeight = 0;
1268 os2->usDefaultChar = 0;
1269 os2->usBreakChar = 0;
1270 os2->usMaxContext = 0;
1271 os2->usLowerOpticalPointSize = 0;
1272 os2->usUpperOpticalPointSize = 0xFFFF;
1273
1274 if ( os2->version >= 0x0001 )
1275 {
1276 /* only version 1 tables */
1277 if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
1278 goto Exit;
1279
1280 if ( os2->version >= 0x0002 )
1281 {
1282 /* only version 2 tables */
1283 if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
1284 goto Exit;
1285
1286 if ( os2->version >= 0x0005 )
1287 {
1288 /* only version 5 tables */
1289 if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
1290 goto Exit;
1291 }
1292 }
1293 }
1294
1295 FT_TRACE3(( "sTypoAscender: %4hd\n", os2->sTypoAscender ));
1296 FT_TRACE3(( "sTypoDescender: %4hd\n", os2->sTypoDescender ));
1297 FT_TRACE3(( "usWinAscent: %4hu\n", os2->usWinAscent ));
1298 FT_TRACE3(( "usWinDescent: %4hu\n", os2->usWinDescent ));
1299 FT_TRACE3(( "fsSelection: 0x%2hx\n", os2->fsSelection ));
1300
1301 Exit:
1302 return error;
1303 }
1304
1305
1306 /**************************************************************************
1307 *
1308 * @Function:
1309 * tt_face_load_postscript
1310 *
1311 * @Description:
1312 * Loads the Postscript table.
1313 *
1314 * @Input:
1315 * face ::
1316 * A handle to the target face object.
1317 *
1318 * stream ::
1319 * A handle to the input stream.
1320 *
1321 * @Return:
1322 * FreeType error code. 0 means success.
1323 */
1324 FT_LOCAL_DEF( FT_Error )
1325 tt_face_load_post( TT_Face face,
1326 FT_Stream stream )
1327 {
1328 FT_Error error;
1329 TT_Postscript* post = &face->postscript;
1330
1331 static const FT_Frame_Field post_fields[] =
1332 {
1333#undef FT_STRUCTURE
1334#define FT_STRUCTURE TT_Postscript
1335
1336 FT_FRAME_START( 32 ),
1337 FT_FRAME_LONG ( FormatType ),
1338 FT_FRAME_LONG ( italicAngle ),
1339 FT_FRAME_SHORT( underlinePosition ),
1340 FT_FRAME_SHORT( underlineThickness ),
1341 FT_FRAME_ULONG( isFixedPitch ),
1342 FT_FRAME_ULONG( minMemType42 ),
1343 FT_FRAME_ULONG( maxMemType42 ),
1344 FT_FRAME_ULONG( minMemType1 ),
1345 FT_FRAME_ULONG( maxMemType1 ),
1346 FT_FRAME_END
1347 };
1348
1349
1350 error = face->goto_table( face, TTAG_post, stream, 0 );
1351 if ( error )
1352 return error;
1353
1354 if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
1355 return error;
1356
1357 if ( post->FormatType != 0x00030000L &&
1358 post->FormatType != 0x00025000L &&
1359 post->FormatType != 0x00020000L &&
1360 post->FormatType != 0x00010000L )
1361 return FT_THROW( Invalid_Post_Table_Format );
1362
1363 /* we don't load the glyph names, we do that in another */
1364 /* module (ttpost). */
1365
1366 FT_TRACE3(( "FormatType: 0x%lx\n", post->FormatType ));
1367 FT_TRACE3(( "isFixedPitch: %s\n", post->isFixedPitch
1368 ? " yes" : " no" ));
1369
1370 return FT_Err_Ok;
1371 }
1372
1373
1374 /**************************************************************************
1375 *
1376 * @Function:
1377 * tt_face_load_pclt
1378 *
1379 * @Description:
1380 * Loads the PCL 5 Table.
1381 *
1382 * @Input:
1383 * face ::
1384 * A handle to the target face object.
1385 *
1386 * stream ::
1387 * A handle to the input stream.
1388 *
1389 * @Return:
1390 * FreeType error code. 0 means success.
1391 */
1392 FT_LOCAL_DEF( FT_Error )
1393 tt_face_load_pclt( TT_Face face,
1394 FT_Stream stream )
1395 {
1396 static const FT_Frame_Field pclt_fields[] =
1397 {
1398#undef FT_STRUCTURE
1399#define FT_STRUCTURE TT_PCLT
1400
1401 FT_FRAME_START( 54 ),
1402 FT_FRAME_ULONG ( Version ),
1403 FT_FRAME_ULONG ( FontNumber ),
1404 FT_FRAME_USHORT( Pitch ),
1405 FT_FRAME_USHORT( xHeight ),
1406 FT_FRAME_USHORT( Style ),
1407 FT_FRAME_USHORT( TypeFamily ),
1408 FT_FRAME_USHORT( CapHeight ),
1409 FT_FRAME_USHORT( SymbolSet ),
1410 FT_FRAME_BYTES ( TypeFace, 16 ),
1411 FT_FRAME_BYTES ( CharacterComplement, 8 ),
1412 FT_FRAME_BYTES ( FileName, 6 ),
1413 FT_FRAME_CHAR ( StrokeWeight ),
1414 FT_FRAME_CHAR ( WidthType ),
1415 FT_FRAME_BYTE ( SerifStyle ),
1416 FT_FRAME_BYTE ( Reserved ),
1417 FT_FRAME_END
1418 };
1419
1420 FT_Error error;
1421 TT_PCLT* pclt = &face->pclt;
1422
1423
1424 /* optional table */
1425 error = face->goto_table( face, TTAG_PCLT, stream, 0 );
1426 if ( error )
1427 goto Exit;
1428
1429 if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
1430 goto Exit;
1431
1432 Exit:
1433 return error;
1434 }
1435
1436
1437 /**************************************************************************
1438 *
1439 * @Function:
1440 * tt_face_load_gasp
1441 *
1442 * @Description:
1443 * Loads the `gasp' table into a face object.
1444 *
1445 * @Input:
1446 * face ::
1447 * A handle to the target face object.
1448 *
1449 * stream ::
1450 * The input stream.
1451 *
1452 * @Return:
1453 * FreeType error code. 0 means success.
1454 */
1455 FT_LOCAL_DEF( FT_Error )
1456 tt_face_load_gasp( TT_Face face,
1457 FT_Stream stream )
1458 {
1459 FT_Error error;
1460 FT_Memory memory = stream->memory;
1461
1462 FT_UShort j, num_ranges;
1463 TT_GaspRange gasp_ranges = NULL;
1464
1465
1466 /* the gasp table is optional */
1467 error = face->goto_table( face, TTAG_gasp, stream, 0 );
1468 if ( error )
1469 goto Exit;
1470
1471 if ( FT_FRAME_ENTER( 4L ) )
1472 goto Exit;
1473
1474 face->gasp.version = FT_GET_USHORT();
1475 num_ranges = FT_GET_USHORT();
1476
1477 FT_FRAME_EXIT();
1478
1479 /* only support versions 0 and 1 of the table */
1480 if ( face->gasp.version >= 2 )
1481 {
1482 face->gasp.numRanges = 0;
1483 error = FT_THROW( Invalid_Table );
1484 goto Exit;
1485 }
1486
1487 FT_TRACE3(( "numRanges: %hu\n", num_ranges ));
1488
1489 if ( FT_QNEW_ARRAY( gasp_ranges, num_ranges ) ||
1490 FT_FRAME_ENTER( num_ranges * 4L ) )
1491 goto Exit;
1492
1493 for ( j = 0; j < num_ranges; j++ )
1494 {
1495 gasp_ranges[j].maxPPEM = FT_GET_USHORT();
1496 gasp_ranges[j].gaspFlag = FT_GET_USHORT();
1497
1498 FT_TRACE3(( "gaspRange %hu: rangeMaxPPEM %5hu, rangeGaspBehavior 0x%hx\n",
1499 j,
1500 gasp_ranges[j].maxPPEM,
1501 gasp_ranges[j].gaspFlag ));
1502 }
1503
1504 face->gasp.gaspRanges = gasp_ranges;
1505 gasp_ranges = NULL;
1506 face->gasp.numRanges = num_ranges;
1507
1508 FT_FRAME_EXIT();
1509
1510 Exit:
1511 FT_FREE( gasp_ranges );
1512 return error;
1513 }
1514
1515
1516/* END */