home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: SysTools
/
SysTools.zip
/
ft-beta.zip
/
freetype
/
lib
/
extend
/
ttkern.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-10-06
|
14KB
|
505 lines
/*******************************************************************
*
* ttkern.c 1.0
*
* Kerning support extension.
*
* Copyright 1996, 1997 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 "freetype.h"
#include "ttextend.h"
#include "tttypes.h"
#include "tterror.h"
#include "ttmemory.h"
#include "ttfile.h"
#include "ttobjs.h"
#include "ttload.h" /* For the macros */
#include "tttags.h"
#include "ttkern.h"
/*******************************************************************
*
* 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,
TT_Stream input )
{
DEFINE_LOAD_LOCALS(input);
int num_pairs, n;
if ( ACCESS_Frame( 8 ) )
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 when 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 * 2L ) )
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();
}
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,
TT_Stream input )
{
DEFINE_LOAD_LOCALS(input);
long table_base;
int left_offset, right_offset, array_offset, array_size;
int left_max, right_max, n;
/* record the table offset */
table_base = FILE_Pos();
if ( ACCESS_Frame( 8 ) )
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( 4 ) )
return error;
kern2->leftClass.firstGlyph = GET_UShort();
kern2->leftClass.nGlyphs = GET_UShort();
FORGET_Frame();
if ( ALLOC_ARRAY( kern2->leftClass.classes,
kern2->leftClass.nGlyphs,
Short ) )
return error;
/* load left offsets */
if ( ACCESS_Frame( kern2->leftClass.nGlyphs * 2 ) )
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( 4 ) )
goto Fail_Left;
kern2->rightClass.firstGlyph = GET_UShort();
kern2->rightClass.nGlyphs = GET_UShort();
FORGET_Frame();
if ( ALLOC_ARRAY( kern2->rightClass.classes,
kern2->rightClass.nGlyphs,
Short ) )
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 thus compute it from what we now.. */
/* 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_UShort();
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 : Create the kerning directory when a face is
* loaded. The tables however are loaded on
* demand to save space.
*
* Input : face pointeur to the parent face object
* kern pointeur 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 when an error
* occurs during the loading of a face).
*
******************************************************************/
TT_Error Kerning_Create( PFace face,
TT_Kerning* kern )
{
DEFINE_LOAD_LOCALS(face->stream);
int table, num_tables;
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 using the stream */
kern->version = 0;
kern->nTables = 0;
kern->tables = NULL;
table = LookUp_TrueType_Table( face, TTAG_kern );
if (table < 0)
return TT_Err_Ok; /* The table is optional */
if ( FILE_Seek( face->dirTables[table].Offset ) ||
ACCESS_Frame( 4 ) )
return error;
kern->version = GET_Short();
num_tables = GET_Short();
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( 6 ) )
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 : Destroy 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.
*
******************************************************************/
TT_Error Kerning_Destroy( TT_Kerning* kern )
{
TT_Kern_Subtable* sub;
int 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.
*
******************************************************************/
TT_Error TT_Get_Kerning_Directory( TT_Face face,
TT_Kerning* directory )
{
PFace faze = HANDLE_Face(face);
if (!faze)
return TT_Err_Invalid_Face_Handle;
/* copy directory header */
*directory = ((TExtension*)faze->extension)->kerning;
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : TT_Load_Kerning_Table
*
* Description : Load a kerning table intro memory
*
* Input : face face handle
* kern_index index in the face's kerning directory
*
* Output : error code
*
* Notes :
*
******************************************************************/
TT_Error TT_Load_Kerning_Table( TT_Face face,
int 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;
kern = &((TExtension*)faze->extension)->kerning;
if (kern_index < 0 || kern_index >= kern->nTables)
return TT_Err_Bad_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, stream );
else if (sub->format == 2)
error = Subtable_Load_2( &sub->t.kern2, stream );
if (!error)
sub->loaded = TRUE;
Fail:
/* release stream */
DONE_Stream(stream);
return error;
}
/* END */