diff options
Diffstat (limited to 'Build/source/libs/libttf/extend/ftxkern.c')
-rw-r--r-- | Build/source/libs/libttf/extend/ftxkern.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/Build/source/libs/libttf/extend/ftxkern.c b/Build/source/libs/libttf/extend/ftxkern.c new file mode 100644 index 00000000000..94e10822816 --- /dev/null +++ b/Build/source/libs/libttf/extend/ftxkern.c @@ -0,0 +1,603 @@ +/******************************************************************* + * + * ftxkern.c 1.0 + * + * Kerning support extension. + * + * Copyright 1996-2000 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. + * + * + * The kerning support is currently part of the engine extensions. + * + ******************************************************************/ + +#include "ttconfig.h" +#include "ftxkern.h" + +#include "ttextend.h" +#include "tttypes.h" +#include "ttdebug.h" +#include "ttmemory.h" +#include "ttfile.h" +#include "ttobjs.h" +#include "ttload.h" /* For the macros */ +#include "tttags.h" + +#include <stdlib.h> /* for qsort */ + +/* Required by the tracing mode */ +#undef TT_COMPONENT +#define TT_COMPONENT trace_any + +#define KERNING_ID Build_Extension_ID( 'k', 'e', 'r', 'n' ) + +#undef KERN_INDEX +#define KERN_INDEX(g1,g2) (((TT_ULong)g1 << 16) | g2) + + /* compare two kerning pairs, for qsort() */ + static + int __cdecl compare_kern_pairs( 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; + + TT_ULong index1 = KERN_INDEX( pair1->left, pair1->right ); + TT_ULong index2 = KERN_INDEX( pair2->left, pair2->right ); + + return ( index1 < index2 ? -1 : + ( index1 > index2 ? 1 : 0 )); + } + + /* check whether the kern table is sorted */ + static + int is_sorted( TT_Kern_0_Pair* table, TT_ULong num_pairs ) + { + TT_ULong i; + + for ( i=1; i<num_pairs; i++ ) + { + if ( compare_kern_pairs(&table[i-1], &table[i]) != -1 ) + return 0; + } + return 1; + } + + +/******************************************************************* + * + * Function : SubTable_Load_0 + * + * Description : Loads a format 0 kerning subtable data. + * + * Input : kern0 pointer to the kerning subtable + * + * Output : error code + * + * Notes : - Assumes that the stream is already `used' + * + * - the file cursor must be set by the caller + * + * - in case of error, the function _must_ destroy + * the data it allocates! + * + ******************************************************************/ + + static TT_Error Subtable_Load_0( TT_Kern_0* kern0, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort num_pairs, n; + + + if ( ACCESS_Frame( 8L ) ) + return error; + + num_pairs = GET_UShort(); + kern0->nPairs = 0; + kern0->searchRange = GET_UShort(); + kern0->entrySelector = GET_UShort(); + kern0->rangeShift = GET_UShort(); + + /* we only set kern0->nPairs if the subtable has been loaded */ + + FORGET_Frame(); + + if ( ALLOC_ARRAY( kern0->pairs, num_pairs, TT_Kern_0_Pair ) ) + return error; + + if ( ACCESS_Frame( num_pairs * 6L ) ) + goto Fail; + + for ( n = 0; n < num_pairs; n++ ) + { + kern0->pairs[n].left = GET_UShort(); + kern0->pairs[n].right = GET_UShort(); + kern0->pairs[n].value = GET_UShort(); + + if ( kern0->pairs[n].left >= input->numGlyphs || + kern0->pairs[n].right >= input->numGlyphs ) + { + FORGET_Frame(); + error = TT_Err_Invalid_Kerning_Table; + goto Fail; + } + } + + FORGET_Frame(); + + /* we're ok, set the pairs count */ + kern0->nPairs = num_pairs; + + /* the spec says that the kerning pairs must be sorted, + but some brain damaged font producers don't do that correctly.. (JvR 3/4/2000) */ + if ( !is_sorted( kern0->pairs, num_pairs ) ) + qsort( kern0->pairs, num_pairs, sizeof(TT_Kern_0_Pair), compare_kern_pairs ); + + return TT_Err_Ok; + + Fail: + FREE( kern0->pairs ); + return error; + } + + +/******************************************************************* + * + * Function : SubTable_Load_2 + * + * Description : Loads a format 2 kerning subtable data. + * + * Input : kern2 pointer to the kerning subtable + * length subtable length. This is required as + * the subheader doesn't give any indication + * of the size of the `array' table. + * + * Output : error code + * + * Notes : - Assumes that the stream is already `used' + * + * - the file cursor must be set by the caller + * + * - in case of error, the function _must_ destroy + * the data it allocates! + * + ******************************************************************/ + + static TT_Error Subtable_Load_2( TT_Kern_2* kern2, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + Long table_base; + + UShort left_offset, right_offset, array_offset; + ULong array_size; + UShort left_max, right_max, n; + + + /* record the table offset */ + table_base = FILE_Pos(); + + if ( ACCESS_Frame( 8L ) ) + return error; + + kern2->rowWidth = GET_UShort(); + left_offset = GET_UShort(); + right_offset = GET_UShort(); + array_offset = GET_UShort(); + + FORGET_Frame(); + + /* first load left and right glyph classes */ + + if ( FILE_Seek( table_base + left_offset ) || + ACCESS_Frame( 4L ) ) + return error; + + kern2->leftClass.firstGlyph = GET_UShort(); + kern2->leftClass.nGlyphs = GET_UShort(); + + FORGET_Frame(); + + if ( ALLOC_ARRAY( kern2->leftClass.classes, + kern2->leftClass.nGlyphs, + UShort ) ) + return error; + + /* load left offsets */ + + if ( ACCESS_Frame( kern2->leftClass.nGlyphs * 2L ) ) + goto Fail_Left; + + for ( n = 0; n < kern2->leftClass.nGlyphs; n++ ) + kern2->leftClass.classes[n] = GET_UShort(); + + FORGET_Frame(); + + /* right class */ + + if ( FILE_Seek( table_base + right_offset ) || + ACCESS_Frame( 4L ) ) + goto Fail_Left; + + kern2->rightClass.firstGlyph = GET_UShort(); + kern2->rightClass.nGlyphs = GET_UShort(); + + FORGET_Frame(); + + if ( ALLOC_ARRAY( kern2->rightClass.classes, + kern2->rightClass.nGlyphs, + UShort ) ) + goto Fail_Left; + + /* load right offsets */ + + if ( ACCESS_Frame( kern2->rightClass.nGlyphs * 2L ) ) + goto Fail_Right; + + for ( n = 0; n < kern2->rightClass.nGlyphs; n++ ) + kern2->rightClass.classes[n] = GET_UShort(); + + FORGET_Frame(); + + /* Now load the kerning array. We don't have its size, we */ + /* must compute it from what we know. */ + + /* We thus compute the maximum left and right offsets and */ + /* add them to get the array size. */ + + left_max = right_max = 0; + + for ( n = 0; n < kern2->leftClass.nGlyphs; n++ ) + left_max = MAX( left_max, kern2->leftClass.classes[n] ); + + for ( n = 0; n < kern2->rightClass.nGlyphs; n++ ) + right_max = MAX( right_max, kern2->leftClass.classes[n] ); + + array_size = left_max + right_max + 2; + + if ( ALLOC( kern2->array, array_size ) ) + goto Fail_Right; + + if ( ACCESS_Frame( array_size ) ) + goto Fail_Array; + + for ( n = 0; n < array_size/2; n++ ) + kern2->array[n] = GET_Short(); + + FORGET_Frame(); + + /* we're good now */ + + return TT_Err_Ok; + + Fail_Array: + FREE( kern2->array ); + + Fail_Right: + FREE( kern2->rightClass.classes ); + kern2->rightClass.nGlyphs = 0; + + Fail_Left: + FREE( kern2->leftClass.classes ); + kern2->leftClass.nGlyphs = 0; + + return error; + } + + +/******************************************************************* + * + * Function : Kerning_Create + * + * Description : Creates the kerning directory if a face is + * loaded. The tables however are loaded on + * demand to save space. + * + * Input : face pointer to the parent face object + * kern pointer to the extension's kerning field + * + * Output : error code + * + * Notes : as in all constructors, the memory allocated isn't + * released in case of failure. Rather, the task is left + * to the destructor (which is called if an error + * occurs during the loading of a face). + * + ******************************************************************/ + + static TT_Error Kerning_Create( void* ext, + PFace face ) + { + DEFINE_LOAD_LOCALS( face->stream ); + + TT_Kerning* kern = (TT_Kerning*)ext; + UShort num_tables; + Long table; + + TT_Kern_Subtable* sub; + + + /* by convention */ + if ( !kern ) + return TT_Err_Ok; + + /* Now load the kerning directory. We're called from the face */ + /* constructor. We thus need not use the stream. */ + + kern->version = 0; + kern->nTables = 0; + kern->tables = NULL; + + table = TT_LookUp_Table( face, TTAG_kern ); + if ( table < 0 ) + return TT_Err_Ok; /* The table is optional */ + + if ( FILE_Seek( face->dirTables[table].Offset ) || + ACCESS_Frame( 4L ) ) + return error; + + kern->version = GET_UShort(); + num_tables = GET_UShort(); + + FORGET_Frame(); + + /* we don't set kern->nTables until we have allocated the array */ + + if ( ALLOC_ARRAY( kern->tables, num_tables, TT_Kern_Subtable ) ) + return error; + + kern->nTables = num_tables; + + /* now load the directory entries, but do _not_ load the tables ! */ + + sub = kern->tables; + + for ( table = 0; table < num_tables; table++ ) + { + if ( ACCESS_Frame( 6L ) ) + return error; + + sub->loaded = FALSE; /* redundant, but good to see */ + sub->version = GET_UShort(); + sub->length = GET_UShort() - 6; /* substract header length */ + sub->format = GET_Byte(); + sub->coverage = GET_Byte(); + + FORGET_Frame(); + + sub->offset = FILE_Pos(); + + /* now skip to the next table */ + + if ( FILE_Skip( sub->length ) ) + return error; + + sub++; + } + + /* that's fine, leave now */ + + return TT_Err_Ok; + } + + +/******************************************************************* + * + * Function : Kerning_Destroy + * + * Description : Destroys all kerning information. + * + * Input : kern pointer to the extension's kerning field + * + * Output : error code + * + * Notes : This function is a destructor; it must be able + * to destroy partially built tables. + * + ******************************************************************/ + + static TT_Error Kerning_Destroy( void* ext, + PFace face ) + { + TT_Kerning* kern = (TT_Kerning*)ext; + TT_Kern_Subtable* sub; + UShort n; + + + /* by convention */ + if ( !kern ) + return TT_Err_Ok; + + if ( kern->nTables == 0 ) + return TT_Err_Ok; /* no tables to release */ + + /* scan the table directory and release loaded entries */ + + sub = kern->tables; + for ( n = 0; n < kern->nTables; n++ ) + { + if ( sub->loaded ) + { + switch ( sub->format ) + { + case 0: + FREE( sub->t.kern0.pairs ); + sub->t.kern0.nPairs = 0; + sub->t.kern0.searchRange = 0; + sub->t.kern0.entrySelector = 0; + sub->t.kern0.rangeShift = 0; + break; + + case 2: + FREE( sub->t.kern2.leftClass.classes ); + sub->t.kern2.leftClass.firstGlyph = 0; + sub->t.kern2.leftClass.nGlyphs = 0; + + FREE( sub->t.kern2.rightClass.classes ); + sub->t.kern2.rightClass.firstGlyph = 0; + sub->t.kern2.rightClass.nGlyphs = 0; + + FREE( sub->t.kern2.array ); + sub->t.kern2.rowWidth = 0; + break; + + default: + ; /* invalid subtable format - do nothing */ + } + + sub->loaded = FALSE; + sub->version = 0; + sub->offset = 0; + sub->length = 0; + sub->coverage = 0; + sub->format = 0; + } + sub++; + } + + FREE( kern->tables ); + kern->nTables = 0; + + return TT_Err_Ok; + } + + +/******************************************************************* + * + * Function : TT_Get_Kerning_Directory + * + * Description : Returns a given face's kerning directory. + * + * Input : face handle to the face object + * directory pointer to client's target directory + * + * Output : error code + * + * Notes : The kerning table directory is loaded with the face + * through the extension constructor. However, the kerning + * tables themselves are only loaded on demand, as they + * may represent a lot of data, unneeded by most uses of + * the engine. + * + ******************************************************************/ + + EXPORT_FUNC + TT_Error TT_Get_Kerning_Directory( TT_Face face, + TT_Kerning* directory ) + { + PFace faze = HANDLE_Face( face ); + TT_Error error; + TT_Kerning* kerning; + + + if ( !faze ) + return TT_Err_Invalid_Face_Handle; + + /* copy directory header */ + error = TT_Extension_Get( faze, KERNING_ID, (void**)&kerning ); + if ( !error ) + *directory = *kerning; + + return error; + } + + +/******************************************************************* + * + * Function : TT_Load_Kerning_Table + * + * Description : Loads a kerning table intro memory. + * + * Input : face face handle + * kern_index index in the face's kerning directory + * + * Output : error code + * + * Notes : + * + ******************************************************************/ + + EXPORT_FUNC + TT_Error TT_Load_Kerning_Table( TT_Face face, + TT_UShort kern_index ) + { + TT_Error error; + TT_Stream stream; + + TT_Kerning* kern; + TT_Kern_Subtable* sub; + + + PFace faze = HANDLE_Face( face ); + + if ( !faze ) + return TT_Err_Invalid_Face_Handle; + + error = TT_Extension_Get( faze, KERNING_ID, (void**)&kern ); + if ( error ) + return error; + + if ( kern->nTables == 0 ) + return TT_Err_Table_Missing; + + if ( kern_index >= kern->nTables ) + return TT_Err_Invalid_Argument; + + sub = kern->tables + kern_index; + + if ( sub->format != 0 && sub->format != 2 ) + return TT_Err_Invalid_Kerning_Table_Format; + + /* now access stream */ + if ( USE_Stream( faze->stream, stream ) ) + return error; + + if ( FILE_Seek( sub->offset ) ) + goto Fail; + + if ( sub->format == 0 ) + error = Subtable_Load_0( &sub->t.kern0, faze ); + else if ( sub->format == 2 ) + error = Subtable_Load_2( &sub->t.kern2, faze ); + + if ( !error ) + sub->loaded = TRUE; + + Fail: + /* release stream */ + DONE_Stream( stream ); + + return error; + } + + + EXPORT_FUNC + TT_Error TT_Init_Kerning_Extension( TT_Engine engine ) + { + PEngine_Instance _engine = HANDLE_Engine( engine ); + + TT_Error error; + + + if ( !_engine ) + return TT_Err_Invalid_Engine; + + error = TT_Register_Extension( _engine, + KERNING_ID, + sizeof ( TT_Kerning ), + Kerning_Create, + Kerning_Destroy ); + return error; + } + + +/* END */ |