/******************************************************************* * * ttload.c 1.0 * * TrueType Tables Loader. * * Copyright 1996-1999 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used * modified and distributed under the terms of the FreeType project * license, LICENSE.TXT. By continuing to use, modify, or distribute * this file you indicate that you have read the license and * understand and accept it fully. * ******************************************************************/ #include "tttypes.h" #include "ttdebug.h" #include "ttcalc.h" #include "ttfile.h" #include "tttables.h" #include "ttobjs.h" #include "ttmemory.h" #include "tttags.h" #include "ttload.h" /* required by the tracing mode */ #undef TT_COMPONENT #define TT_COMPONENT trace_load /* In all functions, the stream is taken from the 'face' object */ #define DEFINE_LOCALS DEFINE_LOAD_LOCALS( face->stream ) #define DEFINE_LOCALS_WO_FRAME DEFINE_LOAD_LOCALS_WO_FRAME( face->stream ) /******************************************************************* * * Function : LookUp_TrueType_Table * * Description : Looks for a TrueType table by name. * * Input : face face table to look for * tag searched tag * * Output : Index of table if found, -1 otherwise. * ******************************************************************/ EXPORT_FUNC Long TT_LookUp_Table( PFace face, ULong tag ) { UShort i; PTRACE4(( "TT_LookUp_Table( %08lx, %c%c%c%c )\n", (Long)face, (Char)(tag >> 24), (Char)(tag >> 16), (Char)(tag >> 8), (Char)(tag) )); for ( i = 0; i < face->numTables; i++ ) if ( face->dirTables[i].Tag == tag ) return i; PTRACE4(( " Could not find table!\n" )); return -1; } /******************************************************************* * * Function : Load_TrueType_Collection * * Description : Loads the TTC table directory into face table. * * Input : face face record to look for * * Output : Error code. * ******************************************************************/ static TT_Error Load_TrueType_Collection( PFace face ) { DEFINE_LOCALS; ULong n; PTRACE3(( "Load_TrueType_Collection( %08lx )\n", (long)face )); if ( FILE_Seek ( 0L ) || ACCESS_Frame( 12L ) ) return error; face->ttcHeader.Tag = GET_Tag4(); face->ttcHeader.version = GET_Long(); face->ttcHeader.DirCount = GET_Long(); FORGET_Frame(); if ( face->ttcHeader.Tag != TTAG_ttcf ) { face->ttcHeader.Tag = 0; face->ttcHeader.version = 0; face->ttcHeader.DirCount = 0; face->ttcHeader.TableDirectory = NULL; PTRACE3(("skipped.\n")); return TT_Err_File_Is_Not_Collection; } if ( ALLOC_ARRAY( face->ttcHeader.TableDirectory, face->ttcHeader.DirCount, ULong ) || ACCESS_Frame( face->ttcHeader.DirCount * 4L ) ) return error; for ( n = 0; n < face->ttcHeader.DirCount; n++ ) face->ttcHeader.TableDirectory[n] = GET_ULong(); FORGET_Frame(); PTRACE3(( "collections directory loaded.\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Directory * * Description : Loads the table directory into face table. * * Input : face face record to look for * * faceIndex the index of the TrueType font, when * we're opening a collection. * * Output : SUCCESS on success. FAILURE on error. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Directory( PFace face, ULong faceIndex ) { DEFINE_LOCALS; UShort n, limit; TTableDir tableDir; PTableDirEntry entry; PTRACE2(("Load_TT_Directory( %08lx, %ld )\n", (long)face, faceIndex)); error = Load_TrueType_Collection( face ); if ( error ) { if ( error != TT_Err_File_Is_Not_Collection ) return error; /* the file isn't a collection, exit if we're asking */ /* for a collected font */ if ( faceIndex != 0 ) return error; /* Now skip to the beginning of the file */ if ( FILE_Seek( 0L ) ) return error; } else { /* The file is a collection. Check the font index */ if ( faceIndex >= face->ttcHeader.DirCount ) return TT_Err_Invalid_Argument; /* select a TrueType font in the ttc file */ if ( FILE_Seek( face->ttcHeader.TableDirectory[faceIndex] ) ) return error; } if ( ACCESS_Frame( 12L ) ) return error; tableDir.version = GET_Long(); tableDir.numTables = GET_UShort(); tableDir.searchRange = GET_UShort(); tableDir.entrySelector = GET_UShort(); tableDir.rangeShift = GET_UShort(); FORGET_Frame(); PTRACE2(( "-- Tables count : %12u\n", tableDir.numTables )); PTRACE2(( "-- Format version : %08lx\n", tableDir.version )); /* Check that we have a 'sfnt' format there */ if ( tableDir.version != 0x00010000 && /* MS fonts */ tableDir.version != 0x74727565 && /* Mac fonts */ tableDir.version != 0x00000000 ) /* some Korean fonts */ { PERROR(( "!! invalid file format" )); return TT_Err_Invalid_File_Format; } face->numTables = tableDir.numTables; if ( ALLOC_ARRAY( face->dirTables, face->numTables, TTableDirEntry ) ) return error; if ( ACCESS_Frame( face->numTables * 16L ) ) return error; limit = face->numTables; entry = face->dirTables; for ( n = 0; n < limit; n++ ) { /* loop through the tables and get all entries */ entry->Tag = GET_Tag4(); entry->CheckSum = GET_ULong(); entry->Offset = GET_Long(); entry->Length = GET_Long(); PTRACE2(( " %c%c%c%c - %08lx - %08lx\n", (Char)(entry->Tag >> 24), (Char)(entry->Tag >> 16), (Char)(entry->Tag >> 8 ), (Char)(entry->Tag), entry->Offset, entry->Length )); entry++; } FORGET_Frame(); PTRACE2(( "Directory loaded\n\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_MaxProfile * * Description : Loads the maxp table into face table. * * Input : face face table to look for * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_MaxProfile( PFace face ) { DEFINE_LOCALS; Long i; PMaxProfile maxProfile = &face->maxProfile; PTRACE2(( "Load_TT_MaxProfile( %08lx )\n", (long)face )); if ( ( i = TT_LookUp_Table( face, TTAG_maxp ) ) < 0 ) return TT_Err_Max_Profile_Missing; if ( FILE_Seek( face->dirTables[i].Offset ) ) /* seek to maxprofile */ return error; if ( ACCESS_Frame( 32L ) ) /* read into frame */ return error; /* read frame data into face table */ maxProfile->version = GET_ULong(); maxProfile->numGlyphs = GET_UShort(); maxProfile->maxPoints = GET_UShort(); maxProfile->maxContours = GET_UShort(); maxProfile->maxCompositePoints = GET_UShort(); maxProfile->maxCompositeContours = GET_UShort(); maxProfile->maxZones = GET_UShort(); maxProfile->maxTwilightPoints = GET_UShort(); maxProfile->maxStorage = GET_UShort(); maxProfile->maxFunctionDefs = GET_UShort(); maxProfile->maxInstructionDefs = GET_UShort(); maxProfile->maxStackElements = GET_UShort(); maxProfile->maxSizeOfInstructions = GET_UShort(); maxProfile->maxComponentElements = GET_UShort(); maxProfile->maxComponentDepth = GET_UShort(); FORGET_Frame(); /* XXX : an adjustement that is necessary to load certain */ /* broken fonts like "Keystrokes MT" :-( */ /* */ /* We allocate 64 function entries by default when */ /* the maxFunctionDefs field is null. */ if (maxProfile->maxFunctionDefs == 0) maxProfile->maxFunctionDefs = 64; face->numGlyphs = maxProfile->numGlyphs; face->maxPoints = MAX( maxProfile->maxCompositePoints, maxProfile->maxPoints ); face->maxContours = MAX( maxProfile->maxCompositeContours, maxProfile->maxContours ); face->maxComponents = maxProfile->maxComponentElements + maxProfile->maxComponentDepth; /* XXX: Some fonts have maxComponents set to 0; we will */ /* then use 16 of them by default. */ if ( face->maxComponents == 0 ) face->maxComponents = 16; /* We also increase maxPoints and maxContours in order to support */ /* some broken fonts. */ face->maxPoints += 8; face->maxContours += 4; PTRACE2(( "GASP loaded.\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Gasp * * Description : Loads the TrueType Gasp table into the face * table. * * Input : face face table to look for * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Gasp( PFace face ) { DEFINE_LOCALS; Long i; UShort j; TGasp* gas; GaspRange* gaspranges; PTRACE2(( "Load_TT_Gasp( %08lx )\n", (long)face )); if ( ( i = TT_LookUp_Table( face, TTAG_gasp ) ) < 0 ) return TT_Err_Ok; /* gasp table is not required */ if ( FILE_Seek( face->dirTables[i].Offset ) || ACCESS_Frame( 4L ) ) return error; gas = &face->gasp; gas->version = GET_UShort(); gas->numRanges = GET_UShort(); FORGET_Frame(); PTRACE3(( "number of ranges = %d\n", gas->numRanges )); if ( ALLOC_ARRAY( gaspranges, gas->numRanges, GaspRange ) || ACCESS_Frame( gas->numRanges * 4L ) ) goto Fail; face->gasp.gaspRanges = gaspranges; for ( j = 0; j < gas->numRanges; j++ ) { gaspranges[j].maxPPEM = GET_UShort(); gaspranges[j].gaspFlag = GET_UShort(); PTRACE3(( " [max:%d flag:%d]", gaspranges[j].maxPPEM, gaspranges[j].gaspFlag )); } PTRACE3(("\n")); FORGET_Frame(); PTRACE2(( "GASP loaded\n" )); return TT_Err_Ok; Fail: FREE( gaspranges ); gas->numRanges = 0; return error; } /******************************************************************* * * Function : Load_TrueType_Header * * Description : Loads the TrueType header table into the face * table. * * Input : face face table to look for * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Header( PFace face ) { DEFINE_LOCALS; Long i; TT_Header* header; PTRACE2(( "Load_TT_Header( %08lx )\n", (long)face )); if ( ( i = TT_LookUp_Table( face, TTAG_head ) ) < 0 ) { PTRACE0(( "Font Header is missing !!\n" )); return TT_Err_Header_Table_Missing; } if ( FILE_Seek( face->dirTables[i].Offset ) || ACCESS_Frame( 54L ) ) return error; header = &face->fontHeader; header->Table_Version = GET_ULong(); header->Font_Revision = GET_ULong(); header->CheckSum_Adjust = GET_Long(); header->Magic_Number = GET_Long(); header->Flags = GET_UShort(); header->Units_Per_EM = GET_UShort(); header->Created [0] = GET_Long(); header->Created [1] = GET_Long(); header->Modified[0] = GET_Long(); header->Modified[1] = GET_Long(); header->xMin = GET_Short(); header->yMin = GET_Short(); header->xMax = GET_Short(); header->yMax = GET_Short(); header->Mac_Style = GET_UShort(); header->Lowest_Rec_PPEM = GET_UShort(); header->Font_Direction = GET_Short(); header->Index_To_Loc_Format = GET_Short(); header->Glyph_Data_Format = GET_Short(); FORGET_Frame(); PTRACE2(( " Units per EM : %8u\n", header->Units_Per_EM )); PTRACE2(( " IndexToLoc : %8d\n", header->Index_To_Loc_Format )); PTRACE2(( "Font Header Loaded.\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Metrics * * Description : Loads the horizontal or vertical metrics table * into face object. * * Input : face * vertical set to true when loading the vmtx table, * or false for hmtx * * Output : Error code. * ******************************************************************/ static TT_Error Load_TrueType_Metrics( PFace face, Bool vertical ) { DEFINE_LOCALS; Long n, num_shorts, num_shorts_checked, num_longs; PLongMetrics* longs; PShortMetrics* shorts; PLongMetrics long_metric; PTRACE2(( "Load_TT_%s_Metrics( %08lx )\n", vertical ? "Vertical" : "Horizontal", (long)face )); if ( vertical ) { /* The table is optional, quit silently if it wasn't found */ /* XXX : Some fonts have a valid vertical header with a non-null */ /* "number_of_VMetrics" fields, but no corresponding */ /* 'vmtx' table to get the metrics from (e.g. mingliu) */ /* */ /* For safety, we set the field to 0 ! */ /* */ n = TT_LookUp_Table( face, TTAG_vmtx ); if ( n < 0 ) { /* Set the number_Of_VMetrics to 0! */ PTRACE2(( " no vertical header in file.\n" )); face->verticalHeader.number_Of_VMetrics = 0; return TT_Err_Ok; } num_longs = face->verticalHeader.number_Of_VMetrics; longs = (PLongMetrics*)&face->verticalHeader.long_metrics; shorts = (PShortMetrics*)&face->verticalHeader.short_metrics; } else { if ( ( n = TT_LookUp_Table( face, TTAG_hmtx ) ) < 0 ) { PERROR(( "!! No Horizontal metrics in file !!\n" )); return TT_Err_Hmtx_Table_Missing; } num_longs = face->horizontalHeader.number_Of_HMetrics; longs = (PLongMetrics*)&face->horizontalHeader.long_metrics; shorts = (PShortMetrics*)&face->horizontalHeader.short_metrics; } /* never trust derived values! */ num_shorts = face->maxProfile.numGlyphs - num_longs; num_shorts_checked = ( face->dirTables[n].Length - num_longs * 4 ) / 2; if ( num_shorts < 0 ) /* sanity check */ { PERROR(( "!! more metrics than glyphs!\n" )); if ( vertical ) return TT_Err_Invalid_Vert_Metrics; else return TT_Err_Invalid_Horiz_Metrics; } if ( ALLOC_ARRAY( *longs, num_longs, TLongMetrics ) || ALLOC_ARRAY( *shorts, num_shorts, TShortMetrics ) ) return error; if ( FILE_Seek( face->dirTables[n].Offset ) || ACCESS_Frame( face->dirTables[n].Length ) ) return error; long_metric = *longs; for ( n = 0; n < num_longs; n++ ) { long_metric->advance = GET_UShort(); long_metric->bearing = GET_Short(); long_metric++; } /* do we have an inconsistent number of metric values? */ if ( num_shorts > num_shorts_checked ) { for ( n = 0; n < num_shorts_checked; n++ ) (*shorts)[n] = GET_Short(); /* we fill up the missing left side bearings with the */ /* last valid value. Since this will occur for buggy CJK */ /* fonts usually, nothing serious will happen. */ for ( n = num_shorts_checked; n < num_shorts; n++ ) (*shorts)[n] = (*shorts)[num_shorts_checked - 1]; } else { for ( n = 0; n < num_shorts; n++ ) (*shorts)[n] = GET_Short(); } FORGET_Frame(); PTRACE2(( "loaded\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Metrics_Header * * Description : Loads either the "hhea" or "vhea" table in memory * * Input : face face table to look for * vertical a boolean. When set, queries the optional * "vhea" table. Otherwise, load the mandatory * "hhea" horizontal header. * * Output : Error code. * * Note : This function now loads the corresponding metrics table * (either hmtx or vmtx) and attaches it to the header. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Metrics_Header( PFace face, Bool vertical ) { DEFINE_LOCALS; Long i; TT_Horizontal_Header* header; PTRACE2(( vertical ? "Vertical header" : "Horizontal header " )); if ( vertical ) { face->verticalInfo = 0; /* The vertical header table is optional, so return quietly if */ /* we don't find it.. */ if ( ( i = TT_LookUp_Table( face, TTAG_vhea ) ) < 0 ) return TT_Err_Ok; face->verticalInfo = 1; header = (TT_Horizontal_Header*)&face->verticalHeader; } else { /* The orizontal header is mandatory, return an error if we */ /* don't find it. */ if ( ( i = TT_LookUp_Table( face, TTAG_hhea ) ) < 0 ) return TT_Err_Horiz_Header_Missing; header = &face->horizontalHeader; } if ( FILE_Seek( face->dirTables[i].Offset ) || ACCESS_Frame( 36L ) ) return error; header->Version = GET_ULong(); header->Ascender = GET_Short(); header->Descender = GET_Short(); header->Line_Gap = GET_Short(); header->advance_Width_Max = GET_UShort(); header->min_Left_Side_Bearing = GET_Short(); header->min_Right_Side_Bearing = GET_Short(); header->xMax_Extent = GET_Short(); header->caret_Slope_Rise = GET_Short(); header->caret_Slope_Run = GET_Short(); header->Reserved0 = GET_Short(); /* this is caret_Offset for vertical headers */ header->Reserved1 = GET_Short(); header->Reserved2 = GET_Short(); header->Reserved3 = GET_Short(); header->Reserved4 = GET_Short(); header->metric_Data_Format = GET_Short(); header->number_Of_HMetrics = GET_UShort(); FORGET_Frame(); header->long_metrics = NULL; header->short_metrics = NULL; PTRACE2(( "loaded\n" )); /* Now try to load the corresponding metrics */ return Load_TrueType_Metrics( face, vertical ); } /******************************************************************* * * Function : Load_TrueType_Locations * * Description : Loads the location table into face table. * * Input : face face table to look for * * Output : Error code. * * NOTE: * The Font Header *must* be loaded in the leading segment * calling this function. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Locations( PFace face ) { DEFINE_LOCALS; Long n, limit; Short LongOffsets; PTRACE2(( "Locations " )); LongOffsets = face->fontHeader.Index_To_Loc_Format; if ( ( n = TT_LookUp_Table( face, TTAG_loca ) ) < 0 ) return TT_Err_Locations_Missing; if ( FILE_Seek( face->dirTables[n].Offset ) ) return error; if ( LongOffsets != 0 ) { face->numLocations = face->dirTables[n].Length >> 2; PTRACE2(( "(32 bit offsets): %12lu ", face->numLocations )); if ( ALLOC_ARRAY( face->glyphLocations, face->numLocations, Long ) ) return error; if ( ACCESS_Frame( face->numLocations * 4L ) ) return error; limit = face->numLocations; for ( n = 0; n < limit; n++ ) face->glyphLocations[n] = GET_Long(); FORGET_Frame(); } else { face->numLocations = face->dirTables[n].Length >> 1; PTRACE2(( "(16 bit offsets): %12lu ", face->numLocations )); if ( ALLOC_ARRAY( face->glyphLocations, face->numLocations, Long ) ) return error; if ( ACCESS_Frame( face->numLocations * 2L ) ) return error; limit = face->numLocations; for ( n = 0; n < limit; n++ ) face->glyphLocations[n] = (Long)((ULong)GET_UShort() * 2); FORGET_Frame(); } PTRACE2(( "loaded\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Names * * Description : Loads the name table into face table. * * Input : face face table to look for * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Names( PFace face ) { DEFINE_LOCALS; UShort i, bytes; Long n; PByte storage; TName_Table* names; TNameRec* namerec; PTRACE2(( "Names " )); if ( ( n = TT_LookUp_Table( face, TTAG_name ) ) < 0 ) { /* The name table is required so indicate failure. */ PTRACE2(( "is missing!\n" )); return TT_Err_Name_Table_Missing; } /* Seek to the beginning of the table and check the frame access. */ /* The names table has a 6 byte header. */ if ( FILE_Seek( face->dirTables[n].Offset ) || ACCESS_Frame( 6L ) ) return error; names = &face->nameTable; /* Load the initial names data. */ names->format = GET_UShort(); names->numNameRecords = GET_UShort(); names->storageOffset = GET_UShort(); FORGET_Frame(); /* Allocate the array of name records. */ if ( ALLOC_ARRAY( names->names, names->numNameRecords, TNameRec ) || ACCESS_Frame( names->numNameRecords * 12L ) ) { names->numNameRecords = 0; goto Fail; } /* Load the name records and determine how much storage is needed */ /* to hold the strings themselves. */ for ( i = bytes = 0; i < names->numNameRecords; i++ ) { namerec = names->names + i; namerec->platformID = GET_UShort(); namerec->encodingID = GET_UShort(); namerec->languageID = GET_UShort(); namerec->nameID = GET_UShort(); namerec->stringLength = GET_UShort(); namerec->stringOffset = GET_UShort(); #if 0 /* check the ids */ if ( namerec->platformID <= 3 ) { #endif /* this test takes care of 'holes' in the names tables, as */ /* reported by Erwin */ if ( (namerec->stringOffset + namerec->stringLength) > bytes ) bytes = namerec->stringOffset + namerec->stringLength; #if 0 } #endif } FORGET_Frame(); /* Allocate storage for the strings if they exist. */ names->storage = NULL; if ( bytes > 0 ) { if ( ALLOC( storage, bytes ) || FILE_Read_At( face->dirTables[n].Offset + names->storageOffset, (void*)storage, bytes ) ) goto Fail_Storage; names->storage = storage; /* Go through and assign the string pointers to the name records. */ for ( i = 0; i < names->numNameRecords; i++ ) { namerec = names->names + i; namerec->string = storage + names->names[i].stringOffset; /* It is possible (but rather unlikely) that a new platform ID will be */ /* added by Apple, so we can't rule out IDs > 3. */ #if 0 if ( namerec->platformID <= 3 ) namerec->string = storage + names->names[i].stringOffset; else { namerec->string = NULL; namerec->stringLength = 0; } #endif } } #ifdef DEBUG_LEVEL_TRACE for ( i = 0; i < names->numNameRecords; i++ ) { int j; PTRACE2(( "%d %d %x %d ", names->names[i].platformID, names->names[i].encodingID, names->names[i].languageID, names->names[i].nameID )); /* I know that M$ encoded strings are Unicode, */ /* but this works reasonable well for debugging purposes. */ for ( j = 0; j < names->names[i].stringLength; j++ ) { if (names->names[i].string) { Char c = *(names->names[i].string + j); if ( (Byte)c < 128 ) PTRACE2(( "%c", c )); } } PTRACE2(( "\n" )); } #endif /* DEBUG_LEVEL_TRACE */ PTRACE2(( "loaded\n" )); return TT_Err_Ok; Fail_Storage: FREE( storage ); Fail: Free_TrueType_Names( face ); return error; } /******************************************************************* * * Function : Free_TrueType_Names * * Description : Frees a name table. * * Input : face face table to look for * * Output : TT_Err_Ok. * ******************************************************************/ LOCAL_FUNC TT_Error Free_TrueType_Names( PFace face ) { TName_Table* names = &face->nameTable; /* free strings table */ FREE( names->names ); /* free strings storage */ FREE( names->storage ); names->numNameRecords = 0; names->format = 0; names->storageOffset = 0; return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_CVT * * Description : Loads cvt table into resident table. * * Input : face face table to look for * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_CVT( PFace face ) { DEFINE_LOCALS; Long n, limit; PTRACE2(( "CVT " )); if ( ( n = TT_LookUp_Table( face, TTAG_cvt ) ) < 0 ) { PTRACE2(( "is missing!\n" )); face->cvtSize = 0; face->cvt = NULL; return TT_Err_Ok; } face->cvtSize = face->dirTables[n].Length / 2; if ( ALLOC_ARRAY( face->cvt, face->cvtSize, Short ) ) return error; if ( FILE_Seek( face->dirTables[n].Offset ) || ACCESS_Frame( face->cvtSize * 2L ) ) return error; limit = face->cvtSize; for ( n = 0; n < limit; n++ ) face->cvt[n] = GET_Short(); FORGET_Frame(); PTRACE2(( "loaded\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_CMap * * Description : Loads the cmap directory in memory. * The cmaps themselves are loaded in ttcmap.c . * * Input : face * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_CMap( PFace face ) { DEFINE_LOCALS; Long off, table_start; Long n, limit; TCMapDir cmap_dir; TCMapDirEntry entry_; PCMapTable cmap; PTRACE2(( "CMaps " )); if ( ( n = TT_LookUp_Table( face, TTAG_cmap ) ) < 0 ) return TT_Err_CMap_Table_Missing; table_start = face->dirTables[n].Offset; if ( ( FILE_Seek( table_start ) ) || ( ACCESS_Frame( 4L ) ) ) /* 4 bytes cmap header */ return error; cmap_dir.tableVersionNumber = GET_UShort(); cmap_dir.numCMaps = GET_UShort(); FORGET_Frame(); off = FILE_Pos(); /* save offset to cmapdir[] which follows */ /* save space in face table for cmap tables */ if ( ALLOC_ARRAY( face->cMaps, cmap_dir.numCMaps, TCMapTable ) ) return error; face->numCMaps = cmap_dir.numCMaps; limit = face->numCMaps; cmap = face->cMaps; for ( n = 0; n < limit; n++ ) { if ( FILE_Seek( off ) || ACCESS_Frame( 8L ) ) return error; /* extra code using entry_ for platxxx could be cleaned up later */ cmap->loaded = FALSE; cmap->platformID = entry_.platformID = GET_UShort(); cmap->platformEncodingID = entry_.platformEncodingID = GET_UShort(); entry_.offset = GET_Long(); FORGET_Frame(); off = FILE_Pos(); if ( FILE_Seek( table_start + entry_.offset ) || ACCESS_Frame( 6L ) ) return error; cmap->format = GET_UShort(); cmap->length = GET_UShort(); cmap->version = GET_UShort(); FORGET_Frame(); cmap->offset = FILE_Pos(); cmap++; } PTRACE2(( "loaded\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Programs * * Description : Loads the font (fpgm) and cvt programs into the * face table. * * Input : face * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Programs( PFace face ) { DEFINE_LOCALS_WO_FRAME; Long n; PTRACE2(( "Font program " )); /* The font program is optional */ if ( ( n = TT_LookUp_Table( face, TTAG_fpgm ) ) < 0 ) { face->fontProgram = NULL; face->fontPgmSize = 0; PTRACE2(( "is missing!\n" )); } else { face->fontPgmSize = face->dirTables[n].Length; if ( ALLOC( face->fontProgram, face->fontPgmSize ) || FILE_Read_At( face->dirTables[n].Offset, (void*)face->fontProgram, face->fontPgmSize ) ) return error; PTRACE2(( "loaded, %12d bytes\n", face->fontPgmSize )); } PTRACE2(( "Prep program " )); if ( ( n = TT_LookUp_Table( face, TTAG_prep ) ) < 0 ) { face->cvtProgram = NULL; face->cvtPgmSize = 0; PTRACE2(( "is missing!\n" )); } else { face->cvtPgmSize = face->dirTables[n].Length; if ( ALLOC( face->cvtProgram, face->cvtPgmSize ) || FILE_Read_At( face->dirTables[n].Offset, (void*)face->cvtProgram, face->cvtPgmSize ) ) return error; PTRACE2(( "loaded, %12d bytes\n", face->cvtPgmSize )); } return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_OS2 * * Description : Loads the OS2 Table. * * Input : face * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_OS2( PFace face ) { DEFINE_LOCALS; Long i; TT_OS2* os2; PTRACE2(( "OS/2 Table " )); /* We now support old Mac fonts where the OS/2 table doesn't */ /* exist. Simply put, we set the `version' field to 0xFFFF */ /* and test this value each time we need to access the table. */ if ( ( i = TT_LookUp_Table( face, TTAG_OS2 ) ) < 0 ) { PTRACE2(( "is missing\n!" )); face->os2.version = 0xFFFF; error = TT_Err_Ok; return TT_Err_Ok; } if ( FILE_Seek( face->dirTables[i].Offset ) || ACCESS_Frame( 78L ) ) return error; os2 = &face->os2; os2->version = GET_UShort(); os2->xAvgCharWidth = GET_Short(); os2->usWeightClass = GET_UShort(); os2->usWidthClass = GET_UShort(); os2->fsType = GET_Short(); os2->ySubscriptXSize = GET_Short(); os2->ySubscriptYSize = GET_Short(); os2->ySubscriptXOffset = GET_Short(); os2->ySubscriptYOffset = GET_Short(); os2->ySuperscriptXSize = GET_Short(); os2->ySuperscriptYSize = GET_Short(); os2->ySuperscriptXOffset = GET_Short(); os2->ySuperscriptYOffset = GET_Short(); os2->yStrikeoutSize = GET_Short(); os2->yStrikeoutPosition = GET_Short(); os2->sFamilyClass = GET_Short(); for ( i = 0; i < 10; i++ ) os2->panose[i] = GET_Byte(); os2->ulUnicodeRange1 = GET_ULong(); os2->ulUnicodeRange2 = GET_ULong(); os2->ulUnicodeRange3 = GET_ULong(); os2->ulUnicodeRange4 = GET_ULong(); for ( i = 0; i < 4; i++ ) os2->achVendID[i] = GET_Byte(); os2->fsSelection = GET_UShort(); os2->usFirstCharIndex = GET_UShort(); os2->usLastCharIndex = GET_UShort(); os2->sTypoAscender = GET_Short(); os2->sTypoDescender = GET_Short(); os2->sTypoLineGap = GET_Short(); os2->usWinAscent = GET_UShort(); os2->usWinDescent = GET_UShort(); FORGET_Frame(); if ( os2->version >= 0x0001 ) { /* only version 1 tables */ if ( ACCESS_Frame( 8L ) ) /* read into frame */ return error; os2->ulCodePageRange1 = GET_ULong(); os2->ulCodePageRange2 = GET_ULong(); FORGET_Frame(); } else { os2->ulCodePageRange1 = 0; os2->ulCodePageRange2 = 0; } PTRACE2(( "loaded\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_PostScript * * Description : Loads the post table into face table. * * Input : face face table to look for * * Output : SUCCESS on success. FAILURE on error. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_PostScript( PFace face ) { DEFINE_LOCALS; Long i; TT_Postscript* post = &face->postscript; PTRACE2(( "PostScript " )); if ( ( i = TT_LookUp_Table( face, TTAG_post ) ) < 0 ) return TT_Err_Post_Table_Missing; if ( FILE_Seek( face->dirTables[i].Offset ) || ACCESS_Frame( 32L ) ) return error; /* read frame data into face table */ post->FormatType = GET_ULong(); post->italicAngle = GET_ULong(); post->underlinePosition = GET_Short(); post->underlineThickness = GET_Short(); post->isFixedPitch = GET_ULong(); post->minMemType42 = GET_ULong(); post->maxMemType42 = GET_ULong(); post->minMemType1 = GET_ULong(); post->maxMemType1 = GET_ULong(); FORGET_Frame(); /* we don't load the glyph names, we do that in a */ /* library extension (ftxpost). */ PTRACE2(( "loaded\n" )); return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Hdmx * * Description : Loads the horizontal device metrics table. * * Input : face face object to look for * * Output : SUCCESS on success. FAILURE on error. * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Hdmx( PFace face ) { DEFINE_LOCALS; TT_Hdmx_Record* rec; TT_Hdmx hdmx; Long table; UShort n, num_glyphs; Long record_size; hdmx.version = 0; hdmx.num_records = 0; hdmx.records = 0; face->hdmx = hdmx; if ( ( table = TT_LookUp_Table( face, TTAG_hdmx ) ) < 0 ) return TT_Err_Ok; if ( FILE_Seek( face->dirTables[table].Offset ) || ACCESS_Frame( 8L ) ) return error; hdmx.version = GET_UShort(); hdmx.num_records = GET_Short(); record_size = GET_Long(); FORGET_Frame(); /* Only recognize format 0 */ if ( hdmx.version != 0 ) return TT_Err_Ok; if ( ALLOC( hdmx.records, sizeof ( TT_Hdmx_Record ) * hdmx.num_records ) ) return error; num_glyphs = face->numGlyphs; record_size -= num_glyphs+2; rec = hdmx.records; for ( n = 0; n < hdmx.num_records; n++ ) { /* read record */ if ( ACCESS_Frame( 2L ) ) goto Fail; rec->ppem = GET_Byte(); rec->max_width = GET_Byte(); FORGET_Frame(); if ( ALLOC( rec->widths, num_glyphs ) || FILE_Read( rec->widths, num_glyphs ) ) goto Fail; /* skip padding bytes */ if ( record_size > 0 ) if ( FILE_Skip( record_size ) ) goto Fail; rec++; } face->hdmx = hdmx; return TT_Err_Ok; Fail: for ( n = 0; n < hdmx.num_records; n++ ) FREE( hdmx.records[n].widths ); FREE( hdmx.records ); return error; } /******************************************************************* * * Function : Free_TrueType_Hdmx * * Description : Frees the horizontal device metrics table. * * Input : face face object to look for * * Output : TT_Err_Ok. * ******************************************************************/ LOCAL_FUNC TT_Error Free_TrueType_Hdmx( PFace face ) { UShort n; if ( !face ) return TT_Err_Ok; for ( n = 0; n < face->hdmx.num_records; n++ ) FREE( face->hdmx.records[n].widths ); FREE( face->hdmx.records ); face->hdmx.num_records = 0; return TT_Err_Ok; } /******************************************************************* * * Function : Load_TrueType_Any * * Description : Loads any font table into client memory. Used by * the TT_Get_Font_Data() API function. * * Input : face face object to look for * * tag tag of table to load. Use the value 0 if you * want to access the whole font file, else set * this parameter to a valid TrueType table tag * that you can forge with the MAKE_TT_TAG * macro. * * offset starting offset in the table (or the file * if tag == 0 ) * * buffer address of target buffer * * length address of decision variable : * * if length == NULL : * load the whole table. returns an * an error if 'offset' == 0 !! * * if *length == 0 : * exit immediately, returning the * length of the given table, or of * the font file, depending on the * value of 'tag' * * if *length != 0 : * load the next 'length' bytes of * table or font, starting at offset * 'offset' (in table or font too). * * Output : Error condition * ******************************************************************/ LOCAL_FUNC TT_Error Load_TrueType_Any( PFace face, ULong tag, Long offset, void* buffer, Long* length ) { TT_Stream stream; TT_Error error; Long table; ULong size; if ( tag != 0 ) { /* look for tag in font directory */ table = TT_LookUp_Table( face, tag ); if ( table < 0 ) return TT_Err_Table_Missing; offset += face->dirTables[table].Offset; size = face->dirTables[table].Length; } else /* tag = 0 -- the use want to access the font file directly */ size = TT_Stream_Size( face->stream ); if ( length && *length == 0 ) { *length = size; return TT_Err_Ok; } if ( length ) size = *length; if ( !USE_Stream( face->stream, stream ) ) (void)FILE_Read_At( offset, buffer, size ); DONE_Stream( stream ); return error; } /* END */