e5680279b2
drastically reduce the heap usage of FreeType, especially in the case of memory-mapped files. The idea is to avoid loading and decoding tables in the heap, and instead access the raw data whenever possible (i.e. when it doesn't compromise performance). This had several impacts: first, opening vera.ttf uses a ridiculous amount of memory (when the FT_Library footprint is accounted for), until you start loading glyphs. Even then, you'll save at least 20 Kb compared to the non optimized case. performance of various operations, including open/close has also been dramatically improved. More optimisations to come. The auto-hinter eats memory like crazy? This must be stopped...
2120 lines
68 KiB
C
2120 lines
68 KiB
C
/***************************************************************************/
|
|
/* */
|
|
/* ttgload.c */
|
|
/* */
|
|
/* TrueType Glyph Loader (body). */
|
|
/* */
|
|
/* Copyright 1996-2001, 2002, 2003, 2004 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_CALC_H
|
|
#include FT_INTERNAL_STREAM_H
|
|
#include FT_INTERNAL_SFNT_H
|
|
#include FT_TRUETYPE_TAGS_H
|
|
#include FT_OUTLINE_H
|
|
|
|
#include "ttgload.h"
|
|
|
|
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
|
|
#include "ttgxvar.h"
|
|
#endif
|
|
|
|
#include "tterrors.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_ttgload
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Composite font flags. */
|
|
/* */
|
|
#define ARGS_ARE_WORDS 0x0001
|
|
#define ARGS_ARE_XY_VALUES 0x0002
|
|
#define ROUND_XY_TO_GRID 0x0004
|
|
#define WE_HAVE_A_SCALE 0x0008
|
|
/* reserved 0x0010 */
|
|
#define MORE_COMPONENTS 0x0020
|
|
#define WE_HAVE_AN_XY_SCALE 0x0040
|
|
#define WE_HAVE_A_2X2 0x0080
|
|
#define WE_HAVE_INSTR 0x0100
|
|
#define USE_MY_METRICS 0x0200
|
|
#define OVERLAP_COMPOUND 0x0400
|
|
#define SCALED_COMPONENT_OFFSET 0x0800
|
|
#define UNSCALED_COMPONENT_OFFSET 0x1000
|
|
|
|
|
|
/* Maximum recursion depth we allow for composite glyphs.
|
|
* The TrueType spec doesn't say anything about recursion,
|
|
* so it isn't clear that recursion is allowed at all. But
|
|
* we'll be generous.
|
|
*/
|
|
#define TT_MAX_COMPOSITE_RECURSE 5
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* TT_Get_Metrics */
|
|
/* */
|
|
/* <Description> */
|
|
/* Returns the horizontal or vertical metrics in font units for a */
|
|
/* given glyph. The metrics are the left side bearing (resp. top */
|
|
/* side bearing) and advance width (resp. advance height). */
|
|
/* */
|
|
/* <Input> */
|
|
/* header :: A pointer to either the horizontal or vertical metrics */
|
|
/* structure. */
|
|
/* */
|
|
/* idx :: The glyph index. */
|
|
/* */
|
|
/* <Output> */
|
|
/* bearing :: The bearing, either left side or top side. */
|
|
/* */
|
|
/* advance :: The advance width resp. advance height. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This function will much probably move to another component in the */
|
|
/* near future, but I haven't decided which yet. */
|
|
/* */
|
|
#ifdef FT_OPTIMIZE_MEMORY
|
|
static void
|
|
tt_face_get_metrics( TT_Face face,
|
|
FT_Bool vertical,
|
|
FT_UInt idx,
|
|
FT_Short *abearing,
|
|
FT_UShort *aadvance )
|
|
{
|
|
TT_HoriHeader* header;
|
|
FT_Byte* p;
|
|
FT_Byte* limit;
|
|
FT_UShort k;
|
|
|
|
if ( vertical )
|
|
{
|
|
header = (TT_HoriHeader*)&face->vertical;
|
|
p = face->vert_metrics;
|
|
limit = p + face->vert_metrics_size;
|
|
}
|
|
else
|
|
{
|
|
header = &face->horizontal;
|
|
p = face->horz_metrics;
|
|
limit = p + face->horz_metrics_size;
|
|
}
|
|
|
|
k = header->number_Of_HMetrics;
|
|
|
|
if ( k > 0 )
|
|
{
|
|
if ( idx < (FT_UInt)k )
|
|
{
|
|
p += 4*idx;
|
|
if ( p+4 >= limit )
|
|
goto NoData;
|
|
|
|
*aadvance = FT_NEXT_USHORT(p);
|
|
*abearing = FT_NEXT_SHORT(p);
|
|
}
|
|
else
|
|
{
|
|
p += 4*(k-1);
|
|
if ( p+4 > limit )
|
|
goto NoData;
|
|
|
|
*aadvance = FT_NEXT_USHORT(p);
|
|
p += 2 + 2*(idx-k);
|
|
if ( p+2 > limit )
|
|
*abearing = 0;
|
|
else
|
|
*abearing = FT_PEEK_SHORT(p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NoData:
|
|
*abearing = 0;
|
|
*aadvance = 0;
|
|
}
|
|
}
|
|
#else
|
|
static void
|
|
tt_face_get_metrics( TT_Face face,
|
|
FT_Bool vertical,
|
|
FT_UInt idx,
|
|
FT_Short *abearing,
|
|
FT_UShort *aadvance )
|
|
{
|
|
TT_HoriHeader* header = (vertical ? (TT_HoriHeader*)&face->vertical
|
|
: &face->horizontal);
|
|
TT_LongMetrics longs_m;
|
|
FT_UShort k = header->number_Of_HMetrics;
|
|
|
|
|
|
if ( k == 0 )
|
|
{
|
|
*abearing = *aadvance = 0;
|
|
return;
|
|
}
|
|
|
|
if ( idx < (FT_UInt)k )
|
|
{
|
|
longs_m = (TT_LongMetrics )header->long_metrics + idx;
|
|
*abearing = longs_m->bearing;
|
|
*aadvance = longs_m->advance;
|
|
}
|
|
else
|
|
{
|
|
*abearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k];
|
|
*aadvance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Returns the horizontal metrics in font units for a given glyph. If */
|
|
/* `check' is true, take care of monospaced fonts by returning the */
|
|
/* advance width maximum. */
|
|
/* */
|
|
static void
|
|
Get_HMetrics( TT_Face face,
|
|
FT_UInt idx,
|
|
FT_Bool check,
|
|
FT_Short* lsb,
|
|
FT_UShort* aw )
|
|
{
|
|
tt_face_get_metrics( face, 0, idx, lsb, aw );
|
|
|
|
if ( check && face->postscript.isFixedPitch )
|
|
*aw = face->horizontal.advance_Width_Max;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Returns the advance width table for a given pixel size if it is found */
|
|
/* in the font's `hdmx' table (if any). */
|
|
/* */
|
|
static FT_Byte*
|
|
Get_Advance_WidthPtr( TT_Face face,
|
|
FT_Int ppem,
|
|
FT_UInt gindex )
|
|
{
|
|
#ifdef FT_OPTIMIZE_MEMORY
|
|
FT_UInt nn;
|
|
FT_Byte* result = NULL;
|
|
FT_ULong record_size = face->hdmx_record_size;
|
|
FT_Byte* record = face->hdmx_table + 8;
|
|
|
|
for ( nn = 0; nn < face->hdmx_record_count; nn++ )
|
|
if ( face->hdmx_record_sizes[nn] == ppem )
|
|
{
|
|
gindex += 2;
|
|
if ( gindex < record_size )
|
|
result = record + gindex;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
#else
|
|
FT_UShort n;
|
|
|
|
|
|
for ( n = 0; n < face->hdmx.num_records; n++ )
|
|
if ( face->hdmx.records[n].ppem == ppem )
|
|
return &face->hdmx.records[n].widths[gindex];
|
|
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Returns the vertical metrics in font units for a given glyph. */
|
|
/* Greg Hitchcock from Microsoft told us that if there were no `vmtx' */
|
|
/* table, typoAscender/Descender from the `OS/2' table would be used */
|
|
/* instead, and if there were no `OS/2' table, use ascender/descender */
|
|
/* from the `hhea' table. But that is not what Microsoft's rasterizer */
|
|
/* apparently does: It uses the ppem value as the advance height, and */
|
|
/* sets the top side bearing to be zero. */
|
|
/* */
|
|
/* The monospace `check' is probably not meaningful here, but we leave */
|
|
/* it in for a consistent interface. */
|
|
/* */
|
|
static void
|
|
Get_VMetrics( TT_Face face,
|
|
FT_UInt idx,
|
|
FT_Bool check,
|
|
FT_Short* tsb,
|
|
FT_UShort* ah )
|
|
{
|
|
FT_UNUSED( check );
|
|
|
|
if ( face->vertical_info )
|
|
tt_face_get_metrics( face, 1, idx, tsb, ah );
|
|
|
|
#if 1 /* Emperically determined, at variance with what MS said */
|
|
|
|
else
|
|
{
|
|
*tsb = 0;
|
|
*ah = face->root.units_per_EM;
|
|
}
|
|
|
|
#else /* This is what MS said to do. It isn't what they do, however. */
|
|
|
|
else if ( face->os2.version != 0xFFFFU )
|
|
{
|
|
*tsb = face->os2.sTypoAscender;
|
|
*ah = face->os2.sTypoAscender - face->os2.sTypoDescender;
|
|
}
|
|
else
|
|
{
|
|
*tsb = face->horizontal.Ascender;
|
|
*ah = face->horizontal.Ascender - face->horizontal.Descender;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#define cur_to_org( n, zone ) \
|
|
FT_ARRAY_COPY( (zone)->org, (zone)->cur, (n) )
|
|
|
|
#define org_to_cur( n, zone ) \
|
|
FT_ARRAY_COPY( (zone)->cur, (zone)->org, (n) )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Translates an array of coordinates. */
|
|
/* */
|
|
static void
|
|
translate_array( FT_UInt n,
|
|
FT_Vector* coords,
|
|
FT_Pos delta_x,
|
|
FT_Pos delta_y )
|
|
{
|
|
FT_UInt k;
|
|
|
|
|
|
if ( delta_x )
|
|
for ( k = 0; k < n; k++ )
|
|
coords[k].x += delta_x;
|
|
|
|
if ( delta_y )
|
|
for ( k = 0; k < n; k++ )
|
|
coords[k].y += delta_y;
|
|
}
|
|
|
|
|
|
static void
|
|
tt_prepare_zone( TT_GlyphZone zone,
|
|
FT_GlyphLoad load,
|
|
FT_UInt start_point,
|
|
FT_UInt start_contour )
|
|
{
|
|
zone->n_points = (FT_UShort)( load->outline.n_points - start_point );
|
|
zone->n_contours = (FT_Short) ( load->outline.n_contours - start_contour );
|
|
zone->org = load->extra_points + start_point;
|
|
zone->cur = load->outline.points + start_point;
|
|
zone->tags = (FT_Byte*)load->outline.tags + start_point;
|
|
zone->contours = (FT_UShort*)load->outline.contours + start_contour;
|
|
}
|
|
|
|
|
|
#undef IS_HINTED
|
|
#define IS_HINTED( flags ) ( ( flags & FT_LOAD_NO_HINTING ) == 0 )
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* The following functions are used by default with TrueType fonts. */
|
|
/* However, they can be replaced by alternatives if we need to support */
|
|
/* TrueType-compressed formats (like MicroType) in the future. */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
TT_Access_Glyph_Frame( TT_Loader loader,
|
|
FT_UInt glyph_index,
|
|
FT_ULong offset,
|
|
FT_UInt byte_count )
|
|
{
|
|
FT_Error error;
|
|
FT_Stream stream = loader->stream;
|
|
|
|
/* for non-debug mode */
|
|
FT_UNUSED( glyph_index );
|
|
|
|
|
|
FT_TRACE5(( "Glyph %ld\n", glyph_index ));
|
|
|
|
/* the following line sets the `error' variable through macros! */
|
|
if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( byte_count ) )
|
|
return error;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( void )
|
|
TT_Forget_Glyph_Frame( TT_Loader loader )
|
|
{
|
|
FT_Stream stream = loader->stream;
|
|
|
|
|
|
FT_FRAME_EXIT();
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
TT_Load_Glyph_Header( TT_Loader loader )
|
|
{
|
|
FT_Stream stream = loader->stream;
|
|
FT_Int byte_len = loader->byte_len - 10;
|
|
|
|
|
|
if ( byte_len < 0 )
|
|
return TT_Err_Invalid_Outline;
|
|
|
|
loader->n_contours = FT_GET_SHORT();
|
|
|
|
loader->bbox.xMin = FT_GET_SHORT();
|
|
loader->bbox.yMin = FT_GET_SHORT();
|
|
loader->bbox.xMax = FT_GET_SHORT();
|
|
loader->bbox.yMax = FT_GET_SHORT();
|
|
|
|
FT_TRACE5(( " # of contours: %d\n", loader->n_contours ));
|
|
FT_TRACE5(( " xMin: %4d xMax: %4d\n", loader->bbox.xMin,
|
|
loader->bbox.xMax ));
|
|
FT_TRACE5(( " yMin: %4d yMax: %4d\n", loader->bbox.yMin,
|
|
loader->bbox.yMax ));
|
|
loader->byte_len = byte_len;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
TT_Load_Simple_Glyph( TT_Loader load )
|
|
{
|
|
FT_Error error;
|
|
FT_Stream stream = load->stream;
|
|
FT_GlyphLoader gloader = load->gloader;
|
|
FT_Int n_contours = load->n_contours;
|
|
FT_Outline* outline;
|
|
TT_Face face = (TT_Face)load->face;
|
|
TT_GlyphSlot slot = (TT_GlyphSlot)load->glyph;
|
|
FT_UShort n_ins;
|
|
FT_Int n, n_points;
|
|
FT_Int byte_len = load->byte_len;
|
|
|
|
FT_Byte *flag, *flag_limit;
|
|
FT_Byte c, count;
|
|
FT_Vector *vec, *vec_limit;
|
|
FT_Pos x;
|
|
FT_Short *cont, *cont_limit;
|
|
|
|
|
|
/* reading the contours' endpoints & number of points */
|
|
cont = gloader->current.outline.contours;
|
|
cont_limit = cont + n_contours;
|
|
|
|
/* check space for contours array + instructions count */
|
|
byte_len -= 2 * ( n_contours + 1 );
|
|
if ( byte_len < 0 )
|
|
goto Invalid_Outline;
|
|
|
|
for ( ; cont < cont_limit; cont++ )
|
|
cont[0] = FT_GET_USHORT();
|
|
|
|
n_points = 0;
|
|
if ( n_contours > 0 )
|
|
n_points = cont[-1] + 1;
|
|
|
|
error = FT_GlyphLoader_CheckPoints( gloader, n_points + 4, 0 );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
/* we'd better check the contours table right now */
|
|
outline = &gloader->current.outline;
|
|
|
|
for ( cont = outline->contours + 1; cont < cont_limit; cont++ )
|
|
if ( cont[-1] >= cont[0] )
|
|
goto Invalid_Outline;
|
|
|
|
/* reading the bytecode instructions */
|
|
slot->control_len = 0;
|
|
slot->control_data = 0;
|
|
|
|
n_ins = FT_GET_USHORT();
|
|
|
|
FT_TRACE5(( " Instructions size: %u\n", n_ins ));
|
|
|
|
if ( n_ins > face->max_profile.maxSizeOfInstructions )
|
|
{
|
|
FT_TRACE0(( "TT_Load_Simple_Glyph: Too many instructions!\n" ));
|
|
error = TT_Err_Too_Many_Hints;
|
|
goto Fail;
|
|
}
|
|
|
|
byte_len -= (FT_Int)n_ins;
|
|
if ( byte_len < 0 )
|
|
{
|
|
FT_TRACE0(( "TT_Load_Simple_Glyph: Instruction count mismatch!\n" ));
|
|
error = TT_Err_Too_Many_Hints;
|
|
goto Fail;
|
|
}
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
if ( ( load->load_flags &
|
|
( FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) ) == 0 &&
|
|
load->instructions )
|
|
{
|
|
slot->control_len = n_ins;
|
|
slot->control_data = load->instructions;
|
|
|
|
FT_MEM_COPY( load->instructions, stream->cursor, (FT_Long)n_ins );
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
|
|
|
|
stream->cursor += (FT_Int)n_ins;
|
|
|
|
/* reading the point tags */
|
|
flag = (FT_Byte*)outline->tags;
|
|
flag_limit = flag + n_points;
|
|
|
|
FT_ASSERT( flag != NULL );
|
|
|
|
while ( flag < flag_limit )
|
|
{
|
|
if ( --byte_len < 0 )
|
|
goto Invalid_Outline;
|
|
|
|
*flag++ = c = FT_GET_BYTE();
|
|
if ( c & 8 )
|
|
{
|
|
if ( --byte_len < 0 )
|
|
goto Invalid_Outline;
|
|
|
|
count = FT_GET_BYTE();
|
|
if ( flag + (FT_Int)count > flag_limit )
|
|
goto Invalid_Outline;
|
|
|
|
for ( ; count > 0; count-- )
|
|
*flag++ = c;
|
|
}
|
|
}
|
|
|
|
/* check that there is enough room to load the coordinates */
|
|
for ( flag = (FT_Byte*)outline->tags; flag < flag_limit; flag++ )
|
|
{
|
|
if ( *flag & 2 )
|
|
byte_len -= 1;
|
|
else if ( ( *flag & 16 ) == 0 )
|
|
byte_len -= 2;
|
|
|
|
if ( *flag & 4 )
|
|
byte_len -= 1;
|
|
else if ( ( *flag & 32 ) == 0 )
|
|
byte_len -= 2;
|
|
}
|
|
|
|
if ( byte_len < 0 )
|
|
goto Invalid_Outline;
|
|
|
|
/* reading the X coordinates */
|
|
|
|
vec = outline->points;
|
|
vec_limit = vec + n_points;
|
|
flag = (FT_Byte*)outline->tags;
|
|
x = 0;
|
|
|
|
for ( ; vec < vec_limit; vec++, flag++ )
|
|
{
|
|
FT_Pos y = 0;
|
|
|
|
|
|
if ( *flag & 2 )
|
|
{
|
|
y = (FT_Pos)FT_GET_BYTE();
|
|
if ( ( *flag & 16 ) == 0 )
|
|
y = -y;
|
|
}
|
|
else if ( ( *flag & 16 ) == 0 )
|
|
y = (FT_Pos)FT_GET_SHORT();
|
|
|
|
x += y;
|
|
vec->x = x;
|
|
}
|
|
|
|
/* reading the Y coordinates */
|
|
|
|
vec = gloader->current.outline.points;
|
|
vec_limit = vec + n_points;
|
|
flag = (FT_Byte*)outline->tags;
|
|
x = 0;
|
|
|
|
for ( ; vec < vec_limit; vec++, flag++ )
|
|
{
|
|
FT_Pos y = 0;
|
|
|
|
|
|
if ( *flag & 4 )
|
|
{
|
|
y = (FT_Pos)FT_GET_BYTE();
|
|
if ( ( *flag & 32 ) == 0 )
|
|
y = -y;
|
|
}
|
|
else if ( ( *flag & 32 ) == 0 )
|
|
y = (FT_Pos)FT_GET_SHORT();
|
|
|
|
x += y;
|
|
vec->y = x;
|
|
}
|
|
|
|
/* clear the touch tags */
|
|
for ( n = 0; n < n_points; n++ )
|
|
outline->tags[n] &= FT_CURVE_TAG_ON;
|
|
|
|
outline->n_points = (FT_UShort)n_points;
|
|
outline->n_contours = (FT_Short) n_contours;
|
|
|
|
load->byte_len = byte_len;
|
|
|
|
Fail:
|
|
return error;
|
|
|
|
Invalid_Outline:
|
|
error = TT_Err_Invalid_Outline;
|
|
goto Fail;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
TT_Load_Composite_Glyph( TT_Loader loader )
|
|
{
|
|
FT_Error error;
|
|
FT_Stream stream = loader->stream;
|
|
FT_GlyphLoader gloader = loader->gloader;
|
|
FT_SubGlyph subglyph;
|
|
FT_UInt num_subglyphs;
|
|
FT_Int byte_len = loader->byte_len;
|
|
|
|
|
|
num_subglyphs = 0;
|
|
|
|
do
|
|
{
|
|
FT_Fixed xx, xy, yy, yx;
|
|
|
|
|
|
/* check that we can load a new subglyph */
|
|
error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs + 1 );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
/* check space */
|
|
byte_len -= 4;
|
|
if ( byte_len < 0 )
|
|
goto Invalid_Composite;
|
|
|
|
subglyph = gloader->current.subglyphs + num_subglyphs;
|
|
|
|
subglyph->arg1 = subglyph->arg2 = 0;
|
|
|
|
subglyph->flags = FT_GET_USHORT();
|
|
subglyph->index = FT_GET_USHORT();
|
|
|
|
/* check space */
|
|
byte_len -= 2;
|
|
if ( subglyph->flags & ARGS_ARE_WORDS )
|
|
byte_len -= 2;
|
|
if ( subglyph->flags & WE_HAVE_A_SCALE )
|
|
byte_len -= 2;
|
|
else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
|
|
byte_len -= 4;
|
|
else if ( subglyph->flags & WE_HAVE_A_2X2 )
|
|
byte_len -= 8;
|
|
|
|
if ( byte_len < 0 )
|
|
goto Invalid_Composite;
|
|
|
|
/* read arguments */
|
|
if ( subglyph->flags & ARGS_ARE_WORDS )
|
|
{
|
|
subglyph->arg1 = FT_GET_SHORT();
|
|
subglyph->arg2 = FT_GET_SHORT();
|
|
}
|
|
else
|
|
{
|
|
subglyph->arg1 = FT_GET_CHAR();
|
|
subglyph->arg2 = FT_GET_CHAR();
|
|
}
|
|
|
|
/* read transform */
|
|
xx = yy = 0x10000L;
|
|
xy = yx = 0;
|
|
|
|
if ( subglyph->flags & WE_HAVE_A_SCALE )
|
|
{
|
|
xx = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
yy = xx;
|
|
}
|
|
else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
|
|
{
|
|
xx = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
yy = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
}
|
|
else if ( subglyph->flags & WE_HAVE_A_2X2 )
|
|
{
|
|
xx = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
yx = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
xy = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
yy = (FT_Fixed)FT_GET_SHORT() << 2;
|
|
}
|
|
|
|
subglyph->transform.xx = xx;
|
|
subglyph->transform.xy = xy;
|
|
subglyph->transform.yx = yx;
|
|
subglyph->transform.yy = yy;
|
|
|
|
num_subglyphs++;
|
|
|
|
} while ( subglyph->flags & MORE_COMPONENTS );
|
|
|
|
gloader->current.num_subglyphs = num_subglyphs;
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
{
|
|
/* we must undo the FT_FRAME_ENTER in order to point to the */
|
|
/* composite instructions, if we find some. */
|
|
/* we will process them later... */
|
|
/* */
|
|
loader->ins_pos = (FT_ULong)( FT_STREAM_POS() +
|
|
stream->cursor - stream->limit );
|
|
}
|
|
#endif
|
|
|
|
loader->byte_len = byte_len;
|
|
|
|
Fail:
|
|
return error;
|
|
|
|
Invalid_Composite:
|
|
error = TT_Err_Invalid_Composite;
|
|
goto Fail;
|
|
}
|
|
|
|
|
|
FT_LOCAL_DEF( void )
|
|
TT_Init_Glyph_Loading( TT_Face face )
|
|
{
|
|
face->access_glyph_frame = TT_Access_Glyph_Frame;
|
|
face->read_glyph_header = TT_Load_Glyph_Header;
|
|
face->read_simple_glyph = TT_Load_Simple_Glyph;
|
|
face->read_composite_glyph = TT_Load_Composite_Glyph;
|
|
face->forget_glyph_frame = TT_Forget_Glyph_Frame;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* TT_Process_Simple_Glyph */
|
|
/* */
|
|
/* <Description> */
|
|
/* Once a simple glyph has been loaded, it needs to be processed. */
|
|
/* Usually, this means scaling and hinting through bytecode */
|
|
/* interpretation. */
|
|
/* */
|
|
static FT_Error
|
|
TT_Process_Simple_Glyph( TT_Loader load,
|
|
FT_Bool debug )
|
|
{
|
|
FT_GlyphLoader gloader = load->gloader;
|
|
FT_Outline* outline = &gloader->current.outline;
|
|
FT_UInt n_points = outline->n_points;
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
FT_UInt n_ins;
|
|
#endif
|
|
TT_GlyphZone zone = &load->zone;
|
|
FT_Error error = TT_Err_Ok;
|
|
|
|
FT_UNUSED( debug ); /* used by truetype interpreter only */
|
|
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
n_ins = load->glyph->control_len;
|
|
#endif
|
|
|
|
/* add shadow points */
|
|
|
|
/* Add two horizontal shadow points at n and n+1. */
|
|
/* We need the left side bearing and advance width. */
|
|
/* Add two vertical shadow points at n+2 and n+3. */
|
|
/* We need the top side bearing and advance height. */
|
|
|
|
{
|
|
FT_Vector* pp1;
|
|
FT_Vector* pp2;
|
|
FT_Vector* pp3;
|
|
FT_Vector* pp4;
|
|
|
|
|
|
/* pp1 = xMin - lsb */
|
|
pp1 = outline->points + n_points;
|
|
pp1->x = load->bbox.xMin - load->left_bearing;
|
|
pp1->y = 0;
|
|
|
|
/* pp2 = pp1 + aw */
|
|
pp2 = pp1 + 1;
|
|
pp2->x = pp1->x + load->advance;
|
|
pp2->y = 0;
|
|
|
|
/* pp3 = top side bearing */
|
|
pp3 = pp1 + 2;
|
|
pp3->x = 0;
|
|
pp3->y = load->top_bearing + load->bbox.yMax;
|
|
|
|
/* pp4 = pp3 - ah */
|
|
pp4 = pp1 + 3;
|
|
pp4->x = 0;
|
|
pp4->y = pp3->y - load->vadvance;
|
|
|
|
outline->tags[n_points ] = 0;
|
|
outline->tags[n_points + 1] = 0;
|
|
outline->tags[n_points + 2] = 0;
|
|
outline->tags[n_points + 3] = 0;
|
|
}
|
|
|
|
/* Note that we return four more points that are not */
|
|
/* part of the glyph outline. */
|
|
|
|
n_points += 4;
|
|
|
|
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
|
|
|
|
if ( ((TT_Face)load->face)->doblend )
|
|
{
|
|
/* Deltas apply to the unscaled data. */
|
|
FT_Vector* deltas;
|
|
FT_Memory memory = load->face->memory;
|
|
FT_StreamRec saved_stream = *(load->stream);
|
|
FT_UInt i;
|
|
|
|
|
|
/* TT_Vary_Get_Glyph_Deltas uses a frame, thus we have to save */
|
|
/* (and restore) the current one */
|
|
load->stream->cursor = 0;
|
|
load->stream->limit = 0;
|
|
|
|
error = TT_Vary_Get_Glyph_Deltas( (TT_Face)(load->face),
|
|
load->glyph_index,
|
|
&deltas,
|
|
n_points );
|
|
|
|
*(load->stream) = saved_stream;
|
|
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
for ( i = 0; i < n_points; ++i )
|
|
{
|
|
outline->points[i].x += deltas[i].x;
|
|
outline->points[i].y += deltas[i].y;
|
|
}
|
|
|
|
FT_FREE( deltas );
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
|
|
|
|
/* set up zone for hinting */
|
|
tt_prepare_zone( zone, &gloader->current, 0, 0 );
|
|
|
|
/* eventually scale the glyph */
|
|
if ( !( load->load_flags & FT_LOAD_NO_SCALE ) )
|
|
{
|
|
FT_Vector* vec = zone->cur;
|
|
FT_Vector* limit = vec + n_points;
|
|
FT_Fixed x_scale = ((TT_Size)load->size)->metrics.x_scale;
|
|
FT_Fixed y_scale = ((TT_Size)load->size)->metrics.y_scale;
|
|
|
|
/* first scale the glyph points */
|
|
for ( ; vec < limit; vec++ )
|
|
{
|
|
vec->x = FT_MulFix( vec->x, x_scale );
|
|
vec->y = FT_MulFix( vec->y, y_scale );
|
|
}
|
|
}
|
|
|
|
cur_to_org( n_points, zone );
|
|
|
|
/* eventually hint the glyph */
|
|
if ( IS_HINTED( load->load_flags ) )
|
|
{
|
|
FT_Pos x = zone->org[n_points-4].x;
|
|
|
|
|
|
x = FT_PIX_ROUND( x ) - x;
|
|
translate_array( n_points, zone->org, x, 0 );
|
|
|
|
org_to_cur( n_points, zone );
|
|
|
|
zone->cur[n_points - 3].x = FT_PIX_ROUND( zone->cur[n_points - 3].x );
|
|
zone->cur[n_points - 1].y = FT_PIX_ROUND( zone->cur[n_points - 1].y );
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
/* now consider hinting */
|
|
if ( n_ins > 0 )
|
|
{
|
|
error = TT_Set_CodeRange( load->exec, tt_coderange_glyph,
|
|
load->exec->glyphIns, n_ins );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
load->exec->is_composite = FALSE;
|
|
load->exec->pts = *zone;
|
|
load->exec->pts.n_points += 4;
|
|
|
|
error = TT_Run_Context( load->exec, debug );
|
|
if ( error && load->exec->pedantic_hinting )
|
|
goto Exit;
|
|
|
|
error = TT_Err_Ok; /* ignore bytecode errors in non-pedantic mode */
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
|
|
|
|
}
|
|
|
|
/* save glyph phantom points */
|
|
if ( !load->preserve_pps )
|
|
{
|
|
load->pp1 = zone->cur[n_points - 4];
|
|
load->pp2 = zone->cur[n_points - 3];
|
|
load->pp3 = zone->cur[n_points - 2];
|
|
load->pp4 = zone->cur[n_points - 1];
|
|
}
|
|
|
|
#if defined( TT_CONFIG_OPTION_BYTECODE_INTERPRETER ) || \
|
|
defined( TT_CONFIG_OPTION_GX_VAR_SUPPORT )
|
|
Exit:
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* load_truetype_glyph */
|
|
/* */
|
|
/* <Description> */
|
|
/* Loads a given truetype glyph. Handles composites and uses a */
|
|
/* TT_Loader object. */
|
|
/* */
|
|
static FT_Error
|
|
load_truetype_glyph( TT_Loader loader,
|
|
FT_UInt glyph_index,
|
|
FT_UInt recurse_count )
|
|
{
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
FT_Stream stream = loader->stream;
|
|
#endif
|
|
|
|
FT_Error error;
|
|
TT_Face face = (TT_Face)loader->face;
|
|
FT_ULong offset;
|
|
FT_Int contours_count;
|
|
FT_UInt num_points, count;
|
|
FT_Fixed x_scale, y_scale;
|
|
FT_GlyphLoader gloader = loader->gloader;
|
|
FT_Bool opened_frame = 0;
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
FT_StreamRec inc_stream;
|
|
FT_Data glyph_data;
|
|
FT_Bool glyph_data_loaded = 0;
|
|
#endif
|
|
|
|
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
|
|
FT_Vector *deltas;
|
|
#endif
|
|
|
|
|
|
if ( recurse_count >= TT_MAX_COMPOSITE_RECURSE )
|
|
{
|
|
error = TT_Err_Invalid_Composite;
|
|
goto Exit;
|
|
}
|
|
|
|
/* check glyph index */
|
|
if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
|
|
{
|
|
error = TT_Err_Invalid_Glyph_Index;
|
|
goto Exit;
|
|
}
|
|
|
|
loader->glyph_index = glyph_index;
|
|
num_points = 0;
|
|
|
|
x_scale = 0x10000L;
|
|
y_scale = 0x10000L;
|
|
if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
|
|
{
|
|
x_scale = ((TT_Size)loader->size)->metrics.x_scale;
|
|
y_scale = ((TT_Size)loader->size)->metrics.y_scale;
|
|
}
|
|
|
|
/* get metrics, horizontal and vertical */
|
|
{
|
|
FT_Short left_bearing = 0, top_bearing = 0;
|
|
FT_UShort advance_width = 0, advance_height = 0;
|
|
|
|
|
|
Get_HMetrics( face, glyph_index,
|
|
(FT_Bool)!( loader->load_flags &
|
|
FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ),
|
|
&left_bearing,
|
|
&advance_width );
|
|
Get_VMetrics( face, glyph_index,
|
|
(FT_Bool)!( loader->load_flags &
|
|
FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ),
|
|
&top_bearing,
|
|
&advance_height );
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
|
|
/* If this is an incrementally loaded font see if there are */
|
|
/* overriding metrics for this glyph. */
|
|
if ( face->root.internal->incremental_interface &&
|
|
face->root.internal->incremental_interface->funcs->get_glyph_metrics )
|
|
{
|
|
FT_Incremental_MetricsRec metrics;
|
|
|
|
|
|
metrics.bearing_x = left_bearing;
|
|
metrics.bearing_y = 0;
|
|
metrics.advance = advance_width;
|
|
error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
|
|
face->root.internal->incremental_interface->object,
|
|
glyph_index, FALSE, &metrics );
|
|
if ( error )
|
|
goto Exit;
|
|
left_bearing = (FT_Short)metrics.bearing_x;
|
|
advance_width = (FT_UShort)metrics.advance;
|
|
}
|
|
|
|
# if 0
|
|
/* GWW: Do I do the same for vertical metrics ??? */
|
|
if ( face->root.internal->incremental_interface &&
|
|
face->root.internal->incremental_interface->funcs->get_glyph_metrics )
|
|
{
|
|
FT_Incremental_MetricsRec metrics;
|
|
|
|
|
|
metrics.bearing_x = 0;
|
|
metrics.bearing_y = top_bearing;
|
|
metrics.advance = advance_height;
|
|
error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
|
|
face->root.internal->incremental_interface->object,
|
|
glyph_index, TRUE, &metrics );
|
|
if ( error )
|
|
goto Exit;
|
|
top_bearing = (FT_Short)metrics.bearing_y;
|
|
advance_height = (FT_UShort)metrics.advance;
|
|
}
|
|
# endif
|
|
|
|
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
|
|
|
|
loader->left_bearing = left_bearing;
|
|
loader->advance = advance_width;
|
|
loader->top_bearing = top_bearing;
|
|
loader->vadvance = advance_height;
|
|
|
|
if ( !loader->linear_def )
|
|
{
|
|
loader->linear_def = 1;
|
|
loader->linear = advance_width;
|
|
}
|
|
}
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
|
|
/* Set `offset' to the start of the glyph program relative to the */
|
|
/* start of the 'glyf' table, and `count' to the length of the */
|
|
/* glyph program in bytes. */
|
|
/* */
|
|
/* If we are loading glyph data via the incremental interface, set */
|
|
/* the loader stream to a memory stream reading the data returned */
|
|
/* by the interface. */
|
|
|
|
if ( face->root.internal->incremental_interface )
|
|
{
|
|
error = face->root.internal->incremental_interface->funcs->get_glyph_data(
|
|
face->root.internal->incremental_interface->object,
|
|
glyph_index, &glyph_data );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
glyph_data_loaded = 1;
|
|
offset = 0;
|
|
count = glyph_data.length;
|
|
|
|
FT_MEM_ZERO( &inc_stream, sizeof ( inc_stream ) );
|
|
FT_Stream_OpenMemory( &inc_stream,
|
|
glyph_data.pointer, glyph_data.length );
|
|
|
|
loader->stream = &inc_stream;
|
|
}
|
|
else
|
|
|
|
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
|
|
|
|
offset = tt_face_get_location( face, glyph_index, &count );
|
|
|
|
if ( count == 0 )
|
|
{
|
|
/* as described by Frederic Loyer, these are spaces, and */
|
|
/* not the unknown glyph. */
|
|
loader->bbox.xMin = 0;
|
|
loader->bbox.xMax = 0;
|
|
loader->bbox.yMin = 0;
|
|
loader->bbox.yMax = 0;
|
|
|
|
loader->pp1.x = 0;
|
|
loader->pp2.x = loader->advance;
|
|
loader->pp3.y = 0;
|
|
loader->pp4.y = loader->pp3.y-loader->vadvance;
|
|
|
|
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
|
|
if ( ((TT_Face)(loader->face))->doblend )
|
|
{
|
|
/* this must be done before scaling */
|
|
FT_Memory memory = loader->face->memory;
|
|
|
|
|
|
error = TT_Vary_Get_Glyph_Deltas( (TT_Face)(loader->face),
|
|
glyph_index, &deltas, 4 );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
loader->pp1.x += deltas[0].x; loader->pp1.y += deltas[0].y;
|
|
loader->pp2.x += deltas[1].x; loader->pp2.y += deltas[1].y;
|
|
loader->pp3.x += deltas[2].x; loader->pp3.y += deltas[2].y;
|
|
loader->pp4.x += deltas[3].x; loader->pp4.y += deltas[3].y;
|
|
|
|
FT_FREE( deltas );
|
|
}
|
|
#endif
|
|
|
|
if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
|
|
{
|
|
loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
|
|
loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
|
|
}
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
if ( loader->exec )
|
|
loader->exec->glyphSize = 0;
|
|
|
|
#endif
|
|
|
|
error = TT_Err_Ok;
|
|
goto Exit;
|
|
}
|
|
|
|
loader->byte_len = (FT_Int)count;
|
|
|
|
offset = loader->glyf_offset + offset;
|
|
|
|
/* access glyph frame */
|
|
error = face->access_glyph_frame( loader, glyph_index, offset, count );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
opened_frame = 1;
|
|
|
|
/* read first glyph header */
|
|
error = face->read_glyph_header( loader );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
contours_count = loader->n_contours;
|
|
|
|
count -= 10;
|
|
|
|
loader->pp1.x = loader->bbox.xMin - loader->left_bearing;
|
|
loader->pp1.y = 0;
|
|
loader->pp2.x = loader->pp1.x + loader->advance;
|
|
loader->pp2.y = 0;
|
|
|
|
loader->pp3.x = 0;
|
|
loader->pp3.y = loader->top_bearing + loader->bbox.yMax;
|
|
loader->pp4.x = 0;
|
|
loader->pp4.y = loader->pp3.y - loader->vadvance;
|
|
|
|
/***********************************************************************/
|
|
/***********************************************************************/
|
|
/***********************************************************************/
|
|
|
|
/* if it is a simple glyph, load it */
|
|
|
|
if ( contours_count >= 0 )
|
|
{
|
|
/* check that we can add the contours to the glyph */
|
|
error = FT_GlyphLoader_CheckPoints( gloader, 0, contours_count );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
error = face->read_simple_glyph( loader );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
{
|
|
TT_Size size = (TT_Size)loader->size;
|
|
|
|
|
|
error = TT_Process_Simple_Glyph( loader,
|
|
(FT_Bool)( size && size->debug ) );
|
|
}
|
|
|
|
#else
|
|
|
|
error = TT_Process_Simple_Glyph( loader, 0 );
|
|
|
|
#endif
|
|
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
FT_GlyphLoader_Add( gloader );
|
|
|
|
/* Note: We could have put the simple loader source there */
|
|
/* but the code is fat enough already :-) */
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/***********************************************************************/
|
|
/***********************************************************************/
|
|
|
|
/* otherwise, load a composite! */
|
|
else if ( contours_count == -1 )
|
|
{
|
|
TT_GlyphSlot glyph = (TT_GlyphSlot)loader->glyph;
|
|
FT_UInt start_point;
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
FT_UInt start_contour;
|
|
FT_ULong ins_pos; /* position of composite instructions, if any */
|
|
#endif
|
|
|
|
|
|
/* for each subglyph, read composite header */
|
|
start_point = gloader->base.outline.n_points;
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
start_contour = gloader->base.outline.n_contours;
|
|
#endif
|
|
|
|
error = face->read_composite_glyph( loader );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
ins_pos = loader->ins_pos;
|
|
#endif
|
|
face->forget_glyph_frame( loader );
|
|
opened_frame = 0;
|
|
|
|
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
|
|
|
|
if ( face->doblend )
|
|
{
|
|
FT_Int i, limit;
|
|
FT_SubGlyph subglyph;
|
|
FT_Memory memory = face->root.memory;
|
|
|
|
|
|
/* this provides additional offsets */
|
|
/* for each component's translation */
|
|
|
|
if ( (error = TT_Vary_Get_Glyph_Deltas(
|
|
face,
|
|
glyph_index,
|
|
&deltas,
|
|
gloader->current.num_subglyphs + 4 )) != 0 )
|
|
goto Exit;
|
|
|
|
/* Note: No subglyph reallocation here, our pointers are stable. */
|
|
subglyph = gloader->current.subglyphs + gloader->base.num_subglyphs;
|
|
limit = gloader->current.num_subglyphs;
|
|
|
|
for ( i = 0; i < limit; ++i, ++subglyph )
|
|
{
|
|
if ( subglyph->flags & ARGS_ARE_XY_VALUES )
|
|
{
|
|
subglyph->arg1 += deltas[i].x;
|
|
subglyph->arg2 += deltas[i].y;
|
|
}
|
|
}
|
|
|
|
loader->pp1.x += deltas[i + 0].x; loader->pp1.y += deltas[i + 0].y;
|
|
loader->pp2.x += deltas[i + 1].x; loader->pp2.y += deltas[i + 1].y;
|
|
loader->pp3.x += deltas[i + 2].x; loader->pp3.y += deltas[i + 2].y;
|
|
loader->pp4.x += deltas[i + 3].x; loader->pp4.y += deltas[i + 3].y;
|
|
|
|
FT_FREE( deltas );
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
|
|
|
|
if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
|
|
{
|
|
loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale );
|
|
loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
|
|
loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale );
|
|
loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
|
|
}
|
|
|
|
/* if the flag FT_LOAD_NO_RECURSE is set, we return the subglyph */
|
|
/* `as is' in the glyph slot (the client application will be */
|
|
/* responsible for interpreting these data)... */
|
|
/* */
|
|
if ( loader->load_flags & FT_LOAD_NO_RECURSE )
|
|
{
|
|
/* set up remaining glyph fields */
|
|
FT_GlyphLoader_Add( gloader );
|
|
|
|
glyph->num_subglyphs = gloader->base.num_subglyphs;
|
|
glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
|
|
glyph->subglyphs = gloader->base.subglyphs;
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
|
|
/* Now, read each subglyph independently. */
|
|
{
|
|
FT_Int n, num_base_points, num_new_points;
|
|
FT_SubGlyph subglyph = 0;
|
|
|
|
FT_UInt num_subglyphs = gloader->current.num_subglyphs;
|
|
FT_UInt num_base_subgs = gloader->base.num_subglyphs;
|
|
|
|
|
|
FT_GlyphLoader_Add( gloader );
|
|
|
|
for ( n = 0; n < (FT_Int)num_subglyphs; n++ )
|
|
{
|
|
FT_Vector pp1, pp2, pp3, pp4;
|
|
FT_Pos x, y;
|
|
|
|
|
|
/* Each time we call load_truetype_glyph in this loop, the */
|
|
/* value of `gloader.base.subglyphs' can change due to table */
|
|
/* reallocations. We thus need to recompute the subglyph */
|
|
/* pointer on each iteration. */
|
|
subglyph = gloader->base.subglyphs + num_base_subgs + n;
|
|
|
|
pp1 = loader->pp1;
|
|
pp2 = loader->pp2;
|
|
pp3 = loader->pp3;
|
|
pp4 = loader->pp4;
|
|
|
|
num_base_points = gloader->base.outline.n_points;
|
|
|
|
error = load_truetype_glyph( loader, subglyph->index,
|
|
recurse_count + 1 );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
/* restore subglyph pointer */
|
|
subglyph = gloader->base.subglyphs + num_base_subgs + n;
|
|
|
|
if ( subglyph->flags & USE_MY_METRICS )
|
|
{
|
|
pp1 = loader->pp1;
|
|
pp2 = loader->pp2;
|
|
pp3 = loader->pp3;
|
|
pp4 = loader->pp4;
|
|
}
|
|
else
|
|
{
|
|
loader->pp1 = pp1;
|
|
loader->pp2 = pp2;
|
|
loader->pp3 = pp3;
|
|
loader->pp4 = pp4;
|
|
}
|
|
|
|
num_points = gloader->base.outline.n_points;
|
|
|
|
num_new_points = num_points - num_base_points;
|
|
|
|
/* now perform the transform required for this subglyph */
|
|
|
|
if ( subglyph->flags & ( WE_HAVE_A_SCALE |
|
|
WE_HAVE_AN_XY_SCALE |
|
|
WE_HAVE_A_2X2 ) )
|
|
{
|
|
FT_Vector* cur = gloader->base.outline.points +
|
|
num_base_points;
|
|
FT_Vector* org = gloader->base.extra_points +
|
|
num_base_points;
|
|
FT_Vector* limit = cur + num_new_points;
|
|
|
|
|
|
for ( ; cur < limit; cur++, org++ )
|
|
{
|
|
FT_Vector_Transform( cur, &subglyph->transform );
|
|
FT_Vector_Transform( org, &subglyph->transform );
|
|
}
|
|
}
|
|
|
|
/* apply offset */
|
|
|
|
if ( !( subglyph->flags & ARGS_ARE_XY_VALUES ) )
|
|
{
|
|
FT_UInt k = subglyph->arg1;
|
|
FT_UInt l = subglyph->arg2;
|
|
FT_Vector* p1;
|
|
FT_Vector* p2;
|
|
|
|
|
|
if ( start_point + k >= (FT_UInt)num_base_points ||
|
|
l >= (FT_UInt)num_new_points )
|
|
{
|
|
error = TT_Err_Invalid_Composite;
|
|
goto Fail;
|
|
}
|
|
|
|
l += num_base_points;
|
|
|
|
p1 = gloader->base.outline.points + start_point + k;
|
|
p2 = gloader->base.outline.points + start_point + l;
|
|
|
|
x = p1->x - p2->x;
|
|
y = p1->y - p2->y;
|
|
}
|
|
else
|
|
{
|
|
x = subglyph->arg1;
|
|
y = subglyph->arg2;
|
|
|
|
/* Use a default value dependent on */
|
|
/* TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED. This is useful for old TT */
|
|
/* fonts which don't set the xxx_COMPONENT_OFFSET bit. */
|
|
|
|
#ifdef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED
|
|
if ( !( subglyph->flags & UNSCALED_COMPONENT_OFFSET ) &&
|
|
#else
|
|
if ( ( subglyph->flags & SCALED_COMPONENT_OFFSET ) &&
|
|
#endif
|
|
( subglyph->flags & ( WE_HAVE_A_SCALE |
|
|
WE_HAVE_AN_XY_SCALE |
|
|
WE_HAVE_A_2X2 )) )
|
|
{
|
|
#if 0
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* This algorithm is what Apple documents. But it doesn't work. */
|
|
/* */
|
|
int a = subglyph->transform.xx > 0 ? subglyph->transform.xx
|
|
: -subglyph->transform.xx;
|
|
int b = subglyph->transform.yx > 0 ? subglyph->transform.yx
|
|
: -subglyph->transform.yx;
|
|
int c = subglyph->transform.xy > 0 ? subglyph->transform.xy
|
|
: -subglyph->transform.xy;
|
|
int d = subglyph->transform.yy > 0 ? subglyph->transform.yy
|
|
: -subglyph->transform.yy;
|
|
int m = a > b ? a : b;
|
|
int n = c > d ? c : d;
|
|
|
|
|
|
if ( a - b <= 33 && a - b >= -33 )
|
|
m *= 2;
|
|
if ( c - d <= 33 && c - d >= -33 )
|
|
n *= 2;
|
|
x = FT_MulFix( x, m );
|
|
y = FT_MulFix( y, n );
|
|
|
|
#else /* 0 */
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* This algorithm is a guess and works much better than the above. */
|
|
/* */
|
|
FT_Fixed mac_xscale = FT_SqrtFixed(
|
|
FT_MulFix( subglyph->transform.xx,
|
|
subglyph->transform.xx ) +
|
|
FT_MulFix( subglyph->transform.xy,
|
|
subglyph->transform.xy) );
|
|
FT_Fixed mac_yscale = FT_SqrtFixed(
|
|
FT_MulFix( subglyph->transform.yy,
|
|
subglyph->transform.yy ) +
|
|
FT_MulFix( subglyph->transform.yx,
|
|
subglyph->transform.yx ) );
|
|
|
|
|
|
x = FT_MulFix( x, mac_xscale );
|
|
y = FT_MulFix( y, mac_yscale );
|
|
#endif /* 0 */
|
|
|
|
}
|
|
|
|
if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) )
|
|
{
|
|
x = FT_MulFix( x, x_scale );
|
|
y = FT_MulFix( y, y_scale );
|
|
|
|
if ( subglyph->flags & ROUND_XY_TO_GRID )
|
|
{
|
|
x = FT_PIX_ROUND( x );
|
|
y = FT_PIX_ROUND( y );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( x || y )
|
|
{
|
|
translate_array( num_new_points,
|
|
gloader->base.outline.points + num_base_points,
|
|
x, y );
|
|
|
|
translate_array( num_new_points,
|
|
gloader->base.extra_points + num_base_points,
|
|
x, y );
|
|
}
|
|
}
|
|
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
/* we have finished loading all sub-glyphs; now, look for */
|
|
/* instructions for this composite! */
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
if ( num_subglyphs > 0 &&
|
|
loader->exec &&
|
|
ins_pos > 0 &&
|
|
subglyph->flags & WE_HAVE_INSTR )
|
|
{
|
|
FT_UShort n_ins;
|
|
TT_ExecContext exec = loader->exec;
|
|
TT_GlyphZone pts;
|
|
FT_Vector* pp1;
|
|
|
|
|
|
/* read size of instructions */
|
|
if ( FT_STREAM_SEEK( ins_pos ) ||
|
|
FT_READ_USHORT( n_ins ) )
|
|
goto Fail;
|
|
FT_TRACE5(( " Instructions size = %d\n", n_ins ));
|
|
|
|
/* in some fonts? */
|
|
if ( n_ins == 0xFFFFU )
|
|
n_ins = 0;
|
|
|
|
/* check it */
|
|
if ( n_ins > face->max_profile.maxSizeOfInstructions )
|
|
{
|
|
FT_TRACE0(( "Too many instructions (%d) in composite glyph %ld\n",
|
|
n_ins, subglyph->index ));
|
|
error = TT_Err_Too_Many_Hints;
|
|
goto Fail;
|
|
}
|
|
|
|
/* read the instructions */
|
|
if ( FT_STREAM_READ( exec->glyphIns, n_ins ) )
|
|
goto Fail;
|
|
|
|
glyph->control_data = exec->glyphIns;
|
|
glyph->control_len = n_ins;
|
|
|
|
error = TT_Set_CodeRange( exec,
|
|
tt_coderange_glyph,
|
|
exec->glyphIns,
|
|
n_ins );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
error = FT_GlyphLoader_CheckPoints( gloader, num_points + 4, 0 );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
/* prepare the execution context */
|
|
tt_prepare_zone( &exec->pts, &gloader->base,
|
|
start_point, start_contour );
|
|
pts = &exec->pts;
|
|
|
|
pts->n_points = (short)( num_points + 4 );
|
|
pts->n_contours = gloader->base.outline.n_contours;
|
|
|
|
/* add phantom points */
|
|
pp1 = pts->cur + num_points;
|
|
pp1[0] = loader->pp1;
|
|
pp1[1] = loader->pp2;
|
|
pp1[2] = loader->pp3;
|
|
pp1[3] = loader->pp4;
|
|
|
|
pts->tags[num_points ] = 0;
|
|
pts->tags[num_points + 1] = 0;
|
|
pts->tags[num_points + 2] = 0;
|
|
pts->tags[num_points + 3] = 0;
|
|
|
|
/* if hinting, round the phantom points */
|
|
if ( IS_HINTED( loader->load_flags ) )
|
|
{
|
|
pp1[0].x = FT_PIX_ROUND( loader->pp1.x );
|
|
pp1[1].x = FT_PIX_ROUND( loader->pp2.x );
|
|
pp1[2].y = FT_PIX_ROUND( loader->pp3.y );
|
|
pp1[3].y = FT_PIX_ROUND( loader->pp4.y );
|
|
}
|
|
|
|
{
|
|
FT_UInt k;
|
|
|
|
|
|
for ( k = 0; k < num_points; k++ )
|
|
pts->tags[k] &= FT_CURVE_TAG_ON;
|
|
}
|
|
|
|
cur_to_org( num_points + 4, pts );
|
|
|
|
/* now consider hinting */
|
|
if ( IS_HINTED( loader->load_flags ) && n_ins > 0 )
|
|
{
|
|
exec->is_composite = TRUE;
|
|
error = TT_Run_Context( exec, ((TT_Size)loader->size)->debug );
|
|
if ( error && exec->pedantic_hinting )
|
|
goto Fail;
|
|
}
|
|
|
|
/* save glyph origin and advance points */
|
|
loader->pp1 = pp1[0];
|
|
loader->pp2 = pp1[1];
|
|
loader->pp3 = pp1[2];
|
|
loader->pp4 = pp1[3];
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
|
|
|
|
}
|
|
/* end of composite loading */
|
|
}
|
|
else
|
|
{
|
|
/* invalid composite count ( negative but not -1 ) */
|
|
error = TT_Err_Invalid_Outline;
|
|
goto Fail;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/***********************************************************************/
|
|
/***********************************************************************/
|
|
|
|
Fail:
|
|
if ( opened_frame )
|
|
face->forget_glyph_frame( loader );
|
|
|
|
Exit:
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
if ( glyph_data_loaded )
|
|
face->root.internal->incremental_interface->funcs->free_glyph_data(
|
|
face->root.internal->incremental_interface->object,
|
|
&glyph_data );
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
static FT_Error
|
|
compute_glyph_metrics( TT_Loader loader,
|
|
FT_UInt glyph_index )
|
|
{
|
|
FT_BBox bbox;
|
|
TT_Face face = (TT_Face)loader->face;
|
|
FT_Fixed y_scale;
|
|
TT_GlyphSlot glyph = loader->glyph;
|
|
TT_Size size = (TT_Size)loader->size;
|
|
|
|
|
|
y_scale = 0x10000L;
|
|
if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
|
|
y_scale = size->root.metrics.y_scale;
|
|
|
|
if ( glyph->format != FT_GLYPH_FORMAT_COMPOSITE )
|
|
{
|
|
glyph->outline.flags &= ~FT_OUTLINE_SINGLE_PASS;
|
|
|
|
/* copy outline to our glyph slot */
|
|
FT_GlyphLoader_CopyPoints( glyph->internal->loader, loader->gloader );
|
|
glyph->outline = glyph->internal->loader->base.outline;
|
|
|
|
/* translate array so that (0,0) is the glyph's origin */
|
|
FT_Outline_Translate( &glyph->outline, -loader->pp1.x, 0 );
|
|
|
|
FT_Outline_Get_CBox( &glyph->outline, &bbox );
|
|
|
|
if ( IS_HINTED( loader->load_flags ) )
|
|
{
|
|
/* grid-fit the bounding box */
|
|
bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
|
|
bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
|
|
bbox.xMax = FT_PIX_CEIL( bbox.xMax );
|
|
bbox.yMax = FT_PIX_CEIL( bbox.yMax );
|
|
}
|
|
}
|
|
else
|
|
bbox = loader->bbox;
|
|
|
|
/* get the device-independent horizontal advance. It is scaled later */
|
|
/* by the base layer. */
|
|
{
|
|
FT_Pos advance = loader->linear;
|
|
|
|
|
|
/* the flag FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH was introduced to */
|
|
/* correctly support DynaLab fonts, which have an incorrect */
|
|
/* `advance_Width_Max' field! It is used, to my knowledge, */
|
|
/* exclusively in the X-TrueType font server. */
|
|
/* */
|
|
if ( face->postscript.isFixedPitch &&
|
|
( loader->load_flags & FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ) == 0 )
|
|
advance = face->horizontal.advance_Width_Max;
|
|
|
|
/* we need to return the advance in font units in linearHoriAdvance, */
|
|
/* it will be scaled later by the base layer. */
|
|
glyph->linearHoriAdvance = advance;
|
|
}
|
|
|
|
glyph->metrics.horiBearingX = bbox.xMin;
|
|
glyph->metrics.horiBearingY = bbox.yMax;
|
|
glyph->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
|
|
|
|
/* don't forget to hint the advance when we need to */
|
|
if ( IS_HINTED( loader->load_flags ) )
|
|
glyph->metrics.horiAdvance = FT_PIX_ROUND( glyph->metrics.horiAdvance );
|
|
|
|
/* Now take care of vertical metrics. In the case where there is */
|
|
/* no vertical information within the font (relatively common), make */
|
|
/* up some metrics by `hand'... */
|
|
|
|
{
|
|
FT_Short top_bearing; /* vertical top side bearing (EM units) */
|
|
FT_UShort advance_height; /* vertical advance height (EM units) */
|
|
|
|
FT_Pos left; /* scaled vertical left side bearing */
|
|
FT_Pos top; /* scaled vertical top side bearing */
|
|
FT_Pos advance; /* scaled vertical advance height */
|
|
|
|
|
|
/* Get the unscaled top bearing and advance height. */
|
|
if ( face->vertical_info &&
|
|
face->vertical.number_Of_VMetrics > 0 )
|
|
{
|
|
advance_height = (FT_UShort)( loader->pp4.y - loader->pp3.y );
|
|
top_bearing = (FT_Short)( loader->pp3.y - bbox.yMax );
|
|
}
|
|
else
|
|
{
|
|
/* Make up the distances from the horizontal header. */
|
|
|
|
/* NOTE: The OS/2 values are the only `portable' ones, */
|
|
/* which is why we use them, if there is an OS/2 */
|
|
/* table in the font. Otherwise, we use the */
|
|
/* values defined in the horizontal header. */
|
|
/* */
|
|
/* NOTE2: The sTypoDescender is negative, which is why */
|
|
/* we compute the baseline-to-baseline distance */
|
|
/* here with: */
|
|
/* ascender - descender + linegap */
|
|
/* */
|
|
/* NOTE3: This is different from what MS's rasterizer */
|
|
/* appears to do when getting default values */
|
|
/* for the vertical phantom points. We leave */
|
|
/* the old code untouched, but relying on */
|
|
/* phantom points alone might be reasonable */
|
|
/* (i.e., removing the `if' above). */
|
|
if ( face->os2.version != 0xFFFFU )
|
|
{
|
|
top_bearing = (FT_Short)( face->os2.sTypoLineGap / 2 );
|
|
advance_height = (FT_UShort)( face->os2.sTypoAscender -
|
|
face->os2.sTypoDescender +
|
|
face->os2.sTypoLineGap );
|
|
}
|
|
else
|
|
{
|
|
top_bearing = (FT_Short)( face->horizontal.Line_Gap / 2 );
|
|
advance_height = (FT_UShort)( face->horizontal.Ascender +
|
|
face->horizontal.Descender +
|
|
face->horizontal.Line_Gap );
|
|
}
|
|
}
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
|
|
/* If this is an incrementally loaded font see if there are */
|
|
/* overriding metrics for this glyph. */
|
|
if ( face->root.internal->incremental_interface &&
|
|
face->root.internal->incremental_interface->funcs->get_glyph_metrics )
|
|
{
|
|
FT_Incremental_MetricsRec metrics;
|
|
FT_Error error = 0;
|
|
|
|
|
|
metrics.bearing_x = 0;
|
|
metrics.bearing_y = top_bearing;
|
|
metrics.advance = advance_height;
|
|
error =
|
|
face->root.internal->incremental_interface->funcs->get_glyph_metrics(
|
|
face->root.internal->incremental_interface->object,
|
|
glyph_index, TRUE, &metrics );
|
|
|
|
if ( error )
|
|
return error;
|
|
|
|
top_bearing = (FT_Short)metrics.bearing_y;
|
|
advance_height = (FT_UShort)metrics.advance;
|
|
}
|
|
|
|
/* GWW: Do vertical metrics get loaded incrementally too? */
|
|
|
|
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
|
|
|
|
/* We must adjust the top_bearing value from the bounding box given */
|
|
/* in the glyph header to the bounding box calculated with */
|
|
/* FT_Get_Outline_CBox(). */
|
|
|
|
/* scale the metrics */
|
|
if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) )
|
|
{
|
|
top = FT_MulFix( top_bearing + loader->bbox.yMax, y_scale )
|
|
- bbox.yMax;
|
|
advance = FT_MulFix( advance_height, y_scale );
|
|
}
|
|
else
|
|
{
|
|
top = top_bearing + loader->bbox.yMax - bbox.yMax;
|
|
advance = advance_height;
|
|
}
|
|
|
|
/* set the advance height in design units. It is scaled later by */
|
|
/* the base layer. */
|
|
glyph->linearVertAdvance = advance_height;
|
|
|
|
/* XXX: for now, we have no better algorithm for the lsb, but it */
|
|
/* should work fine. */
|
|
/* */
|
|
left = ( bbox.xMin - bbox.xMax ) / 2;
|
|
|
|
/* grid-fit them if necessary */
|
|
if ( IS_HINTED( loader->load_flags ) )
|
|
{
|
|
left = FT_PIX_FLOOR( left );
|
|
top = FT_PIX_CEIL( top );
|
|
advance = FT_PIX_ROUND( advance );
|
|
}
|
|
|
|
glyph->metrics.vertBearingX = left;
|
|
glyph->metrics.vertBearingY = top;
|
|
glyph->metrics.vertAdvance = advance;
|
|
}
|
|
|
|
/* adjust advance width to the value contained in the hdmx table */
|
|
if ( !face->postscript.isFixedPitch && size &&
|
|
IS_HINTED( loader->load_flags ) )
|
|
{
|
|
FT_Byte* widthp = Get_Advance_WidthPtr( face,
|
|
size->root.metrics.x_ppem,
|
|
glyph_index );
|
|
|
|
|
|
if ( widthp )
|
|
glyph->metrics.horiAdvance = *widthp << 6;
|
|
}
|
|
|
|
/* set glyph dimensions */
|
|
glyph->metrics.width = bbox.xMax - bbox.xMin;
|
|
glyph->metrics.height = bbox.yMax - bbox.yMin;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* TT_Load_Glyph */
|
|
/* */
|
|
/* <Description> */
|
|
/* A function used to load a single glyph within a given glyph slot, */
|
|
/* for a given size. */
|
|
/* */
|
|
/* <Input> */
|
|
/* glyph :: A handle to a target slot object where the glyph */
|
|
/* will be loaded. */
|
|
/* */
|
|
/* size :: A handle to the source face size at which the glyph */
|
|
/* must be scaled/loaded. */
|
|
/* */
|
|
/* glyph_index :: The index of the glyph in the font file. */
|
|
/* */
|
|
/* load_flags :: A flag indicating what to load for this glyph. The */
|
|
/* FT_LOAD_XXX constants can be used to control the */
|
|
/* glyph loading process (e.g., whether the outline */
|
|
/* should be scaled, whether to load bitmaps or not, */
|
|
/* whether to hint the outline, etc). */
|
|
/* */
|
|
/* <Return> */
|
|
/* FreeType error code. 0 means success. */
|
|
/* */
|
|
FT_LOCAL_DEF( FT_Error )
|
|
TT_Load_Glyph( TT_Size size,
|
|
TT_GlyphSlot glyph,
|
|
FT_UInt glyph_index,
|
|
FT_Int32 load_flags )
|
|
{
|
|
SFNT_Service sfnt;
|
|
TT_Face face;
|
|
FT_Stream stream;
|
|
FT_Error error;
|
|
TT_LoaderRec loader;
|
|
|
|
|
|
face = (TT_Face)glyph->face;
|
|
sfnt = (SFNT_Service)face->sfnt;
|
|
stream = face->root.stream;
|
|
error = 0;
|
|
|
|
if ( !size || ( load_flags & FT_LOAD_NO_SCALE ) ||
|
|
( load_flags & FT_LOAD_NO_RECURSE ) )
|
|
{
|
|
size = NULL;
|
|
load_flags |= FT_LOAD_NO_SCALE |
|
|
FT_LOAD_NO_HINTING |
|
|
FT_LOAD_NO_BITMAP;
|
|
}
|
|
|
|
glyph->num_subglyphs = 0;
|
|
|
|
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
|
|
|
|
/* try to load embedded bitmap if any */
|
|
/* */
|
|
/* XXX: The convention should be emphasized in */
|
|
/* the documents because it can be confusing. */
|
|
if ( size &&
|
|
size->strike_index != 0xFFFFU &&
|
|
sfnt->load_sbits &&
|
|
( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
|
|
|
|
{
|
|
TT_SBit_MetricsRec metrics;
|
|
|
|
|
|
error = sfnt->load_sbit_image( face,
|
|
(FT_ULong)size->strike_index,
|
|
glyph_index,
|
|
(FT_Int)load_flags,
|
|
stream,
|
|
&glyph->bitmap,
|
|
&metrics );
|
|
if ( !error )
|
|
{
|
|
glyph->outline.n_points = 0;
|
|
glyph->outline.n_contours = 0;
|
|
|
|
glyph->metrics.width = (FT_Pos)metrics.width << 6;
|
|
glyph->metrics.height = (FT_Pos)metrics.height << 6;
|
|
|
|
glyph->metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6;
|
|
glyph->metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6;
|
|
glyph->metrics.horiAdvance = (FT_Pos)metrics.horiAdvance << 6;
|
|
|
|
glyph->metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6;
|
|
glyph->metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6;
|
|
glyph->metrics.vertAdvance = (FT_Pos)metrics.vertAdvance << 6;
|
|
|
|
glyph->format = FT_GLYPH_FORMAT_BITMAP;
|
|
if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
|
|
{
|
|
glyph->bitmap_left = metrics.vertBearingX;
|
|
glyph->bitmap_top = metrics.vertBearingY;
|
|
}
|
|
else
|
|
{
|
|
glyph->bitmap_left = metrics.horiBearingX;
|
|
glyph->bitmap_top = metrics.horiBearingY;
|
|
}
|
|
return error;
|
|
}
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
|
|
|
|
/* return immediately if we only want the embedded bitmaps */
|
|
if ( load_flags & FT_LOAD_SBITS_ONLY )
|
|
return TT_Err_Invalid_Argument;
|
|
|
|
/* seek to the beginning of the glyph table. For Type 42 fonts */
|
|
/* the table might be accessed from a Postscript stream or something */
|
|
/* else... */
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
|
|
/* Don't look for the glyph table if this is an incremental font. */
|
|
if ( !face->root.internal->incremental_interface )
|
|
|
|
#endif
|
|
|
|
{
|
|
error = face->goto_table( face, TTAG_glyf, stream, 0 );
|
|
if ( error )
|
|
{
|
|
FT_ERROR(( "TT_Load_Glyph: could not access glyph table\n" ));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
FT_MEM_ZERO( &loader, sizeof ( loader ) );
|
|
|
|
/* update the glyph zone bounds */
|
|
{
|
|
FT_GlyphLoader gloader = FT_FACE_DRIVER(face)->glyph_loader;
|
|
|
|
|
|
loader.gloader = gloader;
|
|
|
|
FT_GlyphLoader_Rewind( gloader );
|
|
|
|
tt_prepare_zone( &loader.zone, &gloader->base, 0, 0 );
|
|
tt_prepare_zone( &loader.base, &gloader->base, 0, 0 );
|
|
}
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
if ( size )
|
|
{
|
|
/* query new execution context */
|
|
loader.exec = size->debug ? size->context : TT_New_Context( face );
|
|
if ( !loader.exec )
|
|
return TT_Err_Could_Not_Find_Context;
|
|
|
|
TT_Load_Context( loader.exec, face, size );
|
|
loader.instructions = loader.exec->glyphIns;
|
|
|
|
/* load default graphics state - if needed */
|
|
if ( size->GS.instruct_control & 2 )
|
|
loader.exec->GS = tt_default_graphics_state;
|
|
|
|
loader.exec->pedantic_hinting =
|
|
FT_BOOL( load_flags & FT_LOAD_PEDANTIC );
|
|
|
|
loader.exec->grayscale =
|
|
FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != FT_LOAD_TARGET_MONO );
|
|
}
|
|
|
|
#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
|
|
|
|
/* clear all outline flags, except the `owner' one */
|
|
glyph->outline.flags = 0;
|
|
|
|
/* let's initialize the rest of our loader now */
|
|
|
|
loader.load_flags = load_flags;
|
|
|
|
loader.face = (FT_Face)face;
|
|
loader.size = (FT_Size)size;
|
|
loader.glyph = (FT_GlyphSlot)glyph;
|
|
loader.stream = stream;
|
|
|
|
#ifdef FT_CONFIG_OPTION_INCREMENTAL
|
|
|
|
if ( face->root.internal->incremental_interface )
|
|
loader.glyf_offset = 0;
|
|
else
|
|
|
|
#endif
|
|
|
|
loader.glyf_offset = FT_STREAM_POS();
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
/* if the cvt program has disabled hinting, the argument */
|
|
/* is ignored. */
|
|
if ( size && ( size->GS.instruct_control & 1 ) )
|
|
loader.load_flags |= FT_LOAD_NO_HINTING;
|
|
|
|
#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
|
|
|
|
/* Main loading loop */
|
|
glyph->format = FT_GLYPH_FORMAT_OUTLINE;
|
|
glyph->num_subglyphs = 0;
|
|
|
|
error = load_truetype_glyph( &loader, glyph_index, 0 );
|
|
if ( !error )
|
|
compute_glyph_metrics( &loader, glyph_index );
|
|
|
|
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
|
|
|
|
if ( !size || !size->debug )
|
|
TT_Done_Context( loader.exec );
|
|
|
|
#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
|
|
|
|
/* Set the `high precision' bit flag. */
|
|
/* This is _critical_ to get correct output for monochrome */
|
|
/* TrueType glyphs at all sizes using the bytecode interpreter. */
|
|
/* */
|
|
if ( size && size->root.metrics.y_ppem < 24 )
|
|
glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
|
|
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
|
|
/* END */
|