blob: 89e168a942d5c8d69ac5ad1c53029ee7e0318356 [file] [log] [blame] [edit]
/***************************************************************************/
/* */
/* psft.c */
/* */
/* FreeType Glue Component to Adobe's Interpreter (body). */
/* */
/* Copyright 2013-2014 Adobe Systems Incorporated. */
/* */
/* This software, and all works of authorship, whether in source or */
/* object code form as indicated by the copyright notice(s) included */
/* herein (collectively, the "Work") is made available, and may only be */
/* used, modified, and distributed under the FreeType Project License, */
/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
/* FreeType Project License, each contributor to the Work hereby grants */
/* to any individual or legal entity exercising permissions granted by */
/* the FreeType Project License and this section (hereafter, "You" or */
/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
/* royalty-free, irrevocable (except as stated in this section) patent */
/* license to make, have made, use, offer to sell, sell, import, and */
/* otherwise transfer the Work, where such license applies only to those */
/* patent claims licensable by such contributor that are necessarily */
/* infringed by their contribution(s) alone or by combination of their */
/* contribution(s) with the Work to which such contribution(s) was */
/* submitted. If You institute patent litigation against any entity */
/* (including a cross-claim or counterclaim in a lawsuit) alleging that */
/* the Work or a contribution incorporated within the Work constitutes */
/* direct or contributory patent infringement, then any patent licenses */
/* granted to You under this License for that Work shall terminate as of */
/* the date such litigation is filed. */
/* */
/* By using, modifying, or distributing the Work you indicate that you */
/* have read and understood the terms and conditions of the */
/* FreeType Project License as well as those provided in this section, */
/* and you accept them fully. */
/* */
/***************************************************************************/
#include "psft.h"
#include FT_INTERNAL_DEBUG_H
#include "psfont.h"
#include "pserror.h"
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include FT_MULTIPLE_MASTERS_H
#include FT_SERVICE_MULTIPLE_MASTERS_H
#endif
#include FT_SERVICE_CFF_TABLE_LOAD_H
#define CF2_MAX_SIZE cf2_intToFixed( 2000 ) /* max ppem */
/*
* This check should avoid most internal overflow cases. Clients should
* generally respond to `Glyph_Too_Big' by getting a glyph outline
* at EM size, scaling it and filling it as a graphics operation.
*
*/
static FT_Error
cf2_checkTransform( const CF2_Matrix* transform,
CF2_Int unitsPerEm )
{
CF2_Fixed maxScale;
FT_ASSERT( unitsPerEm > 0 );
if ( transform->a <= 0 || transform->d <= 0 )
return FT_THROW( Invalid_Size_Handle );
FT_ASSERT( transform->b == 0 && transform->c == 0 );
FT_ASSERT( transform->tx == 0 && transform->ty == 0 );
if ( unitsPerEm > 0x7FFF )
return FT_THROW( Glyph_Too_Big );
maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );
if ( transform->a > maxScale || transform->d > maxScale )
return FT_THROW( Glyph_Too_Big );
return FT_Err_Ok;
}
static void
cf2_setGlyphWidth( CF2_Outline outline,
CF2_Fixed width )
{
PS_Decoder* decoder = outline->decoder;
FT_ASSERT( decoder );
decoder->glyph_width = cf2_fixedToInt( width );
}
/* Clean up font instance. */
static void
cf2_free_instance( void* ptr )
{
CF2_Font font = (CF2_Font)ptr;
if ( font )
{
FT_Memory memory = font->memory;
FT_FREE( font->blend.lastNDV );
FT_FREE( font->blend.BV );
}
}
/********************************************/
/* */
/* functions for handling client outline; */
/* FreeType uses coordinates in 26.6 format */
/* */
/********************************************/
static void
cf2_builder_moveTo( CF2_OutlineCallbacks callbacks,
const CF2_CallbackParams params )
{
/* downcast the object pointer */
CF2_Outline outline = (CF2_Outline)callbacks;
PS_Builder* builder;
(void)params; /* only used in debug mode */
FT_ASSERT( outline && outline->decoder );
FT_ASSERT( params->op == CF2_PathOpMoveTo );
builder = &outline->decoder->builder;
/* note: two successive moves simply close the contour twice */
ps_builder_close_contour( builder );
builder->path_begun = 0;
}
static void
cf2_builder_lineTo( CF2_OutlineCallbacks callbacks,
const CF2_CallbackParams params )
{
FT_Error error;
/* downcast the object pointer */
CF2_Outline outline = (CF2_Outline)callbacks;
PS_Builder* builder;
FT_ASSERT( outline && outline->decoder );
FT_ASSERT( params->op == CF2_PathOpLineTo );
builder = &outline->decoder->builder;
if ( !builder->path_begun )
{
/* record the move before the line; also check points and set */
/* `path_begun' */
error = ps_builder_start_point( builder,
params->pt0.x,
params->pt0.y );
if ( error )
{
if ( !*callbacks->error )
*callbacks->error = error;
return;
}
}
/* `ps_builder_add_point1' includes a check_points call for one point */
error = ps_builder_add_point1( builder,
params->pt1.x,
params->pt1.y );
if ( error )
{
if ( !*callbacks->error )
*callbacks->error = error;
return;
}
}
static void
cf2_builder_cubeTo( CF2_OutlineCallbacks callbacks,
const CF2_CallbackParams params )
{
FT_Error error;
/* downcast the object pointer */
CF2_Outline outline = (CF2_Outline)callbacks;
PS_Builder* builder;
FT_ASSERT( outline && outline->decoder );
FT_ASSERT( params->op == CF2_PathOpCubeTo );
builder = &outline->decoder->builder;
if ( !builder->path_begun )
{
/* record the move before the line; also check points and set */
/* `path_begun' */
error = ps_builder_start_point( builder,
params->pt0.x,
params->pt0.y );
if ( error )
{
if ( !*callbacks->error )
*callbacks->error = error;
return;
}
}
/* prepare room for 3 points: 2 off-curve, 1 on-curve */
error = ps_builder_check_points( builder, 3 );
if ( error )
{
if ( !*callbacks->error )
*callbacks->error = error;
return;
}
ps_builder_add_point( builder,
params->pt1.x,
params->pt1.y, 0 );
ps_builder_add_point( builder,
params->pt2.x,
params->pt2.y, 0 );
ps_builder_add_point( builder,
params->pt3.x,
params->pt3.y, 1 );
}
static void
cf2_outline_init( CF2_Outline outline,
FT_Memory memory,
FT_Error* error )
{
FT_ZERO( outline );
outline->root.memory = memory;
outline->root.error = error;
outline->root.moveTo = cf2_builder_moveTo;
outline->root.lineTo = cf2_builder_lineTo;
outline->root.cubeTo = cf2_builder_cubeTo;
}
/* get scaling and hint flag from GlyphSlot */
static void
cf2_getScaleAndHintFlag( PS_Decoder* decoder,
CF2_Fixed* x_scale,
CF2_Fixed* y_scale,
FT_Bool* hinted,
FT_Bool* scaled )
{
FT_ASSERT( decoder && decoder->builder.glyph );
/* note: FreeType scale includes a factor of 64 */
*hinted = decoder->builder.glyph->hint;
*scaled = decoder->builder.glyph->scaled;
if ( *hinted )
{
*x_scale = ADD_INT32( decoder->builder.glyph->x_scale, 32 ) / 64;
*y_scale = ADD_INT32( decoder->builder.glyph->y_scale, 32 ) / 64;
}
else
{
/* for unhinted outlines, `cff_slot_load' does the scaling, */
/* thus render at `unity' scale */
*x_scale = 0x0400; /* 1/64 as 16.16 */
*y_scale = 0x0400;
}
}
/* get units per em from `FT_Face' */
/* TODO: should handle font matrix concatenation? */
static FT_UShort
cf2_getUnitsPerEm( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->builder.face );
FT_ASSERT( decoder->builder.face->units_per_EM );
return decoder->builder.face->units_per_EM;
}
/* Main entry point: Render one glyph. */
FT_LOCAL_DEF( FT_Error )
cf2_decoder_parse_charstrings( PS_Decoder* decoder,
FT_Byte* charstring_base,
FT_ULong charstring_len )
{
FT_Memory memory;
FT_Error error = FT_Err_Ok;
CF2_Font font;
FT_Bool is_t1 = decoder->builder.is_t1;
FT_ASSERT( decoder &&
( is_t1 || decoder->cff ) );
if ( is_t1 && !decoder->current_subfont )
{
FT_ERROR(( "cf2_decoder_parse_charstrings (Type 1): "
"SubFont missing. Use `t1_make_subfont' first\n" ));
return FT_THROW( Invalid_Table );
}
memory = decoder->builder.memory;
/* CF2 data is saved here across glyphs */
font = (CF2_Font)decoder->cf2_instance->data;
/* on first glyph, allocate instance structure */
if ( !decoder->cf2_instance->data )
{
decoder->cf2_instance->finalizer =
(FT_Generic_Finalizer)cf2_free_instance;
if ( FT_ALLOC( decoder->cf2_instance->data,
sizeof ( CF2_FontRec ) ) )
return FT_THROW( Out_Of_Memory );
font = (CF2_Font)decoder->cf2_instance->data;
font->memory = memory;
if ( !is_t1 )
font->cffload = (FT_Service_CFFLoad)decoder->cff->cffload;
/* initialize a client outline, to be shared by each glyph rendered */
cf2_outline_init( &font->outline, font->memory, &font->error );
}
/* save decoder; it is a stack variable and will be different on each */
/* call */
font->decoder = decoder;
font->outline.decoder = decoder;
{
/* build parameters for Adobe engine */
PS_Builder* builder = &decoder->builder;
PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
FT_Bool no_stem_darkening_driver =
driver->no_stem_darkening;
FT_Char no_stem_darkening_font =
builder->face->internal->no_stem_darkening;
/* local error */
FT_Error error2 = FT_Err_Ok;
CF2_BufferRec buf;
CF2_Matrix transform;
CF2_F16Dot16 glyphWidth;
FT_Bool hinted;
FT_Bool scaled;
/* FreeType has already looked up the GID; convert to */
/* `RegionBuffer', assuming that the input has been validated */
FT_ASSERT( charstring_base + charstring_len >= charstring_base );
FT_ZERO( &buf );
buf.start =
buf.ptr = charstring_base;
buf.end = charstring_base + charstring_len;
FT_ZERO( &transform );
cf2_getScaleAndHintFlag( decoder,
&transform.a,
&transform.d,
&hinted,
&scaled );
if ( is_t1 )
font->isCFF2 = FALSE;
else
{
/* copy isCFF2 boolean from TT_Face to CF2_Font */
font->isCFF2 = ((TT_Face)builder->face)->is_cff2;
}
font->isT1 = is_t1;
font->renderingFlags = 0;
if ( hinted )
font->renderingFlags |= CF2_FlagsHinted;
if ( scaled && ( !no_stem_darkening_font ||
( no_stem_darkening_font < 0 &&
!no_stem_darkening_driver ) ) )
font->renderingFlags |= CF2_FlagsDarkened;
font->darkenParams[0] = driver->darken_params[0];
font->darkenParams[1] = driver->darken_params[1];
font->darkenParams[2] = driver->darken_params[2];
font->darkenParams[3] = driver->darken_params[3];
font->darkenParams[4] = driver->darken_params[4];
font->darkenParams[5] = driver->darken_params[5];
font->darkenParams[6] = driver->darken_params[6];
font->darkenParams[7] = driver->darken_params[7];
/* now get an outline for this glyph; */
/* also get units per em to validate scale */
font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
if ( scaled )
{
error2 = cf2_checkTransform( &transform, font->unitsPerEm );
if ( error2 )
return error2;
}
error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth );
if ( error2 )
return FT_ERR( Invalid_File_Format );
cf2_setGlyphWidth( &font->outline, glyphWidth );
return FT_Err_Ok;
}
}
/* get pointer to current FreeType subfont (based on current glyphID) */
FT_LOCAL_DEF( CFF_SubFont )
cf2_getSubfont( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->current_subfont );
return decoder->current_subfont;
}
/* get pointer to VStore structure */
FT_LOCAL_DEF( CFF_VStore )
cf2_getVStore( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->cff );
return &decoder->cff->vstore;
}
/* get maxstack value from CFF2 Top DICT */
FT_LOCAL_DEF( FT_UInt )
cf2_getMaxstack( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->cff );
return decoder->cff->top_font.font_dict.maxstack;
}
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* Get normalized design vector for current render request; */
/* return pointer and length. */
/* */
/* Note: Uses FT_Fixed not CF2_Fixed for the vector. */
FT_LOCAL_DEF( FT_Error )
cf2_getNormalizedVector( PS_Decoder* decoder,
CF2_UInt *len,
FT_Fixed* *vec )
{
TT_Face face;
FT_Service_MultiMasters mm;
FT_ASSERT( decoder && decoder->builder.face );
FT_ASSERT( vec && len );
FT_ASSERT( !decoder->builder.is_t1 );
face = (TT_Face)decoder->builder.face;
mm = (FT_Service_MultiMasters)face->mm;
return mm->get_var_blend( FT_FACE( face ), len, NULL, vec, NULL );
}
#endif
/* get `y_ppem' from `CFF_Size' */
FT_LOCAL_DEF( CF2_Fixed )
cf2_getPpemY( PS_Decoder* decoder )
{
FT_ASSERT( decoder &&
decoder->builder.face &&
decoder->builder.face->size );
/*
* Note that `y_ppem' can be zero if there wasn't a call to
* `FT_Set_Char_Size' or something similar. However, this isn't a
* problem since we come to this place in the code only if
* FT_LOAD_NO_SCALE is set (the other case gets caught by
* `cf2_checkTransform'). The ppem value is needed to compute the stem
* darkening, which is disabled for getting the unscaled outline.
*
*/
return cf2_intToFixed(
decoder->builder.face->size->metrics.y_ppem );
}
/* get standard stem widths for the current subfont; */
/* FreeType stores these as integer font units */
/* (note: variable names seem swapped) */
FT_LOCAL_DEF( CF2_Fixed )
cf2_getStdVW( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->current_subfont );
return cf2_intToFixed(
decoder->current_subfont->private_dict.standard_height );
}
FT_LOCAL_DEF( CF2_Fixed )
cf2_getStdHW( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->current_subfont );
return cf2_intToFixed(
decoder->current_subfont->private_dict.standard_width );
}
/* note: FreeType stores 1000 times the actual value for `BlueScale' */
FT_LOCAL_DEF( void )
cf2_getBlueMetrics( PS_Decoder* decoder,
CF2_Fixed* blueScale,
CF2_Fixed* blueShift,
CF2_Fixed* blueFuzz )
{
FT_ASSERT( decoder && decoder->current_subfont );
*blueScale = FT_DivFix(
decoder->current_subfont->private_dict.blue_scale,
cf2_intToFixed( 1000 ) );
*blueShift = cf2_intToFixed(
decoder->current_subfont->private_dict.blue_shift );
*blueFuzz = cf2_intToFixed(
decoder->current_subfont->private_dict.blue_fuzz );
}
/* get blue values counts and arrays; the FreeType parser has validated */
/* the counts and verified that each is an even number */
FT_LOCAL_DEF( void )
cf2_getBlueValues( PS_Decoder* decoder,
size_t* count,
FT_Pos* *data )
{
FT_ASSERT( decoder && decoder->current_subfont );
*count = decoder->current_subfont->private_dict.num_blue_values;
*data = (FT_Pos*)
&decoder->current_subfont->private_dict.blue_values;
}
FT_LOCAL_DEF( void )
cf2_getOtherBlues( PS_Decoder* decoder,
size_t* count,
FT_Pos* *data )
{
FT_ASSERT( decoder && decoder->current_subfont );
*count = decoder->current_subfont->private_dict.num_other_blues;
*data = (FT_Pos*)
&decoder->current_subfont->private_dict.other_blues;
}
FT_LOCAL_DEF( void )
cf2_getFamilyBlues( PS_Decoder* decoder,
size_t* count,
FT_Pos* *data )
{
FT_ASSERT( decoder && decoder->current_subfont );
*count = decoder->current_subfont->private_dict.num_family_blues;
*data = (FT_Pos*)
&decoder->current_subfont->private_dict.family_blues;
}
FT_LOCAL_DEF( void )
cf2_getFamilyOtherBlues( PS_Decoder* decoder,
size_t* count,
FT_Pos* *data )
{
FT_ASSERT( decoder && decoder->current_subfont );
*count = decoder->current_subfont->private_dict.num_family_other_blues;
*data = (FT_Pos*)
&decoder->current_subfont->private_dict.family_other_blues;
}
FT_LOCAL_DEF( CF2_Int )
cf2_getLanguageGroup( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->current_subfont );
return decoder->current_subfont->private_dict.language_group;
}
/* convert unbiased subroutine index to `CF2_Buffer' and */
/* return 0 on success */
FT_LOCAL_DEF( CF2_Int )
cf2_initGlobalRegionBuffer( PS_Decoder* decoder,
CF2_Int subrNum,
CF2_Buffer buf )
{
CF2_UInt idx;
FT_ASSERT( decoder );
FT_ZERO( buf );
idx = (CF2_UInt)( subrNum + decoder->globals_bias );
if ( idx >= decoder->num_globals )
return TRUE; /* error */
FT_ASSERT( decoder->globals );
buf->start =
buf->ptr = decoder->globals[idx];
buf->end = decoder->globals[idx + 1];
return FALSE; /* success */
}
/* convert AdobeStandardEncoding code to CF2_Buffer; */
/* used for seac component */
FT_LOCAL_DEF( FT_Error )
cf2_getSeacComponent( PS_Decoder* decoder,
CF2_Int code,
CF2_Buffer buf )
{
CF2_Int gid;
FT_Byte* charstring;
FT_ULong len;
FT_Error error;
FT_ASSERT( decoder );
FT_ASSERT( !decoder->builder.is_t1 );
FT_ZERO( buf );
#ifdef FT_CONFIG_OPTION_INCREMENTAL
/* Incremental fonts don't necessarily have valid charsets. */
/* They use the character code, not the glyph index, in this case. */
if ( decoder->builder.face->internal->incremental_interface )
gid = code;
else
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
{
gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
if ( gid < 0 )
return FT_THROW( Invalid_Glyph_Format );
}
error = decoder->get_glyph_callback( (TT_Face)decoder->builder.face,
(CF2_UInt)gid,
&charstring,
&len );
/* TODO: for now, just pass the FreeType error through */
if ( error )
return error;
/* assume input has been validated */
FT_ASSERT( charstring + len >= charstring );
buf->start = charstring;
buf->end = charstring + len;
buf->ptr = buf->start;
return FT_Err_Ok;
}
FT_LOCAL_DEF( void )
cf2_freeSeacComponent( PS_Decoder* decoder,
CF2_Buffer buf )
{
FT_ASSERT( decoder );
FT_ASSERT( !decoder->builder.is_t1 );
decoder->free_glyph_callback( (TT_Face)decoder->builder.face,
(FT_Byte**)&buf->start,
(FT_ULong)( buf->end - buf->start ) );
}
FT_LOCAL_DEF( FT_Error )
cf2_getT1SeacComponent( PS_Decoder* decoder,
FT_UInt glyph_index,
CF2_Buffer buf )
{
FT_Data glyph_data;
FT_Error error = FT_Err_Ok;
T1_Face face = (T1_Face)decoder->builder.face;
T1_Font type1 = &face->type1;
#ifdef FT_CONFIG_OPTION_INCREMENTAL
FT_Incremental_InterfaceRec *inc =
face->root.internal->incremental_interface;
/* For incremental fonts get the character data using the */
/* callback function. */
if ( inc )
error = inc->funcs->get_glyph_data( inc->object,
glyph_index, &glyph_data );
else
#endif
/* For ordinary fonts get the character data stored in the face record. */
{
glyph_data.pointer = type1->charstrings[glyph_index];
glyph_data.length = (FT_Int)type1->charstrings_len[glyph_index];
}
if ( !error )
{
FT_Byte* charstring_base = (FT_Byte*)glyph_data.pointer;
FT_ULong charstring_len = (FT_ULong)glyph_data.length;
FT_ASSERT( charstring_base + charstring_len >= charstring_base );
FT_ZERO( buf );
buf->start =
buf->ptr = charstring_base;
buf->end = charstring_base + charstring_len;
}
return error;
}
FT_LOCAL_DEF( void )
cf2_freeT1SeacComponent( PS_Decoder* decoder,
CF2_Buffer buf )
{
T1_Face face;
FT_Data data;
FT_ASSERT( decoder );
#ifdef FT_CONFIG_OPTION_INCREMENTAL
face = (T1_Face)decoder->builder.face;
data.pointer = buf->start;
data.length = (FT_Int)( buf->end - buf->start );
if ( face->root.internal->incremental_interface )
face->root.internal->incremental_interface->funcs->free_glyph_data(
face->root.internal->incremental_interface->object,
&data );
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
}
FT_LOCAL_DEF( CF2_Int )
cf2_initLocalRegionBuffer( PS_Decoder* decoder,
CF2_Int subrNum,
CF2_Buffer buf )
{
CF2_UInt idx;
FT_ASSERT( decoder );
FT_ZERO( buf );
idx = (CF2_UInt)( subrNum + decoder->locals_bias );
if ( idx < 0 || idx >= decoder->num_locals )
return TRUE; /* error */
FT_ASSERT( decoder->locals );
buf->start = decoder->locals[idx];
if ( decoder->builder.is_t1 )
{
/* The Type 1 driver stores subroutines without the seed bytes. */
/* The CID driver stores subroutines with seed bytes. This */
/* case is taken care of when decoder->subrs_len == 0. */
if ( decoder->locals_len )
buf->end = buf->start + decoder->locals_len[idx];
else
{
/* We are using subroutines from a CID font. We must adjust */
/* for the seed bytes. */
buf->start += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
buf->end = decoder->locals[idx + 1];
}
if ( !buf->start )
{
FT_ERROR(( "cf2_initLocalRegionBuffer (Type 1 mode):"
" invoking empty subrs\n" ));
}
}
else
{
buf->end = decoder->locals[idx + 1];
}
buf->ptr = buf->start;
return FALSE; /* success */
}
FT_LOCAL_DEF( CF2_Fixed )
cf2_getDefaultWidthX( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->current_subfont );
return cf2_intToFixed(
decoder->current_subfont->private_dict.default_width );
}
FT_LOCAL_DEF( CF2_Fixed )
cf2_getNominalWidthX( PS_Decoder* decoder )
{
FT_ASSERT( decoder && decoder->current_subfont );
return cf2_intToFixed(
decoder->current_subfont->private_dict.nominal_width );
}
FT_LOCAL_DEF( void )
cf2_outline_reset( CF2_Outline outline )
{
PS_Decoder* decoder = outline->decoder;
FT_ASSERT( decoder );
outline->root.windingMomentum = 0;
FT_GlyphLoader_Rewind( decoder->builder.loader );
}
FT_LOCAL_DEF( void )
cf2_outline_close( CF2_Outline outline )
{
PS_Decoder* decoder = outline->decoder;
FT_ASSERT( decoder );
ps_builder_close_contour( &decoder->builder );
FT_GlyphLoader_Add( decoder->builder.loader );
}
/* END */