blob: 4db3deb4649c1fb2f21d97fed4261655ec2c9fee [file] [log] [blame]
/***************************************************************************/
/* */
/* ttload.c */
/* */
/* Load the basic TrueType tables, i.e., tables that can be either in */
/* TTF or OTF fonts (body). */
/* */
/* Copyright 1996-2001 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_TAGS_H
#include "ttload.h"
#include "ttcmap.h"
#include "sferrors.h"
#include <stdlib.h> /* for qsort */
#include <stdio.h> /* for printf */
/*************************************************************************/
/* */
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
/* messages during execution. */
/* */
#undef FT_COMPONENT
#define FT_COMPONENT trace_ttload
/*************************************************************************/
/* */
/* <Function> */
/* TT_LookUp_Table */
/* */
/* <Description> */
/* Looks for a TrueType table by name. */
/* */
/* <Input> */
/* face :: A face object handle. */
/* tag :: The searched tag. */
/* */
/* <Return> */
/* A pointer to the table directory entry. 0 if not found. */
/* */
FT_LOCAL_DEF TT_Table*
TT_LookUp_Table( TT_Face face,
FT_ULong tag )
{
TT_Table* entry;
TT_Table* limit;
FT_TRACE3(( "TT_LookUp_Table: %08p, `%c%c%c%c' -- ",
face,
(FT_Char)( tag >> 24 ),
(FT_Char)( tag >> 16 ),
(FT_Char)( tag >> 8 ),
(FT_Char)( tag ) ));
entry = face->dir_tables;
limit = entry + face->num_tables;
for ( ; entry < limit; entry++ )
{
if ( entry->Tag == tag )
{
FT_TRACE3(( "found table.\n" ));
return entry;
}
}
FT_TRACE3(( "could not find table!\n" ));
return 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Goto_Table */
/* */
/* <Description> */
/* Looks for a TrueType table by name, then seek a stream to it. */
/* */
/* <Input> */
/* face :: A face object handle. */
/* tag :: The searched tag. */
/* stream :: The stream to seek when the table is found. */
/* */
/* <Output> */
/* length :: The length of the table if found, undefined otherwise. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Goto_Table( TT_Face face,
FT_ULong tag,
FT_Stream stream,
FT_ULong* length )
{
TT_Table* table;
FT_Error error;
table = TT_LookUp_Table( face, tag );
if ( table )
{
if ( length )
*length = table->Length;
if ( FILE_Seek( table->Offset ) )
goto Exit;
}
else
error = SFNT_Err_Table_Missing;
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_SFNT_Header */
/* */
/* <Description> */
/* Loads the header of a SFNT font file. Supports collections. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* face_index :: If the font is a collection, the number of the font */
/* in the collection, ignored otherwise. */
/* */
/* <Output> */
/* sfnt :: The SFNT header. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* The stream cursor must be at the font file's origin. */
/* */
/* This function recognizes fonts embedded in a `TrueType collection' */
/* */
/* The header will be checked whether it is valid by looking at the */
/* values of `search_range', `entry_selector', and `range_shift'. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_SFNT_Header( TT_Face face,
FT_Stream stream,
FT_Long face_index,
SFNT_Header* sfnt )
{
FT_Error error;
FT_ULong format_tag;
FT_Memory memory = stream->memory;
const FT_Frame_Field sfnt_header_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE SFNT_Header
FT_FRAME_START( 8 ),
FT_FRAME_USHORT( num_tables ),
FT_FRAME_USHORT( search_range ),
FT_FRAME_USHORT( entry_selector ),
FT_FRAME_USHORT( range_shift ),
FT_FRAME_END
};
const FT_Frame_Field ttc_header_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TTC_Header
FT_FRAME_START( 8 ),
FT_FRAME_LONG( version ),
FT_FRAME_LONG( count ),
FT_FRAME_END };
FT_TRACE2(( "TT_Load_SFNT_Header: %08p, %ld\n",
face, face_index ));
face->ttc_header.tag = 0;
face->ttc_header.version = 0;
face->ttc_header.count = 0;
face->num_tables = 0;
/* first of all, read the first 4 bytes. If it is `ttcf', then the */
/* file is a TrueType collection, otherwise it can be any other */
/* kind of font. */
if ( READ_ULong( format_tag ) )
goto Exit;
if ( format_tag == TTAG_ttcf )
{
FT_Int n;
FT_TRACE3(( "TT_Load_SFNT_Header: file is a collection\n" ));
/* it's a TrueType collection, i.e. a file containing several */
/* font files. Read the font directory now */
if ( READ_Fields( ttc_header_fields, &face->ttc_header ) )
goto Exit;
/* now read the offsets of each font in the file */
if ( ALLOC_ARRAY( face->ttc_header.offsets,
face->ttc_header.count,
FT_ULong ) ||
ACCESS_Frame( face->ttc_header.count * 4L ) )
goto Exit;
for ( n = 0; n < face->ttc_header.count; n++ )
face->ttc_header.offsets[n] = GET_ULong();
FORGET_Frame();
/* check face index */
if ( face_index >= face->ttc_header.count )
{
error = SFNT_Err_Bad_Argument;
goto Exit;
}
/* seek to the appropriate TrueType file, then read tag */
if ( FILE_Seek( face->ttc_header.offsets[face_index] ) ||
READ_Long( format_tag ) )
goto Exit;
}
/* the format tag was read, now check the rest of the header */
sfnt->format_tag = format_tag;
if ( READ_Fields( sfnt_header_fields, sfnt ) )
goto Exit;
/* now, check the values of `num_tables', `seach_range', etc. */
{
FT_UInt num_tables = sfnt->num_tables;
FT_ULong entry_selector = 1L << sfnt->entry_selector;
/* IMPORTANT: Many fonts have an incorrect `search_range' value, so */
/* we only check the `entry_selector' correctness here. */
/* */
if ( num_tables == 0 ||
entry_selector > num_tables ||
entry_selector * 2 <= num_tables )
{
FT_TRACE2(( "TT_Load_SFNT_Header: file is not SFNT!\n" ));
error = SFNT_Err_Unknown_File_Format;
}
}
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Directory */
/* */
/* <Description> */
/* Loads the table directory into a face object. */
/* */
/* <InOut> */
/* face :: A handle to the target face object. */
/* */
/* <Input> */
/* stream :: The input stream. */
/* sfnt :: The SFNT directory header. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* The stream cursor must be at the font file's origin. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Directory( TT_Face face,
FT_Stream stream,
SFNT_Header* sfnt )
{
FT_Error error;
FT_Memory memory = stream->memory;
TT_Table *entry, *limit;
FT_TRACE2(( "TT_Load_Directory: %08p\n", face ));
FT_TRACE2(( "-- Tables count: %12u\n", sfnt->num_tables ));
FT_TRACE2(( "-- Format version: %08lx\n", sfnt->format_tag ));
face->num_tables = sfnt->num_tables;
if ( ALLOC_ARRAY( face->dir_tables,
face->num_tables,
TT_Table ) )
goto Exit;
if ( ACCESS_Frame( face->num_tables * 16L ) )
goto Exit;
entry = face->dir_tables;
limit = entry + face->num_tables;
for ( ; entry < limit; entry++ )
{ /* loop through the tables and get all entries */
entry->Tag = GET_Tag4();
entry->CheckSum = GET_ULong();
entry->Offset = GET_Long();
entry->Length = GET_Long();
FT_TRACE2(( " %c%c%c%c - %08lx - %08lx\n",
(FT_Char)( entry->Tag >> 24 ),
(FT_Char)( entry->Tag >> 16 ),
(FT_Char)( entry->Tag >> 8 ),
(FT_Char)( entry->Tag ),
entry->Offset,
entry->Length ));
}
FORGET_Frame();
FT_TRACE2(( "Directory loaded\n\n" ));
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Any */
/* */
/* <Description> */
/* Loads any font table into client memory. */
/* */
/* <Input> */
/* face :: The face object to look for. */
/* */
/* tag :: The 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 :: The starting offset in the table (or the file if */
/* tag == 0). */
/* */
/* length :: The address of the decision variable: */
/* */
/* If length == NULL: */
/* Loads the whole table. Returns an error if */
/* `offset' == 0! */
/* */
/* If *length == 0: */
/* Exits immediately; returning the length of the given */
/* table or of the font file, depending on the value of */
/* `tag'. */
/* */
/* If *length != 0: */
/* Loads the next `length' bytes of table or font, */
/* starting at offset `offset' (in table or font too). */
/* */
/* <Output> */
/* buffer :: The address of target buffer. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Any( TT_Face face,
FT_ULong tag,
FT_Long offset,
FT_Byte* buffer,
FT_ULong* length )
{
FT_Error error;
FT_Stream stream;
TT_Table* table;
FT_ULong size;
if ( tag != 0 )
{
/* look for tag in font directory */
table = TT_LookUp_Table( face, tag );
if ( !table )
{
error = SFNT_Err_Table_Missing;
goto Exit;
}
offset += table->Offset;
size = table->Length;
}
else
/* tag == 0 -- the user wants to access the font file directly */
size = face->root.stream->size;
if ( length && *length == 0 )
{
*length = size;
return SFNT_Err_Ok;
}
if ( length )
size = *length;
stream = face->root.stream;
/* the `if' is syntactic sugar for picky compilers */
if ( FILE_Read_At( offset, buffer, size ) )
goto Exit;
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Generic_Header */
/* */
/* <Description> */
/* Loads the TrueType table `head' or `bhed'. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
static FT_Error
TT_Load_Generic_Header( TT_Face face,
FT_Stream stream,
FT_ULong tag )
{
FT_Error error;
TT_Header* header;
static const FT_Frame_Field header_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_Header
FT_FRAME_START( 54 ),
FT_FRAME_ULONG ( Table_Version ),
FT_FRAME_ULONG ( Font_Revision ),
FT_FRAME_LONG ( CheckSum_Adjust ),
FT_FRAME_LONG ( Magic_Number ),
FT_FRAME_USHORT( Flags ),
FT_FRAME_USHORT( Units_Per_EM ),
FT_FRAME_LONG ( Created[0] ),
FT_FRAME_LONG ( Created[1] ),
FT_FRAME_LONG ( Modified[0] ),
FT_FRAME_LONG ( Modified[1] ),
FT_FRAME_SHORT ( xMin ),
FT_FRAME_SHORT ( yMin ),
FT_FRAME_SHORT ( xMax ),
FT_FRAME_SHORT ( yMax ),
FT_FRAME_USHORT( Mac_Style ),
FT_FRAME_USHORT( Lowest_Rec_PPEM ),
FT_FRAME_SHORT ( Font_Direction ),
FT_FRAME_SHORT ( Index_To_Loc_Format ),
FT_FRAME_SHORT ( Glyph_Data_Format ),
FT_FRAME_END
};
FT_TRACE2(( "TT_Load_Generic_Header: "
"%08p, looking up font table `%c%c%c%c'.\n",
face,
(FT_Char)( tag >> 24 ),
(FT_Char)( tag >> 16 ),
(FT_Char)( tag >> 8 ),
(FT_Char)( tag ) ));
error = face->goto_table( face, tag, stream, 0 );
if ( error )
{
FT_TRACE2(( "TT_Load_Generic_Header: Font table is missing!\n" ));
goto Exit;
}
header = &face->header;
if ( READ_Fields( header_fields, header ) )
goto Exit;
FT_TRACE2(( " Units per EM: %8u\n", header->Units_Per_EM ));
FT_TRACE2(( " IndexToLoc: %8d\n", header->Index_To_Loc_Format ));
FT_TRACE2(( "TT_Load_Generic_Header: Font table loaded.\n" ));
Exit:
return error;
}
FT_LOCAL_DEF FT_Error
TT_Load_Header( TT_Face face,
FT_Stream stream )
{
return TT_Load_Generic_Header( face, stream, TTAG_head );
}
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
FT_LOCAL_DEF FT_Error
TT_Load_Bitmap_Header( TT_Face face,
FT_Stream stream )
{
return TT_Load_Generic_Header( face, stream, TTAG_bhed );
}
#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_MaxProfile */
/* */
/* <Description> */
/* Loads the maximum profile into a face object. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_MaxProfile( TT_Face face,
FT_Stream stream )
{
FT_Error error;
TT_MaxProfile* maxProfile = &face->max_profile;
const FT_Frame_Field maxp_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_MaxProfile
FT_FRAME_START( 6 ),
FT_FRAME_LONG ( version ),
FT_FRAME_USHORT( numGlyphs ),
FT_FRAME_END
};
const FT_Frame_Field maxp_fields_extra[] =
{
FT_FRAME_START( 26 ),
FT_FRAME_USHORT( maxPoints ),
FT_FRAME_USHORT( maxContours ),
FT_FRAME_USHORT( maxCompositePoints ),
FT_FRAME_USHORT( maxCompositeContours ),
FT_FRAME_USHORT( maxZones ),
FT_FRAME_USHORT( maxTwilightPoints ),
FT_FRAME_USHORT( maxStorage ),
FT_FRAME_USHORT( maxFunctionDefs ),
FT_FRAME_USHORT( maxInstructionDefs ),
FT_FRAME_USHORT( maxStackElements ),
FT_FRAME_USHORT( maxSizeOfInstructions ),
FT_FRAME_USHORT( maxComponentElements ),
FT_FRAME_USHORT( maxComponentDepth ),
FT_FRAME_END
};
FT_TRACE2(( "Load_TT_MaxProfile: %08p\n", face ));
error = face->goto_table( face, TTAG_maxp, stream, 0 );
if ( error )
goto Exit;
if ( READ_Fields( maxp_fields, maxProfile ) )
goto Exit;
maxProfile->maxPoints = 0;
maxProfile->maxContours = 0;
maxProfile->maxCompositePoints = 0;
maxProfile->maxCompositeContours = 0;
maxProfile->maxZones = 0;
maxProfile->maxTwilightPoints = 0;
maxProfile->maxStorage = 0;
maxProfile->maxFunctionDefs = 0;
maxProfile->maxInstructionDefs = 0;
maxProfile->maxStackElements = 0;
maxProfile->maxSizeOfInstructions = 0;
maxProfile->maxComponentElements = 0;
maxProfile->maxComponentDepth = 0;
if ( maxProfile->version >= 0x10000L )
{
if ( READ_Fields( maxp_fields_extra, maxProfile ) )
goto Exit;
/* XXX: an adjustment 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->root.num_glyphs = maxProfile->numGlyphs;
face->root.internal->max_points =
(FT_UShort)MAX( maxProfile->maxCompositePoints,
maxProfile->maxPoints );
face->root.internal->max_contours =
(FT_Short)MAX( maxProfile->maxCompositeContours,
maxProfile->maxContours );
face->max_components = (FT_ULong)maxProfile->maxComponentElements +
maxProfile->maxComponentDepth;
/* XXX: some fonts have maxComponents set to 0; we will */
/* then use 16 of them by default. */
if ( face->max_components == 0 )
face->max_components = 16;
/* We also increase maxPoints and maxContours in order to support */
/* some broken fonts. */
face->root.internal->max_points += (FT_UShort)8;
face->root.internal->max_contours += (FT_Short) 4;
}
FT_TRACE2(( "MAXP loaded.\n" ));
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Metrics */
/* */
/* <Description> */
/* Loads the horizontal or vertical metrics table into a face object. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* vertical :: A boolean flag. If set, load vertical metrics. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
static FT_Error
TT_Load_Metrics( TT_Face face,
FT_Stream stream,
FT_Bool vertical )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_ULong table_len;
FT_Long num_shorts, num_longs, num_shorts_checked;
TT_LongMetrics** longs;
TT_ShortMetrics** shorts;
FT_TRACE2(( "TT_Load_%s_Metrics: %08p\n", vertical ? "Vertical"
: "Horizontal",
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! */
/* */
error = face->goto_table( face, TTAG_vmtx, stream, &table_len );
if ( error )
{
/* Set number_Of_VMetrics to 0! */
FT_TRACE2(( " no vertical header in file.\n" ));
face->vertical.number_Of_VMetrics = 0;
error = SFNT_Err_Ok;
goto Exit;
}
num_longs = face->vertical.number_Of_VMetrics;
longs = (TT_LongMetrics**)&face->vertical.long_metrics;
shorts = (TT_ShortMetrics**)&face->vertical.short_metrics;
}
else
{
error = face->goto_table( face, TTAG_hmtx, stream, &table_len );
if ( error )
{
FT_ERROR(( " no horizontal metrics in file!\n" ));
error = SFNT_Err_Hmtx_Table_Missing;
goto Exit;
}
num_longs = face->horizontal.number_Of_HMetrics;
longs = (TT_LongMetrics**)&face->horizontal.long_metrics;
shorts = (TT_ShortMetrics**)&face->horizontal.short_metrics;
}
/* never trust derived values */
num_shorts = face->max_profile.numGlyphs - num_longs;
num_shorts_checked = ( table_len - num_longs * 4L ) / 2;
if ( num_shorts < 0 )
{
FT_ERROR(( "TT_Load_%s_Metrics: more metrics than glyphs!\n",
vertical ? "Vertical"
: "Horizontal" ));
error = vertical ? SFNT_Err_Invalid_Vert_Metrics
: SFNT_Err_Invalid_Horiz_Metrics;
goto Exit;
}
if ( ALLOC_ARRAY( *longs, num_longs, TT_LongMetrics ) ||
ALLOC_ARRAY( *shorts, num_shorts, TT_ShortMetrics ) )
goto Exit;
if ( ACCESS_Frame( table_len ) )
goto Exit;
{
TT_LongMetrics* cur = *longs;
TT_LongMetrics* limit = cur + num_longs;
for ( ; cur < limit; cur++ )
{
cur->advance = GET_UShort();
cur->bearing = GET_Short();
}
}
/* do we have an inconsistent number of metric values? */
{
TT_ShortMetrics* cur = *shorts;
TT_ShortMetrics* limit = cur + MIN( num_shorts, num_shorts_checked );
for ( ; cur < limit; cur++ )
*cur = GET_Short();
/* we fill up the missing left side bearings with the */
/* last valid value. Since this will occur for buggy CJK */
/* fonts usually only, nothing serious will happen */
if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 )
{
FT_Short val = (*shorts)[num_shorts_checked - 1];
limit = *shorts + num_shorts;
for ( ; cur < limit; cur++ )
*cur = val;
}
}
FORGET_Frame();
FT_TRACE2(( "loaded\n" ));
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Metrics_Header */
/* */
/* <Description> */
/* Loads the horizontal or vertical header in a face object. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* vertical :: A boolean flag. If set, load vertical metrics. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Metrics_Header( TT_Face face,
FT_Stream stream,
FT_Bool vertical )
{
FT_Error error;
TT_HoriHeader* header;
const FT_Frame_Field metrics_header_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_HoriHeader
FT_FRAME_START( 36 ),
FT_FRAME_ULONG ( Version ),
FT_FRAME_SHORT ( Ascender ),
FT_FRAME_SHORT ( Descender ),
FT_FRAME_SHORT ( Line_Gap ),
FT_FRAME_USHORT( advance_Width_Max ),
FT_FRAME_SHORT ( min_Left_Side_Bearing ),
FT_FRAME_SHORT ( min_Right_Side_Bearing ),
FT_FRAME_SHORT ( xMax_Extent ),
FT_FRAME_SHORT ( caret_Slope_Rise ),
FT_FRAME_SHORT ( caret_Slope_Run ),
FT_FRAME_SHORT ( caret_Offset ),
FT_FRAME_SHORT ( Reserved[0] ),
FT_FRAME_SHORT ( Reserved[1] ),
FT_FRAME_SHORT ( Reserved[2] ),
FT_FRAME_SHORT ( Reserved[3] ),
FT_FRAME_SHORT ( metric_Data_Format ),
FT_FRAME_USHORT( number_Of_HMetrics ),
FT_FRAME_END
};
FT_TRACE2(( vertical ? "Vertical header " : "Horizontal header " ));
if ( vertical )
{
face->vertical_info = 0;
/* The vertical header table is optional, so return quietly if */
/* we don't find it. */
error = face->goto_table( face, TTAG_vhea, stream, 0 );
if ( error )
{
error = SFNT_Err_Ok;
goto Exit;
}
face->vertical_info = 1;
header = (TT_HoriHeader*)&face->vertical;
}
else
{
/* The horizontal header is mandatory; return an error if we */
/* don't find it. */
error = face->goto_table( face, TTAG_hhea, stream, 0 );
if ( error )
{
error = SFNT_Err_Horiz_Header_Missing;
goto Exit;
}
header = &face->horizontal;
}
if ( READ_Fields( metrics_header_fields, header ) )
goto Exit;
header->long_metrics = NULL;
header->short_metrics = NULL;
FT_TRACE2(( "loaded\n" ));
/* Now try to load the corresponding metrics */
error = TT_Load_Metrics( face, stream, vertical );
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Names */
/* */
/* <Description> */
/* Loads the name records. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Names( TT_Face face,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_ULong table_pos, table_len;
FT_ULong storageSize, storageOffset;
FT_Byte* storage;
TT_NameTable* table;
TT_NameRec* names;
FT_UInt num_names;
const FT_Frame_Field name_table_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_NameTable
FT_FRAME_START( 6 ),
FT_FRAME_USHORT( format ),
FT_FRAME_USHORT( numNameRecords ),
FT_FRAME_USHORT( storageOffset ),
FT_FRAME_END
};
const FT_Frame_Field name_record_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_NameRec
/* no FT_FRAME_START */
FT_FRAME_USHORT( platformID ),
FT_FRAME_USHORT( encodingID ),
FT_FRAME_USHORT( languageID ),
FT_FRAME_USHORT( nameID ),
FT_FRAME_USHORT( stringLength ),
FT_FRAME_USHORT( stringOffset ),
FT_FRAME_END
};
FT_TRACE2(( "Names " ));
error = face->goto_table( face, TTAG_name, stream, &table_len );
if ( error )
{
/* The name table is required so indicate failure. */
FT_TRACE2(( "is missing!\n" ));
error = SFNT_Err_Name_Table_Missing;
goto Exit;
}
table_pos = FILE_Pos();
table = &face->name_table;
if ( READ_Fields( name_table_fields, table ) )
goto Exit;
num_names = (FT_UInt) table->numNameRecords;
storageOffset = table->storageOffset;
storageSize = (FT_ULong)( table_len - storageOffset );
/* check the storage offset field */
if ( storageOffset < 6 + 12*num_names ||
table_len < storageOffset )
{
FT_TRACE2(( "table is broken.. Ignoring !!\n" ));
table->numNameRecords = 0;
table->storageOffset = 0;
error = SFNT_Err_Name_Table_Missing;
goto Exit;
}
/* Allocate the array of name records. */
if ( ALLOC( table->names,
num_names*sizeof(table->names[0]) + storageSize ) ||
ACCESS_Frame( num_names*12L ) )
goto Exit;
names = table->names;
storage = (FT_Byte*)(names + num_names);
/* Load the name records and determine how much storage is needed */
/* to hold the strings themselves. */
{
TT_NameRec* cur = names;
TT_NameRec* limit = cur + num_names;
for ( ; cur < limit; cur ++ )
{
if ( READ_Fields( name_record_fields, cur ) )
break;
/* check the fields */
if ( cur->stringOffset + cur->stringLength <= (FT_Long)storageSize )
cur->string = storage + cur->stringOffset;
else
{
/* that's an invalid entry !! */
cur->stringOffset = 0;
cur->string = NULL;
}
}
}
FORGET_Frame();
if ( error )
goto Exit;
storageOffset -= 6 + 12*num_names;
if ( FILE_Skip( storageOffset ) ||
FILE_Read( storage, storageSize ) )
goto Exit;
/* Print Name Record Table in case of debugging */
if ( FT_TRACE_TEST(3) )
{
TT_NameRec* cur = table->names;
TT_NameRec* limit = cur + num_names;
for ( ; cur < limit; cur++ )
{
FT_UInt j;
printf( "%2d %2d %4x %2d:",
cur->platformID,
cur->encodingID,
cur->languageID,
cur->nameID );
/* printf name id */
switch ( cur->nameID )
{
case 0: printf( " copyright" ); break;
case 1: printf( " font_family" ); break;
case 2: printf( " style" ); break;
case 3: printf( " unique_id" ); break;
case 4: printf( " full_name" ); break;
case 5: printf( " version" ); break;
case 6: printf( " postscript" ); break;
case 7: printf( " trademark" ); break;
case 8: printf( " manufacturer" ); break;
case 9: printf( " designer" ); break;
case 10: printf( " description" ); break;
case 11: printf( " url_vendor" ); break;
case 12: printf( " url_designer" ); break;
case 13: printf( " license" ); break;
case 14: printf( " url_license" ); break;
case 15: printf( " reserved (15)" ); break;
case 16: printf( " prefered_family" ); break;
case 17: printf( " prefered_style" ); break;
case 18: printf( " mac_full_name" ); break;
case 19: printf( " sample_text" ); break;
case 20: printf( " cid_name" ); break;
default: printf( " unknown_type(%d)", cur->nameID );
}
/* printf platform/encoding id */
j = 0;
switch ( cur->platformID )
{
case 0: /* Unicode encodings */
{
switch ( cur->encodingID )
{
case 0: printf( " unicode_1.0" ); break;
case 1: printf( " unicode_1.1" ); break;
case 2: printf( " ISO_10646" ); break;
case 3: printf( " unicode_2.0" ); break;
default:
j = 1;
}
break;
}
case 1: /* Apple encodings */
{
switch ( cur->encodingID )
{
case 0: printf( " mac_Roman" ); break;
case 1: printf( " mac_Japanese" ); break;
case 2: printf( " mac_Chinese_traditional" ); break;
case 3: printf( " mac_Korean" ); break;
case 4: printf( " mac_Arabic" ); break;
case 5: printf( " mac_Hebrew" ); break;
case 6: printf( " mac_Greek" ); break;
case 7: printf( " mac_Russian" ); break;
case 8: printf( " mac_RSymbol" ); break;
case 9: printf( " mac_Devanagari" ); break;
case 10: printf( " mac_Gurmukhi" ); break;
case 11: printf( " mac_Gujarati" ); break;
case 12: printf( " mac_Oriya" ); break;
case 13: printf( " mac_Bengali" ); break;
case 14: printf( " mac_Tamil" ); break;
case 15: printf( " mac_Telugu" ); break;
case 16: printf( " mac_Kannada" ); break;
case 17: printf( " mac_Malayalam" ); break;
case 18: printf( " mac_Sinhalese" ); break;
case 19: printf( " mac_Burmese" ); break;
case 20: printf( " mac_Khmer" ); break;
case 21: printf( " mac_Thai" ); break;
case 22: printf( " mac_Laotian" ); break;
case 23: printf( " mac_Goergian" ); break;
case 24: printf( " mac_Armenian" ); break;
case 25: printf( " mac_Chinese_simplified" ); break;
case 26: printf( " mac_Tibetan" ); break;
case 27: printf( " mac_Mongolian" ); break;
case 28: printf( " mac_Geez" ); break;
case 29: printf( " mac_Slavic" ); break;
case 30: printf( " mac_Vietnamese" ); break;
case 31: printf( " mac_Sindhi" ); break;
case 32: printf( " mac_Uninterpreted" ); break;
default:
j = 1;
}
break;
}
case 2: /* deprecated IDO encoding */
{
switch ( cur->encodingID )
{
case 0: printf( " iso_ASCII" ); break;
case 1: printf( " iso_10646" ); break;
case 2: printf( " iso_8859-1" ); break;
default:
j = 1;
}
break;
}
case 3: /* Windows encodings */
{
switch ( cur->encodingID )
{
case 0: printf( " win_symbol" ); break;
case 1: printf( " win_unicode" ); break;
case 2: printf( " win_shiftJIS" ); break;
case 3: printf( " win_PRC" ); break;
case 4: printf( " win_Big5" ); break;
case 5: printf( " win_Wansung" ); break;
case 6: printf( " win_Johab" ); break;
case 10: printf( " win_UCS4" ); break;
default:
j = 1;
}
break;
}
default:
j = 1;
}
if ( j == 1 )
printf( " unknown_encoding" );
printf( "\n " );
/* printf language - XXXX for later ....*/
/* I know that M$ encoded strings are Unicode, */
/* but this works reasonable well for debugging purposes. */
if ( cur->string )
{
for ( j = 0; j < (FT_UInt)cur->stringLength; j++ )
{
FT_Byte c = (FT_Byte)*( cur->string + j );
if ( c < 32 || c > 127 )
c = '.';
printf( "%c", c );
}
}
else
printf( "INVALID_ENTRY!!" );
printf( "\n" );
}
printf( "\n" );
}
FT_TRACE2(( "loaded\n" ));
/* everything went well, update face->num_names */
face->num_names = (FT_UShort)num_names;
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Free_Names */
/* */
/* <Description> */
/* Frees the name records. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* */
FT_LOCAL_DEF void
TT_Free_Names( TT_Face face )
{
FT_Memory memory = face->root.driver->root.memory;
TT_NameTable* names = &face->name_table;
/* free strings table */
FREE( names->names );
/* free strings storage */
FREE( names->storage );
names->numNameRecords = 0;
names->format = 0;
names->storageOffset = 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_CMap */
/* */
/* <Description> */
/* Loads the cmap directory in a face object. The cmaps itselves are */
/* loaded on demand in the `ttcmap.c' module. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: A handle to the input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_CMap( TT_Face face,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_Long table_start;
TT_CMapDir cmap_dir;
const FT_Frame_Field cmap_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_CMapDir
FT_FRAME_START( 4 ),
FT_FRAME_USHORT( tableVersionNumber ),
FT_FRAME_USHORT( numCMaps ),
FT_FRAME_END
};
const FT_Frame_Field cmap_rec_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_CMapTable
FT_FRAME_START( 4 ),
FT_FRAME_USHORT( format ),
FT_FRAME_USHORT( length ),
FT_FRAME_END
};
FT_TRACE2(( "CMaps " ));
error = face->goto_table( face, TTAG_cmap, stream, 0 );
if ( error )
{
error = SFNT_Err_CMap_Table_Missing;
goto Exit;
}
table_start = FILE_Pos();
if ( READ_Fields( cmap_fields, &cmap_dir ) )
goto Exit;
/* reserve space in face table for cmap tables */
if ( ALLOC_ARRAY( face->charmaps,
cmap_dir.numCMaps,
TT_CharMapRec ) )
goto Exit;
face->num_charmaps = cmap_dir.numCMaps;
{
TT_CharMap charmap = face->charmaps;
TT_CharMap limit = charmap + face->num_charmaps;
/* read the header of each charmap first */
if ( ACCESS_Frame( face->num_charmaps * 8L ) )
goto Exit;
for ( ; charmap < limit; charmap++ )
{
TT_CMapTable* cmap;
charmap->root.face = (FT_Face)face;
cmap = &charmap->cmap;
cmap->loaded = FALSE;
cmap->platformID = GET_UShort();
cmap->platformEncodingID = GET_UShort();
cmap->offset = (FT_ULong)GET_Long();
}
FORGET_Frame();
/* now read the rest of each table */
for ( charmap = face->charmaps; charmap < limit; charmap++ )
{
TT_CMapTable* cmap = &charmap->cmap;
if ( FILE_Seek( table_start + (FT_Long)cmap->offset ) ||
READ_Fields( cmap_rec_fields, cmap ) )
goto Exit;
cmap->offset = FILE_Pos();
}
}
FT_TRACE2(( "loaded\n" ));
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_OS2 */
/* */
/* <Description> */
/* Loads the OS2 table. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: A handle to the input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_OS2( TT_Face face,
FT_Stream stream )
{
FT_Error error;
TT_OS2* os2;
const FT_Frame_Field os2_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_OS2
FT_FRAME_START( 78 ),
FT_FRAME_USHORT( version ),
FT_FRAME_SHORT ( xAvgCharWidth ),
FT_FRAME_USHORT( usWeightClass ),
FT_FRAME_USHORT( usWidthClass ),
FT_FRAME_SHORT ( fsType ),
FT_FRAME_SHORT ( ySubscriptXSize ),
FT_FRAME_SHORT ( ySubscriptYSize ),
FT_FRAME_SHORT ( ySubscriptXOffset ),
FT_FRAME_SHORT ( ySubscriptYOffset ),
FT_FRAME_SHORT ( ySuperscriptXSize ),
FT_FRAME_SHORT ( ySuperscriptYSize ),
FT_FRAME_SHORT ( ySuperscriptXOffset ),
FT_FRAME_SHORT ( ySuperscriptYOffset ),
FT_FRAME_SHORT ( yStrikeoutSize ),
FT_FRAME_SHORT ( yStrikeoutPosition ),
FT_FRAME_SHORT ( sFamilyClass ),
FT_FRAME_BYTE ( panose[0] ),
FT_FRAME_BYTE ( panose[1] ),
FT_FRAME_BYTE ( panose[2] ),
FT_FRAME_BYTE ( panose[3] ),
FT_FRAME_BYTE ( panose[4] ),
FT_FRAME_BYTE ( panose[5] ),
FT_FRAME_BYTE ( panose[6] ),
FT_FRAME_BYTE ( panose[7] ),
FT_FRAME_BYTE ( panose[8] ),
FT_FRAME_BYTE ( panose[9] ),
FT_FRAME_ULONG ( ulUnicodeRange1 ),
FT_FRAME_ULONG ( ulUnicodeRange2 ),
FT_FRAME_ULONG ( ulUnicodeRange3 ),
FT_FRAME_ULONG ( ulUnicodeRange4 ),
FT_FRAME_BYTE ( achVendID[0] ),
FT_FRAME_BYTE ( achVendID[1] ),
FT_FRAME_BYTE ( achVendID[2] ),
FT_FRAME_BYTE ( achVendID[3] ),
FT_FRAME_USHORT( fsSelection ),
FT_FRAME_USHORT( usFirstCharIndex ),
FT_FRAME_USHORT( usLastCharIndex ),
FT_FRAME_SHORT ( sTypoAscender ),
FT_FRAME_SHORT ( sTypoDescender ),
FT_FRAME_SHORT ( sTypoLineGap ),
FT_FRAME_USHORT( usWinAscent ),
FT_FRAME_USHORT( usWinDescent ),
FT_FRAME_END
};
const FT_Frame_Field os2_fields_extra[] =
{
FT_FRAME_START( 8 ),
FT_FRAME_ULONG( ulCodePageRange1 ),
FT_FRAME_ULONG( ulCodePageRange2 ),
FT_FRAME_END
};
const FT_Frame_Field os2_fields_extra2[] =
{
FT_FRAME_START( 10 ),
FT_FRAME_SHORT ( sxHeight ),
FT_FRAME_SHORT ( sCapHeight ),
FT_FRAME_USHORT( usDefaultChar ),
FT_FRAME_USHORT( usBreakChar ),
FT_FRAME_USHORT( usMaxContext ),
FT_FRAME_END
};
FT_TRACE2(( "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. */
error = face->goto_table( face, TTAG_OS2, stream, 0 );
if ( error )
{
FT_TRACE2(( "is missing!\n" ));
face->os2.version = 0xFFFF;
error = SFNT_Err_Ok;
goto Exit;
}
os2 = &face->os2;
if ( READ_Fields( os2_fields, os2 ) )
goto Exit;
os2->ulCodePageRange1 = 0;
os2->ulCodePageRange2 = 0;
os2->sxHeight = 0;
os2->sCapHeight = 0;
os2->usDefaultChar = 0;
os2->usBreakChar = 0;
os2->usMaxContext = 0;
if ( os2->version >= 0x0001 )
{
/* only version 1 tables */
if ( READ_Fields( os2_fields_extra, os2 ) )
goto Exit;
if ( os2->version >= 0x0002 )
{
/* only version 2 tables */
if ( READ_Fields( os2_fields_extra2, os2 ) )
goto Exit;
}
}
FT_TRACE2(( "loaded\n" ));
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Postscript */
/* */
/* <Description> */
/* Loads the Postscript table. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: A handle to the input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_PostScript( TT_Face face,
FT_Stream stream )
{
FT_Error error;
TT_Postscript* post = &face->postscript;
static const FT_Frame_Field post_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_Postscript
FT_FRAME_START( 32 ),
FT_FRAME_ULONG( FormatType ),
FT_FRAME_ULONG( italicAngle ),
FT_FRAME_SHORT( underlinePosition ),
FT_FRAME_SHORT( underlineThickness ),
FT_FRAME_ULONG( isFixedPitch ),
FT_FRAME_ULONG( minMemType42 ),
FT_FRAME_ULONG( maxMemType42 ),
FT_FRAME_ULONG( minMemType1 ),
FT_FRAME_ULONG( maxMemType1 ),
FT_FRAME_END
};
FT_TRACE2(( "PostScript " ));
error = face->goto_table( face, TTAG_post, stream, 0 );
if ( error )
return SFNT_Err_Post_Table_Missing;
if ( READ_Fields( post_fields, post ) )
return error;
/* we don't load the glyph names, we do that in another */
/* module (ttpost). */
FT_TRACE2(( "loaded\n" ));
return SFNT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_PCLT */
/* */
/* <Description> */
/* Loads the PCL 5 Table. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: A handle to the input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_PCLT( TT_Face face,
FT_Stream stream )
{
static const FT_Frame_Field pclt_fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE TT_PCLT
FT_FRAME_START( 54 ),
FT_FRAME_ULONG ( Version ),
FT_FRAME_ULONG ( FontNumber ),
FT_FRAME_USHORT( Pitch ),
FT_FRAME_USHORT( xHeight ),
FT_FRAME_USHORT( Style ),
FT_FRAME_USHORT( TypeFamily ),
FT_FRAME_USHORT( CapHeight ),
FT_FRAME_BYTES ( TypeFace, 16 ),
FT_FRAME_BYTES ( CharacterComplement, 8 ),
FT_FRAME_BYTES ( FileName, 6 ),
FT_FRAME_CHAR ( StrokeWeight ),
FT_FRAME_CHAR ( WidthType ),
FT_FRAME_BYTE ( SerifStyle ),
FT_FRAME_BYTE ( Reserved ),
FT_FRAME_END
};
FT_Error error;
TT_PCLT* pclt = &face->pclt;
FT_TRACE2(( "PCLT " ));
/* optional table */
error = face->goto_table( face, TTAG_PCLT, stream, 0 );
if ( error )
{
FT_TRACE2(( "missing (optional)\n" ));
pclt->Version = 0;
return SFNT_Err_Ok;
}
if ( READ_Fields( pclt_fields, pclt ) )
goto Exit;
FT_TRACE2(( "loaded\n" ));
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Gasp */
/* */
/* <Description> */
/* Loads the `gasp' table into a face object. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Gasp( TT_Face face,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UInt j,num_ranges;
TT_GaspRange* gaspranges;
FT_TRACE2(( "TT_Load_Gasp: %08p\n", face ));
/* the gasp table is optional */
error = face->goto_table( face, TTAG_gasp, stream, 0 );
if ( error )
return SFNT_Err_Ok;
if ( ACCESS_Frame( 4L ) )
goto Exit;
face->gasp.version = GET_UShort();
face->gasp.numRanges = GET_UShort();
FORGET_Frame();
num_ranges = face->gasp.numRanges;
FT_TRACE3(( "number of ranges = %d\n", num_ranges ));
if ( ALLOC_ARRAY( gaspranges, num_ranges, TT_GaspRange ) ||
ACCESS_Frame( num_ranges * 4L ) )
goto Exit;
face->gasp.gaspRanges = gaspranges;
for ( j = 0; j < num_ranges; j++ )
{
gaspranges[j].maxPPEM = GET_UShort();
gaspranges[j].gaspFlag = GET_UShort();
FT_TRACE3(( " [max:%d flag:%d]",
gaspranges[j].maxPPEM,
gaspranges[j].gaspFlag ));
}
FT_TRACE3(( "\n" ));
FORGET_Frame();
FT_TRACE2(( "GASP loaded\n" ));
Exit:
return error;
}
FT_CALLBACK_DEF( int )
tt_kern_pair_compare( const void* a,
const void* b );
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Kern */
/* */
/* <Description> */
/* Loads the first kerning table with format 0 in the font. Only */
/* accepts the first horizontal kerning table. Developers should use */
/* the `ftxkern' extension to access other kerning tables in the font */
/* file, if they really want to. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: The input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Kern( TT_Face face,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UInt n, num_tables;
/* the kern table is optional; exit silently if it is missing */
error = face->goto_table( face, TTAG_kern, stream, 0 );
if ( error )
return SFNT_Err_Ok;
if ( ACCESS_Frame( 4L ) )
goto Exit;
(void)GET_UShort(); /* version */
num_tables = GET_UShort();
FORGET_Frame();
for ( n = 0; n < num_tables; n++ )
{
FT_UInt coverage;
FT_UInt length;
if ( ACCESS_Frame( 6L ) )
goto Exit;
(void)GET_UShort(); /* version */
length = GET_UShort() - 6; /* substract header length */
coverage = GET_UShort();
FORGET_Frame();
if ( coverage == 0x0001 )
{
FT_UInt num_pairs;
TT_Kern_0_Pair* pair;
TT_Kern_0_Pair* limit;
/* found a horizontal format 0 kerning table! */
if ( ACCESS_Frame( 8L ) )
goto Exit;
num_pairs = GET_UShort();
/* skip the rest */
FORGET_Frame();
/* allocate array of kerning pairs */
if ( ALLOC_ARRAY( face->kern_pairs, num_pairs, TT_Kern_0_Pair ) ||
ACCESS_Frame( 6L * num_pairs ) )
goto Exit;
pair = face->kern_pairs;
limit = pair + num_pairs;
for ( ; pair < limit; pair++ )
{
pair->left = GET_UShort();
pair->right = GET_UShort();
pair->value = GET_UShort();
}
FORGET_Frame();
face->num_kern_pairs = num_pairs;
face->kern_table_index = n;
/* ensure that the kerning pair table is sorted (yes, some */
/* fonts have unsorted tables!) */
{
FT_UInt i;
TT_Kern_0_Pair* pair0;
pair0 = face->kern_pairs;
for ( i = 1; i < num_pairs; i++, pair0++ )
{
if ( tt_kern_pair_compare( pair0, pair0 + 1 ) != -1 )
{
qsort( (void*)face->kern_pairs, (int)num_pairs,
sizeof ( TT_Kern_0_Pair ), tt_kern_pair_compare );
break;
}
}
}
goto Exit;
}
if ( FILE_Skip( length ) )
goto Exit;
}
/* no kern table found -- doesn't matter */
face->kern_table_index = -1;
face->num_kern_pairs = 0;
face->kern_pairs = NULL;
Exit:
return error;
}
#undef TT_KERN_INDEX
#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 )
FT_CALLBACK_DEF( int )
tt_kern_pair_compare( const void* a,
const void* b )
{
TT_Kern_0_Pair* pair1 = (TT_Kern_0_Pair*)a;
TT_Kern_0_Pair* pair2 = (TT_Kern_0_Pair*)b;
FT_ULong index1 = TT_KERN_INDEX( pair1->left, pair1->right );
FT_ULong index2 = TT_KERN_INDEX( pair2->left, pair2->right );
return ( index1 < index2 ? -1 :
( index1 > index2 ? 1 : 0 ));
}
#undef TT_KERN_INDEX
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Hdmx */
/* */
/* <Description> */
/* Loads the horizontal device metrics table. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* stream :: A handle to the input stream. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Hdmx( TT_Face face,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
TT_Hdmx* hdmx = &face->hdmx;
FT_Long num_glyphs;
FT_Long record_size;
hdmx->version = 0;
hdmx->num_records = 0;
hdmx->records = 0;
/* this table is optional */
error = face->goto_table( face, TTAG_hdmx, stream, 0 );
if ( error )
return SFNT_Err_Ok;
if ( ACCESS_Frame( 8L ) )
goto Exit;
hdmx->version = GET_UShort();
hdmx->num_records = GET_Short();
record_size = GET_Long();
FORGET_Frame();
/* Only recognize format 0 */
if ( hdmx->version != 0 )
goto Exit;
if ( ALLOC_ARRAY( hdmx->records, hdmx->num_records, TT_HdmxRec ) )
goto Exit;
num_glyphs = face->root.num_glyphs;
record_size -= num_glyphs + 2;
{
TT_HdmxRec* cur = hdmx->records;
TT_HdmxRec* limit = cur + hdmx->num_records;
for ( ; cur < limit; cur++ )
{
/* read record */
if ( READ_Byte( cur->ppem ) ||
READ_Byte( cur->max_width ) )
goto Exit;
if ( ALLOC( cur->widths, num_glyphs ) ||
FILE_Read( cur->widths, num_glyphs ) )
goto Exit;
/* skip padding bytes */
if ( record_size > 0 && FILE_Skip( record_size ) )
goto Exit;
}
}
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Free_Hdmx */
/* */
/* <Description> */
/* Frees the horizontal device metrics table. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* */
FT_LOCAL_DEF void
TT_Free_Hdmx( TT_Face face )
{
if ( face )
{
FT_Int n;
FT_Memory memory = face->root.driver->root.memory;
for ( n = 0; n < face->hdmx.num_records; n++ )
FREE( face->hdmx.records[n].widths );
FREE( face->hdmx.records );
face->hdmx.num_records = 0;
}
}
/* END */