720 lines
17 KiB
C
720 lines
17 KiB
C
/****************************************************************************
|
|
*
|
|
* gflib.c
|
|
*
|
|
* FreeType font driver for METAFONT GF FONT files
|
|
*
|
|
* Copyright 1996-2018 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_FREETYPE_H
|
|
#include FT_INTERNAL_DEBUG_H
|
|
#include FT_INTERNAL_STREAM_H
|
|
#include FT_INTERNAL_OBJECTS_H
|
|
#include FT_SYSTEM_H
|
|
#include FT_CONFIG_CONFIG_H
|
|
#include FT_ERRORS_H
|
|
#include FT_TYPES_H
|
|
|
|
#include "gf.h"
|
|
#include "gfdrivr.h"
|
|
#include "gferror.h"
|
|
|
|
|
|
/**************************************************************************
|
|
*
|
|
* 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_gflib
|
|
|
|
FT_Byte bit_table[] = {
|
|
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
|
|
|
|
typedef struct GF_CharOffsetRec_
|
|
{
|
|
FT_Long char_offset;
|
|
FT_UShort code;
|
|
FT_Short gid;
|
|
|
|
} GF_CharOffsetRec, *GF_CharOffset;
|
|
|
|
/**************************************************************************
|
|
*
|
|
* GF font utility functions.
|
|
*
|
|
*/
|
|
|
|
FT_Long gf_read_intn(FT_Stream, FT_Int);
|
|
FT_ULong gf_read_uintn(FT_Stream, FT_Int);
|
|
|
|
#define READ_UINT1( stream ) (FT_Byte)gf_read_uintn( stream, 1)
|
|
#define READ_UINT2( stream ) (FT_Byte)gf_read_uintn( stream, 2)
|
|
#define READ_UINT3( stream ) (FT_Byte)gf_read_uintn( stream, 3)
|
|
#define READ_UINT4( stream ) (FT_Byte)gf_read_uintn( stream, 4)
|
|
#define READ_UINTN( stream,n) (FT_ULong)gf_read_uintn( stream, n)
|
|
#define READ_INT1( stream ) (FT_String)gf_read_intn( stream, 1)
|
|
#define READ_INT4( stream ) (FT_Long)gf_read_intn( stream, 4)
|
|
|
|
/*
|
|
* Reading a Number from file
|
|
*/
|
|
FT_ULong
|
|
gf_read_uintn(FT_Stream stream, FT_Int size)
|
|
{
|
|
FT_ULong v,k;
|
|
FT_Error error;
|
|
FT_Byte tp;
|
|
|
|
v = 0L;
|
|
|
|
while (size >= 1)
|
|
{
|
|
if ( FT_READ_BYTE(tp) )
|
|
return 0;
|
|
k = (FT_ULong)tp;
|
|
v = v*256L + k;
|
|
--size;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
FT_Long
|
|
gf_read_intn(FT_Stream stream, FT_Int size)
|
|
{
|
|
FT_Long v;
|
|
FT_Byte tp;
|
|
FT_Error error;
|
|
FT_ULong z;
|
|
|
|
if ( FT_READ_BYTE(tp) )
|
|
return 0;
|
|
z = (FT_ULong)tp;
|
|
v = (FT_Long)z & 0xffL;
|
|
|
|
if (v & 0x80L)
|
|
v = v - 256L;
|
|
--size;
|
|
|
|
while (size >= 1)
|
|
{
|
|
if ( FT_READ_BYTE(tp) )
|
|
return 0;
|
|
z = (FT_ULong)tp;
|
|
v = v*256L + z;
|
|
--size;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static int
|
|
compare( FT_Long* a,
|
|
FT_Long* b )
|
|
{
|
|
if ( *a < *b )
|
|
return -1;
|
|
else if ( *a > *b )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* API.
|
|
*
|
|
*/
|
|
|
|
static FT_Error
|
|
gf_set_encodings( GF_CharOffset of,
|
|
FT_Int ngphs,
|
|
GF_Glyph go,
|
|
FT_Memory memory )
|
|
{
|
|
FT_Error error;
|
|
FT_ULong nencoding;
|
|
FT_Int i, j;
|
|
FT_ULong k;
|
|
GF_Encoding encoding = NULL;
|
|
FT_Long *tosort;
|
|
|
|
nencoding = ngphs;
|
|
FT_TRACE2(( "gf_set_encodings: Reached here.\n" ));
|
|
|
|
if ( FT_NEW_ARRAY( encoding, nencoding ) )
|
|
return error;
|
|
|
|
if ( FT_NEW_ARRAY( tosort, nencoding ) )
|
|
return error;
|
|
|
|
|
|
FT_TRACE2(( "gf_set_encodings: Allocated sufficient memory.\n" ));
|
|
|
|
for( i = 0 ; i < ngphs ; i++ )
|
|
{
|
|
if( of[i].char_offset >= 0 )
|
|
tosort[i] = of[i].char_offset;
|
|
}
|
|
|
|
ft_qsort( (void*)tosort, ngphs, sizeof(FT_Long),
|
|
(int(*)(const void*, const void*) )compare );
|
|
|
|
k = 0;
|
|
for ( i = 0; i < ngphs; i++ )
|
|
{
|
|
for ( j = 0; j < ngphs; j++ )
|
|
{
|
|
if( of[j].char_offset == tosort[i] )
|
|
break;
|
|
}
|
|
encoding[k].enc = of[j].code;
|
|
encoding[k].glyph = k;
|
|
of[j].gid = k;
|
|
k++;
|
|
}
|
|
|
|
FT_FREE(tosort);
|
|
|
|
go->nencodings = k;
|
|
go->encodings = encoding;
|
|
|
|
return error;
|
|
}
|
|
|
|
FT_LOCAL_DEF( FT_Error )
|
|
gf_read_glyph( FT_Stream stream,
|
|
GF_Bitmap bm,
|
|
FT_Memory memory )
|
|
{
|
|
FT_Long m, n;
|
|
FT_Int paint_sw;
|
|
FT_Int instr,inst;
|
|
FT_Long min_m, max_m, min_n, max_n, del_m, del_n;
|
|
FT_Long w, h, d;
|
|
FT_Int m_b, k;
|
|
FT_Byte *ptr;
|
|
FT_Error error = FT_Err_Ok;
|
|
|
|
for ( ; ; )
|
|
{
|
|
inst = READ_UINT1( stream );
|
|
switch ((FT_Int)inst)
|
|
{
|
|
case GF_BOC:
|
|
if ( FT_STREAM_SKIP( 4 ) )
|
|
return -1;
|
|
if ( FT_STREAM_SKIP( 4 ) )
|
|
return -1;
|
|
min_m = READ_INT4( stream );
|
|
max_m = READ_INT4( stream );
|
|
min_n = READ_INT4( stream );
|
|
max_n = READ_INT4( stream );
|
|
goto BOC;
|
|
break;
|
|
case GF_BOC1:
|
|
if ( FT_STREAM_SKIP( 1 ) )
|
|
return -1;
|
|
del_m = (FT_Long)READ_UINT1( stream );
|
|
max_m = (FT_Long)READ_UINT1( stream );
|
|
del_n = (FT_Long)READ_UINT1( stream );
|
|
max_n = (FT_Long)READ_UINT1( stream );
|
|
min_m = max_m - del_m;
|
|
min_n = max_n - del_n;
|
|
goto BOC;
|
|
break;
|
|
case GF_XXX1:
|
|
k = (FT_ULong)READ_UINT1( stream );
|
|
if ( FT_STREAM_SKIP( k ) )
|
|
return -1;
|
|
break;
|
|
case GF_XXX2:
|
|
k = (FT_ULong)READ_UINT2( stream );
|
|
if ( FT_STREAM_SKIP( k ) )
|
|
return -1;
|
|
break;
|
|
case GF_XXX3:
|
|
k = (FT_ULong)READ_UINT3( stream );
|
|
if ( FT_STREAM_SKIP( k ) )
|
|
return -1;
|
|
break;
|
|
case GF_XXX4:
|
|
k = (FT_ULong)READ_UINT4( stream );
|
|
if ( FT_STREAM_SKIP( k ) )
|
|
return -1;
|
|
break;
|
|
case GF_YYY :
|
|
if ( FT_STREAM_SKIP( 4 ) )
|
|
return -1;
|
|
break;
|
|
case GF_NO_OP:
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
BOC:
|
|
if(error != FT_Err_Ok)
|
|
return -1;
|
|
w = max_m - min_m + 1;
|
|
h = max_n - min_n + 1;
|
|
if ((w < 0) || (h < 0))
|
|
{
|
|
FT_ERROR(( "gf_read_glyph: invalid w and h values\n" ));
|
|
error = FT_THROW( Invalid_File_Format );
|
|
return -1;
|
|
}
|
|
|
|
/* allocate and build bitmap */
|
|
if ((bm->bitmap = (FT_Byte*)malloc(h*((w+7)/8))) == NULL)
|
|
{
|
|
error = FT_THROW( Invalid_File_Format );
|
|
return -1;
|
|
}
|
|
|
|
memset(bm->bitmap, 0, h*((w+7)/8));
|
|
bm->raster = (FT_UInt)(w+7)/8;
|
|
bm->bbx_width = w;
|
|
bm->bbx_height = h;
|
|
bm->off_x = -min_m;
|
|
bm->off_y = max_n;
|
|
#if 0
|
|
bm->mv_x = -min_m;
|
|
bm->mv_y = max_n;
|
|
#endif
|
|
|
|
m = min_m;
|
|
n = max_n;
|
|
paint_sw = 0;
|
|
while ((instr = (FT_Int)READ_UINT1( stream )) != GF_EOC)
|
|
{
|
|
if (instr == GF_PAINT_0)
|
|
{
|
|
paint_sw = 1 - paint_sw;
|
|
}
|
|
else if ((GF_NEW_ROW_0 <= instr) && (instr <= GF_NEW_ROW_164))
|
|
{
|
|
m = min_m + (instr - GF_NEW_ROW_0);
|
|
n = n - 1;
|
|
paint_sw = 1;
|
|
}
|
|
else if ((GF_PAINT_1 <= instr) && (instr <= GF_PAINT_63))
|
|
{
|
|
d = (instr - GF_PAINT_1 + 1);
|
|
goto Paint;
|
|
}
|
|
else
|
|
{
|
|
switch ((FT_Int)instr)
|
|
{
|
|
case GF_PAINT1:
|
|
case GF_PAINT2:
|
|
case GF_PAINT3:
|
|
d = (FT_ULong)READ_UINTN( stream, (instr - GF_PAINT1 + 1));
|
|
Paint:
|
|
if (paint_sw == 0)
|
|
{
|
|
m = m + d;
|
|
}
|
|
else
|
|
{
|
|
ptr = &bm->bitmap[(max_n - n) * bm->raster + (m - min_m)/8];
|
|
m_b = (m - min_m) % 8;
|
|
while (d > 0)
|
|
{
|
|
*ptr |= bit_table[m_b];
|
|
m++;
|
|
if (++m_b >= 8)
|
|
{
|
|
m_b = 0;
|
|
++ptr;
|
|
}
|
|
d--;
|
|
}
|
|
}
|
|
paint_sw = 1 - paint_sw;
|
|
break;
|
|
case GF_SKIP0:
|
|
m = min_m;
|
|
n = n - 1;
|
|
paint_sw = 0;
|
|
break;
|
|
case GF_SKIP1:
|
|
case GF_SKIP2:
|
|
case GF_SKIP3:
|
|
m = min_m;
|
|
n = n - (FT_ULong)READ_UINTN( stream, (instr - GF_SKIP1 + 1)) - 1;
|
|
paint_sw = 0;
|
|
break;
|
|
case GF_XXX1:
|
|
case GF_XXX2:
|
|
case GF_XXX3:
|
|
case GF_XXX4:
|
|
k = READ_UINTN( stream, instr - GF_XXX1 + 1);
|
|
if ( FT_STREAM_SKIP( k ) )
|
|
return -1;
|
|
break;
|
|
case GF_YYY:
|
|
if ( FT_STREAM_SKIP( 4 ) )
|
|
return -1;
|
|
break;
|
|
case GF_NO_OP:
|
|
break;
|
|
default:
|
|
FT_FREE(bm->bitmap);
|
|
bm->bitmap = NULL;
|
|
error = FT_THROW( Invalid_File_Format );
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
FT_LOCAL_DEF( FT_Error )
|
|
gf_load_font( FT_Stream stream,
|
|
FT_Memory extmemory,
|
|
GF_Glyph *goptr )
|
|
{
|
|
GF_Glyph go;
|
|
GF_Bitmap bm;
|
|
GF_CharOffset of;
|
|
FT_Byte instr, d, pre, id, k, code;
|
|
FT_Long ds, check_sum, hppp, vppp;
|
|
FT_Long min_m, max_m, min_n, max_n, w;
|
|
FT_UInt dx, dy;
|
|
FT_Long ptr_post, ptr_p, ptr, optr, rptr;
|
|
FT_Int bc, ec, nchars, i, ngphs, idx;
|
|
FT_Error error = FT_Err_Ok;
|
|
FT_Memory memory = extmemory; /* needed for FT_NEW */
|
|
|
|
go = NULL;
|
|
nchars = -1;
|
|
|
|
if( FT_STREAM_SEEK( 0 ) )
|
|
goto Exit;
|
|
|
|
pre = READ_UINT1( stream );
|
|
if (pre != GF_PRE)
|
|
{
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
id = READ_UINT1( stream );
|
|
if (id != GF_ID)
|
|
{
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
k = READ_UINT1( stream );
|
|
if ( FT_STREAM_SKIP( k ) )
|
|
goto Exit;
|
|
|
|
/* seek to post_post instr. */
|
|
if( FT_STREAM_SEEK( stream->size - 1 ) )
|
|
goto Exit;
|
|
if( FT_STREAM_SEEK( stream->size - 1 ) )
|
|
goto Exit;
|
|
|
|
while ( READ_UINT1( stream ) == 223)
|
|
{
|
|
if( FT_STREAM_SEEK( stream->pos -2 ) )
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
if( FT_STREAM_SEEK( stream->pos -1 ) )
|
|
goto Exit;
|
|
d= READ_UINT1( stream );
|
|
|
|
if (d != GF_ID)
|
|
{
|
|
FT_ERROR(( "gf_load_font: missing GF_ID(131) field\n" ));
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
FT_TRACE2(( "gf_load_font: GF_ID(131) found\n" ));
|
|
|
|
if(FT_STREAM_SEEK( stream->pos -6 ))
|
|
goto Exit;
|
|
|
|
/* check if the code is post_post */
|
|
if (READ_UINT1( stream ) != GF_POST_POST)
|
|
{
|
|
FT_ERROR(( "gf_load_font: missing GF_POST_POST(249) field\n" ));
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
FT_TRACE2(( "gf_load_font: GF_POST_POST(249) found\n" ));
|
|
|
|
/* read pointer to post instr. */
|
|
if(FT_READ_ULONG( ptr_post ))
|
|
goto Exit;
|
|
|
|
if (ptr_post == -1)
|
|
{
|
|
FT_ERROR(( "gf_load_font: invalid postamble pointer\n" ));
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
/* goto post instr. and read it */
|
|
if(FT_STREAM_SEEK( ptr_post ))
|
|
goto Exit;
|
|
|
|
if (READ_UINT1( stream ) != GF_POST)
|
|
{
|
|
FT_ERROR(( "gf_load_font: missing GF_POST(248) field\n" ));
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
FT_TRACE2(( "gf_load_font: GF Postamble found\n" ));
|
|
|
|
if(FT_READ_ULONG( ptr_p ))
|
|
goto Exit;
|
|
if(FT_READ_ULONG( ds ))
|
|
goto Exit;
|
|
if(FT_READ_ULONG( check_sum ))
|
|
goto Exit;
|
|
if(FT_READ_ULONG( hppp ))
|
|
goto Exit;
|
|
if(FT_READ_ULONG( vppp ))
|
|
goto Exit;
|
|
min_m = READ_INT4( stream );
|
|
max_m = READ_INT4( stream );
|
|
min_n = READ_INT4( stream );
|
|
max_n = READ_INT4( stream );
|
|
|
|
FT_TRACE5(( "gf_load_font: checksum is %ld\n",check_sum ));
|
|
|
|
if( ptr_p < 0 ) /* Defined to use ptr_p */
|
|
{
|
|
FT_ERROR(( "gf_load_font: invalid pointer in postamble\n" ));
|
|
goto Exit;
|
|
}
|
|
|
|
if( check_sum < 0 ) /* Defined to use check_sum */
|
|
{
|
|
FT_ERROR(( "gf_load_font: invalid check sum value\n" ));
|
|
goto Exit;
|
|
}
|
|
#if 0
|
|
gptr = ftell(fp);
|
|
#endif
|
|
|
|
#if 0
|
|
/* read min & max char code */
|
|
bc = 256;
|
|
ec = -1;
|
|
for ( ; ; )
|
|
{
|
|
instr = READ_UINT1(fp);
|
|
if (instr == GF_POST_POST)
|
|
{
|
|
break;
|
|
}
|
|
else if (instr == GF_CHAR_LOC)
|
|
{
|
|
code = READ_UINT1(fp);
|
|
(void)SKIP_N(fp, 16);
|
|
}
|
|
else if (instr == GF_CHAR_LOC0)
|
|
{
|
|
code = READ_UINT1(fp);
|
|
(void)SKIP_N(fp, 9);
|
|
}
|
|
else
|
|
{
|
|
error = FT_THROW( Invalid_File_Format );
|
|
goto Exit;
|
|
}
|
|
if (code < bc)
|
|
bc = code;
|
|
if (code > ec)
|
|
ec = code;
|
|
}
|
|
#else
|
|
bc = 0;
|
|
ec = 255;
|
|
#endif
|
|
|
|
nchars = ec - bc + 1;
|
|
|
|
if( FT_ALLOC(go, sizeof(GF_GlyphRec)) )
|
|
goto Exit;
|
|
|
|
FT_TRACE2(( "gf_load_font: Allocated bitmap table\n" ));
|
|
|
|
go->ds = (FT_UInt)ds/(1<<20);
|
|
go->hppp = (FT_UInt)hppp/(1<<16);
|
|
go->vppp = (FT_UInt)vppp/(1<<16);
|
|
go->font_bbx_w = max_m - min_m;
|
|
go->font_bbx_h = max_n - min_n;
|
|
go->font_bbx_xoff = min_m;
|
|
go->font_bbx_yoff = min_n;
|
|
go->code_min = bc;
|
|
go->code_max = ec;
|
|
|
|
go->nglyphs = 0;
|
|
|
|
if( FT_ALLOC_MULT(of, sizeof(GF_CharOffsetRec), nchars) )
|
|
goto Exit;
|
|
|
|
rptr = stream->pos;
|
|
i =0;
|
|
ngphs=0;
|
|
|
|
for ( ; ; )
|
|
{
|
|
if ((instr = READ_UINT1( stream )) == GF_POST_POST)
|
|
break;
|
|
switch ((FT_Int)instr)
|
|
{
|
|
case GF_CHAR_LOC:
|
|
code = READ_UINT1( stream );
|
|
dx = (FT_UInt)READ_INT4( stream )/(FT_UInt)(1<<16);
|
|
dy = (FT_UInt)READ_INT4( stream )/(FT_UInt)(1<<16);
|
|
w = READ_INT4( stream );
|
|
ptr = READ_INT4( stream );
|
|
break;
|
|
case GF_CHAR_LOC0:
|
|
code = READ_UINT1( stream );
|
|
dx = (FT_UInt)READ_INT1( stream );
|
|
dy = (FT_UInt)0;
|
|
w = READ_INT4( stream );
|
|
ptr = READ_INT4( stream );
|
|
break;
|
|
default:
|
|
FT_ERROR(( "gf_load_font: missing character locators in postamble\n" ));
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
of[i].char_offset = (FT_ULong)ptr;
|
|
of[i].code = (FT_UShort)code;
|
|
of[i].gid = -1;
|
|
ngphs += 1;
|
|
i++;
|
|
}
|
|
|
|
if( FT_ALLOC_MULT(go->bm_table, sizeof(GF_BitmapRec), ngphs) )
|
|
goto Exit;
|
|
|
|
error = gf_set_encodings( of, ngphs, go, memory );
|
|
if( error )
|
|
goto Exit;
|
|
|
|
if( FT_STREAM_SEEK( rptr ) )
|
|
goto Exit;
|
|
|
|
for ( ; ; )
|
|
{
|
|
if ((instr = READ_UINT1( stream )) == GF_POST_POST)
|
|
break;
|
|
switch ((FT_Int)instr)
|
|
{
|
|
case GF_CHAR_LOC:
|
|
code = READ_UINT1( stream );
|
|
dx = (FT_UInt)READ_INT4( stream )/(FT_UInt)(1<<16);
|
|
dy = (FT_UInt)READ_INT4( stream )/(FT_UInt)(1<<16);
|
|
w = READ_INT4( stream );
|
|
ptr = READ_INT4( stream );
|
|
break;
|
|
case GF_CHAR_LOC0:
|
|
code = READ_UINT1( stream );
|
|
dx = (FT_UInt)READ_INT1( stream );
|
|
dy = (FT_UInt)0;
|
|
w = READ_INT4( stream );
|
|
ptr = READ_INT4( stream );
|
|
break;
|
|
default:
|
|
FT_ERROR(( "gf_load_font: missing character locators in postamble\n" ));
|
|
error = FT_THROW( Unknown_File_Format );
|
|
goto Exit;
|
|
}
|
|
|
|
optr = stream->pos;
|
|
if( FT_STREAM_SEEK( ptr ) )
|
|
goto Exit;
|
|
|
|
for ( i = 0; i < 256; i++ )
|
|
{
|
|
if( of[i].code == code && of[i].char_offset == ptr )
|
|
{
|
|
idx = of[i].gid;
|
|
break;
|
|
}
|
|
}
|
|
bm = &go->bm_table[idx];
|
|
|
|
bm->mv_x = dx;
|
|
bm->mv_y = dy;
|
|
bm->code = code;
|
|
go->nglyphs += 1;
|
|
|
|
if (gf_read_glyph( stream, bm, memory ) < 0)
|
|
goto Exit;
|
|
|
|
if(FT_STREAM_SEEK( optr ))
|
|
goto Exit;
|
|
}
|
|
|
|
FT_FREE(of);
|
|
*goptr = go;
|
|
return error;
|
|
|
|
Exit:
|
|
if (go != NULL)
|
|
{
|
|
if( go->bm_table )
|
|
{
|
|
for (i = 0; i < nchars; i++)
|
|
FT_FREE(go->bm_table[i].bitmap);
|
|
}
|
|
FT_FREE(go->bm_table);
|
|
FT_FREE(go);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
FT_LOCAL_DEF( void )
|
|
gf_free_font( GF_Face face )
|
|
{
|
|
FT_Memory memory = FT_FACE( face )->memory;
|
|
GF_Glyph go = face->gf_glyph;
|
|
FT_UInt nchars = FT_FACE( face )->num_glyphs,i;
|
|
|
|
if ( !go )
|
|
return;
|
|
|
|
if( go->bm_table )
|
|
{
|
|
for (i = 0; i < nchars; i++)
|
|
FT_FREE(go->bm_table[i].bitmap);
|
|
}
|
|
FT_FREE(go->bm_table);
|
|
FT_FREE(go);
|
|
}
|
|
|
|
|
|
/* END */
|