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-2023 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
20#include <freetype/internal/ftdebug.h>
21#include <freetype/internal/ftstream.h>
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.
539 *
540 * offset ::
541 * The starting offset in the table (or the file if
542 * tag == 0).
543 *
544 * length ::
545 * The address of the decision variable:
546 *
547 * If length == NULL:
548 * Loads the whole table. Returns an error if
549 * `offset' == 0!
550 *
551 * If *length == 0:
552 * Exits immediately; returning the length of the given
553 * table or of the font file, depending on the value of
554 * `tag'.
555 *
556 * If *length != 0:
557 * Loads the next `length' bytes of table or font,
558 * starting at offset `offset' (in table or font too).
559 *
560 * @Output:
561 * buffer ::
562 * The address of target buffer.
563 *
564 * @Return:
565 * FreeType error code. 0 means success.
566 */
567 FT_LOCAL_DEF( FT_Error )
568 tt_face_load_any( TT_Face face,
569 FT_ULong tag,
570 FT_Long offset,
571 FT_Byte* buffer,
572 FT_ULong* length )
573 {
574 FT_Error error;
575 FT_Stream stream;
576 TT_Table table;
577 FT_ULong size;
578
579
580 if ( tag != 0 )
581 {
582 /* look for tag in font directory */
583 table = tt_face_lookup_table( face, tag );
584 if ( !table )
585 {
586 error = FT_THROW( Table_Missing );
587 goto Exit;
588 }
589
590 offset += table->Offset;
591 size = table->Length;
592 }
593 else
594 /* tag == 0 -- the user wants to access the font file directly */
595 size = face->root.stream->size;
596
597 if ( length && *length == 0 )
598 {
599 *length = size;
600
601 return FT_Err_Ok;
602 }
603
604 if ( length )
605 size = *length;
606
607 stream = face->root.stream;
608 /* the `if' is syntactic sugar for picky compilers */
609 if ( FT_STREAM_READ_AT( offset, buffer, size ) )
610 goto Exit;
611
612 Exit:
613 return error;
614 }
615
616
617 /**************************************************************************
618 *
619 * @Function:
620 * tt_face_load_generic_header
621 *
622 * @Description:
623 * Loads the TrueType table `head' or `bhed'.
624 *
625 * @Input:
626 * face ::
627 * A handle to the target face object.
628 *
629 * stream ::
630 * The input stream.
631 *
632 * @Return:
633 * FreeType error code. 0 means success.
634 */
635 static FT_Error
636 tt_face_load_generic_header( TT_Face face,
637 FT_Stream stream,
638 FT_ULong tag )
639 {
640 FT_Error error;
641 TT_Header* header;
642
643 static const FT_Frame_Field header_fields[] =
644 {
645#undef FT_STRUCTURE
646#define FT_STRUCTURE TT_Header
647
648 FT_FRAME_START( 54 ),
649 FT_FRAME_ULONG ( Table_Version ),
650 FT_FRAME_ULONG ( Font_Revision ),
651 FT_FRAME_LONG ( CheckSum_Adjust ),
652 FT_FRAME_LONG ( Magic_Number ),
653 FT_FRAME_USHORT( Flags ),
654 FT_FRAME_USHORT( Units_Per_EM ),
655 FT_FRAME_ULONG ( Created[0] ),
656 FT_FRAME_ULONG ( Created[1] ),
657 FT_FRAME_ULONG ( Modified[0] ),
658 FT_FRAME_ULONG ( Modified[1] ),
659 FT_FRAME_SHORT ( xMin ),
660 FT_FRAME_SHORT ( yMin ),
661 FT_FRAME_SHORT ( xMax ),
662 FT_FRAME_SHORT ( yMax ),
663 FT_FRAME_USHORT( Mac_Style ),
664 FT_FRAME_USHORT( Lowest_Rec_PPEM ),
665 FT_FRAME_SHORT ( Font_Direction ),
666 FT_FRAME_SHORT ( Index_To_Loc_Format ),
667 FT_FRAME_SHORT ( Glyph_Data_Format ),
668 FT_FRAME_END
669 };
670
671
672 error = face->goto_table( face, tag, stream, 0 );
673 if ( error )
674 goto Exit;
675
676 header = &face->header;
677
678 if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
679 goto Exit;
680
681 FT_TRACE3(( "Units per EM: %4hu\n", header->Units_Per_EM ));
682 FT_TRACE3(( "IndexToLoc: %4hd\n", header->Index_To_Loc_Format ));
683
684 Exit:
685 return error;
686 }
687
688
689 FT_LOCAL_DEF( FT_Error )
690 tt_face_load_head( TT_Face face,
691 FT_Stream stream )
692 {
693 return tt_face_load_generic_header( face, stream, TTAG_head );
694 }
695
696
697#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
698
699 FT_LOCAL_DEF( FT_Error )
700 tt_face_load_bhed( TT_Face face,
701 FT_Stream stream )
702 {
703 return tt_face_load_generic_header( face, stream, TTAG_bhed );
704 }
705
706#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
707
708
709 /**************************************************************************
710 *
711 * @Function:
712 * tt_face_load_maxp
713 *
714 * @Description:
715 * Loads the maximum profile into a face object.
716 *
717 * @Input:
718 * face ::
719 * A handle to the target face object.
720 *
721 * stream ::
722 * The input stream.
723 *
724 * @Return:
725 * FreeType error code. 0 means success.
726 */
727 FT_LOCAL_DEF( FT_Error )
728 tt_face_load_maxp( TT_Face face,
729 FT_Stream stream )
730 {
731 FT_Error error;
732 TT_MaxProfile* maxProfile = &face->max_profile;
733
734 static const FT_Frame_Field maxp_fields[] =
735 {
736#undef FT_STRUCTURE
737#define FT_STRUCTURE TT_MaxProfile
738
739 FT_FRAME_START( 6 ),
740 FT_FRAME_LONG ( version ),
741 FT_FRAME_USHORT( numGlyphs ),
742 FT_FRAME_END
743 };
744
745 static const FT_Frame_Field maxp_fields_extra[] =
746 {
747 FT_FRAME_START( 26 ),
748 FT_FRAME_USHORT( maxPoints ),
749 FT_FRAME_USHORT( maxContours ),
750 FT_FRAME_USHORT( maxCompositePoints ),
751 FT_FRAME_USHORT( maxCompositeContours ),
752 FT_FRAME_USHORT( maxZones ),
753 FT_FRAME_USHORT( maxTwilightPoints ),
754 FT_FRAME_USHORT( maxStorage ),
755 FT_FRAME_USHORT( maxFunctionDefs ),
756 FT_FRAME_USHORT( maxInstructionDefs ),
757 FT_FRAME_USHORT( maxStackElements ),
758 FT_FRAME_USHORT( maxSizeOfInstructions ),
759 FT_FRAME_USHORT( maxComponentElements ),
760 FT_FRAME_USHORT( maxComponentDepth ),
761 FT_FRAME_END
762 };
763
764
765 error = face->goto_table( face, TTAG_maxp, stream, 0 );
766 if ( error )
767 goto Exit;
768
769 if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
770 goto Exit;
771
772 maxProfile->maxPoints = 0;
773 maxProfile->maxContours = 0;
774 maxProfile->maxCompositePoints = 0;
775 maxProfile->maxCompositeContours = 0;
776 maxProfile->maxZones = 0;
777 maxProfile->maxTwilightPoints = 0;
778 maxProfile->maxStorage = 0;
779 maxProfile->maxFunctionDefs = 0;
780 maxProfile->maxInstructionDefs = 0;
781 maxProfile->maxStackElements = 0;
782 maxProfile->maxSizeOfInstructions = 0;
783 maxProfile->maxComponentElements = 0;
784 maxProfile->maxComponentDepth = 0;
785
786 if ( maxProfile->version >= 0x10000L )
787 {
788 if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
789 goto Exit;
790
791 /* XXX: an adjustment that is necessary to load certain */
792 /* broken fonts like `Keystrokes MT' :-( */
793 /* */
794 /* We allocate 64 function entries by default when */
795 /* the maxFunctionDefs value is smaller. */
796
797 if ( maxProfile->maxFunctionDefs < 64 )
798 maxProfile->maxFunctionDefs = 64;
799
800 /* we add 4 phantom points later */
801 if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) )
802 {
803 FT_TRACE0(( "tt_face_load_maxp:"
804 " too much twilight points in `maxp' table;\n" ));
805 FT_TRACE0(( " "
806 " some glyphs might be rendered incorrectly\n" ));
807
808 maxProfile->maxTwilightPoints = 0xFFFFU - 4;
809 }
810 }
811
812 FT_TRACE3(( "numGlyphs: %hu\n", maxProfile->numGlyphs ));
813
814 Exit:
815 return error;
816 }
817
818
819 /**************************************************************************
820 *
821 * @Function:
822 * tt_face_load_name
823 *
824 * @Description:
825 * Loads the name records.
826 *
827 * @Input:
828 * face ::
829 * A handle to the target face object.
830 *
831 * stream ::
832 * The input stream.
833 *
834 * @Return:
835 * FreeType error code. 0 means success.
836 */
837 FT_LOCAL_DEF( FT_Error )
838 tt_face_load_name( TT_Face face,
839 FT_Stream stream )
840 {
841 FT_Error error;
842 FT_Memory memory = stream->memory;
843 FT_ULong table_pos, table_len;
844 FT_ULong storage_start, storage_limit;
845 TT_NameTable table;
846 TT_Name names = NULL;
847 TT_LangTag langTags = NULL;
848
849 static const FT_Frame_Field name_table_fields[] =
850 {
851#undef FT_STRUCTURE
852#define FT_STRUCTURE TT_NameTableRec
853
854 FT_FRAME_START( 6 ),
855 FT_FRAME_USHORT( format ),
856 FT_FRAME_USHORT( numNameRecords ),
857 FT_FRAME_USHORT( storageOffset ),
858 FT_FRAME_END
859 };
860
861 static const FT_Frame_Field name_record_fields[] =
862 {
863#undef FT_STRUCTURE
864#define FT_STRUCTURE TT_NameRec
865
866 /* no FT_FRAME_START */
867 FT_FRAME_USHORT( platformID ),
868 FT_FRAME_USHORT( encodingID ),
869 FT_FRAME_USHORT( languageID ),
870 FT_FRAME_USHORT( nameID ),
871 FT_FRAME_USHORT( stringLength ),
872 FT_FRAME_USHORT( stringOffset ),
873 FT_FRAME_END
874 };
875
876 static const FT_Frame_Field langTag_record_fields[] =
877 {
878#undef FT_STRUCTURE
879#define FT_STRUCTURE TT_LangTagRec
880
881 /* no FT_FRAME_START */
882 FT_FRAME_USHORT( stringLength ),
883 FT_FRAME_USHORT( stringOffset ),
884 FT_FRAME_END
885 };
886
887
888 table = &face->name_table;
889 table->stream = stream;
890
891 error = face->goto_table( face, TTAG_name, stream, &table_len );
892 if ( error )
893 goto Exit;
894
895 table_pos = FT_STREAM_POS();
896
897 if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
898 goto Exit;
899
900 /* Some popular Asian fonts have an invalid `storageOffset' value (it */
901 /* should be at least `6 + 12*numNameRecords'). However, the string */
902 /* offsets, computed as `storageOffset + entry->stringOffset', are */
903 /* valid pointers within the name table... */
904 /* */
905 /* We thus can't check `storageOffset' right now. */
906 /* */
907 storage_start = table_pos + 6 + 12 * table->numNameRecords;
908 storage_limit = table_pos + table_len;
909
910 if ( storage_start > storage_limit )
911 {
912 FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
913 error = FT_THROW( Name_Table_Missing );
914 goto Exit;
915 }
916
917 /* `name' format 1 contains additional language tag records, */
918 /* which we load first */
919 if ( table->format == 1 )
920 {
921 if ( FT_STREAM_SEEK( storage_start ) ||
922 FT_READ_USHORT( table->numLangTagRecords ) )
923 goto Exit;
924
925 storage_start += 2 + 4 * table->numLangTagRecords;
926
927 /* allocate language tag records array */
928 if ( FT_QNEW_ARRAY( langTags, table->numLangTagRecords ) ||
929 FT_FRAME_ENTER( table->numLangTagRecords * 4 ) )
930 goto Exit;
931
932 /* load language tags */
933 {
934 TT_LangTag entry = langTags;
935 TT_LangTag limit = FT_OFFSET( entry, table->numLangTagRecords );
936
937
938 for ( ; entry < limit; entry++ )
939 {
940 (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
941
942 /* check that the langTag string is within the table */
943 entry->stringOffset += table_pos + table->storageOffset;
944 if ( entry->stringOffset < storage_start ||
945 entry->stringOffset + entry->stringLength > storage_limit )
946 {
947 /* invalid entry; ignore it */
948 entry->stringLength = 0;
949 }
950
951 /* mark the string as not yet loaded */
952 entry->string = NULL;
953 }
954
955 table->langTags = langTags;
956 langTags = NULL;
957 }
958
959 FT_FRAME_EXIT();
960
961 (void)FT_STREAM_SEEK( table_pos + 6 );
962 }
963
964 /* allocate name records array */
965 if ( FT_QNEW_ARRAY( names, table->numNameRecords ) ||
966 FT_FRAME_ENTER( table->numNameRecords * 12 ) )
967 goto Exit;
968
969 /* load name records */
970 {
971 TT_Name entry = names;
972 FT_UInt count = table->numNameRecords;
973 FT_UInt valid = 0;
974
975
976 for ( ; count > 0; count-- )
977 {
978 if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
979 continue;
980
981 /* check that the name is not empty */
982 if ( entry->stringLength == 0 )
983 continue;
984
985 /* check that the name string is within the table */
986 entry->stringOffset += table_pos + table->storageOffset;
987 if ( entry->stringOffset < storage_start ||
988 entry->stringOffset + entry->stringLength > storage_limit )
989 {
990 /* invalid entry; ignore it */
991 continue;
992 }
993
994 /* assure that we have a valid language tag ID, and */
995 /* that the corresponding langTag entry is valid, too */
996 if ( table->format == 1 && entry->languageID >= 0x8000U )
997 {
998 if ( entry->languageID - 0x8000U >= table->numLangTagRecords ||
999 !table->langTags[entry->languageID - 0x8000U].stringLength )
1000 {
1001 /* invalid entry; ignore it */
1002 continue;
1003 }
1004 }
1005
1006 /* mark the string as not yet converted */
1007 entry->string = NULL;
1008
1009 valid++;
1010 entry++;
1011 }
1012
1013 /* reduce array size to the actually used elements */
1014 FT_MEM_QRENEW_ARRAY( names,
1015 table->numNameRecords,
1016 valid );
1017 table->names = names;
1018 names = NULL;
1019 table->numNameRecords = valid;
1020 }
1021
1022 FT_FRAME_EXIT();
1023
1024 /* everything went well, update face->num_names */
1025 face->num_names = (FT_UShort)table->numNameRecords;
1026
1027 Exit:
1028 FT_FREE( names );
1029 FT_FREE( langTags );
1030 return error;
1031 }
1032
1033
1034 /**************************************************************************
1035 *
1036 * @Function:
1037 * tt_face_free_name
1038 *
1039 * @Description:
1040 * Frees the name records.
1041 *
1042 * @Input:
1043 * face ::
1044 * A handle to the target face object.
1045 */
1046 FT_LOCAL_DEF( void )
1047 tt_face_free_name( TT_Face face )
1048 {
1049 FT_Memory memory = face->root.driver->root.memory;
1050 TT_NameTable table = &face->name_table;
1051
1052
1053 if ( table->names )
1054 {
1055 TT_Name entry = table->names;
1056 TT_Name limit = entry + table->numNameRecords;
1057
1058
1059 for ( ; entry < limit; entry++ )
1060 FT_FREE( entry->string );
1061
1062 FT_FREE( table->names );
1063 }
1064
1065 if ( table->langTags )
1066 {
1067 TT_LangTag entry = table->langTags;
1068 TT_LangTag limit = entry + table->numLangTagRecords;
1069
1070
1071 for ( ; entry < limit; entry++ )
1072 FT_FREE( entry->string );
1073
1074 FT_FREE( table->langTags );
1075 }
1076
1077 table->numNameRecords = 0;
1078 table->numLangTagRecords = 0;
1079 table->format = 0;
1080 table->storageOffset = 0;
1081 }
1082
1083
1084 /**************************************************************************
1085 *
1086 * @Function:
1087 * tt_face_load_cmap
1088 *
1089 * @Description:
1090 * Loads the cmap directory in a face object. The cmaps themselves
1091 * are loaded on demand in the `ttcmap.c' module.
1092 *
1093 * @Input:
1094 * face ::
1095 * A handle to the target face object.
1096 *
1097 * stream ::
1098 * A handle to the input stream.
1099 *
1100 * @Return:
1101 * FreeType error code. 0 means success.
1102 */
1103
1104 FT_LOCAL_DEF( FT_Error )
1105 tt_face_load_cmap( TT_Face face,
1106 FT_Stream stream )
1107 {
1108 FT_Error error;
1109
1110
1111 error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
1112 if ( error )
1113 goto Exit;
1114
1115 if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
1116 face->cmap_size = 0;
1117
1118 Exit:
1119 return error;
1120 }
1121
1122
1123
1124 /**************************************************************************
1125 *
1126 * @Function:
1127 * tt_face_load_os2
1128 *
1129 * @Description:
1130 * Loads the OS2 table.
1131 *
1132 * @Input:
1133 * face ::
1134 * A handle to the target face object.
1135 *
1136 * stream ::
1137 * A handle to the input stream.
1138 *
1139 * @Return:
1140 * FreeType error code. 0 means success.
1141 */
1142 FT_LOCAL_DEF( FT_Error )
1143 tt_face_load_os2( TT_Face face,
1144 FT_Stream stream )
1145 {
1146 FT_Error error;
1147 TT_OS2* os2;
1148
1149 static const FT_Frame_Field os2_fields[] =
1150 {
1151#undef FT_STRUCTURE
1152#define FT_STRUCTURE TT_OS2
1153
1154 FT_FRAME_START( 78 ),
1155 FT_FRAME_USHORT( version ),
1156 FT_FRAME_SHORT ( xAvgCharWidth ),
1157 FT_FRAME_USHORT( usWeightClass ),
1158 FT_FRAME_USHORT( usWidthClass ),
1159 FT_FRAME_SHORT ( fsType ),
1160 FT_FRAME_SHORT ( ySubscriptXSize ),
1161 FT_FRAME_SHORT ( ySubscriptYSize ),
1162 FT_FRAME_SHORT ( ySubscriptXOffset ),
1163 FT_FRAME_SHORT ( ySubscriptYOffset ),
1164 FT_FRAME_SHORT ( ySuperscriptXSize ),
1165 FT_FRAME_SHORT ( ySuperscriptYSize ),
1166 FT_FRAME_SHORT ( ySuperscriptXOffset ),
1167 FT_FRAME_SHORT ( ySuperscriptYOffset ),
1168 FT_FRAME_SHORT ( yStrikeoutSize ),
1169 FT_FRAME_SHORT ( yStrikeoutPosition ),
1170 FT_FRAME_SHORT ( sFamilyClass ),
1171 FT_FRAME_BYTE ( panose[0] ),
1172 FT_FRAME_BYTE ( panose[1] ),
1173 FT_FRAME_BYTE ( panose[2] ),
1174 FT_FRAME_BYTE ( panose[3] ),
1175 FT_FRAME_BYTE ( panose[4] ),
1176 FT_FRAME_BYTE ( panose[5] ),
1177 FT_FRAME_BYTE ( panose[6] ),
1178 FT_FRAME_BYTE ( panose[7] ),
1179 FT_FRAME_BYTE ( panose[8] ),
1180 FT_FRAME_BYTE ( panose[9] ),
1181 FT_FRAME_ULONG ( ulUnicodeRange1 ),
1182 FT_FRAME_ULONG ( ulUnicodeRange2 ),
1183 FT_FRAME_ULONG ( ulUnicodeRange3 ),
1184 FT_FRAME_ULONG ( ulUnicodeRange4 ),
1185 FT_FRAME_BYTE ( achVendID[0] ),
1186 FT_FRAME_BYTE ( achVendID[1] ),
1187 FT_FRAME_BYTE ( achVendID[2] ),
1188 FT_FRAME_BYTE ( achVendID[3] ),
1189
1190 FT_FRAME_USHORT( fsSelection ),
1191 FT_FRAME_USHORT( usFirstCharIndex ),
1192 FT_FRAME_USHORT( usLastCharIndex ),
1193 FT_FRAME_SHORT ( sTypoAscender ),
1194 FT_FRAME_SHORT ( sTypoDescender ),
1195 FT_FRAME_SHORT ( sTypoLineGap ),
1196 FT_FRAME_USHORT( usWinAscent ),
1197 FT_FRAME_USHORT( usWinDescent ),
1198 FT_FRAME_END
1199 };
1200
1201 /* `OS/2' version 1 and newer */
1202 static const FT_Frame_Field os2_fields_extra1[] =
1203 {
1204 FT_FRAME_START( 8 ),
1205 FT_FRAME_ULONG( ulCodePageRange1 ),
1206 FT_FRAME_ULONG( ulCodePageRange2 ),
1207 FT_FRAME_END
1208 };
1209
1210 /* `OS/2' version 2 and newer */
1211 static const FT_Frame_Field os2_fields_extra2[] =
1212 {
1213 FT_FRAME_START( 10 ),
1214 FT_FRAME_SHORT ( sxHeight ),
1215 FT_FRAME_SHORT ( sCapHeight ),
1216 FT_FRAME_USHORT( usDefaultChar ),
1217 FT_FRAME_USHORT( usBreakChar ),
1218 FT_FRAME_USHORT( usMaxContext ),
1219 FT_FRAME_END
1220 };
1221
1222 /* `OS/2' version 5 and newer */
1223 static const FT_Frame_Field os2_fields_extra5[] =
1224 {
1225 FT_FRAME_START( 4 ),
1226 FT_FRAME_USHORT( usLowerOpticalPointSize ),
1227 FT_FRAME_USHORT( usUpperOpticalPointSize ),
1228 FT_FRAME_END
1229 };
1230
1231
1232 /* We now support old Mac fonts where the OS/2 table doesn't */
1233 /* exist. Simply put, we set the `version' field to 0xFFFF */
1234 /* and test this value each time we need to access the table. */
1235 error = face->goto_table( face, TTAG_OS2, stream, 0 );
1236 if ( error )
1237 goto Exit;
1238
1239 os2 = &face->os2;
1240
1241 if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
1242 goto Exit;
1243
1244 os2->ulCodePageRange1 = 0;
1245 os2->ulCodePageRange2 = 0;
1246 os2->sxHeight = 0;
1247 os2->sCapHeight = 0;
1248 os2->usDefaultChar = 0;
1249 os2->usBreakChar = 0;
1250 os2->usMaxContext = 0;
1251 os2->usLowerOpticalPointSize = 0;
1252 os2->usUpperOpticalPointSize = 0xFFFF;
1253
1254 if ( os2->version >= 0x0001 )
1255 {
1256 /* only version 1 tables */
1257 if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
1258 goto Exit;
1259
1260 if ( os2->version >= 0x0002 )
1261 {
1262 /* only version 2 tables */
1263 if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
1264 goto Exit;
1265
1266 if ( os2->version >= 0x0005 )
1267 {
1268 /* only version 5 tables */
1269 if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
1270 goto Exit;
1271 }
1272 }
1273 }
1274
1275 FT_TRACE3(( "sTypoAscender: %4hd\n", os2->sTypoAscender ));
1276 FT_TRACE3(( "sTypoDescender: %4hd\n", os2->sTypoDescender ));
1277 FT_TRACE3(( "usWinAscent: %4hu\n", os2->usWinAscent ));
1278 FT_TRACE3(( "usWinDescent: %4hu\n", os2->usWinDescent ));
1279 FT_TRACE3(( "fsSelection: 0x%2hx\n", os2->fsSelection ));
1280
1281 Exit:
1282 return error;
1283 }
1284
1285
1286 /**************************************************************************
1287 *
1288 * @Function:
1289 * tt_face_load_postscript
1290 *
1291 * @Description:
1292 * Loads the Postscript table.
1293 *
1294 * @Input:
1295 * face ::
1296 * A handle to the target face object.
1297 *
1298 * stream ::
1299 * A handle to the input stream.
1300 *
1301 * @Return:
1302 * FreeType error code. 0 means success.
1303 */
1304 FT_LOCAL_DEF( FT_Error )
1305 tt_face_load_post( TT_Face face,
1306 FT_Stream stream )
1307 {
1308 FT_Error error;
1309 TT_Postscript* post = &face->postscript;
1310
1311 static const FT_Frame_Field post_fields[] =
1312 {
1313#undef FT_STRUCTURE
1314#define FT_STRUCTURE TT_Postscript
1315
1316 FT_FRAME_START( 32 ),
1317 FT_FRAME_LONG ( FormatType ),
1318 FT_FRAME_LONG ( italicAngle ),
1319 FT_FRAME_SHORT( underlinePosition ),
1320 FT_FRAME_SHORT( underlineThickness ),
1321 FT_FRAME_ULONG( isFixedPitch ),
1322 FT_FRAME_ULONG( minMemType42 ),
1323 FT_FRAME_ULONG( maxMemType42 ),
1324 FT_FRAME_ULONG( minMemType1 ),
1325 FT_FRAME_ULONG( maxMemType1 ),
1326 FT_FRAME_END
1327 };
1328
1329
1330 error = face->goto_table( face, TTAG_post, stream, 0 );
1331 if ( error )
1332 return error;
1333
1334 if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
1335 return error;
1336
1337 if ( post->FormatType != 0x00030000L &&
1338 post->FormatType != 0x00025000L &&
1339 post->FormatType != 0x00020000L &&
1340 post->FormatType != 0x00010000L )
1341 return FT_THROW( Invalid_Post_Table_Format );
1342
1343 /* we don't load the glyph names, we do that in another */
1344 /* module (ttpost). */
1345
1346 FT_TRACE3(( "FormatType: 0x%lx\n", post->FormatType ));
1347 FT_TRACE3(( "isFixedPitch: %s\n", post->isFixedPitch
1348 ? " yes" : " no" ));
1349
1350 return FT_Err_Ok;
1351 }
1352
1353
1354 /**************************************************************************
1355 *
1356 * @Function:
1357 * tt_face_load_pclt
1358 *
1359 * @Description:
1360 * Loads the PCL 5 Table.
1361 *
1362 * @Input:
1363 * face ::
1364 * A handle to the target face object.
1365 *
1366 * stream ::
1367 * A handle to the input stream.
1368 *
1369 * @Return:
1370 * FreeType error code. 0 means success.
1371 */
1372 FT_LOCAL_DEF( FT_Error )
1373 tt_face_load_pclt( TT_Face face,
1374 FT_Stream stream )
1375 {
1376 static const FT_Frame_Field pclt_fields[] =
1377 {
1378#undef FT_STRUCTURE
1379#define FT_STRUCTURE TT_PCLT
1380
1381 FT_FRAME_START( 54 ),
1382 FT_FRAME_ULONG ( Version ),
1383 FT_FRAME_ULONG ( FontNumber ),
1384 FT_FRAME_USHORT( Pitch ),
1385 FT_FRAME_USHORT( xHeight ),
1386 FT_FRAME_USHORT( Style ),
1387 FT_FRAME_USHORT( TypeFamily ),
1388 FT_FRAME_USHORT( CapHeight ),
1389 FT_FRAME_USHORT( SymbolSet ),
1390 FT_FRAME_BYTES ( TypeFace, 16 ),
1391 FT_FRAME_BYTES ( CharacterComplement, 8 ),
1392 FT_FRAME_BYTES ( FileName, 6 ),
1393 FT_FRAME_CHAR ( StrokeWeight ),
1394 FT_FRAME_CHAR ( WidthType ),
1395 FT_FRAME_BYTE ( SerifStyle ),
1396 FT_FRAME_BYTE ( Reserved ),
1397 FT_FRAME_END
1398 };
1399
1400 FT_Error error;
1401 TT_PCLT* pclt = &face->pclt;
1402
1403
1404 /* optional table */
1405 error = face->goto_table( face, TTAG_PCLT, stream, 0 );
1406 if ( error )
1407 goto Exit;
1408
1409 if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
1410 goto Exit;
1411
1412 Exit:
1413 return error;
1414 }
1415
1416
1417 /**************************************************************************
1418 *
1419 * @Function:
1420 * tt_face_load_gasp
1421 *
1422 * @Description:
1423 * Loads the `gasp' table into a face object.
1424 *
1425 * @Input:
1426 * face ::
1427 * A handle to the target face object.
1428 *
1429 * stream ::
1430 * The input stream.
1431 *
1432 * @Return:
1433 * FreeType error code. 0 means success.
1434 */
1435 FT_LOCAL_DEF( FT_Error )
1436 tt_face_load_gasp( TT_Face face,
1437 FT_Stream stream )
1438 {
1439 FT_Error error;
1440 FT_Memory memory = stream->memory;
1441
1442 FT_UShort j, num_ranges;
1443 TT_GaspRange gasp_ranges = NULL;
1444
1445
1446 /* the gasp table is optional */
1447 error = face->goto_table( face, TTAG_gasp, stream, 0 );
1448 if ( error )
1449 goto Exit;
1450
1451 if ( FT_FRAME_ENTER( 4L ) )
1452 goto Exit;
1453
1454 face->gasp.version = FT_GET_USHORT();
1455 num_ranges = FT_GET_USHORT();
1456
1457 FT_FRAME_EXIT();
1458
1459 /* only support versions 0 and 1 of the table */
1460 if ( face->gasp.version >= 2 )
1461 {
1462 face->gasp.numRanges = 0;
1463 error = FT_THROW( Invalid_Table );
1464 goto Exit;
1465 }
1466
1467 FT_TRACE3(( "numRanges: %hu\n", num_ranges ));
1468
1469 if ( FT_QNEW_ARRAY( gasp_ranges, num_ranges ) ||
1470 FT_FRAME_ENTER( num_ranges * 4L ) )
1471 goto Exit;
1472
1473 for ( j = 0; j < num_ranges; j++ )
1474 {
1475 gasp_ranges[j].maxPPEM = FT_GET_USHORT();
1476 gasp_ranges[j].gaspFlag = FT_GET_USHORT();
1477
1478 FT_TRACE3(( "gaspRange %hu: rangeMaxPPEM %5hu, rangeGaspBehavior 0x%hx\n",
1479 j,
1480 gasp_ranges[j].maxPPEM,
1481 gasp_ranges[j].gaspFlag ));
1482 }
1483
1484 face->gasp.gaspRanges = gasp_ranges;
1485 gasp_ranges = NULL;
1486 face->gasp.numRanges = num_ranges;
1487
1488 FT_FRAME_EXIT();
1489
1490 Exit:
1491 FT_FREE( gasp_ranges );
1492 return error;
1493 }
1494
1495
1496/* END */
1497