FreeType 1.31.1
This commit is contained in:
564
lib/extend/ftxkern.c
Normal file
564
lib/extend/ftxkern.c
Normal file
@@ -0,0 +1,564 @@
|
||||
/*******************************************************************
|
||||
*
|
||||
* ftxkern.c 1.0
|
||||
*
|
||||
* Kerning support extension.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* The kerning support is currently part of the engine extensions.
|
||||
*
|
||||
******************************************************************/
|
||||
|
||||
#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"
|
||||
|
||||
/* Required by the tracing mode */
|
||||
#undef TT_COMPONENT
|
||||
#define TT_COMPONENT trace_any
|
||||
|
||||
#define KERNING_ID Build_Extension_ID( 'k', 'e', 'r', 'n' )
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
* 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;
|
||||
|
||||
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 */
|
||||
Reference in New Issue
Block a user