summaryrefslogtreecommitdiffstats
path: root/modules/freetype2/src/psaux
diff options
context:
space:
mode:
Diffstat (limited to 'modules/freetype2/src/psaux')
-rw-r--r--modules/freetype2/src/psaux/afmparse.c1094
-rw-r--r--modules/freetype2/src/psaux/afmparse.h88
-rw-r--r--modules/freetype2/src/psaux/cffdecode.c2423
-rw-r--r--modules/freetype2/src/psaux/cffdecode.h63
-rw-r--r--modules/freetype2/src/psaux/module.mk23
-rw-r--r--modules/freetype2/src/psaux/psarrst.c240
-rw-r--r--modules/freetype2/src/psaux/psarrst.h99
-rw-r--r--modules/freetype2/src/psaux/psaux.c40
-rw-r--r--modules/freetype2/src/psaux/psauxerr.h42
-rw-r--r--modules/freetype2/src/psaux/psauxmod.c190
-rw-r--r--modules/freetype2/src/psaux/psauxmod.h60
-rw-r--r--modules/freetype2/src/psaux/psblues.c583
-rw-r--r--modules/freetype2/src/psaux/psblues.h185
-rw-r--r--modules/freetype2/src/psaux/psconv.c610
-rw-r--r--modules/freetype2/src/psaux/psconv.h70
-rw-r--r--modules/freetype2/src/psaux/pserror.c52
-rw-r--r--modules/freetype2/src/psaux/pserror.h120
-rw-r--r--modules/freetype2/src/psaux/psfixed.h94
-rw-r--r--modules/freetype2/src/psaux/psfont.c566
-rw-r--r--modules/freetype2/src/psaux/psfont.h134
-rw-r--r--modules/freetype2/src/psaux/psft.c895
-rw-r--r--modules/freetype2/src/psaux/psft.h167
-rw-r--r--modules/freetype2/src/psaux/psglue.h144
-rw-r--r--modules/freetype2/src/psaux/pshints.c1952
-rw-r--r--modules/freetype2/src/psaux/pshints.h288
-rw-r--r--modules/freetype2/src/psaux/psintrp.c3059
-rw-r--r--modules/freetype2/src/psaux/psintrp.h83
-rw-r--r--modules/freetype2/src/psaux/psobjs.c2562
-rw-r--r--modules/freetype2/src/psaux/psobjs.h312
-rw-r--r--modules/freetype2/src/psaux/psread.c112
-rw-r--r--modules/freetype2/src/psaux/psread.h68
-rw-r--r--modules/freetype2/src/psaux/psstack.c329
-rw-r--r--modules/freetype2/src/psaux/psstack.h122
-rw-r--r--modules/freetype2/src/psaux/pstypes.h77
-rw-r--r--modules/freetype2/src/psaux/rules.mk89
-rw-r--r--modules/freetype2/src/psaux/t1cmap.c374
-rw-r--r--modules/freetype2/src/psaux/t1cmap.h104
-rw-r--r--modules/freetype2/src/psaux/t1decode.c2159
-rw-r--r--modules/freetype2/src/psaux/t1decode.h73
39 files changed, 19745 insertions, 0 deletions
diff --git a/modules/freetype2/src/psaux/afmparse.c b/modules/freetype2/src/psaux/afmparse.c
new file mode 100644
index 0000000000..68f95698e6
--- /dev/null
+++ b/modules/freetype2/src/psaux/afmparse.c
@@ -0,0 +1,1094 @@
+/****************************************************************************
+ *
+ * afmparse.c
+ *
+ * AFM parser (body).
+ *
+ * Copyright (C) 2006-2023 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 <freetype/freetype.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/psaux.h>
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+
+#include "afmparse.h"
+#include "psconv.h"
+
+#include "psauxerr.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 afmparse
+
+
+ /**************************************************************************
+ *
+ * AFM_Stream
+ *
+ * The use of AFM_Stream is largely inspired by parseAFM.[ch] from t1lib.
+ *
+ */
+
+ enum
+ {
+ AFM_STREAM_STATUS_NORMAL,
+ AFM_STREAM_STATUS_EOC,
+ AFM_STREAM_STATUS_EOL,
+ AFM_STREAM_STATUS_EOF
+ };
+
+
+ typedef struct AFM_StreamRec_
+ {
+ FT_Byte* cursor;
+ FT_Byte* base;
+ FT_Byte* limit;
+
+ FT_Int status;
+
+ } AFM_StreamRec;
+
+
+#ifndef EOF
+#define EOF -1
+#endif
+
+
+ /* this works because empty lines are ignored */
+#define AFM_IS_NEWLINE( ch ) ( (ch) == '\r' || (ch) == '\n' )
+
+#define AFM_IS_EOF( ch ) ( (ch) == EOF || (ch) == '\x1a' )
+#define AFM_IS_SPACE( ch ) ( (ch) == ' ' || (ch) == '\t' )
+
+ /* column separator; there is no `column' in the spec actually */
+#define AFM_IS_SEP( ch ) ( (ch) == ';' )
+
+#define AFM_GETC() \
+ ( ( (stream)->cursor < (stream)->limit ) ? *(stream)->cursor++ \
+ : EOF )
+
+#define AFM_STREAM_KEY_BEGIN( stream ) \
+ (char*)( (stream)->cursor - 1 )
+
+#define AFM_STREAM_KEY_LEN( stream, key ) \
+ (FT_Offset)( (char*)(stream)->cursor - key - 1 )
+
+#define AFM_STATUS_EOC( stream ) \
+ ( (stream)->status >= AFM_STREAM_STATUS_EOC )
+
+#define AFM_STATUS_EOL( stream ) \
+ ( (stream)->status >= AFM_STREAM_STATUS_EOL )
+
+#define AFM_STATUS_EOF( stream ) \
+ ( (stream)->status >= AFM_STREAM_STATUS_EOF )
+
+
+ static int
+ afm_stream_skip_spaces( AFM_Stream stream )
+ {
+ int ch = 0; /* make stupid compiler happy */
+
+
+ if ( AFM_STATUS_EOC( stream ) )
+ return ';';
+
+ while ( 1 )
+ {
+ ch = AFM_GETC();
+ if ( !AFM_IS_SPACE( ch ) )
+ break;
+ }
+
+ if ( AFM_IS_NEWLINE( ch ) )
+ stream->status = AFM_STREAM_STATUS_EOL;
+ else if ( AFM_IS_SEP( ch ) )
+ stream->status = AFM_STREAM_STATUS_EOC;
+ else if ( AFM_IS_EOF( ch ) )
+ stream->status = AFM_STREAM_STATUS_EOF;
+
+ return ch;
+ }
+
+
+ /* read a key or value in current column */
+ static char*
+ afm_stream_read_one( AFM_Stream stream )
+ {
+ char* str;
+
+
+ afm_stream_skip_spaces( stream );
+ if ( AFM_STATUS_EOC( stream ) )
+ return NULL;
+
+ str = AFM_STREAM_KEY_BEGIN( stream );
+
+ while ( 1 )
+ {
+ int ch = AFM_GETC();
+
+
+ if ( AFM_IS_SPACE( ch ) )
+ break;
+ else if ( AFM_IS_NEWLINE( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOL;
+ break;
+ }
+ else if ( AFM_IS_SEP( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOC;
+ break;
+ }
+ else if ( AFM_IS_EOF( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOF;
+ break;
+ }
+ }
+
+ return str;
+ }
+
+
+ /* read a string (i.e., read to EOL) */
+ static char*
+ afm_stream_read_string( AFM_Stream stream )
+ {
+ char* str;
+
+
+ afm_stream_skip_spaces( stream );
+ if ( AFM_STATUS_EOL( stream ) )
+ return NULL;
+
+ str = AFM_STREAM_KEY_BEGIN( stream );
+
+ /* scan to eol */
+ while ( 1 )
+ {
+ int ch = AFM_GETC();
+
+
+ if ( AFM_IS_NEWLINE( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOL;
+ break;
+ }
+ else if ( AFM_IS_EOF( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOF;
+ break;
+ }
+ }
+
+ return str;
+ }
+
+
+ /**************************************************************************
+ *
+ * AFM_Parser
+ *
+ */
+
+ /* all keys defined in Ch. 7-10 of 5004.AFM_Spec.pdf */
+ typedef enum AFM_Token_
+ {
+ AFM_TOKEN_ASCENDER,
+ AFM_TOKEN_AXISLABEL,
+ AFM_TOKEN_AXISTYPE,
+ AFM_TOKEN_B,
+ AFM_TOKEN_BLENDAXISTYPES,
+ AFM_TOKEN_BLENDDESIGNMAP,
+ AFM_TOKEN_BLENDDESIGNPOSITIONS,
+ AFM_TOKEN_C,
+ AFM_TOKEN_CC,
+ AFM_TOKEN_CH,
+ AFM_TOKEN_CAPHEIGHT,
+ AFM_TOKEN_CHARWIDTH,
+ AFM_TOKEN_CHARACTERSET,
+ AFM_TOKEN_CHARACTERS,
+ AFM_TOKEN_DESCENDER,
+ AFM_TOKEN_ENCODINGSCHEME,
+ AFM_TOKEN_ENDAXIS,
+ AFM_TOKEN_ENDCHARMETRICS,
+ AFM_TOKEN_ENDCOMPOSITES,
+ AFM_TOKEN_ENDDIRECTION,
+ AFM_TOKEN_ENDFONTMETRICS,
+ AFM_TOKEN_ENDKERNDATA,
+ AFM_TOKEN_ENDKERNPAIRS,
+ AFM_TOKEN_ENDTRACKKERN,
+ AFM_TOKEN_ESCCHAR,
+ AFM_TOKEN_FAMILYNAME,
+ AFM_TOKEN_FONTBBOX,
+ AFM_TOKEN_FONTNAME,
+ AFM_TOKEN_FULLNAME,
+ AFM_TOKEN_ISBASEFONT,
+ AFM_TOKEN_ISCIDFONT,
+ AFM_TOKEN_ISFIXEDPITCH,
+ AFM_TOKEN_ISFIXEDV,
+ AFM_TOKEN_ITALICANGLE,
+ AFM_TOKEN_KP,
+ AFM_TOKEN_KPH,
+ AFM_TOKEN_KPX,
+ AFM_TOKEN_KPY,
+ AFM_TOKEN_L,
+ AFM_TOKEN_MAPPINGSCHEME,
+ AFM_TOKEN_METRICSSETS,
+ AFM_TOKEN_N,
+ AFM_TOKEN_NOTICE,
+ AFM_TOKEN_PCC,
+ AFM_TOKEN_STARTAXIS,
+ AFM_TOKEN_STARTCHARMETRICS,
+ AFM_TOKEN_STARTCOMPOSITES,
+ AFM_TOKEN_STARTDIRECTION,
+ AFM_TOKEN_STARTFONTMETRICS,
+ AFM_TOKEN_STARTKERNDATA,
+ AFM_TOKEN_STARTKERNPAIRS,
+ AFM_TOKEN_STARTKERNPAIRS0,
+ AFM_TOKEN_STARTKERNPAIRS1,
+ AFM_TOKEN_STARTTRACKKERN,
+ AFM_TOKEN_STDHW,
+ AFM_TOKEN_STDVW,
+ AFM_TOKEN_TRACKKERN,
+ AFM_TOKEN_UNDERLINEPOSITION,
+ AFM_TOKEN_UNDERLINETHICKNESS,
+ AFM_TOKEN_VV,
+ AFM_TOKEN_VVECTOR,
+ AFM_TOKEN_VERSION,
+ AFM_TOKEN_W,
+ AFM_TOKEN_W0,
+ AFM_TOKEN_W0X,
+ AFM_TOKEN_W0Y,
+ AFM_TOKEN_W1,
+ AFM_TOKEN_W1X,
+ AFM_TOKEN_W1Y,
+ AFM_TOKEN_WX,
+ AFM_TOKEN_WY,
+ AFM_TOKEN_WEIGHT,
+ AFM_TOKEN_WEIGHTVECTOR,
+ AFM_TOKEN_XHEIGHT,
+ N_AFM_TOKENS,
+ AFM_TOKEN_UNKNOWN
+
+ } AFM_Token;
+
+
+ static const char* const afm_key_table[N_AFM_TOKENS] =
+ {
+ "Ascender",
+ "AxisLabel",
+ "AxisType",
+ "B",
+ "BlendAxisTypes",
+ "BlendDesignMap",
+ "BlendDesignPositions",
+ "C",
+ "CC",
+ "CH",
+ "CapHeight",
+ "CharWidth",
+ "CharacterSet",
+ "Characters",
+ "Descender",
+ "EncodingScheme",
+ "EndAxis",
+ "EndCharMetrics",
+ "EndComposites",
+ "EndDirection",
+ "EndFontMetrics",
+ "EndKernData",
+ "EndKernPairs",
+ "EndTrackKern",
+ "EscChar",
+ "FamilyName",
+ "FontBBox",
+ "FontName",
+ "FullName",
+ "IsBaseFont",
+ "IsCIDFont",
+ "IsFixedPitch",
+ "IsFixedV",
+ "ItalicAngle",
+ "KP",
+ "KPH",
+ "KPX",
+ "KPY",
+ "L",
+ "MappingScheme",
+ "MetricsSets",
+ "N",
+ "Notice",
+ "PCC",
+ "StartAxis",
+ "StartCharMetrics",
+ "StartComposites",
+ "StartDirection",
+ "StartFontMetrics",
+ "StartKernData",
+ "StartKernPairs",
+ "StartKernPairs0",
+ "StartKernPairs1",
+ "StartTrackKern",
+ "StdHW",
+ "StdVW",
+ "TrackKern",
+ "UnderlinePosition",
+ "UnderlineThickness",
+ "VV",
+ "VVector",
+ "Version",
+ "W",
+ "W0",
+ "W0X",
+ "W0Y",
+ "W1",
+ "W1X",
+ "W1Y",
+ "WX",
+ "WY",
+ "Weight",
+ "WeightVector",
+ "XHeight"
+ };
+
+
+ /*
+ * `afm_parser_read_vals' and `afm_parser_next_key' provide
+ * high-level operations to an AFM_Stream. The rest of the
+ * parser functions should use them without accessing the
+ * AFM_Stream directly.
+ */
+
+ FT_LOCAL_DEF( FT_Int )
+ afm_parser_read_vals( AFM_Parser parser,
+ AFM_Value vals,
+ FT_Int n )
+ {
+ AFM_Stream stream = parser->stream;
+ char* str;
+ FT_Int i;
+
+
+ if ( n > AFM_MAX_ARGUMENTS )
+ return 0;
+
+ for ( i = 0; i < n; i++ )
+ {
+ FT_Offset len;
+ AFM_Value val = vals + i;
+
+
+ if ( val->type == AFM_VALUE_TYPE_STRING )
+ str = afm_stream_read_string( stream );
+ else
+ str = afm_stream_read_one( stream );
+
+ if ( !str )
+ break;
+
+ len = AFM_STREAM_KEY_LEN( stream, str );
+
+ switch ( val->type )
+ {
+ case AFM_VALUE_TYPE_STRING:
+ case AFM_VALUE_TYPE_NAME:
+ {
+ FT_Memory memory = parser->memory;
+ FT_Error error;
+
+
+ if ( !FT_QALLOC( val->u.s, len + 1 ) )
+ {
+ ft_memcpy( val->u.s, str, len );
+ val->u.s[len] = '\0';
+ }
+ }
+ break;
+
+ case AFM_VALUE_TYPE_FIXED:
+ val->u.f = PS_Conv_ToFixed( (FT_Byte**)(void*)&str,
+ (FT_Byte*)str + len, 0 );
+ break;
+
+ case AFM_VALUE_TYPE_INTEGER:
+ val->u.i = PS_Conv_ToInt( (FT_Byte**)(void*)&str,
+ (FT_Byte*)str + len );
+ break;
+
+ case AFM_VALUE_TYPE_BOOL:
+ val->u.b = FT_BOOL( len == 4 &&
+ !ft_strncmp( str, "true", 4 ) );
+ break;
+
+ case AFM_VALUE_TYPE_INDEX:
+ if ( parser->get_index )
+ val->u.i = parser->get_index( str, len, parser->user_data );
+ else
+ val->u.i = 0;
+ break;
+ }
+ }
+
+ return i;
+ }
+
+
+ FT_LOCAL_DEF( char* )
+ afm_parser_next_key( AFM_Parser parser,
+ FT_Bool line,
+ FT_Offset* len )
+ {
+ AFM_Stream stream = parser->stream;
+ char* key = NULL; /* make stupid compiler happy */
+
+
+ if ( line )
+ {
+ while ( 1 )
+ {
+ /* skip current line */
+ if ( !AFM_STATUS_EOL( stream ) )
+ afm_stream_read_string( stream );
+
+ stream->status = AFM_STREAM_STATUS_NORMAL;
+ key = afm_stream_read_one( stream );
+
+ /* skip empty line */
+ if ( !key &&
+ !AFM_STATUS_EOF( stream ) &&
+ AFM_STATUS_EOL( stream ) )
+ continue;
+
+ break;
+ }
+ }
+ else
+ {
+ while ( 1 )
+ {
+ /* skip current column */
+ while ( !AFM_STATUS_EOC( stream ) )
+ afm_stream_read_one( stream );
+
+ stream->status = AFM_STREAM_STATUS_NORMAL;
+ key = afm_stream_read_one( stream );
+
+ /* skip empty column */
+ if ( !key &&
+ !AFM_STATUS_EOF( stream ) &&
+ AFM_STATUS_EOC( stream ) )
+ continue;
+
+ break;
+ }
+ }
+
+ if ( len )
+ *len = ( key ) ? (FT_Offset)AFM_STREAM_KEY_LEN( stream, key )
+ : 0;
+
+ return key;
+ }
+
+
+ static AFM_Token
+ afm_tokenize( const char* key,
+ FT_Offset len )
+ {
+ int n;
+
+
+ for ( n = 0; n < N_AFM_TOKENS; n++ )
+ {
+ if ( *( afm_key_table[n] ) == *key )
+ {
+ for ( ; n < N_AFM_TOKENS; n++ )
+ {
+ if ( *( afm_key_table[n] ) != *key )
+ return AFM_TOKEN_UNKNOWN;
+
+ if ( ft_strncmp( afm_key_table[n], key, len ) == 0 )
+ return (AFM_Token) n;
+ }
+ }
+ }
+
+ return AFM_TOKEN_UNKNOWN;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ afm_parser_init( AFM_Parser parser,
+ FT_Memory memory,
+ FT_Byte* base,
+ FT_Byte* limit )
+ {
+ AFM_Stream stream = NULL;
+ FT_Error error;
+
+
+ if ( FT_NEW( stream ) )
+ return error;
+
+ stream->cursor = stream->base = base;
+ stream->limit = limit;
+
+ /* don't skip the first line during the first call */
+ stream->status = AFM_STREAM_STATUS_EOL;
+
+ parser->memory = memory;
+ parser->stream = stream;
+ parser->FontInfo = NULL;
+ parser->get_index = NULL;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ afm_parser_done( AFM_Parser parser )
+ {
+ FT_Memory memory = parser->memory;
+
+
+ FT_FREE( parser->stream );
+ }
+
+
+ static FT_Error
+ afm_parser_read_int( AFM_Parser parser,
+ FT_Int* aint )
+ {
+ AFM_ValueRec val;
+
+
+ val.type = AFM_VALUE_TYPE_INTEGER;
+
+ if ( afm_parser_read_vals( parser, &val, 1 ) == 1 )
+ {
+ *aint = val.u.i;
+
+ return FT_Err_Ok;
+ }
+ else
+ return FT_THROW( Syntax_Error );
+ }
+
+
+ static FT_Error
+ afm_parse_track_kern( AFM_Parser parser )
+ {
+ AFM_FontInfo fi = parser->FontInfo;
+ AFM_Stream stream = parser->stream;
+ AFM_TrackKern tk;
+
+ char* key;
+ FT_Offset len;
+ int n = -1;
+ FT_Int tmp;
+
+
+ if ( afm_parser_read_int( parser, &tmp ) )
+ goto Fail;
+
+ if ( tmp < 0 )
+ {
+ FT_ERROR(( "afm_parse_track_kern: invalid number of track kerns\n" ));
+ goto Fail;
+ }
+
+ fi->NumTrackKern = (FT_UInt)tmp;
+ FT_TRACE3(( "afm_parse_track_kern: %u track kern%s expected\n",
+ fi->NumTrackKern,
+ fi->NumTrackKern == 1 ? "" : "s" ));
+
+ /* Rough sanity check: The minimum line length of the `TrackKern` */
+ /* command is 20 characters (including the EOL character). */
+ if ( (FT_ULong)( stream->limit - stream->cursor ) / 20 <
+ fi->NumTrackKern )
+ {
+ FT_ERROR(( "afm_parse_track_kern:"
+ " number of track kern entries exceeds stream size\n" ));
+ goto Fail;
+ }
+
+ if ( fi->NumTrackKern )
+ {
+ FT_Memory memory = parser->memory;
+ FT_Error error;
+
+
+ if ( FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ) )
+ return error;
+ }
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_ValueRec shared_vals[5];
+
+
+ switch ( afm_tokenize( key, len ) )
+ {
+ case AFM_TOKEN_TRACKKERN:
+ n++;
+
+ if ( n >= (int)fi->NumTrackKern )
+ {
+ FT_ERROR(( "afm_parse_track_kern: too many track kern data\n" ));
+ goto Fail;
+ }
+
+ tk = fi->TrackKerns + n;
+
+ shared_vals[0].type = AFM_VALUE_TYPE_INTEGER;
+ shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[4].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 5 ) != 5 )
+ {
+ FT_ERROR(( "afm_parse_track_kern:"
+ " insufficient number of parameters for entry %d\n",
+ n ));
+ goto Fail;
+ }
+
+ tk->degree = shared_vals[0].u.i;
+ tk->min_ptsize = shared_vals[1].u.f;
+ tk->min_kern = shared_vals[2].u.f;
+ tk->max_ptsize = shared_vals[3].u.f;
+ tk->max_kern = shared_vals[4].u.f;
+
+ break;
+
+ case AFM_TOKEN_ENDTRACKKERN:
+ case AFM_TOKEN_ENDKERNDATA:
+ case AFM_TOKEN_ENDFONTMETRICS:
+ tmp = n + 1;
+ if ( (FT_UInt)tmp != fi->NumTrackKern )
+ {
+ FT_TRACE1(( "afm_parse_track_kern: %s%d track kern entr%s seen\n",
+ tmp == 0 ? "" : "only ",
+ tmp,
+ tmp == 1 ? "y" : "ies" ));
+ fi->NumTrackKern = (FT_UInt)tmp;
+ }
+ else
+ FT_TRACE3(( "afm_parse_track_kern: %d track kern entr%s seen\n",
+ tmp,
+ tmp == 1 ? "y" : "ies" ));
+ return FT_Err_Ok;
+
+ case AFM_TOKEN_UNKNOWN:
+ break;
+
+ default:
+ goto Fail;
+ }
+ }
+
+ Fail:
+ return FT_THROW( Syntax_Error );
+ }
+
+
+#undef KERN_INDEX
+#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 )
+
+
+ /* compare two kerning pairs */
+ FT_COMPARE_DEF( int )
+ afm_compare_kern_pairs( const void* a,
+ const void* b )
+ {
+ AFM_KernPair kp1 = (AFM_KernPair)a;
+ AFM_KernPair kp2 = (AFM_KernPair)b;
+
+ FT_ULong index1 = KERN_INDEX( kp1->index1, kp1->index2 );
+ FT_ULong index2 = KERN_INDEX( kp2->index1, kp2->index2 );
+
+
+ if ( index1 > index2 )
+ return 1;
+ else if ( index1 < index2 )
+ return -1;
+ else
+ return 0;
+ }
+
+
+ static FT_Error
+ afm_parse_kern_pairs( AFM_Parser parser )
+ {
+ AFM_FontInfo fi = parser->FontInfo;
+ AFM_Stream stream = parser->stream;
+ AFM_KernPair kp;
+ char* key;
+ FT_Offset len;
+ int n = -1;
+ FT_Int tmp;
+
+
+ if ( afm_parser_read_int( parser, &tmp ) )
+ goto Fail;
+
+ if ( tmp < 0 )
+ {
+ FT_ERROR(( "afm_parse_kern_pairs: invalid number of kern pairs\n" ));
+ goto Fail;
+ }
+
+ fi->NumKernPair = (FT_UInt)tmp;
+ FT_TRACE3(( "afm_parse_kern_pairs: %u kern pair%s expected\n",
+ fi->NumKernPair,
+ fi->NumKernPair == 1 ? "" : "s" ));
+
+ /* Rough sanity check: The minimum line length of the `KP`, */
+ /* `KPH`,`KPX`, and `KPY` commands is 10 characters (including */
+ /* the EOL character). */
+ if ( (FT_ULong)( stream->limit - stream->cursor ) / 10 <
+ fi->NumKernPair )
+ {
+ FT_ERROR(( "afm_parse_kern_pairs:"
+ " number of kern pairs exceeds stream size\n" ));
+ goto Fail;
+ }
+
+ if ( fi->NumKernPair )
+ {
+ FT_Memory memory = parser->memory;
+ FT_Error error;
+
+
+ if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
+ return error;
+ }
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_Token token = afm_tokenize( key, len );
+
+
+ switch ( token )
+ {
+ case AFM_TOKEN_KP:
+ case AFM_TOKEN_KPX:
+ case AFM_TOKEN_KPY:
+ {
+ FT_Int r;
+ AFM_ValueRec shared_vals[4];
+
+
+ n++;
+
+ if ( n >= (int)fi->NumKernPair )
+ {
+ FT_ERROR(( "afm_parse_kern_pairs: too many kern pairs\n" ));
+ goto Fail;
+ }
+
+ kp = fi->KernPairs + n;
+
+ shared_vals[0].type = AFM_VALUE_TYPE_INDEX;
+ shared_vals[1].type = AFM_VALUE_TYPE_INDEX;
+ shared_vals[2].type = AFM_VALUE_TYPE_INTEGER;
+ shared_vals[3].type = AFM_VALUE_TYPE_INTEGER;
+ r = afm_parser_read_vals( parser, shared_vals, 4 );
+ if ( r < 3 )
+ {
+ FT_ERROR(( "afm_parse_kern_pairs:"
+ " insufficient number of parameters for entry %d\n",
+ n ));
+ goto Fail;
+ }
+
+ /* index values can't be negative */
+ kp->index1 = shared_vals[0].u.u;
+ kp->index2 = shared_vals[1].u.u;
+ if ( token == AFM_TOKEN_KPY )
+ {
+ kp->x = 0;
+ kp->y = shared_vals[2].u.i;
+ }
+ else
+ {
+ kp->x = shared_vals[2].u.i;
+ kp->y = ( token == AFM_TOKEN_KP && r == 4 )
+ ? shared_vals[3].u.i : 0;
+ }
+ }
+ break;
+
+ case AFM_TOKEN_ENDKERNPAIRS:
+ case AFM_TOKEN_ENDKERNDATA:
+ case AFM_TOKEN_ENDFONTMETRICS:
+ tmp = n + 1;
+ if ( (FT_UInt)tmp != fi->NumKernPair )
+ {
+ FT_TRACE1(( "afm_parse_kern_pairs: %s%d kern pair%s seen\n",
+ tmp == 0 ? "" : "only ",
+ tmp,
+ tmp == 1 ? "" : "s" ));
+ fi->NumKernPair = (FT_UInt)tmp;
+ }
+ else
+ FT_TRACE3(( "afm_parse_kern_pairs: %d kern pair%s seen\n",
+ tmp,
+ tmp == 1 ? "" : "s" ));
+
+ ft_qsort( fi->KernPairs, fi->NumKernPair,
+ sizeof ( AFM_KernPairRec ),
+ afm_compare_kern_pairs );
+ return FT_Err_Ok;
+
+ case AFM_TOKEN_UNKNOWN:
+ break;
+
+ default:
+ goto Fail;
+ }
+ }
+
+ Fail:
+ return FT_THROW( Syntax_Error );
+ }
+
+
+ static FT_Error
+ afm_parse_kern_data( AFM_Parser parser )
+ {
+ FT_Error error;
+ char* key;
+ FT_Offset len;
+
+ int have_trackkern = 0;
+ int have_kernpairs = 0;
+
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ switch ( afm_tokenize( key, len ) )
+ {
+ case AFM_TOKEN_STARTTRACKKERN:
+ if ( have_trackkern )
+ {
+ FT_ERROR(( "afm_parse_kern_data:"
+ " invalid second horizontal track kern section\n" ));
+ goto Fail;
+ }
+
+ error = afm_parse_track_kern( parser );
+ if ( error )
+ return error;
+
+ have_trackkern = 1;
+ break;
+
+ case AFM_TOKEN_STARTKERNPAIRS:
+ case AFM_TOKEN_STARTKERNPAIRS0:
+ if ( have_kernpairs )
+ {
+ FT_ERROR(( "afm_parse_kern_data:"
+ " invalid second horizontal kern pair section\n" ));
+ goto Fail;
+ }
+
+ error = afm_parse_kern_pairs( parser );
+ if ( error )
+ return error;
+
+ have_kernpairs = 1;
+ break;
+
+ case AFM_TOKEN_ENDKERNDATA:
+ case AFM_TOKEN_ENDFONTMETRICS:
+ return FT_Err_Ok;
+
+ case AFM_TOKEN_UNKNOWN:
+ break;
+
+ default:
+ goto Fail;
+ }
+ }
+
+ Fail:
+ return FT_THROW( Syntax_Error );
+ }
+
+
+ static FT_Error
+ afm_parser_skip_section( AFM_Parser parser,
+ FT_Int n,
+ AFM_Token end_section )
+ {
+ char* key;
+ FT_Offset len;
+
+
+ while ( n-- > 0 )
+ {
+ key = afm_parser_next_key( parser, 1, NULL );
+ if ( !key )
+ goto Fail;
+ }
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_Token token = afm_tokenize( key, len );
+
+
+ if ( token == end_section || token == AFM_TOKEN_ENDFONTMETRICS )
+ return FT_Err_Ok;
+ }
+
+ Fail:
+ return FT_THROW( Syntax_Error );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ afm_parser_parse( AFM_Parser parser )
+ {
+ FT_Memory memory = parser->memory;
+ AFM_FontInfo fi = parser->FontInfo;
+ FT_Error error = FT_ERR( Syntax_Error );
+ char* key;
+ FT_Offset len;
+ FT_Int metrics_sets = 0;
+
+
+ if ( !fi )
+ return FT_THROW( Invalid_Argument );
+
+ key = afm_parser_next_key( parser, 1, &len );
+ if ( !key || len != 16 ||
+ ft_strncmp( key, "StartFontMetrics", 16 ) != 0 )
+ return FT_THROW( Unknown_File_Format );
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_ValueRec shared_vals[4];
+
+
+ switch ( afm_tokenize( key, len ) )
+ {
+ case AFM_TOKEN_METRICSSETS:
+ if ( afm_parser_read_int( parser, &metrics_sets ) )
+ goto Fail;
+
+ if ( metrics_sets != 0 && metrics_sets != 2 )
+ {
+ error = FT_THROW( Unimplemented_Feature );
+
+ goto Fail;
+ }
+ break;
+
+ case AFM_TOKEN_ISCIDFONT:
+ shared_vals[0].type = AFM_VALUE_TYPE_BOOL;
+ if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+ goto Fail;
+
+ fi->IsCIDFont = shared_vals[0].u.b;
+ break;
+
+ case AFM_TOKEN_FONTBBOX:
+ shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 4 ) != 4 )
+ goto Fail;
+
+ fi->FontBBox.xMin = shared_vals[0].u.f;
+ fi->FontBBox.yMin = shared_vals[1].u.f;
+ fi->FontBBox.xMax = shared_vals[2].u.f;
+ fi->FontBBox.yMax = shared_vals[3].u.f;
+ break;
+
+ case AFM_TOKEN_ASCENDER:
+ shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+ goto Fail;
+
+ fi->Ascender = shared_vals[0].u.f;
+ break;
+
+ case AFM_TOKEN_DESCENDER:
+ shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+ goto Fail;
+
+ fi->Descender = shared_vals[0].u.f;
+ break;
+
+ case AFM_TOKEN_STARTCHARMETRICS:
+ {
+ FT_Int n = 0;
+
+
+ if ( afm_parser_read_int( parser, &n ) )
+ goto Fail;
+
+ error = afm_parser_skip_section( parser, n,
+ AFM_TOKEN_ENDCHARMETRICS );
+ if ( error )
+ return error;
+ }
+ break;
+
+ case AFM_TOKEN_STARTKERNDATA:
+ error = afm_parse_kern_data( parser );
+ if ( error )
+ goto Fail;
+ /* we only support kern data, so ... */
+ FALL_THROUGH;
+
+ case AFM_TOKEN_ENDFONTMETRICS:
+ return FT_Err_Ok;
+
+ default:
+ break;
+ }
+ }
+
+ Fail:
+ FT_FREE( fi->TrackKerns );
+ fi->NumTrackKern = 0;
+
+ FT_FREE( fi->KernPairs );
+ fi->NumKernPair = 0;
+
+ fi->IsCIDFont = 0;
+
+ return error;
+ }
+
+#else /* T1_CONFIG_OPTION_NO_AFM */
+
+ /* ANSI C doesn't like empty source files */
+ typedef int _afm_parse_dummy;
+
+#endif /* T1_CONFIG_OPTION_NO_AFM */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/afmparse.h b/modules/freetype2/src/psaux/afmparse.h
new file mode 100644
index 0000000000..2d3b6e6e16
--- /dev/null
+++ b/modules/freetype2/src/psaux/afmparse.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+ *
+ * afmparse.h
+ *
+ * AFM parser (specification).
+ *
+ * Copyright (C) 2006-2023 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.
+ *
+ */
+
+
+#ifndef AFMPARSE_H_
+#define AFMPARSE_H_
+
+
+#include <freetype/internal/psaux.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ afm_parser_init( AFM_Parser parser,
+ FT_Memory memory,
+ FT_Byte* base,
+ FT_Byte* limit );
+
+
+ FT_LOCAL( void )
+ afm_parser_done( AFM_Parser parser );
+
+
+ FT_LOCAL( FT_Error )
+ afm_parser_parse( AFM_Parser parser );
+
+
+ enum AFM_ValueType_
+ {
+ AFM_VALUE_TYPE_STRING,
+ AFM_VALUE_TYPE_NAME,
+ AFM_VALUE_TYPE_FIXED, /* real number */
+ AFM_VALUE_TYPE_INTEGER,
+ AFM_VALUE_TYPE_BOOL,
+ AFM_VALUE_TYPE_INDEX /* glyph index */
+ };
+
+
+ typedef struct AFM_ValueRec_
+ {
+ enum AFM_ValueType_ type;
+ union
+ {
+ char* s;
+ FT_Fixed f;
+ FT_Int i;
+ FT_UInt u;
+ FT_Bool b;
+
+ } u;
+
+ } AFM_ValueRec, *AFM_Value;
+
+#define AFM_MAX_ARGUMENTS 5
+
+ FT_LOCAL( FT_Int )
+ afm_parser_read_vals( AFM_Parser parser,
+ AFM_Value vals,
+ FT_Int n );
+
+ /* read the next key from the next line or column */
+ FT_LOCAL( char* )
+ afm_parser_next_key( AFM_Parser parser,
+ FT_Bool line,
+ FT_Offset* len );
+
+FT_END_HEADER
+
+#endif /* AFMPARSE_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/cffdecode.c b/modules/freetype2/src/psaux/cffdecode.c
new file mode 100644
index 0000000000..2cd91c96f3
--- /dev/null
+++ b/modules/freetype2/src/psaux/cffdecode.c
@@ -0,0 +1,2423 @@
+/****************************************************************************
+ *
+ * cffdecode.c
+ *
+ * PostScript CFF (Type 2) decoding routines (body).
+ *
+ * Copyright (C) 2017-2023 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 <freetype/freetype.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftserv.h>
+#include <freetype/internal/services/svcfftl.h>
+
+#include "cffdecode.h"
+#include "psobjs.h"
+
+#include "psauxerr.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 cffdecode
+
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+
+ typedef enum CFF_Operator_
+ {
+ cff_op_unknown = 0,
+
+ cff_op_rmoveto,
+ cff_op_hmoveto,
+ cff_op_vmoveto,
+
+ cff_op_rlineto,
+ cff_op_hlineto,
+ cff_op_vlineto,
+
+ cff_op_rrcurveto,
+ cff_op_hhcurveto,
+ cff_op_hvcurveto,
+ cff_op_rcurveline,
+ cff_op_rlinecurve,
+ cff_op_vhcurveto,
+ cff_op_vvcurveto,
+
+ cff_op_flex,
+ cff_op_hflex,
+ cff_op_hflex1,
+ cff_op_flex1,
+
+ cff_op_endchar,
+
+ cff_op_hstem,
+ cff_op_vstem,
+ cff_op_hstemhm,
+ cff_op_vstemhm,
+
+ cff_op_hintmask,
+ cff_op_cntrmask,
+ cff_op_dotsection, /* deprecated, acts as no-op */
+
+ cff_op_abs,
+ cff_op_add,
+ cff_op_sub,
+ cff_op_div,
+ cff_op_neg,
+ cff_op_random,
+ cff_op_mul,
+ cff_op_sqrt,
+
+ cff_op_blend,
+
+ cff_op_drop,
+ cff_op_exch,
+ cff_op_index,
+ cff_op_roll,
+ cff_op_dup,
+
+ cff_op_put,
+ cff_op_get,
+ cff_op_store,
+ cff_op_load,
+
+ cff_op_and,
+ cff_op_or,
+ cff_op_not,
+ cff_op_eq,
+ cff_op_ifelse,
+
+ cff_op_callsubr,
+ cff_op_callgsubr,
+ cff_op_return,
+
+ /* Type 1 opcodes: invalid but seen in real life */
+ cff_op_hsbw,
+ cff_op_closepath,
+ cff_op_callothersubr,
+ cff_op_pop,
+ cff_op_seac,
+ cff_op_sbw,
+ cff_op_setcurrentpoint,
+
+ /* do not remove */
+ cff_op_max
+
+ } CFF_Operator;
+
+
+#define CFF_COUNT_CHECK_WIDTH 0x80
+#define CFF_COUNT_EXACT 0x40
+#define CFF_COUNT_CLEAR_STACK 0x20
+
+ /* count values which have the `CFF_COUNT_CHECK_WIDTH' flag set are */
+ /* used for checking the width and requested numbers of arguments */
+ /* only; they are set to zero afterwards */
+
+ /* the other two flags are informative only and unused currently */
+
+ static const FT_Byte cff_argument_counts[] =
+ {
+ 0, /* unknown */
+
+ 2 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, /* rmoveto */
+ 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
+ 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
+
+ 0 | CFF_COUNT_CLEAR_STACK, /* rlineto */
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+
+ 0 | CFF_COUNT_CLEAR_STACK, /* rrcurveto */
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+
+ 13, /* flex */
+ 7,
+ 9,
+ 11,
+
+ 0 | CFF_COUNT_CHECK_WIDTH, /* endchar */
+
+ 2 | CFF_COUNT_CHECK_WIDTH, /* hstem */
+ 2 | CFF_COUNT_CHECK_WIDTH,
+ 2 | CFF_COUNT_CHECK_WIDTH,
+ 2 | CFF_COUNT_CHECK_WIDTH,
+
+ 0 | CFF_COUNT_CHECK_WIDTH, /* hintmask */
+ 0 | CFF_COUNT_CHECK_WIDTH, /* cntrmask */
+ 0, /* dotsection */
+
+ 1, /* abs */
+ 2,
+ 2,
+ 2,
+ 1,
+ 0,
+ 2,
+ 1,
+
+ 1, /* blend */
+
+ 1, /* drop */
+ 2,
+ 1,
+ 2,
+ 1,
+
+ 2, /* put */
+ 1,
+ 4,
+ 3,
+
+ 2, /* and */
+ 2,
+ 1,
+ 2,
+ 4,
+
+ 1, /* callsubr */
+ 1,
+ 0,
+
+ 2, /* hsbw */
+ 0,
+ 0,
+ 0,
+ 5, /* seac */
+ 4, /* sbw */
+ 2 /* setcurrentpoint */
+ };
+
+
+ static FT_Error
+ cff_operator_seac( CFF_Decoder* decoder,
+ FT_Pos asb,
+ FT_Pos adx,
+ FT_Pos ady,
+ FT_Int bchar,
+ FT_Int achar )
+ {
+ FT_Error error;
+ CFF_Builder* builder = &decoder->builder;
+ FT_Int bchar_index, achar_index;
+ TT_Face face = decoder->builder.face;
+ FT_Vector left_bearing, advance;
+ FT_Byte* charstring;
+ FT_ULong charstring_len;
+ FT_Pos glyph_width;
+
+
+ if ( decoder->seac )
+ {
+ FT_ERROR(( "cff_operator_seac: invalid nested seac\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ adx = ADD_LONG( adx, decoder->builder.left_bearing.x );
+ ady = ADD_LONG( ady, decoder->builder.left_bearing.y );
+
+#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 ( face->root.internal->incremental_interface )
+ {
+ bchar_index = bchar;
+ achar_index = achar;
+ }
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ CFF_Font cff = (CFF_Font)( face->extra.data );
+
+
+ bchar_index = cff_lookup_glyph_by_stdcharcode( cff, bchar );
+ achar_index = cff_lookup_glyph_by_stdcharcode( cff, achar );
+ }
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR(( "cff_operator_seac:"
+ " invalid seac character code arguments\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ /* If we are trying to load a composite glyph, do not load the */
+ /* accent character and return the array of subglyphs. */
+ if ( builder->no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)builder->glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error )
+ goto Exit;
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)( adx >> 16 );
+ subg->arg2 = (FT_Int)( ady >> 16 );
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+ }
+
+ FT_GlyphLoader_Prepare( builder->loader );
+
+ /* First load `bchar' in builder */
+ error = decoder->get_glyph_callback( face, (FT_UInt)bchar_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ /* the seac operator must not be nested */
+ decoder->seac = TRUE;
+ error = cff_decoder_parse_charstrings( decoder, charstring,
+ charstring_len, 0 );
+ decoder->seac = FALSE;
+
+ decoder->free_glyph_callback( face, &charstring, charstring_len );
+
+ if ( error )
+ goto Exit;
+ }
+
+ /* Save the left bearing, advance and glyph width of the base */
+ /* character as they will be erased by the next load. */
+
+ left_bearing = builder->left_bearing;
+ advance = builder->advance;
+ glyph_width = decoder->glyph_width;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+
+ builder->pos_x = SUB_LONG( adx, asb );
+ builder->pos_y = ady;
+
+ /* Now load `achar' on top of the base outline. */
+ error = decoder->get_glyph_callback( face, (FT_UInt)achar_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ /* the seac operator must not be nested */
+ decoder->seac = TRUE;
+ error = cff_decoder_parse_charstrings( decoder, charstring,
+ charstring_len, 0 );
+ decoder->seac = FALSE;
+
+ decoder->free_glyph_callback( face, &charstring, charstring_len );
+
+ if ( error )
+ goto Exit;
+ }
+
+ /* Restore the left side bearing, advance and glyph width */
+ /* of the base character. */
+ builder->left_bearing = left_bearing;
+ builder->advance = advance;
+ decoder->glyph_width = glyph_width;
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ Exit:
+ return error;
+ }
+
+#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** *********/
+ /********** GENERIC CHARSTRING PARSING *********/
+ /********** *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * @Function:
+ * cff_compute_bias
+ *
+ * @Description:
+ * Computes the bias value in dependence of the number of glyph
+ * subroutines.
+ *
+ * @Input:
+ * in_charstring_type ::
+ * The `CharstringType' value of the top DICT
+ * dictionary.
+ *
+ * num_subrs ::
+ * The number of glyph subroutines.
+ *
+ * @Return:
+ * The bias value.
+ */
+ static FT_Int
+ cff_compute_bias( FT_Int in_charstring_type,
+ FT_UInt num_subrs )
+ {
+ FT_Int result;
+
+
+ if ( in_charstring_type == 1 )
+ result = 0;
+ else if ( num_subrs < 1240 )
+ result = 107;
+ else if ( num_subrs < 33900U )
+ result = 1131;
+ else
+ result = 32768U;
+
+ return result;
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ FT_UShort glyph_sid;
+
+ FT_Service_CFFLoad cffload;
+
+
+ /* CID-keyed fonts don't have glyph names */
+ if ( !cff->charset.sids )
+ return -1;
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+#if 0
+ /* retrieve cffload from list of current modules */
+ FT_Service_CFFLoad cffload;
+
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, cffload, CFF_LOAD );
+ if ( !cffload )
+ {
+ FT_ERROR(( "cff_lookup_glyph_by_stdcharcode:"
+ " the `cffload' module is not available\n" ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+#endif
+
+ cffload = (FT_Service_CFFLoad)cff->cffload;
+
+ /* Get code to SID mapping from `cff_standard_encoding'. */
+ glyph_sid = cffload->get_standard_encoding( (FT_UInt)charcode );
+
+ for ( n = 0; n < cff->num_glyphs; n++ )
+ {
+ if ( cff->charset.sids[n] == glyph_sid )
+ return (FT_Int)n;
+ }
+
+ return -1;
+ }
+
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+
+ /**************************************************************************
+ *
+ * @Function:
+ * cff_decoder_parse_charstrings
+ *
+ * @Description:
+ * Parses a given Type 2 charstrings program.
+ *
+ * @InOut:
+ * decoder ::
+ * The current Type 1 decoder.
+ *
+ * @Input:
+ * charstring_base ::
+ * The base of the charstring stream.
+ *
+ * charstring_len ::
+ * The length in bytes of the charstring stream.
+ *
+ * in_dict ::
+ * Set to 1 if function is called from top or
+ * private DICT (needed for Multiple Master CFFs).
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ cff_decoder_parse_charstrings( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len,
+ FT_Bool in_dict )
+ {
+ FT_Error error;
+ CFF_Decoder_Zone* zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ CFF_Builder* builder = &decoder->builder;
+ FT_Pos x, y;
+ FT_Fixed* stack;
+ FT_Int charstring_type =
+ decoder->cff->top_font.font_dict.charstring_type;
+ FT_UShort num_designs =
+ decoder->cff->top_font.font_dict.num_designs;
+ FT_UShort num_axes =
+ decoder->cff->top_font.font_dict.num_axes;
+
+ T2_Hints_Funcs hinter;
+
+
+ /* set default width */
+ decoder->num_hints = 0;
+ decoder->read_width = 1;
+
+ /* initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+ stack = decoder->top;
+
+ hinter = (T2_Hints_Funcs)builder->hints_funcs;
+
+ builder->path_begun = 0;
+
+ if ( !charstring_base )
+ return FT_Err_Ok;
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ error = FT_Err_Ok;
+
+ x = builder->pos_x;
+ y = builder->pos_y;
+
+ /* begin hints recording session, if any */
+ if ( hinter )
+ hinter->open( hinter->hints );
+
+ /* now execute loop */
+ while ( ip < limit )
+ {
+ CFF_Operator op;
+ FT_Byte v;
+
+
+ /*********************************************************************
+ *
+ * Decode operator or operand
+ */
+ v = *ip++;
+ if ( v >= 32 || v == 28 )
+ {
+ FT_Int shift = 16;
+ FT_Int32 val;
+
+
+ /* this is an operand, push it on the stack */
+
+ /* if we use shifts, all computations are done with unsigned */
+ /* values; the conversion to a signed value is the last step */
+ if ( v == 28 )
+ {
+ if ( ip + 1 >= limit )
+ goto Syntax_Error;
+ val = (FT_Short)( ( (FT_UShort)ip[0] << 8 ) | ip[1] );
+ ip += 2;
+ }
+ else if ( v < 247 )
+ val = (FT_Int32)v - 139;
+ else if ( v < 251 )
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ val = ( (FT_Int32)v - 247 ) * 256 + *ip++ + 108;
+ }
+ else if ( v < 255 )
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ val = -( (FT_Int32)v - 251 ) * 256 - *ip++ - 108;
+ }
+ else
+ {
+ if ( ip + 3 >= limit )
+ goto Syntax_Error;
+ val = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
+ ( (FT_UInt32)ip[1] << 16 ) |
+ ( (FT_UInt32)ip[2] << 8 ) |
+ (FT_UInt32)ip[3] );
+ ip += 4;
+ if ( charstring_type == 2 )
+ shift = 0;
+ }
+ if ( decoder->top - stack >= CFF_MAX_OPERANDS )
+ goto Stack_Overflow;
+
+ val = (FT_Int32)( (FT_UInt32)val << shift );
+ *decoder->top++ = val;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( !( val & 0xFFFFL ) )
+ FT_TRACE4(( " %hd", (FT_Short)( (FT_UInt32)val >> 16 ) ));
+ else
+ FT_TRACE4(( " %.5f", val / 65536.0 ));
+#endif
+
+ }
+ else
+ {
+ /* The specification says that normally arguments are to be taken */
+ /* from the bottom of the stack. However, this seems not to be */
+ /* correct, at least for Acroread 7.0.8 on GNU/Linux: It pops the */
+ /* arguments similar to a PS interpreter. */
+
+ FT_Fixed* args = decoder->top;
+ FT_Int num_args = (FT_Int)( args - decoder->stack );
+ FT_Int req_args;
+
+
+ /* find operator */
+ op = cff_op_unknown;
+
+ switch ( v )
+ {
+ case 1:
+ op = cff_op_hstem;
+ break;
+ case 3:
+ op = cff_op_vstem;
+ break;
+ case 4:
+ op = cff_op_vmoveto;
+ break;
+ case 5:
+ op = cff_op_rlineto;
+ break;
+ case 6:
+ op = cff_op_hlineto;
+ break;
+ case 7:
+ op = cff_op_vlineto;
+ break;
+ case 8:
+ op = cff_op_rrcurveto;
+ break;
+ case 9:
+ op = cff_op_closepath;
+ break;
+ case 10:
+ op = cff_op_callsubr;
+ break;
+ case 11:
+ op = cff_op_return;
+ break;
+ case 12:
+ if ( ip >= limit )
+ goto Syntax_Error;
+ v = *ip++;
+
+ switch ( v )
+ {
+ case 0:
+ op = cff_op_dotsection;
+ break;
+ case 1: /* this is actually the Type1 vstem3 operator */
+ op = cff_op_vstem;
+ break;
+ case 2: /* this is actually the Type1 hstem3 operator */
+ op = cff_op_hstem;
+ break;
+ case 3:
+ op = cff_op_and;
+ break;
+ case 4:
+ op = cff_op_or;
+ break;
+ case 5:
+ op = cff_op_not;
+ break;
+ case 6:
+ op = cff_op_seac;
+ break;
+ case 7:
+ op = cff_op_sbw;
+ break;
+ case 8:
+ op = cff_op_store;
+ break;
+ case 9:
+ op = cff_op_abs;
+ break;
+ case 10:
+ op = cff_op_add;
+ break;
+ case 11:
+ op = cff_op_sub;
+ break;
+ case 12:
+ op = cff_op_div;
+ break;
+ case 13:
+ op = cff_op_load;
+ break;
+ case 14:
+ op = cff_op_neg;
+ break;
+ case 15:
+ op = cff_op_eq;
+ break;
+ case 16:
+ op = cff_op_callothersubr;
+ break;
+ case 17:
+ op = cff_op_pop;
+ break;
+ case 18:
+ op = cff_op_drop;
+ break;
+ case 20:
+ op = cff_op_put;
+ break;
+ case 21:
+ op = cff_op_get;
+ break;
+ case 22:
+ op = cff_op_ifelse;
+ break;
+ case 23:
+ op = cff_op_random;
+ break;
+ case 24:
+ op = cff_op_mul;
+ break;
+ case 26:
+ op = cff_op_sqrt;
+ break;
+ case 27:
+ op = cff_op_dup;
+ break;
+ case 28:
+ op = cff_op_exch;
+ break;
+ case 29:
+ op = cff_op_index;
+ break;
+ case 30:
+ op = cff_op_roll;
+ break;
+ case 33:
+ op = cff_op_setcurrentpoint;
+ break;
+ case 34:
+ op = cff_op_hflex;
+ break;
+ case 35:
+ op = cff_op_flex;
+ break;
+ case 36:
+ op = cff_op_hflex1;
+ break;
+ case 37:
+ op = cff_op_flex1;
+ break;
+ default:
+ FT_TRACE4(( " unknown op (12, %d)\n", v ));
+ break;
+ }
+ break;
+ case 13:
+ op = cff_op_hsbw;
+ break;
+ case 14:
+ op = cff_op_endchar;
+ break;
+ case 16:
+ op = cff_op_blend;
+ break;
+ case 18:
+ op = cff_op_hstemhm;
+ break;
+ case 19:
+ op = cff_op_hintmask;
+ break;
+ case 20:
+ op = cff_op_cntrmask;
+ break;
+ case 21:
+ op = cff_op_rmoveto;
+ break;
+ case 22:
+ op = cff_op_hmoveto;
+ break;
+ case 23:
+ op = cff_op_vstemhm;
+ break;
+ case 24:
+ op = cff_op_rcurveline;
+ break;
+ case 25:
+ op = cff_op_rlinecurve;
+ break;
+ case 26:
+ op = cff_op_vvcurveto;
+ break;
+ case 27:
+ op = cff_op_hhcurveto;
+ break;
+ case 29:
+ op = cff_op_callgsubr;
+ break;
+ case 30:
+ op = cff_op_vhcurveto;
+ break;
+ case 31:
+ op = cff_op_hvcurveto;
+ break;
+ default:
+ FT_TRACE4(( " unknown op (%d)\n", v ));
+ break;
+ }
+
+ if ( op == cff_op_unknown )
+ continue;
+
+ /* in Multiple Master CFFs, T2 charstrings can appear in */
+ /* dictionaries, but some operators are prohibited */
+ if ( in_dict )
+ {
+ switch ( op )
+ {
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_vmoveto:
+ case cff_op_rlineto:
+ case cff_op_hlineto:
+ case cff_op_vlineto:
+ case cff_op_rrcurveto:
+ case cff_op_hstemhm:
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ case cff_op_rmoveto:
+ case cff_op_hmoveto:
+ case cff_op_vstemhm:
+ case cff_op_rcurveline:
+ case cff_op_rlinecurve:
+ case cff_op_vvcurveto:
+ case cff_op_hhcurveto:
+ case cff_op_vhcurveto:
+ case cff_op_hvcurveto:
+ case cff_op_hflex:
+ case cff_op_flex:
+ case cff_op_hflex1:
+ case cff_op_flex1:
+ case cff_op_callsubr:
+ case cff_op_callgsubr:
+ /* deprecated opcodes */
+ case cff_op_dotsection:
+ /* invalid Type 1 opcodes */
+ case cff_op_hsbw:
+ case cff_op_closepath:
+ case cff_op_callothersubr:
+ case cff_op_seac:
+ case cff_op_sbw:
+ case cff_op_setcurrentpoint:
+ goto MM_Error;
+
+ default:
+ break;
+ }
+ }
+
+ /* check arguments */
+ req_args = cff_argument_counts[op];
+ if ( req_args & CFF_COUNT_CHECK_WIDTH )
+ {
+ if ( num_args > 0 && decoder->read_width )
+ {
+ /* If `nominal_width' is non-zero, the number is really a */
+ /* difference against `nominal_width'. Else, the number here */
+ /* is truly a width, not a difference against `nominal_width'. */
+ /* If the font does not set `nominal_width', then */
+ /* `nominal_width' defaults to zero, and so we can set */
+ /* `glyph_width' to `nominal_width' plus number on the stack */
+ /* -- for either case. */
+
+ FT_Int set_width_ok;
+
+
+ switch ( op )
+ {
+ case cff_op_hmoveto:
+ case cff_op_vmoveto:
+ set_width_ok = num_args & 2;
+ break;
+
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_hstemhm:
+ case cff_op_vstemhm:
+ case cff_op_rmoveto:
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ set_width_ok = num_args & 1;
+ break;
+
+ case cff_op_endchar:
+ /* If there is a width specified for endchar, we either have */
+ /* 1 argument or 5 arguments. We like to argue. */
+ set_width_ok = in_dict
+ ? 0
+ : ( ( num_args == 5 ) || ( num_args == 1 ) );
+ break;
+
+ default:
+ set_width_ok = 0;
+ break;
+ }
+
+ if ( set_width_ok )
+ {
+ decoder->glyph_width = decoder->nominal_width +
+ ( stack[0] >> 16 );
+
+ if ( decoder->width_only )
+ {
+ /* we only want the advance width; stop here */
+ break;
+ }
+
+ /* Consumed an argument. */
+ num_args--;
+ }
+ }
+
+ decoder->read_width = 0;
+ req_args = 0;
+ }
+
+ req_args &= 0x000F;
+ if ( num_args < req_args )
+ goto Stack_Underflow;
+ args -= req_args;
+ num_args -= req_args;
+
+ /* At this point, `args' points to the first argument of the */
+ /* operand in case `req_args' isn't zero. Otherwise, we have */
+ /* to adjust `args' manually. */
+
+ /* Note that we only pop arguments from the stack which we */
+ /* really need and can digest so that we can continue in case */
+ /* of superfluous stack elements. */
+
+ switch ( op )
+ {
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_hstemhm:
+ case cff_op_vstemhm:
+ /* the number of arguments is always even here */
+ FT_TRACE4(( "%s\n",
+ op == cff_op_hstem ? " hstem" :
+ ( op == cff_op_vstem ? " vstem" :
+ ( op == cff_op_hstemhm ? " hstemhm" : " vstemhm" ) ) ));
+
+ if ( hinter )
+ hinter->stems( hinter->hints,
+ ( op == cff_op_hstem || op == cff_op_hstemhm ),
+ num_args / 2,
+ args - ( num_args & ~1 ) );
+
+ decoder->num_hints += num_args / 2;
+ args = stack;
+ break;
+
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ FT_TRACE4(( "%s", op == cff_op_hintmask ? " hintmask"
+ : " cntrmask" ));
+
+ /* implement vstem when needed -- */
+ /* the specification doesn't say it, but this also works */
+ /* with the 'cntrmask' operator */
+ /* */
+ if ( num_args > 0 )
+ {
+ if ( hinter )
+ hinter->stems( hinter->hints,
+ 0,
+ num_args / 2,
+ args - ( num_args & ~1 ) );
+
+ decoder->num_hints += num_args / 2;
+ }
+
+ /* In a valid charstring there must be at least one byte */
+ /* after `hintmask' or `cntrmask' (e.g., for a `return' */
+ /* instruction). Additionally, there must be space for */
+ /* `num_hints' bits. */
+
+ if ( ( ip + ( ( decoder->num_hints + 7 ) >> 3 ) ) >= limit )
+ goto Syntax_Error;
+
+ if ( hinter )
+ {
+ if ( op == cff_op_hintmask )
+ hinter->hintmask( hinter->hints,
+ (FT_UInt)builder->current->n_points,
+ (FT_UInt)decoder->num_hints,
+ ip );
+ else
+ hinter->counter( hinter->hints,
+ (FT_UInt)decoder->num_hints,
+ ip );
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_UInt maskbyte;
+
+
+ FT_TRACE4(( " (maskbytes:" ));
+
+ for ( maskbyte = 0;
+ maskbyte < (FT_UInt)( ( decoder->num_hints + 7 ) >> 3 );
+ maskbyte++, ip++ )
+ FT_TRACE4(( " 0x%02X", *ip ));
+
+ FT_TRACE4(( ")\n" ));
+ }
+#else
+ ip += ( decoder->num_hints + 7 ) >> 3;
+#endif
+ args = stack;
+ break;
+
+ case cff_op_rmoveto:
+ FT_TRACE4(( " rmoveto\n" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ x = ADD_LONG( x, args[-2] );
+ y = ADD_LONG( y, args[-1] );
+ args = stack;
+ break;
+
+ case cff_op_vmoveto:
+ FT_TRACE4(( " vmoveto\n" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ y = ADD_LONG( y, args[-1] );
+ args = stack;
+ break;
+
+ case cff_op_hmoveto:
+ FT_TRACE4(( " hmoveto\n" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ x = ADD_LONG( x, args[-1] );
+ args = stack;
+ break;
+
+ case cff_op_rlineto:
+ FT_TRACE4(( " rlineto\n" ));
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_args / 2 ) )
+ goto Fail;
+
+ if ( num_args < 2 )
+ goto Stack_Underflow;
+
+ args -= num_args & ~1;
+ while ( args < decoder->top )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 2;
+ }
+ args = stack;
+ break;
+
+ case cff_op_hlineto:
+ case cff_op_vlineto:
+ {
+ FT_Int phase = ( op == cff_op_hlineto );
+
+
+ FT_TRACE4(( "%s\n", op == cff_op_hlineto ? " hlineto"
+ : " vlineto" ));
+
+ if ( num_args < 0 )
+ goto Stack_Underflow;
+
+ /* there exist subsetted fonts (found in PDFs) */
+ /* which call `hlineto' without arguments */
+ if ( num_args == 0 )
+ break;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_args ) )
+ goto Fail;
+
+ args = stack;
+ while ( args < decoder->top )
+ {
+ if ( phase )
+ x = ADD_LONG( x, args[0] );
+ else
+ y = ADD_LONG( y, args[0] );
+
+ if ( cff_builder_add_point1( builder, x, y ) )
+ goto Fail;
+
+ args++;
+ phase ^= 1;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_rrcurveto:
+ {
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " rrcurveto\n" ));
+
+ if ( num_args < 6 )
+ goto Stack_Underflow;
+
+ nargs = num_args - num_args % 6;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, nargs / 2 ) )
+ goto Fail;
+
+ args -= nargs;
+ while ( args < decoder->top )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[4] );
+ y = ADD_LONG( y, args[5] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 6;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_vvcurveto:
+ {
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " vvcurveto\n" ));
+
+ if ( num_args < 4 )
+ goto Stack_Underflow;
+
+ /* if num_args isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+
+ nargs = num_args & ~2;
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args -= nargs;
+
+ if ( nargs & 1 )
+ {
+ x = ADD_LONG( x, args[0] );
+ args++;
+ nargs--;
+ }
+
+ if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) )
+ goto Fail;
+
+ while ( args < decoder->top )
+ {
+ y = ADD_LONG( y, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 4;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_hhcurveto:
+ {
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " hhcurveto\n" ));
+
+ if ( num_args < 4 )
+ goto Stack_Underflow;
+
+ /* if num_args isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+
+ nargs = num_args & ~2;
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args -= nargs;
+ if ( nargs & 1 )
+ {
+ y = ADD_LONG( y, args[0] );
+ args++;
+ nargs--;
+ }
+
+ if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) )
+ goto Fail;
+
+ while ( args < decoder->top )
+ {
+ x = ADD_LONG( x, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[3] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 4;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_vhcurveto:
+ case cff_op_hvcurveto:
+ {
+ FT_Int phase;
+ FT_Int nargs;
+
+
+ FT_TRACE4(( "%s\n", op == cff_op_vhcurveto ? " vhcurveto"
+ : " hvcurveto" ));
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ if ( num_args < 4 )
+ goto Stack_Underflow;
+
+ /* if num_args isn't of the form 8n, 8n+1, 8n+4, or 8n+5, */
+ /* we enforce it by clearing the second bit */
+
+ nargs = num_args & ~2;
+
+ args -= nargs;
+ if ( cff_check_points( builder, ( nargs / 4 ) * 3 ) )
+ goto Stack_Underflow;
+
+ phase = ( op == cff_op_hvcurveto );
+
+ while ( nargs >= 4 )
+ {
+ nargs -= 4;
+ if ( phase )
+ {
+ x = ADD_LONG( x, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ y = ADD_LONG( y, args[3] );
+ if ( nargs == 1 )
+ x = ADD_LONG( x, args[4] );
+ cff_builder_add_point( builder, x, y, 1 );
+ }
+ else
+ {
+ y = ADD_LONG( y, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[3] );
+ if ( nargs == 1 )
+ y = ADD_LONG( y, args[4] );
+ cff_builder_add_point( builder, x, y, 1 );
+ }
+ args += 4;
+ phase ^= 1;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_rlinecurve:
+ {
+ FT_Int num_lines;
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " rlinecurve\n" ));
+
+ if ( num_args < 8 )
+ goto Stack_Underflow;
+
+ nargs = num_args & ~1;
+ num_lines = ( nargs - 6 ) / 2;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_lines + 3 ) )
+ goto Fail;
+
+ args -= nargs;
+
+ /* first, add the line segments */
+ while ( num_lines > 0 )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 2;
+ num_lines--;
+ }
+
+ /* then the curve */
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[4] );
+ y = ADD_LONG( y, args[5] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_rcurveline:
+ {
+ FT_Int num_curves;
+ FT_Int nargs;
+
+
+ FT_TRACE4(( " rcurveline\n" ));
+
+ if ( num_args < 8 )
+ goto Stack_Underflow;
+
+ nargs = num_args - 2;
+ nargs = nargs - nargs % 6 + 2;
+ num_curves = ( nargs - 2 ) / 6;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, num_curves * 3 + 2 ) )
+ goto Fail;
+
+ args -= nargs;
+
+ /* first, add the curves */
+ while ( num_curves > 0 )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, args[4] );
+ y = ADD_LONG( y, args[5] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args += 6;
+ num_curves--;
+ }
+
+ /* then the final line */
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_hflex1:
+ {
+ FT_Pos start_y;
+
+
+ FT_TRACE4(( " hflex1\n" ));
+
+ /* adding five more points: 4 control points, 1 on-curve point */
+ /* -- make sure we have enough space for the start point if it */
+ /* needs to be added */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's y position for later use */
+ start_y = y;
+
+ /* first control point */
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* second control point */
+ x = ADD_LONG( x, args[2] );
+ y = ADD_LONG( y, args[3] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* join point; on curve, with y-value the same as the last */
+ /* control point's y-value */
+ x = ADD_LONG( x, args[4] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ /* third control point, with y-value the same as the join */
+ /* point's y-value */
+ x = ADD_LONG( x, args[5] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* fourth control point */
+ x = ADD_LONG( x, args[6] );
+ y = ADD_LONG( y, args[7] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* ending point, with y-value the same as the start */
+ x = ADD_LONG( x, args[8] );
+ y = start_y;
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_hflex:
+ {
+ FT_Pos start_y;
+
+
+ FT_TRACE4(( " hflex\n" ));
+
+ /* adding six more points; 4 control points, 2 on-curve points */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's y-position for later use */
+ start_y = y;
+
+ /* first control point */
+ x = ADD_LONG( x, args[0] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* second control point */
+ x = ADD_LONG( x, args[1] );
+ y = ADD_LONG( y, args[2] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* join point; on curve, with y-value the same as the last */
+ /* control point's y-value */
+ x = ADD_LONG( x, args[3] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ /* third control point, with y-value the same as the join */
+ /* point's y-value */
+ x = ADD_LONG( x, args[4] );
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* fourth control point */
+ x = ADD_LONG( x, args[5] );
+ y = start_y;
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* ending point, with y-value the same as the start point's */
+ /* y-value -- we don't add this point, though */
+ x = ADD_LONG( x, args[6] );
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_flex1:
+ {
+ FT_Pos start_x, start_y; /* record start x, y values for */
+ /* alter use */
+ FT_Fixed dx = 0, dy = 0; /* used in horizontal/vertical */
+ /* algorithm below */
+ FT_Int horizontal, count;
+ FT_Fixed* temp;
+
+
+ FT_TRACE4(( " flex1\n" ));
+
+ /* adding six more points; 4 control points, 2 on-curve points */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's x, y position for later use */
+ start_x = x;
+ start_y = y;
+
+ /* XXX: figure out whether this is supposed to be a horizontal */
+ /* or vertical flex; the Type 2 specification is vague... */
+
+ temp = args;
+
+ /* grab up to the last argument */
+ for ( count = 5; count > 0; count-- )
+ {
+ dx = ADD_LONG( dx, temp[0] );
+ dy = ADD_LONG( dy, temp[1] );
+ temp += 2;
+ }
+
+ if ( dx < 0 )
+ dx = NEG_LONG( dx );
+ if ( dy < 0 )
+ dy = NEG_LONG( dy );
+
+ /* strange test, but here it is... */
+ horizontal = ( dx > dy );
+
+ for ( count = 5; count > 0; count-- )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y,
+ FT_BOOL( count == 3 ) );
+ args += 2;
+ }
+
+ /* is last operand an x- or y-delta? */
+ if ( horizontal )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = start_y;
+ }
+ else
+ {
+ x = start_x;
+ y = ADD_LONG( y, args[0] );
+ }
+
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_flex:
+ {
+ FT_UInt count;
+
+
+ FT_TRACE4(( " flex\n" ));
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ cff_check_points( builder, 6 ) )
+ goto Fail;
+
+ for ( count = 6; count > 0; count-- )
+ {
+ x = ADD_LONG( x, args[0] );
+ y = ADD_LONG( y, args[1] );
+ cff_builder_add_point( builder, x, y,
+ FT_BOOL( count == 4 || count == 1 ) );
+ args += 2;
+ }
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_seac:
+ FT_TRACE4(( " seac\n" ));
+
+ error = cff_operator_seac( decoder,
+ args[0], args[1], args[2],
+ (FT_Int)( args[3] >> 16 ),
+ (FT_Int)( args[4] >> 16 ) );
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+
+ /* return now! */
+ FT_TRACE4(( "\n" ));
+ return error;
+
+ case cff_op_endchar:
+ /* in dictionaries, `endchar' simply indicates end of data */
+ if ( in_dict )
+ return error;
+
+ FT_TRACE4(( " endchar\n" ));
+
+ /* We are going to emulate the seac operator. */
+ if ( num_args >= 4 )
+ {
+ /* Save glyph width so that the subglyphs don't overwrite it. */
+ FT_Pos glyph_width = decoder->glyph_width;
+
+
+ error = cff_operator_seac( decoder,
+ 0L, args[-4], args[-3],
+ (FT_Int)( args[-2] >> 16 ),
+ (FT_Int)( args[-1] >> 16 ) );
+
+ decoder->glyph_width = glyph_width;
+ }
+ else
+ {
+ cff_builder_close_contour( builder );
+
+ /* close hints recording session */
+ if ( hinter )
+ {
+ if ( hinter->close( hinter->hints,
+ (FT_UInt)builder->current->n_points ) )
+ goto Syntax_Error;
+
+ /* apply hints to the loaded glyph outline now */
+ error = hinter->apply( hinter->hints,
+ builder->current,
+ (PSH_Globals)builder->hints_globals,
+ decoder->hint_mode );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+ }
+
+ /* return now! */
+ FT_TRACE4(( "\n" ));
+ return error;
+
+ case cff_op_abs:
+ FT_TRACE4(( " abs\n" ));
+
+ if ( args[0] < 0 )
+ {
+ if ( args[0] == FT_LONG_MIN )
+ args[0] = FT_LONG_MAX;
+ else
+ args[0] = -args[0];
+ }
+ args++;
+ break;
+
+ case cff_op_add:
+ FT_TRACE4(( " add\n" ));
+
+ args[0] = ADD_LONG( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_sub:
+ FT_TRACE4(( " sub\n" ));
+
+ args[0] = SUB_LONG( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_div:
+ FT_TRACE4(( " div\n" ));
+
+ args[0] = FT_DivFix( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_neg:
+ FT_TRACE4(( " neg\n" ));
+
+ if ( args[0] == FT_LONG_MIN )
+ args[0] = FT_LONG_MAX;
+ args[0] = -args[0];
+ args++;
+ break;
+
+ case cff_op_random:
+ {
+ FT_UInt32* randval = in_dict ? &decoder->cff->top_font.random
+ : &decoder->current_subfont->random;
+
+
+ FT_TRACE4(( " random\n" ));
+
+ /* only use the lower 16 bits of `random' */
+ /* to generate a number in the range (0;1] */
+ args[0] = (FT_Fixed)( ( *randval & 0xFFFF ) + 1 );
+ args++;
+
+ *randval = cff_random( *randval );
+ }
+ break;
+
+ case cff_op_mul:
+ FT_TRACE4(( " mul\n" ));
+
+ args[0] = FT_MulFix( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_sqrt:
+ FT_TRACE4(( " sqrt\n" ));
+
+ /* without upper limit the loop below might not finish */
+ if ( args[0] > 0x7FFFFFFFL )
+ args[0] = 46341;
+ else if ( args[0] > 0 )
+ {
+ FT_Fixed root = args[0];
+ FT_Fixed new_root;
+
+
+ for (;;)
+ {
+ new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1;
+ if ( new_root == root )
+ break;
+ root = new_root;
+ }
+ args[0] = new_root;
+ }
+ else
+ args[0] = 0;
+ args++;
+ break;
+
+ case cff_op_drop:
+ /* nothing */
+ FT_TRACE4(( " drop\n" ));
+
+ break;
+
+ case cff_op_exch:
+ {
+ FT_Fixed tmp;
+
+
+ FT_TRACE4(( " exch\n" ));
+
+ tmp = args[0];
+ args[0] = args[1];
+ args[1] = tmp;
+ args += 2;
+ }
+ break;
+
+ case cff_op_index:
+ {
+ FT_Int idx = (FT_Int)( args[0] >> 16 );
+
+
+ FT_TRACE4(( " index\n" ));
+
+ if ( idx < 0 )
+ idx = 0;
+ else if ( idx > num_args - 2 )
+ idx = num_args - 2;
+ args[0] = args[-( idx + 1 )];
+ args++;
+ }
+ break;
+
+ case cff_op_roll:
+ {
+ FT_Int count = (FT_Int)( args[0] >> 16 );
+ FT_Int idx = (FT_Int)( args[1] >> 16 );
+
+
+ FT_TRACE4(( " roll\n" ));
+
+ if ( count <= 0 )
+ count = 1;
+
+ args -= count;
+ if ( args < stack )
+ goto Stack_Underflow;
+
+ if ( idx >= 0 )
+ {
+ idx = idx % count;
+ while ( idx > 0 )
+ {
+ FT_Fixed tmp = args[count - 1];
+ FT_Int i;
+
+
+ for ( i = count - 2; i >= 0; i-- )
+ args[i + 1] = args[i];
+ args[0] = tmp;
+ idx--;
+ }
+ }
+ else
+ {
+ /* before C99 it is implementation-defined whether */
+ /* the result of `%' is negative if the first operand */
+ /* is negative */
+ idx = -( NEG_INT( idx ) % count );
+ while ( idx < 0 )
+ {
+ FT_Fixed tmp = args[0];
+ FT_Int i;
+
+
+ for ( i = 0; i < count - 1; i++ )
+ args[i] = args[i + 1];
+ args[count - 1] = tmp;
+ idx++;
+ }
+ }
+ args += count;
+ }
+ break;
+
+ case cff_op_dup:
+ FT_TRACE4(( " dup\n" ));
+
+ args[1] = args[0];
+ args += 2;
+ break;
+
+ case cff_op_put:
+ {
+ FT_Fixed val = args[0];
+ FT_UInt idx = (FT_UInt)( args[1] >> 16 );
+
+
+ FT_TRACE4(( " put\n" ));
+
+ /* the Type2 specification before version 16-March-2000 */
+ /* didn't give a hard-coded size limit of the temporary */
+ /* storage array; instead, an argument of the */
+ /* `MultipleMaster' operator set the size */
+ if ( idx < CFF_MAX_TRANS_ELEMENTS )
+ decoder->buildchar[idx] = val;
+ }
+ break;
+
+ case cff_op_get:
+ {
+ FT_UInt idx = (FT_UInt)( args[0] >> 16 );
+ FT_Fixed val = 0;
+
+
+ FT_TRACE4(( " get\n" ));
+
+ if ( idx < CFF_MAX_TRANS_ELEMENTS )
+ val = decoder->buildchar[idx];
+
+ args[0] = val;
+ args++;
+ }
+ break;
+
+ case cff_op_store:
+ /* this operator was removed from the Type2 specification */
+ /* in version 16-March-2000 */
+
+ /* since we currently don't handle interpolation of multiple */
+ /* master fonts, this is a no-op */
+ FT_TRACE4(( " store\n" ));
+ break;
+
+ case cff_op_load:
+ /* this operator was removed from the Type2 specification */
+ /* in version 16-March-2000 */
+ {
+ FT_UInt reg_idx = (FT_UInt)args[0];
+ FT_UInt idx = (FT_UInt)args[1];
+ FT_UInt count = (FT_UInt)args[2];
+
+
+ FT_TRACE4(( " load\n" ));
+
+ /* since we currently don't handle interpolation of multiple */
+ /* master fonts, we store a vector [1 0 0 ...] in the */
+ /* temporary storage array regardless of the Registry index */
+ if ( reg_idx <= 2 &&
+ idx < CFF_MAX_TRANS_ELEMENTS &&
+ count <= num_axes )
+ {
+ FT_UInt end, i;
+
+
+ end = FT_MIN( idx + count, CFF_MAX_TRANS_ELEMENTS );
+
+ if ( idx < end )
+ decoder->buildchar[idx] = 1 << 16;
+
+ for ( i = idx + 1; i < end; i++ )
+ decoder->buildchar[i] = 0;
+ }
+ }
+ break;
+
+ case cff_op_blend:
+ /* this operator was removed from the Type2 specification */
+ /* in version 16-March-2000 */
+ if ( num_designs )
+ {
+ FT_Int num_results = (FT_Int)( args[0] >> 16 );
+
+
+ FT_TRACE4(( " blend\n" ));
+
+ if ( num_results < 0 )
+ goto Syntax_Error;
+
+ if ( num_results > num_args ||
+ num_results * (FT_Int)num_designs > num_args )
+ goto Stack_Underflow;
+
+ /* since we currently don't handle interpolation of multiple */
+ /* master fonts, return the `num_results' values of the */
+ /* first master */
+ args -= num_results * ( num_designs - 1 );
+ num_args -= num_results * ( num_designs - 1 );
+ }
+ else
+ goto Syntax_Error;
+ break;
+
+ case cff_op_dotsection:
+ /* this operator is deprecated and ignored by the parser */
+ FT_TRACE4(( " dotsection\n" ));
+ break;
+
+ case cff_op_closepath:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " closepath (invalid op)\n" ));
+
+ args = stack;
+ break;
+
+ case cff_op_hsbw:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " hsbw (invalid op)\n" ));
+
+ decoder->glyph_width =
+ ADD_LONG( decoder->nominal_width, ( args[1] >> 16 ) );
+
+ decoder->builder.left_bearing.x = args[0];
+ decoder->builder.left_bearing.y = 0;
+
+ x = ADD_LONG( decoder->builder.pos_x, args[0] );
+ y = decoder->builder.pos_y;
+ args = stack;
+ break;
+
+ case cff_op_sbw:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " sbw (invalid op)\n" ));
+
+ decoder->glyph_width =
+ ADD_LONG( decoder->nominal_width, ( args[2] >> 16 ) );
+
+ decoder->builder.left_bearing.x = args[0];
+ decoder->builder.left_bearing.y = args[1];
+
+ x = ADD_LONG( decoder->builder.pos_x, args[0] );
+ y = ADD_LONG( decoder->builder.pos_y, args[1] );
+ args = stack;
+ break;
+
+ case cff_op_setcurrentpoint:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " setcurrentpoint (invalid op)\n" ));
+
+ x = ADD_LONG( decoder->builder.pos_x, args[0] );
+ y = ADD_LONG( decoder->builder.pos_y, args[1] );
+ args = stack;
+ break;
+
+ case cff_op_callothersubr:
+ {
+ FT_Fixed arg;
+
+
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from */
+ /* probably Type 1 to CFF, and some parsers seem to accept */
+ /* it */
+
+ FT_TRACE4(( " callothersubr (invalid op)\n" ));
+
+ /* subsequent `pop' operands should add the arguments, */
+ /* this is the implementation described for `unknown' */
+ /* other subroutines in the Type1 spec. */
+ /* */
+ /* XXX Fix return arguments (see discussion below). */
+
+ arg = 2 + ( args[-2] >> 16 );
+ if ( arg >= CFF_MAX_OPERANDS )
+ goto Stack_Underflow;
+
+ args -= arg;
+ if ( args < stack )
+ goto Stack_Underflow;
+ }
+ break;
+
+ case cff_op_pop:
+ /* this is an invalid Type 2 operator; however, there */
+ /* exist fonts which are incorrectly converted from probably */
+ /* Type 1 to CFF, and some parsers seem to accept it */
+
+ FT_TRACE4(( " pop (invalid op)\n" ));
+
+ /* XXX Increasing `args' is wrong: After a certain number of */
+ /* `pop's we get a stack overflow. Reason for doing it is */
+ /* code like this (actually found in a CFF font): */
+ /* */
+ /* 17 1 3 callothersubr */
+ /* pop */
+ /* callsubr */
+ /* */
+ /* Since we handle `callothersubr' as a no-op, and */
+ /* `callsubr' needs at least one argument, `pop' can't be a */
+ /* no-op too as it basically should be. */
+ /* */
+ /* The right solution would be to provide real support for */
+ /* `callothersubr' as done in `t1decode.c', however, given */
+ /* the fact that CFF fonts with `pop' are invalid, it is */
+ /* questionable whether it is worth the time. */
+ args++;
+ break;
+
+ case cff_op_and:
+ {
+ FT_Fixed cond = ( args[0] && args[1] );
+
+
+ FT_TRACE4(( " and\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_or:
+ {
+ FT_Fixed cond = ( args[0] || args[1] );
+
+
+ FT_TRACE4(( " or\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_not:
+ {
+ FT_Fixed cond = !args[0];
+
+
+ FT_TRACE4(( " not\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_eq:
+ {
+ FT_Fixed cond = ( args[0] == args[1] );
+
+
+ FT_TRACE4(( " eq\n" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_ifelse:
+ {
+ FT_Fixed cond = ( args[2] <= args[3] );
+
+
+ FT_TRACE4(( " ifelse\n" ));
+
+ if ( !cond )
+ args[0] = args[1];
+ args++;
+ }
+ break;
+
+ case cff_op_callsubr:
+ {
+ FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
+ decoder->locals_bias );
+
+
+ FT_TRACE4(( " callsubr (idx %d, entering level %ld)\n",
+ idx,
+ zone - decoder->zones + 1 ));
+
+ if ( idx >= decoder->num_locals )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invalid local subr index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+ zone->base = decoder->locals[idx];
+ zone->limit = decoder->locals[idx + 1];
+ zone->cursor = zone->base;
+
+ if ( !zone->base || zone->limit == zone->base )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invoking empty subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ }
+ break;
+
+ case cff_op_callgsubr:
+ {
+ FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
+ decoder->globals_bias );
+
+
+ FT_TRACE4(( " callgsubr (idx %d, entering level %ld)\n",
+ idx,
+ zone - decoder->zones + 1 ));
+
+ if ( idx >= decoder->num_globals )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invalid global subr index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+ zone->base = decoder->globals[idx];
+ zone->limit = decoder->globals[idx + 1];
+ zone->cursor = zone->base;
+
+ if ( !zone->base || zone->limit == zone->base )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invoking empty subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ }
+ break;
+
+ case cff_op_return:
+ FT_TRACE4(( " return (leaving level %ld)\n",
+ decoder->zone - decoder->zones ));
+
+ if ( decoder->zone <= decoder->zones )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " unexpected return\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone--;
+ zone = decoder->zone;
+ ip = zone->cursor;
+ limit = zone->limit;
+ break;
+
+ default:
+ FT_ERROR(( "Unimplemented opcode: %d", ip[-1] ));
+
+ if ( ip[-1] == 12 )
+ FT_ERROR(( " %d", ip[0] ));
+ FT_ERROR(( "\n" ));
+
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ decoder->top = args;
+
+ if ( decoder->top - stack >= CFF_MAX_OPERANDS )
+ goto Stack_Overflow;
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n" ));
+ FT_TRACE4(( "\n" ));
+
+ Fail:
+ return error;
+
+ MM_Error:
+ FT_TRACE4(( "cff_decoder_parse_charstrings:"
+ " invalid opcode found in top DICT charstring\n"));
+ return FT_THROW( Invalid_File_Format );
+
+ Syntax_Error:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error\n" ));
+ return FT_THROW( Invalid_File_Format );
+
+ Stack_Underflow:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: stack underflow\n" ));
+ return FT_THROW( Too_Few_Arguments );
+
+ Stack_Overflow:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: stack overflow\n" ));
+ return FT_THROW( Stack_Overflow );
+ }
+
+#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * cff_decoder_init
+ *
+ * @Description:
+ * Initializes a given glyph decoder.
+ *
+ * @InOut:
+ * decoder ::
+ * A pointer to the glyph builder to initialize.
+ *
+ * @Input:
+ * face ::
+ * The current face object.
+ *
+ * size ::
+ * The current size object.
+ *
+ * slot ::
+ * The current glyph object.
+ *
+ * hinting ::
+ * Whether hinting is active.
+ *
+ * hint_mode ::
+ * The hinting mode.
+ */
+ FT_LOCAL_DEF( void )
+ cff_decoder_init( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ CFF_Decoder_Get_Glyph_Callback get_callback,
+ CFF_Decoder_Free_Glyph_Callback free_callback )
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+
+
+ /* clear everything */
+ FT_ZERO( decoder );
+
+ /* initialize builder */
+ cff_builder_init( &decoder->builder, face, size, slot, hinting );
+
+ /* initialize Type2 decoder */
+ decoder->cff = cff;
+ decoder->num_globals = cff->global_subrs_index.count;
+ decoder->globals = cff->global_subrs;
+ decoder->globals_bias = cff_compute_bias(
+ cff->top_font.font_dict.charstring_type,
+ decoder->num_globals );
+
+ decoder->hint_mode = hint_mode;
+
+ decoder->get_glyph_callback = get_callback;
+ decoder->free_glyph_callback = free_callback;
+ }
+
+
+ /* this function is used to select the subfont */
+ /* and the locals subrs array */
+ FT_LOCAL_DEF( FT_Error )
+ cff_decoder_prepare( CFF_Decoder* decoder,
+ CFF_Size size,
+ FT_UInt glyph_index )
+ {
+ CFF_Builder *builder = &decoder->builder;
+ CFF_Font cff = (CFF_Font)builder->face->extra.data;
+ CFF_SubFont sub = &cff->top_font;
+ FT_Error error = FT_Err_Ok;
+
+ FT_Service_CFFLoad cffload = (FT_Service_CFFLoad)cff->cffload;
+
+
+ /* manage CID fonts */
+ if ( cff->num_subfonts )
+ {
+ FT_Byte fd_index = cffload->fd_select_get( &cff->fd_select,
+ glyph_index );
+
+
+ if ( fd_index >= cff->num_subfonts )
+ {
+ FT_TRACE4(( "cff_decoder_prepare: invalid CID subfont index\n" ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ FT_TRACE3(( " in subfont %d:\n", fd_index ));
+
+ sub = cff->subfonts[fd_index];
+
+ if ( builder->hints_funcs && size )
+ {
+ FT_Size ftsize = FT_SIZE( size );
+ CFF_Internal internal = (CFF_Internal)ftsize->internal->module_data;
+
+
+ /* for CFFs without subfonts, this value has already been set */
+ builder->hints_globals = (void *)internal->subfonts[fd_index];
+ }
+ }
+
+ decoder->num_locals = sub->local_subrs_index.count;
+ decoder->locals = sub->local_subrs;
+ decoder->locals_bias = cff_compute_bias(
+ decoder->cff->top_font.font_dict.charstring_type,
+ decoder->num_locals );
+
+ decoder->glyph_width = sub->private_dict.default_width;
+ decoder->nominal_width = sub->private_dict.nominal_width;
+
+ decoder->current_subfont = sub;
+
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/cffdecode.h b/modules/freetype2/src/psaux/cffdecode.h
new file mode 100644
index 0000000000..e8bb4001cb
--- /dev/null
+++ b/modules/freetype2/src/psaux/cffdecode.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+ *
+ * cffdecode.h
+ *
+ * PostScript CFF (Type 2) decoding routines (specification).
+ *
+ * Copyright (C) 2017-2023 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.
+ *
+ */
+
+
+#ifndef CFFDECODE_H_
+#define CFFDECODE_H_
+
+
+#include <freetype/internal/psaux.h>
+
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( void )
+ cff_decoder_init( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ CFF_Decoder_Get_Glyph_Callback get_callback,
+ CFF_Decoder_Free_Glyph_Callback free_callback );
+
+ FT_LOCAL( FT_Error )
+ cff_decoder_prepare( CFF_Decoder* decoder,
+ CFF_Size size,
+ FT_UInt glyph_index );
+
+
+ FT_LOCAL( FT_Int )
+ cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
+ FT_Int charcode );
+
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ FT_LOCAL( FT_Error )
+ cff_decoder_parse_charstrings( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len,
+ FT_Bool in_dict );
+#endif
+
+
+FT_END_HEADER
+
+#endif
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/module.mk b/modules/freetype2/src/psaux/module.mk
new file mode 100644
index 0000000000..c6fb4eb509
--- /dev/null
+++ b/modules/freetype2/src/psaux/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 PSaux module definition
+#
+
+
+# Copyright (C) 1996-2023 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.
+
+
+FTMODULE_H_COMMANDS += PSAUX_MODULE
+
+define PSAUX_MODULE
+$(OPEN_DRIVER) FT_Module_Class, psaux_module_class $(CLOSE_DRIVER)
+$(ECHO_DRIVER)psaux $(ECHO_DRIVER_DESC)Postscript Type 1 & Type 2 helper module$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/modules/freetype2/src/psaux/psarrst.c b/modules/freetype2/src/psaux/psarrst.c
new file mode 100644
index 0000000000..70313d283a
--- /dev/null
+++ b/modules/freetype2/src/psaux/psarrst.c
@@ -0,0 +1,240 @@
+/****************************************************************************
+ *
+ * psarrst.c
+ *
+ * Adobe's code for Array Stacks (body).
+ *
+ * Copyright 2007-2013 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 <freetype/internal/ftdebug.h>
+
+#include "psglue.h"
+#include "psarrst.h"
+
+#include "pserror.h"
+
+
+ /*
+ * CF2_ArrStack uses an error pointer, to enable shared errors.
+ * Shared errors are necessary when multiple objects allow the program
+ * to continue after detecting errors. Only the first error should be
+ * recorded.
+ */
+
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_init( CF2_ArrStack arrstack,
+ FT_Memory memory,
+ FT_Error* error,
+ size_t sizeItem )
+ {
+ FT_ASSERT( arrstack );
+
+ /* initialize the structure */
+ arrstack->memory = memory;
+ arrstack->error = error;
+ arrstack->sizeItem = sizeItem;
+ arrstack->allocated = 0;
+ arrstack->count = 0;
+ arrstack->totalSize = 0;
+ arrstack->ptr = NULL;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_finalize( CF2_ArrStack arrstack )
+ {
+ FT_Memory memory = arrstack->memory; /* for FT_FREE */
+
+
+ FT_ASSERT( arrstack );
+
+ arrstack->allocated = 0;
+ arrstack->count = 0;
+ arrstack->totalSize = 0;
+
+ /* free the data buffer */
+ FT_FREE( arrstack->ptr );
+ }
+
+
+ /* allocate or reallocate the buffer size; */
+ /* return false on memory error */
+ static FT_Bool
+ cf2_arrstack_setNumElements( CF2_ArrStack arrstack,
+ size_t numElements )
+ {
+ FT_ASSERT( arrstack );
+
+ {
+ FT_Error error = FT_Err_Ok; /* for FT_REALLOC */
+ FT_Memory memory = arrstack->memory; /* for FT_REALLOC */
+
+ size_t newSize = numElements * arrstack->sizeItem;
+
+
+ if ( numElements > FT_LONG_MAX / arrstack->sizeItem )
+ goto exit;
+
+
+ FT_ASSERT( newSize > 0 ); /* avoid realloc with zero size */
+
+ if ( !FT_QREALLOC( arrstack->ptr, arrstack->totalSize, newSize ) )
+ {
+ arrstack->allocated = numElements;
+ arrstack->totalSize = newSize;
+
+ if ( arrstack->count > numElements )
+ {
+ /* we truncated the list! */
+ CF2_SET_ERROR( arrstack->error, Stack_Overflow );
+ arrstack->count = numElements;
+ return FALSE;
+ }
+
+ return TRUE; /* success */
+ }
+ }
+
+ exit:
+ /* if there's not already an error, store this one */
+ CF2_SET_ERROR( arrstack->error, Out_Of_Memory );
+
+ return FALSE;
+ }
+
+
+ /* set the count, ensuring allocation is sufficient */
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_setCount( CF2_ArrStack arrstack,
+ size_t numElements )
+ {
+ FT_ASSERT( arrstack );
+
+ if ( numElements > arrstack->allocated )
+ {
+ /* expand the allocation first */
+ if ( !cf2_arrstack_setNumElements( arrstack, numElements ) )
+ return;
+ }
+
+ arrstack->count = numElements;
+ }
+
+
+ /* clear the count */
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_clear( CF2_ArrStack arrstack )
+ {
+ FT_ASSERT( arrstack );
+
+ arrstack->count = 0;
+ }
+
+
+ /* current number of items */
+ FT_LOCAL_DEF( size_t )
+ cf2_arrstack_size( const CF2_ArrStack arrstack )
+ {
+ FT_ASSERT( arrstack );
+
+ return arrstack->count;
+ }
+
+
+ FT_LOCAL_DEF( void* )
+ cf2_arrstack_getBuffer( const CF2_ArrStack arrstack )
+ {
+ FT_ASSERT( arrstack );
+
+ return arrstack->ptr;
+ }
+
+
+ /* return pointer to the given element */
+ FT_LOCAL_DEF( void* )
+ cf2_arrstack_getPointer( const CF2_ArrStack arrstack,
+ size_t idx )
+ {
+ void* newPtr;
+
+
+ FT_ASSERT( arrstack );
+
+ if ( idx >= arrstack->count )
+ {
+ /* overflow */
+ CF2_SET_ERROR( arrstack->error, Stack_Overflow );
+ idx = 0; /* choose safe default */
+ }
+
+ newPtr = (FT_Byte*)arrstack->ptr + idx * arrstack->sizeItem;
+
+ return newPtr;
+ }
+
+
+ /* push (append) an element at the end of the list; */
+ /* return false on memory error */
+ /* TODO: should there be a length param for extra checking? */
+ FT_LOCAL_DEF( void )
+ cf2_arrstack_push( CF2_ArrStack arrstack,
+ const void* ptr )
+ {
+ FT_ASSERT( arrstack );
+
+ if ( arrstack->count == arrstack->allocated )
+ {
+ /* increase the buffer size */
+ if ( !cf2_arrstack_setNumElements(
+ arrstack, arrstack->allocated * 2 + 16 ) )
+ {
+ /* on error, ignore the push */
+ return;
+ }
+ }
+
+ FT_ASSERT( ptr );
+
+ {
+ size_t offset = arrstack->count * arrstack->sizeItem;
+ void* newPtr = (FT_Byte*)arrstack->ptr + offset;
+
+
+ FT_MEM_COPY( newPtr, ptr, arrstack->sizeItem );
+ arrstack->count += 1;
+ }
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psarrst.h b/modules/freetype2/src/psaux/psarrst.h
new file mode 100644
index 0000000000..31e5330cc3
--- /dev/null
+++ b/modules/freetype2/src/psaux/psarrst.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+ *
+ * psarrst.h
+ *
+ * Adobe's code for Array Stacks (specification).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSARRST_H_
+#define PSARRST_H_
+
+
+#include "pserror.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* need to define the struct here (not opaque) so it can be allocated by */
+ /* clients */
+ typedef struct CF2_ArrStackRec_
+ {
+ FT_Memory memory;
+ FT_Error* error;
+
+ size_t sizeItem; /* bytes per element */
+ size_t allocated; /* items allocated */
+ size_t count; /* number of elements allocated */
+ size_t totalSize; /* total bytes allocated */
+
+ void* ptr; /* ptr to data */
+
+ } CF2_ArrStackRec, *CF2_ArrStack;
+
+
+ FT_LOCAL( void )
+ cf2_arrstack_init( CF2_ArrStack arrstack,
+ FT_Memory memory,
+ FT_Error* error,
+ size_t sizeItem );
+ FT_LOCAL( void )
+ cf2_arrstack_finalize( CF2_ArrStack arrstack );
+
+ FT_LOCAL( void )
+ cf2_arrstack_setCount( CF2_ArrStack arrstack,
+ size_t numElements );
+ FT_LOCAL( void )
+ cf2_arrstack_clear( CF2_ArrStack arrstack );
+ FT_LOCAL( size_t )
+ cf2_arrstack_size( const CF2_ArrStack arrstack );
+
+ FT_LOCAL( void* )
+ cf2_arrstack_getBuffer( const CF2_ArrStack arrstack );
+ FT_LOCAL( void* )
+ cf2_arrstack_getPointer( const CF2_ArrStack arrstack,
+ size_t idx );
+
+ FT_LOCAL( void )
+ cf2_arrstack_push( CF2_ArrStack arrstack,
+ const void* ptr );
+
+
+FT_END_HEADER
+
+
+#endif /* PSARRST_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psaux.c b/modules/freetype2/src/psaux/psaux.c
new file mode 100644
index 0000000000..5879ed1635
--- /dev/null
+++ b/modules/freetype2/src/psaux/psaux.c
@@ -0,0 +1,40 @@
+/****************************************************************************
+ *
+ * psaux.c
+ *
+ * FreeType auxiliary PostScript driver component (body only).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include "afmparse.c"
+#include "psauxmod.c"
+#include "psconv.c"
+#include "psobjs.c"
+#include "t1cmap.c"
+#include "t1decode.c"
+#include "cffdecode.c"
+
+#include "psarrst.c"
+#include "psblues.c"
+#include "pserror.c"
+#include "psfont.c"
+#include "psft.c"
+#include "pshints.c"
+#include "psintrp.c"
+#include "psread.c"
+#include "psstack.c"
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psauxerr.h b/modules/freetype2/src/psaux/psauxerr.h
new file mode 100644
index 0000000000..895ffa48c2
--- /dev/null
+++ b/modules/freetype2/src/psaux/psauxerr.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+ *
+ * psauxerr.h
+ *
+ * PS auxiliary module error codes (specification only).
+ *
+ * Copyright (C) 2001-2023 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.
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * This file is used to define the PS auxiliary module error enumeration
+ * constants.
+ *
+ */
+
+#ifndef PSAUXERR_H_
+#define PSAUXERR_H_
+
+#include <freetype/ftmoderr.h>
+
+#undef FTERRORS_H_
+
+#undef FT_ERR_PREFIX
+#define FT_ERR_PREFIX PSaux_Err_
+#define FT_ERR_BASE FT_Mod_Err_PSaux
+
+#include <freetype/fterrors.h>
+
+#endif /* PSAUXERR_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psauxmod.c b/modules/freetype2/src/psaux/psauxmod.c
new file mode 100644
index 0000000000..45e35aa53c
--- /dev/null
+++ b/modules/freetype2/src/psaux/psauxmod.c
@@ -0,0 +1,190 @@
+/****************************************************************************
+ *
+ * psauxmod.c
+ *
+ * FreeType auxiliary PostScript module implementation (body).
+ *
+ * Copyright (C) 2000-2023 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 "psauxmod.h"
+#include "psobjs.h"
+#include "t1decode.h"
+#include "t1cmap.h"
+#include "psft.h"
+#include "cffdecode.h"
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+#include "afmparse.h"
+#endif
+
+
+ FT_CALLBACK_TABLE_DEF
+ const PS_Table_FuncsRec ps_table_funcs =
+ {
+ ps_table_new, /* init */
+ ps_table_done, /* done */
+ ps_table_add, /* add */
+ ps_table_release /* release */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const PS_Parser_FuncsRec ps_parser_funcs =
+ {
+ ps_parser_init, /* init */
+ ps_parser_done, /* done */
+
+ ps_parser_skip_spaces, /* skip_spaces */
+ ps_parser_skip_PS_token, /* skip_PS_token */
+
+ ps_parser_to_int, /* to_int */
+ ps_parser_to_fixed, /* to_fixed */
+ ps_parser_to_bytes, /* to_bytes */
+ ps_parser_to_coord_array, /* to_coord_array */
+ ps_parser_to_fixed_array, /* to_fixed_array */
+ ps_parser_to_token, /* to_token */
+ ps_parser_to_token_array, /* to_token_array */
+
+ ps_parser_load_field, /* load_field */
+ ps_parser_load_field_table /* load_field_table */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const PS_Builder_FuncsRec ps_builder_funcs =
+ {
+ ps_builder_init, /* init */
+ ps_builder_done /* done */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const T1_Builder_FuncsRec t1_builder_funcs =
+ {
+ t1_builder_init, /* init */
+ t1_builder_done, /* done */
+
+ t1_builder_check_points, /* check_points */
+ t1_builder_add_point, /* add_point */
+ t1_builder_add_point1, /* add_point1 */
+ t1_builder_add_contour, /* add_contour */
+ t1_builder_start_point, /* start_point */
+ t1_builder_close_contour /* close_contour */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const T1_Decoder_FuncsRec t1_decoder_funcs =
+ {
+ t1_decoder_init, /* init */
+ t1_decoder_done, /* done */
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ t1_decoder_parse_charstrings, /* parse_charstrings_old */
+#else
+ t1_decoder_parse_metrics, /* parse_metrics */
+#endif
+ cf2_decoder_parse_charstrings /* parse_charstrings */
+ };
+
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ FT_CALLBACK_TABLE_DEF
+ const AFM_Parser_FuncsRec afm_parser_funcs =
+ {
+ afm_parser_init, /* init */
+ afm_parser_done, /* done */
+ afm_parser_parse /* parse */
+ };
+#endif
+
+
+ FT_CALLBACK_TABLE_DEF
+ const T1_CMap_ClassesRec t1_cmap_classes =
+ {
+ &t1_cmap_standard_class_rec,
+ &t1_cmap_expert_class_rec,
+ &t1_cmap_custom_class_rec,
+ &t1_cmap_unicode_class_rec
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const CFF_Builder_FuncsRec cff_builder_funcs =
+ {
+ cff_builder_init, /* init */
+ cff_builder_done, /* done */
+
+ cff_check_points, /* check_points */
+ cff_builder_add_point, /* add_point */
+ cff_builder_add_point1, /* add_point1 */
+ cff_builder_add_contour, /* add_contour */
+ cff_builder_start_point, /* start_point */
+ cff_builder_close_contour /* close_contour */
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const CFF_Decoder_FuncsRec cff_decoder_funcs =
+ {
+ cff_decoder_init, /* init */
+ cff_decoder_prepare, /* prepare */
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ cff_decoder_parse_charstrings, /* parse_charstrings_old */
+#endif
+ cf2_decoder_parse_charstrings /* parse_charstrings */
+ };
+
+
+ static
+ const PSAux_Interface psaux_interface =
+ {
+ &ps_table_funcs,
+ &ps_parser_funcs,
+ &t1_builder_funcs,
+ &t1_decoder_funcs,
+ t1_decrypt,
+ cff_random,
+ ps_decoder_init,
+ t1_make_subfont,
+
+ (const T1_CMap_ClassesRec*) &t1_cmap_classes,
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ &afm_parser_funcs,
+#else
+ 0,
+#endif
+
+ &cff_decoder_funcs,
+ };
+
+
+ FT_DEFINE_MODULE(
+ psaux_module_class,
+
+ 0,
+ sizeof ( FT_ModuleRec ),
+ "psaux",
+ 0x20000L,
+ 0x20000L,
+
+ &psaux_interface, /* module-specific interface */
+
+ (FT_Module_Constructor)NULL, /* module_init */
+ (FT_Module_Destructor) NULL, /* module_done */
+ (FT_Module_Requester) NULL /* get_interface */
+ )
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psauxmod.h b/modules/freetype2/src/psaux/psauxmod.h
new file mode 100644
index 0000000000..94dbf48813
--- /dev/null
+++ b/modules/freetype2/src/psaux/psauxmod.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+ *
+ * psauxmod.h
+ *
+ * FreeType auxiliary PostScript module implementation (specification).
+ *
+ * Copyright (C) 2000-2023 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.
+ *
+ */
+
+
+#ifndef PSAUXMOD_H_
+#define PSAUXMOD_H_
+
+
+#include <freetype/ftmodapi.h>
+
+#include <freetype/internal/psaux.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_CALLBACK_TABLE
+ const CFF_Builder_FuncsRec cff_builder_funcs;
+
+ FT_CALLBACK_TABLE
+ const PS_Builder_FuncsRec ps_builder_funcs;
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ FT_CALLBACK_TABLE
+ const AFM_Parser_FuncsRec afm_parser_funcs;
+#endif
+
+ FT_CALLBACK_TABLE
+ const T1_CMap_ClassesRec t1_cmap_classes;
+
+ FT_CALLBACK_TABLE
+ const CFF_Decoder_FuncsRec cff_decoder_funcs;
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) psaux_driver_class;
+
+
+ FT_DECLARE_MODULE( psaux_module_class )
+
+
+FT_END_HEADER
+
+#endif /* PSAUXMOD_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psblues.c b/modules/freetype2/src/psaux/psblues.c
new file mode 100644
index 0000000000..f9c864fea9
--- /dev/null
+++ b/modules/freetype2/src/psaux/psblues.c
@@ -0,0 +1,583 @@
+/****************************************************************************
+ *
+ * psblues.c
+ *
+ * Adobe's code for handling Blue Zones (body).
+ *
+ * Copyright 2009-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 <freetype/internal/ftdebug.h>
+
+#include "psblues.h"
+#include "pshints.h"
+#include "psfont.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 cf2blues
+
+
+ /*
+ * For blue values, the FreeType parser produces an array of integers,
+ * while the Adobe CFF engine produces an array of fixed.
+ * Define a macro to convert FreeType to fixed.
+ */
+#define cf2_blueToFixed( x ) cf2_intToFixed( x )
+
+
+ FT_LOCAL_DEF( void )
+ cf2_blues_init( CF2_Blues blues,
+ CF2_Font font )
+ {
+ /* pointer to parsed font object */
+ PS_Decoder* decoder = font->decoder;
+
+ CF2_Fixed zoneHeight;
+ CF2_Fixed maxZoneHeight = 0;
+ CF2_Fixed csUnitsPerPixel;
+
+ size_t numBlueValues;
+ size_t numOtherBlues;
+ size_t numFamilyBlues;
+ size_t numFamilyOtherBlues;
+
+ FT_Pos* blueValues;
+ FT_Pos* otherBlues;
+ FT_Pos* familyBlues;
+ FT_Pos* familyOtherBlues;
+
+ size_t i;
+ CF2_Fixed emBoxBottom, emBoxTop;
+
+#if 0
+ CF2_Int unitsPerEm = font->unitsPerEm;
+
+
+ if ( unitsPerEm == 0 )
+ unitsPerEm = 1000;
+#endif
+
+ FT_ZERO( blues );
+ blues->scale = font->innerTransform.d;
+
+ cf2_getBlueMetrics( decoder,
+ &blues->blueScale,
+ &blues->blueShift,
+ &blues->blueFuzz );
+
+ cf2_getBlueValues( decoder, &numBlueValues, &blueValues );
+ cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues );
+ cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues );
+ cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues );
+
+ /*
+ * synthetic em box hint heuristic
+ *
+ * Apply this when ideographic dictionary (LanguageGroup 1) has no
+ * real alignment zones. Adobe tools generate dummy zones at -250 and
+ * 1100 for a 1000 unit em. Fonts with ICF-based alignment zones
+ * should not enable the heuristic. When the heuristic is enabled,
+ * the font's blue zones are ignored.
+ *
+ */
+
+ /* get em box from OS/2 typoAscender/Descender */
+ /* TODO: FreeType does not parse these metrics. Skip them for now. */
+#if 0
+ FCM_getHorizontalLineMetrics( &e,
+ font->font,
+ &ascender,
+ &descender,
+ &linegap );
+ if ( ascender - descender == unitsPerEm )
+ {
+ emBoxBottom = cf2_intToFixed( descender );
+ emBoxTop = cf2_intToFixed( ascender );
+ }
+ else
+#endif
+ {
+ emBoxBottom = CF2_ICF_Bottom;
+ emBoxTop = CF2_ICF_Top;
+ }
+
+ if ( cf2_getLanguageGroup( decoder ) == 1 &&
+ ( numBlueValues == 0 ||
+ ( numBlueValues == 4 &&
+ cf2_blueToFixed( blueValues[0] ) < emBoxBottom &&
+ cf2_blueToFixed( blueValues[1] ) < emBoxBottom &&
+ cf2_blueToFixed( blueValues[2] ) > emBoxTop &&
+ cf2_blueToFixed( blueValues[3] ) > emBoxTop ) ) )
+ {
+ /*
+ * Construct hint edges suitable for synthetic ghost hints at top
+ * and bottom of em box. +-CF2_MIN_COUNTER allows for unhinted
+ * features above or below the last hinted edge. This also gives a
+ * net 1 pixel boost to the height of ideographic glyphs.
+ *
+ * Note: Adjust synthetic hints outward by epsilon (0x.0001) to
+ * avoid interference. E.g., some fonts have real hints at
+ * 880 and -120.
+ */
+
+ blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON;
+ blues->emBoxBottomEdge.dsCoord = cf2_fixedRound(
+ FT_MulFix(
+ blues->emBoxBottomEdge.csCoord,
+ blues->scale ) ) -
+ CF2_MIN_COUNTER;
+ blues->emBoxBottomEdge.scale = blues->scale;
+ blues->emBoxBottomEdge.flags = CF2_GhostBottom |
+ CF2_Locked |
+ CF2_Synthetic;
+
+ blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON +
+ 2 * font->darkenY;
+ blues->emBoxTopEdge.dsCoord = cf2_fixedRound(
+ FT_MulFix(
+ blues->emBoxTopEdge.csCoord,
+ blues->scale ) ) +
+ CF2_MIN_COUNTER;
+ blues->emBoxTopEdge.scale = blues->scale;
+ blues->emBoxTopEdge.flags = CF2_GhostTop |
+ CF2_Locked |
+ CF2_Synthetic;
+
+ blues->doEmBoxHints = TRUE; /* enable the heuristic */
+
+ return;
+ }
+
+ /* copy `BlueValues' and `OtherBlues' to a combined array of top and */
+ /* bottom zones */
+ for ( i = 0; i < numBlueValues; i += 2 )
+ {
+ blues->zone[blues->count].csBottomEdge =
+ cf2_blueToFixed( blueValues[i] );
+ blues->zone[blues->count].csTopEdge =
+ cf2_blueToFixed( blueValues[i + 1] );
+
+ zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge,
+ blues->zone[blues->count].csBottomEdge );
+
+ if ( zoneHeight < 0 )
+ {
+ FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
+ continue; /* reject this zone */
+ }
+
+ if ( zoneHeight > maxZoneHeight )
+ {
+ /* take maximum before darkening adjustment */
+ /* so overshoot suppression point doesn't change */
+ maxZoneHeight = zoneHeight;
+ }
+
+ /* adjust both edges of top zone upward by twice darkening amount */
+ if ( i != 0 )
+ {
+ blues->zone[blues->count].csTopEdge += 2 * font->darkenY;
+ blues->zone[blues->count].csBottomEdge += 2 * font->darkenY;
+ }
+
+ /* first `BlueValue' is bottom zone; others are top */
+ if ( i == 0 )
+ {
+ blues->zone[blues->count].bottomZone =
+ TRUE;
+ blues->zone[blues->count].csFlatEdge =
+ blues->zone[blues->count].csTopEdge;
+ }
+ else
+ {
+ blues->zone[blues->count].bottomZone =
+ FALSE;
+ blues->zone[blues->count].csFlatEdge =
+ blues->zone[blues->count].csBottomEdge;
+ }
+
+ blues->count += 1;
+ }
+
+ for ( i = 0; i < numOtherBlues; i += 2 )
+ {
+ blues->zone[blues->count].csBottomEdge =
+ cf2_blueToFixed( otherBlues[i] );
+ blues->zone[blues->count].csTopEdge =
+ cf2_blueToFixed( otherBlues[i + 1] );
+
+ zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge,
+ blues->zone[blues->count].csBottomEdge );
+
+ if ( zoneHeight < 0 )
+ {
+ FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
+ continue; /* reject this zone */
+ }
+
+ if ( zoneHeight > maxZoneHeight )
+ {
+ /* take maximum before darkening adjustment */
+ /* so overshoot suppression point doesn't change */
+ maxZoneHeight = zoneHeight;
+ }
+
+ /* Note: bottom zones are not adjusted for darkening amount */
+
+ /* all OtherBlues are bottom zone */
+ blues->zone[blues->count].bottomZone =
+ TRUE;
+ blues->zone[blues->count].csFlatEdge =
+ blues->zone[blues->count].csTopEdge;
+
+ blues->count += 1;
+ }
+
+ /* Adjust for FamilyBlues */
+
+ /* Search for the nearest flat edge in `FamilyBlues' or */
+ /* `FamilyOtherBlues'. According to the Black Book, any matching edge */
+ /* must be within one device pixel */
+
+ csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale );
+
+ /* loop on all zones in this font */
+ for ( i = 0; i < blues->count; i++ )
+ {
+ size_t j;
+ CF2_Fixed minDiff;
+ CF2_Fixed flatFamilyEdge, diff;
+ /* value for this font */
+ CF2_Fixed flatEdge = blues->zone[i].csFlatEdge;
+
+
+ if ( blues->zone[i].bottomZone )
+ {
+ /* In a bottom zone, the top edge is the flat edge. */
+ /* Search `FamilyOtherBlues' for bottom zones; look for closest */
+ /* Family edge that is within the one pixel threshold. */
+
+ minDiff = CF2_FIXED_MAX;
+
+ for ( j = 0; j < numFamilyOtherBlues; j += 2 )
+ {
+ /* top edge */
+ flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] );
+
+ diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
+
+ if ( diff < minDiff && diff < csUnitsPerPixel )
+ {
+ blues->zone[i].csFlatEdge = flatFamilyEdge;
+ minDiff = diff;
+
+ if ( diff == 0 )
+ break;
+ }
+ }
+
+ /* check the first member of FamilyBlues, which is a bottom zone */
+ if ( numFamilyBlues >= 2 )
+ {
+ /* top edge */
+ flatFamilyEdge = cf2_blueToFixed( familyBlues[1] );
+
+ diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
+
+ if ( diff < minDiff && diff < csUnitsPerPixel )
+ blues->zone[i].csFlatEdge = flatFamilyEdge;
+ }
+ }
+ else
+ {
+ /* In a top zone, the bottom edge is the flat edge. */
+ /* Search `FamilyBlues' for top zones; skip first zone, which is a */
+ /* bottom zone; look for closest Family edge that is within the */
+ /* one pixel threshold */
+
+ minDiff = CF2_FIXED_MAX;
+
+ for ( j = 2; j < numFamilyBlues; j += 2 )
+ {
+ /* bottom edge */
+ flatFamilyEdge = cf2_blueToFixed( familyBlues[j] );
+
+ /* adjust edges of top zone upward by twice darkening amount */
+ flatFamilyEdge += 2 * font->darkenY; /* bottom edge */
+
+ diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
+
+ if ( diff < minDiff && diff < csUnitsPerPixel )
+ {
+ blues->zone[i].csFlatEdge = flatFamilyEdge;
+ minDiff = diff;
+
+ if ( diff == 0 )
+ break;
+ }
+ }
+ }
+ }
+
+ /* TODO: enforce separation of zones, including BlueFuzz */
+
+ /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */
+ /* `bcsetup.c'. */
+
+ if ( maxZoneHeight > 0 )
+ {
+ if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ),
+ maxZoneHeight ) )
+ {
+ /* clamp at maximum scale */
+ blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ),
+ maxZoneHeight );
+ }
+
+ /*
+ * TODO: Revisit the bug fix for 613448. The minimum scale
+ * requirement catches a number of library fonts. For
+ * example, with default BlueScale (.039625) and 0.4 minimum,
+ * the test below catches any font with maxZoneHeight < 10.1.
+ * There are library fonts ranging from 2 to 10 that get
+ * caught, including e.g., Eurostile LT Std Medium with
+ * maxZoneHeight of 6.
+ *
+ */
+#if 0
+ if ( blueScale < .4 / maxZoneHeight )
+ {
+ tetraphilia_assert( 0 );
+ /* clamp at minimum scale, per bug 0613448 fix */
+ blueScale = .4 / maxZoneHeight;
+ }
+#endif
+
+ }
+
+ /*
+ * Suppress overshoot and boost blue zones at small sizes. Boost
+ * amount varies linearly from 0.5 pixel near 0 to 0 pixel at
+ * blueScale cutoff.
+ * Note: This boost amount is different from the coretype heuristic.
+ *
+ */
+
+ if ( blues->scale < blues->blueScale )
+ {
+ blues->suppressOvershoot = TRUE;
+
+ /* Change rounding threshold for `dsFlatEdge'. */
+ /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */
+ /* 10ppem Arial */
+
+ blues->boost = cf2_doubleToFixed( .6 ) -
+ FT_MulDiv( cf2_doubleToFixed ( .6 ),
+ blues->scale,
+ blues->blueScale );
+ if ( blues->boost > 0x7FFF )
+ {
+ /* boost must remain less than 0.5, or baseline could go negative */
+ blues->boost = 0x7FFF;
+ }
+ }
+
+ /* boost and darkening have similar effects; don't do both */
+ if ( font->stemDarkened )
+ blues->boost = 0;
+
+ /* set device space alignment for each zone; */
+ /* apply boost amount before rounding flat edge */
+
+ for ( i = 0; i < blues->count; i++ )
+ {
+ if ( blues->zone[i].bottomZone )
+ blues->zone[i].dsFlatEdge = cf2_fixedRound(
+ FT_MulFix(
+ blues->zone[i].csFlatEdge,
+ blues->scale ) -
+ blues->boost );
+ else
+ blues->zone[i].dsFlatEdge = cf2_fixedRound(
+ FT_MulFix(
+ blues->zone[i].csFlatEdge,
+ blues->scale ) +
+ blues->boost );
+ }
+ }
+
+
+ /*
+ * Check whether `stemHint' is captured by one of the blue zones.
+ *
+ * Zero, one or both edges may be valid; only valid edges can be
+ * captured. For compatibility with CoolType, search top and bottom
+ * zones in the same pass (see `BlueLock'). If a hint is captured,
+ * return true and position the edge(s) in one of 3 ways:
+ *
+ * 1) If `BlueScale' suppresses overshoot, position the captured edge
+ * at the flat edge of the zone.
+ * 2) If overshoot is not suppressed and `BlueShift' requires
+ * overshoot, position the captured edge a minimum of 1 device pixel
+ * from the flat edge.
+ * 3) If overshoot is not suppressed or required, position the captured
+ * edge at the nearest device pixel.
+ *
+ */
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_blues_capture( const CF2_Blues blues,
+ CF2_Hint bottomHintEdge,
+ CF2_Hint topHintEdge )
+ {
+ /* TODO: validate? */
+ CF2_Fixed csFuzz = blues->blueFuzz;
+
+ /* new position of captured edge */
+ CF2_Fixed dsNew;
+
+ /* amount that hint is moved when positioned */
+ CF2_Fixed dsMove = 0;
+
+ FT_Bool captured = FALSE;
+ CF2_UInt i;
+
+
+ /* assert edge flags are consistent */
+ FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) &&
+ !cf2_hint_isBottom( topHintEdge ) );
+
+ /* TODO: search once without blue fuzz for compatibility with coretype? */
+ for ( i = 0; i < blues->count; i++ )
+ {
+ if ( blues->zone[i].bottomZone &&
+ cf2_hint_isBottom( bottomHintEdge ) )
+ {
+ if ( SUB_INT32( blues->zone[i].csBottomEdge, csFuzz ) <=
+ bottomHintEdge->csCoord &&
+ bottomHintEdge->csCoord <=
+ ADD_INT32( blues->zone[i].csTopEdge, csFuzz ) )
+ {
+ /* bottom edge captured by bottom zone */
+
+ if ( blues->suppressOvershoot )
+ dsNew = blues->zone[i].dsFlatEdge;
+
+ else if ( SUB_INT32( blues->zone[i].csTopEdge,
+ bottomHintEdge->csCoord ) >=
+ blues->blueShift )
+ {
+ /* guarantee minimum of 1 pixel overshoot */
+ dsNew = FT_MIN(
+ cf2_fixedRound( bottomHintEdge->dsCoord ),
+ SUB_INT32( blues->zone[i].dsFlatEdge,
+ cf2_intToFixed( 1 ) ) );
+ }
+
+ else
+ {
+ /* simply round captured edge */
+ dsNew = cf2_fixedRound( bottomHintEdge->dsCoord );
+ }
+
+ dsMove = SUB_INT32( dsNew, bottomHintEdge->dsCoord );
+ captured = TRUE;
+
+ break;
+ }
+ }
+
+ if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) )
+ {
+ if ( SUB_INT32( blues->zone[i].csBottomEdge, csFuzz ) <=
+ topHintEdge->csCoord &&
+ topHintEdge->csCoord <=
+ ADD_INT32( blues->zone[i].csTopEdge, csFuzz ) )
+ {
+ /* top edge captured by top zone */
+
+ if ( blues->suppressOvershoot )
+ dsNew = blues->zone[i].dsFlatEdge;
+
+ else if ( SUB_INT32( topHintEdge->csCoord,
+ blues->zone[i].csBottomEdge ) >=
+ blues->blueShift )
+ {
+ /* guarantee minimum of 1 pixel overshoot */
+ dsNew = FT_MAX(
+ cf2_fixedRound( topHintEdge->dsCoord ),
+ blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) );
+ }
+
+ else
+ {
+ /* simply round captured edge */
+ dsNew = cf2_fixedRound( topHintEdge->dsCoord );
+ }
+
+ dsMove = SUB_INT32( dsNew, topHintEdge->dsCoord );
+ captured = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ if ( captured )
+ {
+ /* move both edges and flag them `locked' */
+ if ( cf2_hint_isValid( bottomHintEdge ) )
+ {
+ bottomHintEdge->dsCoord = ADD_INT32( bottomHintEdge->dsCoord,
+ dsMove );
+ cf2_hint_lock( bottomHintEdge );
+ }
+
+ if ( cf2_hint_isValid( topHintEdge ) )
+ {
+ topHintEdge->dsCoord = ADD_INT32( topHintEdge->dsCoord, dsMove );
+ cf2_hint_lock( topHintEdge );
+ }
+ }
+
+ return captured;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psblues.h b/modules/freetype2/src/psaux/psblues.h
new file mode 100644
index 0000000000..55fb88ecdd
--- /dev/null
+++ b/modules/freetype2/src/psaux/psblues.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+ *
+ * psblues.h
+ *
+ * Adobe's code for handling Blue Zones (specification).
+ *
+ * Copyright 2009-2013 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.
+ *
+ */
+
+
+ /*
+ * A `CF2_Blues' object stores the blue zones (horizontal alignment
+ * zones) of a font. These are specified in the CFF private dictionary
+ * by `BlueValues', `OtherBlues', `FamilyBlues', and `FamilyOtherBlues'.
+ * Each zone is defined by a top and bottom edge in character space.
+ * Further, each zone is either a top zone or a bottom zone, as recorded
+ * by `bottomZone'.
+ *
+ * The maximum number of `BlueValues' and `FamilyBlues' is 7 each.
+ * However, these are combined to produce a total of 7 zones.
+ * Similarly, the maximum number of `OtherBlues' and `FamilyOtherBlues'
+ * is 5 and these are combined to produce an additional 5 zones.
+ *
+ * Blue zones are used to `capture' hints and force them to a common
+ * alignment point. This alignment is recorded in device space in
+ * `dsFlatEdge'. Except for this value, a `CF2_Blues' object could be
+ * constructed independently of scaling. Construction may occur once
+ * the matrix is known. Other features implemented in the Capture
+ * method are overshoot suppression, overshoot enforcement, and Blue
+ * Boost.
+ *
+ * Capture is determined by `BlueValues' and `OtherBlues', but the
+ * alignment point may be adjusted to the scaled flat edge of
+ * `FamilyBlues' or `FamilyOtherBlues'. No alignment is done to the
+ * curved edge of a zone.
+ *
+ */
+
+
+#ifndef PSBLUES_H_
+#define PSBLUES_H_
+
+
+#include "psglue.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * `CF2_Hint' is shared by `cf2hints.h' and
+ * `cf2blues.h', but `cf2blues.h' depends on
+ * `cf2hints.h', so define it here. Note: The typedef is in
+ * `cf2glue.h'.
+ *
+ */
+ enum
+ {
+ CF2_GhostBottom = 0x1, /* a single bottom edge */
+ CF2_GhostTop = 0x2, /* a single top edge */
+ CF2_PairBottom = 0x4, /* the bottom edge of a stem hint */
+ CF2_PairTop = 0x8, /* the top edge of a stem hint */
+ CF2_Locked = 0x10, /* this edge has been aligned */
+ /* by a blue zone */
+ CF2_Synthetic = 0x20 /* this edge was synthesized */
+ };
+
+
+ /*
+ * Default value for OS/2 typoAscender/Descender when their difference
+ * is not equal to `unitsPerEm'. The default is based on -250 and 1100
+ * in `CF2_Blues', assuming 1000 units per em here.
+ *
+ */
+ enum
+ {
+ CF2_ICF_Top = cf2_intToFixed( 880 ),
+ CF2_ICF_Bottom = cf2_intToFixed( -120 )
+ };
+
+
+ /*
+ * Constant used for hint adjustment and for synthetic em box hint
+ * placement.
+ */
+#define CF2_MIN_COUNTER cf2_doubleToFixed( 0.5 )
+
+
+ /* shared typedef is in cf2glue.h */
+ struct CF2_HintRec_
+ {
+ CF2_UInt flags; /* attributes of the edge */
+ size_t index; /* index in original stem hint array */
+ /* (if not synthetic) */
+ CF2_Fixed csCoord;
+ CF2_Fixed dsCoord;
+ CF2_Fixed scale;
+ };
+
+
+ typedef struct CF2_BlueRec_
+ {
+ CF2_Fixed csBottomEdge;
+ CF2_Fixed csTopEdge;
+ CF2_Fixed csFlatEdge; /* may be from either local or Family zones */
+ CF2_Fixed dsFlatEdge; /* top edge of bottom zone or bottom edge */
+ /* of top zone (rounded) */
+ FT_Bool bottomZone;
+
+ } CF2_BlueRec;
+
+
+ /* max total blue zones is 12 */
+ enum
+ {
+ CF2_MAX_BLUES = 7,
+ CF2_MAX_OTHERBLUES = 5
+ };
+
+
+ typedef struct CF2_BluesRec_
+ {
+ CF2_Fixed scale;
+ CF2_UInt count;
+ FT_Bool suppressOvershoot;
+ FT_Bool doEmBoxHints;
+
+ CF2_Fixed blueScale;
+ CF2_Fixed blueShift;
+ CF2_Fixed blueFuzz;
+
+ CF2_Fixed boost;
+
+ CF2_HintRec emBoxTopEdge;
+ CF2_HintRec emBoxBottomEdge;
+
+ CF2_BlueRec zone[CF2_MAX_BLUES + CF2_MAX_OTHERBLUES];
+
+ } CF2_BluesRec, *CF2_Blues;
+
+
+ FT_LOCAL( void )
+ cf2_blues_init( CF2_Blues blues,
+ CF2_Font font );
+ FT_LOCAL( FT_Bool )
+ cf2_blues_capture( const CF2_Blues blues,
+ CF2_Hint bottomHintEdge,
+ CF2_Hint topHintEdge );
+
+
+FT_END_HEADER
+
+
+#endif /* PSBLUES_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psconv.c b/modules/freetype2/src/psaux/psconv.c
new file mode 100644
index 0000000000..b9c7138d84
--- /dev/null
+++ b/modules/freetype2/src/psaux/psconv.c
@@ -0,0 +1,610 @@
+/****************************************************************************
+ *
+ * psconv.c
+ *
+ * Some convenience conversions (body).
+ *
+ * Copyright (C) 2006-2023 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 <freetype/internal/psaux.h>
+#include <freetype/internal/ftdebug.h>
+
+#include "psconv.h"
+#include "psauxerr.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 psconv
+
+
+ /* The following array is used by various functions to quickly convert */
+ /* digits (both decimal and non-decimal) into numbers. */
+
+#if 'A' == 65
+ /* ASCII */
+
+ static const FT_Char ft_char_table[128] =
+ {
+ /* 0x00 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ };
+
+ /* no character >= 0x80 can represent a valid number */
+#define OP >=
+
+#endif /* 'A' == 65 */
+
+#if 'A' == 193
+ /* EBCDIC */
+
+ static const FT_Char ft_char_table[128] =
+ {
+ /* 0x80 */
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
+ -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
+ -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
+ -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
+ -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ };
+
+ /* no character < 0x80 can represent a valid number */
+#define OP <
+
+#endif /* 'A' == 193 */
+
+
+ FT_LOCAL_DEF( FT_Long )
+ PS_Conv_Strtol( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Long base )
+ {
+ FT_Byte* p = *cursor;
+
+ FT_Long num = 0;
+ FT_Bool sign = 0;
+ FT_Bool have_overflow = 0;
+
+ FT_Long num_limit;
+ FT_Char c_limit;
+
+
+ if ( p >= limit )
+ goto Bad;
+
+ if ( base < 2 || base > 36 )
+ {
+ FT_TRACE4(( "!!!INVALID BASE:!!!" ));
+ return 0;
+ }
+
+ if ( *p == '-' || *p == '+' )
+ {
+ sign = FT_BOOL( *p == '-' );
+
+ p++;
+ if ( p == limit )
+ goto Bad;
+
+ /* only a single sign is allowed */
+ if ( *p == '-' || *p == '+' )
+ return 0;
+ }
+
+ num_limit = 0x7FFFFFFFL / base;
+ c_limit = (FT_Char)( 0x7FFFFFFFL % base );
+
+ for ( ; p < limit; p++ )
+ {
+ FT_Char c;
+
+
+ if ( IS_PS_SPACE( *p ) || *p OP 0x80 )
+ break;
+
+ c = ft_char_table[*p & 0x7F];
+
+ if ( c < 0 || c >= base )
+ break;
+
+ if ( num > num_limit || ( num == num_limit && c > c_limit ) )
+ have_overflow = 1;
+ else
+ num = num * base + c;
+ }
+
+ *cursor = p;
+
+ if ( have_overflow )
+ {
+ num = 0x7FFFFFFFL;
+ FT_TRACE4(( "!!!OVERFLOW:!!!" ));
+ }
+
+ if ( sign )
+ num = -num;
+
+ return num;
+
+ Bad:
+ FT_TRACE4(( "!!!END OF DATA:!!!" ));
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Long )
+ PS_Conv_ToInt( FT_Byte** cursor,
+ FT_Byte* limit )
+
+ {
+ FT_Byte* p = *cursor;
+ FT_Byte* curp;
+
+ FT_Long num;
+
+
+ curp = p;
+ num = PS_Conv_Strtol( &p, limit, 10 );
+
+ if ( p == curp )
+ return 0;
+
+ if ( p < limit && *p == '#' )
+ {
+ p++;
+
+ curp = p;
+ num = PS_Conv_Strtol( &p, limit, num );
+
+ if ( p == curp )
+ return 0;
+ }
+
+ *cursor = p;
+
+ return num;
+ }
+
+
+ FT_LOCAL_DEF( FT_Fixed )
+ PS_Conv_ToFixed( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Long power_ten )
+ {
+ FT_Byte* p = *cursor;
+ FT_Byte* curp;
+
+ FT_Fixed integral = 0;
+ FT_Long decimal = 0;
+ FT_Long divider = 1;
+
+ FT_Bool sign = 0;
+ FT_Bool have_overflow = 0;
+ FT_Bool have_underflow = 0;
+
+
+ if ( p >= limit )
+ goto Bad;
+
+ if ( *p == '-' || *p == '+' )
+ {
+ sign = FT_BOOL( *p == '-' );
+
+ p++;
+ if ( p == limit )
+ goto Bad;
+
+ /* only a single sign is allowed */
+ if ( *p == '-' || *p == '+' )
+ return 0;
+ }
+
+ /* read the integer part */
+ if ( *p != '.' )
+ {
+ curp = p;
+ integral = PS_Conv_ToInt( &p, limit );
+
+ if ( p == curp )
+ return 0;
+
+ if ( integral > 0x7FFF )
+ have_overflow = 1;
+ else
+ integral = (FT_Fixed)( (FT_UInt32)integral << 16 );
+ }
+
+ /* read the decimal part */
+ if ( p < limit && *p == '.' )
+ {
+ p++;
+
+ for ( ; p < limit; p++ )
+ {
+ FT_Char c;
+
+
+ if ( IS_PS_SPACE( *p ) || *p OP 0x80 )
+ break;
+
+ c = ft_char_table[*p & 0x7F];
+
+ if ( c < 0 || c >= 10 )
+ break;
+
+ /* only add digit if we don't overflow */
+ if ( divider < 0xCCCCCCCL && decimal < 0xCCCCCCCL )
+ {
+ decimal = decimal * 10 + c;
+
+ if ( !integral && power_ten > 0 )
+ power_ten--;
+ else
+ divider *= 10;
+ }
+ }
+ }
+
+ /* read exponent, if any */
+ if ( p + 1 < limit && ( *p == 'e' || *p == 'E' ) )
+ {
+ FT_Long exponent;
+
+
+ p++;
+
+ curp = p;
+ exponent = PS_Conv_ToInt( &p, limit );
+
+ if ( curp == p )
+ return 0;
+
+ /* arbitrarily limit exponent */
+ if ( exponent > 1000 )
+ have_overflow = 1;
+ else if ( exponent < -1000 )
+ have_underflow = 1;
+ else
+ power_ten += exponent;
+ }
+
+ *cursor = p;
+
+ if ( !integral && !decimal )
+ return 0;
+
+ if ( have_overflow )
+ goto Overflow;
+ if ( have_underflow )
+ goto Underflow;
+
+ while ( power_ten > 0 )
+ {
+ if ( integral >= 0xCCCCCCCL )
+ goto Overflow;
+ integral *= 10;
+
+ if ( decimal >= 0xCCCCCCCL )
+ {
+ if ( divider == 1 )
+ goto Overflow;
+ divider /= 10;
+ }
+ else
+ decimal *= 10;
+
+ power_ten--;
+ }
+
+ while ( power_ten < 0 )
+ {
+ integral /= 10;
+ if ( divider < 0xCCCCCCCL )
+ divider *= 10;
+ else
+ decimal /= 10;
+
+ if ( !integral && !decimal )
+ goto Underflow;
+
+ power_ten++;
+ }
+
+ if ( decimal )
+ {
+ decimal = FT_DivFix( decimal, divider );
+ /* it's not necessary to check this addition for overflow */
+ /* due to the structure of the real number representation */
+ integral += decimal;
+ }
+
+ Exit:
+ if ( sign )
+ integral = -integral;
+
+ return integral;
+
+ Bad:
+ FT_TRACE4(( "!!!END OF DATA:!!!" ));
+ return 0;
+
+ Overflow:
+ integral = 0x7FFFFFFFL;
+ FT_TRACE4(( "!!!OVERFLOW:!!!" ));
+ goto Exit;
+
+ Underflow:
+ FT_TRACE4(( "!!!UNDERFLOW:!!!" ));
+ return 0;
+ }
+
+
+#if 0
+ FT_LOCAL_DEF( FT_UInt )
+ PS_Conv_StringDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_Offset n )
+ {
+ FT_Byte* p;
+ FT_UInt r = 0;
+
+
+ for ( p = *cursor; r < n && p < limit; p++ )
+ {
+ FT_Byte b;
+
+
+ if ( *p != '\\' )
+ {
+ buffer[r++] = *p;
+
+ continue;
+ }
+
+ p++;
+
+ switch ( *p )
+ {
+ case 'n':
+ b = '\n';
+ break;
+ case 'r':
+ b = '\r';
+ break;
+ case 't':
+ b = '\t';
+ break;
+ case 'b':
+ b = '\b';
+ break;
+ case 'f':
+ b = '\f';
+ break;
+ case '\r':
+ p++;
+ if ( *p != '\n' )
+ {
+ b = *p;
+
+ break;
+ }
+ /* no break */
+ case '\n':
+ continue;
+ break;
+ default:
+ if ( IS_PS_DIGIT( *p ) )
+ {
+ b = *p - '0';
+
+ p++;
+
+ if ( IS_PS_DIGIT( *p ) )
+ {
+ b = b * 8 + *p - '0';
+
+ p++;
+
+ if ( IS_PS_DIGIT( *p ) )
+ b = b * 8 + *p - '0';
+ else
+ {
+ buffer[r++] = b;
+ b = *p;
+ }
+ }
+ else
+ {
+ buffer[r++] = b;
+ b = *p;
+ }
+ }
+ else
+ b = *p;
+ break;
+ }
+
+ buffer[r++] = b;
+ }
+
+ *cursor = p;
+
+ return r;
+ }
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ PS_Conv_ASCIIHexDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_Offset n )
+ {
+ FT_Byte* p;
+ FT_UInt r = 0;
+ FT_UInt w = 0;
+ FT_UInt pad = 0x01;
+
+
+ n *= 2;
+
+#if 1
+
+ p = *cursor;
+
+ if ( p >= limit )
+ return 0;
+
+ if ( n > (FT_UInt)( limit - p ) )
+ n = (FT_UInt)( limit - p );
+
+ /* we try to process two nibbles at a time to be as fast as possible */
+ for ( ; r < n; r++ )
+ {
+ FT_UInt c = p[r];
+
+
+ if ( IS_PS_SPACE( c ) )
+ continue;
+
+ if ( c OP 0x80 )
+ break;
+
+ c = (FT_UInt)ft_char_table[c & 0x7F];
+ if ( c >= 16 )
+ break;
+
+ pad = ( pad << 4 ) | c;
+ if ( pad & 0x100 )
+ {
+ buffer[w++] = (FT_Byte)pad;
+ pad = 0x01;
+ }
+ }
+
+ if ( pad != 0x01 )
+ buffer[w++] = (FT_Byte)( pad << 4 );
+
+ *cursor = p + r;
+
+ return w;
+
+#else /* 0 */
+
+ for ( r = 0; r < n; r++ )
+ {
+ FT_Char c;
+
+
+ if ( IS_PS_SPACE( *p ) )
+ continue;
+
+ if ( *p OP 0x80 )
+ break;
+
+ c = ft_char_table[*p & 0x7F];
+
+ if ( (unsigned)c >= 16 )
+ break;
+
+ if ( r & 1 )
+ {
+ *buffer = (FT_Byte)( *buffer + c );
+ buffer++;
+ }
+ else
+ *buffer = (FT_Byte)( c << 4 );
+
+ r++;
+ }
+
+ *cursor = p;
+
+ return ( r + 1 ) / 2;
+
+#endif /* 0 */
+
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ PS_Conv_EexecDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_Offset n,
+ FT_UShort* seed )
+ {
+ FT_Byte* p;
+ FT_UInt r;
+ FT_UInt s = *seed;
+
+
+#if 1
+
+ p = *cursor;
+
+ if ( p >= limit )
+ return 0;
+
+ if ( n > (FT_UInt)( limit - p ) )
+ n = (FT_UInt)( limit - p );
+
+ for ( r = 0; r < n; r++ )
+ {
+ FT_UInt val = p[r];
+ FT_UInt b = ( val ^ ( s >> 8 ) );
+
+
+ s = ( (val + s)*52845U + 22719 ) & 0xFFFFU;
+ buffer[r] = (FT_Byte) b;
+ }
+
+ *cursor = p + n;
+ *seed = (FT_UShort)s;
+
+#else /* 0 */
+
+ for ( r = 0, p = *cursor; r < n && p < limit; r++, p++ )
+ {
+ FT_Byte b = (FT_Byte)( *p ^ ( s >> 8 ) );
+
+
+ s = (FT_UShort)( ( *p + s ) * 52845U + 22719 );
+ *buffer++ = b;
+ }
+ *cursor = p;
+ *seed = s;
+
+#endif /* 0 */
+
+ return r;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psconv.h b/modules/freetype2/src/psaux/psconv.h
new file mode 100644
index 0000000000..b7c3ee00be
--- /dev/null
+++ b/modules/freetype2/src/psaux/psconv.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ *
+ * psconv.h
+ *
+ * Some convenience conversions (specification).
+ *
+ * Copyright (C) 2006-2023 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.
+ *
+ */
+
+
+#ifndef PSCONV_H_
+#define PSCONV_H_
+
+
+#include <freetype/internal/psaux.h>
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Long )
+ PS_Conv_Strtol( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Long base );
+
+
+ FT_LOCAL( FT_Long )
+ PS_Conv_ToInt( FT_Byte** cursor,
+ FT_Byte* limit );
+
+ FT_LOCAL( FT_Fixed )
+ PS_Conv_ToFixed( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Long power_ten );
+
+#if 0
+ FT_LOCAL( FT_UInt )
+ PS_Conv_StringDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_Offset n );
+#endif
+
+ FT_LOCAL( FT_UInt )
+ PS_Conv_ASCIIHexDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_Offset n );
+
+ FT_LOCAL( FT_UInt )
+ PS_Conv_EexecDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_Offset n,
+ FT_UShort* seed );
+
+
+FT_END_HEADER
+
+#endif /* PSCONV_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/pserror.c b/modules/freetype2/src/psaux/pserror.c
new file mode 100644
index 0000000000..98cebcf74d
--- /dev/null
+++ b/modules/freetype2/src/psaux/pserror.c
@@ -0,0 +1,52 @@
+/****************************************************************************
+ *
+ * pserror.c
+ *
+ * Adobe's code for error handling (body).
+ *
+ * Copyright 2006-2013 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 "pserror.h"
+
+
+ FT_LOCAL_DEF( void )
+ cf2_setError( FT_Error* error,
+ FT_Error value )
+ {
+ if ( error && !*error )
+ *error = value;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/pserror.h b/modules/freetype2/src/psaux/pserror.h
new file mode 100644
index 0000000000..5738853fac
--- /dev/null
+++ b/modules/freetype2/src/psaux/pserror.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+ *
+ * pserror.h
+ *
+ * Adobe's code for error handling (specification).
+ *
+ * Copyright 2006-2013 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.
+ *
+ */
+
+
+#ifndef PSERROR_H_
+#define PSERROR_H_
+
+
+#include <freetype/ftmoderr.h>
+
+#undef FTERRORS_H_
+
+#undef FT_ERR_PREFIX
+#define FT_ERR_PREFIX CF2_Err_
+#define FT_ERR_BASE FT_Mod_Err_CF2
+
+
+#include <freetype/fterrors.h>
+#include <freetype/internal/compiler-macros.h>
+#include "psft.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * A poor-man error facility.
+ *
+ * This code being written in vanilla C, doesn't have the luxury of a
+ * language-supported exception mechanism such as the one available in
+ * Java. Instead, we are stuck with using error codes that must be
+ * carefully managed and preserved. However, it is convenient for us to
+ * model our error mechanism on a Java-like exception mechanism.
+ * When we assign an error code we are thus `throwing' an error.
+ *
+ * The preservation of an error code is done by coding convention.
+ * Upon a function call if the error code is anything other than
+ * `FT_Err_Ok', which is guaranteed to be zero, we
+ * will return without altering that error. This will allow the
+ * error to propagate and be handled at the appropriate location in
+ * the code.
+ *
+ * This allows a style of code where the error code is initialized
+ * up front and a block of calls are made with the error code only
+ * being checked after the block. If a new error occurs, the original
+ * error will be preserved and a functional no-op should result in any
+ * subsequent function that has an initial error code not equal to
+ * `FT_Err_Ok'.
+ *
+ * Errors are encoded by calling the `FT_THROW' macro. For example,
+ *
+ * {
+ * FT_Error e;
+ *
+ *
+ * ...
+ * e = FT_THROW( Out_Of_Memory );
+ * }
+ *
+ */
+
+
+ /* Set error code to a particular value. */
+ FT_LOCAL( void )
+ cf2_setError( FT_Error* error,
+ FT_Error value );
+
+
+ /*
+ * A macro that conditionally sets an error code.
+ *
+ * This macro will first check whether `error' is set;
+ * if not, it will set it to `e'.
+ *
+ */
+#define CF2_SET_ERROR( error, e ) \
+ cf2_setError( error, FT_THROW( e ) )
+
+
+FT_END_HEADER
+
+
+#endif /* PSERROR_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psfixed.h b/modules/freetype2/src/psaux/psfixed.h
new file mode 100644
index 0000000000..299d076370
--- /dev/null
+++ b/modules/freetype2/src/psaux/psfixed.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+ *
+ * psfixed.h
+ *
+ * Adobe's code for Fixed-Point Mathematics (specification only).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSFIXED_H_
+#define PSFIXED_H_
+
+
+FT_BEGIN_HEADER
+
+
+ /* rasterizer integer and fixed-point arithmetic must be 32-bit */
+
+#define CF2_Fixed CF2_F16Dot16
+ typedef FT_Int32 CF2_Frac; /* 2.30 fixed-point */
+
+
+#define CF2_FIXED_MAX ( (CF2_Fixed)0x7FFFFFFFL )
+#define CF2_FIXED_MIN ( (CF2_Fixed)0x80000000L )
+#define CF2_FIXED_ONE ( (CF2_Fixed)0x10000L )
+#define CF2_FIXED_EPSILON ( (CF2_Fixed)0x0001 )
+
+ /* in C 89, left and right shift of negative numbers is */
+ /* implementation specific behaviour in the general case */
+
+#define cf2_intToFixed( i ) \
+ ( (CF2_Fixed)( (FT_UInt32)(i) << 16 ) )
+#define cf2_fixedToInt( x ) \
+ ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+#define cf2_fixedRound( x ) \
+ ( (CF2_Fixed)( ( (FT_UInt32)(x) + 0x8000U ) & 0xFFFF0000UL ) )
+#define cf2_doubleToFixed( f ) \
+ ( (CF2_Fixed)( (f) * 65536.0 + 0.5 ) )
+#define cf2_fixedAbs( x ) \
+ ( (x) < 0 ? NEG_INT32( x ) : (x) )
+#define cf2_fixedFloor( x ) \
+ ( (CF2_Fixed)( (FT_UInt32)(x) & 0xFFFF0000UL ) )
+#define cf2_fixedFraction( x ) \
+ ( (x) - cf2_fixedFloor( x ) )
+#define cf2_fracToFixed( x ) \
+ ( ( (x) + 0x2000 - ( (x) < 0 ) ) >> 14 )
+
+
+ /* signed numeric types */
+ typedef enum CF2_NumberType_
+ {
+ CF2_NumberFixed, /* 16.16 */
+ CF2_NumberFrac, /* 2.30 */
+ CF2_NumberInt /* 32.0 */
+
+ } CF2_NumberType;
+
+
+FT_END_HEADER
+
+
+#endif /* PSFIXED_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psfont.c b/modules/freetype2/src/psaux/psfont.c
new file mode 100644
index 0000000000..0db1f0c5bc
--- /dev/null
+++ b/modules/freetype2/src/psaux/psfont.c
@@ -0,0 +1,566 @@
+/****************************************************************************
+ *
+ * psfont.c
+ *
+ * Adobe's code for font instances (body).
+ *
+ * Copyright 2007-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 <freetype/internal/ftcalc.h>
+
+#include "psft.h"
+
+#include "psglue.h"
+#include "psfont.h"
+#include "pserror.h"
+#include "psintrp.h"
+
+
+ /* Compute a stem darkening amount in character space. */
+ static void
+ cf2_computeDarkening( CF2_Fixed emRatio,
+ CF2_Fixed ppem,
+ CF2_Fixed stemWidth,
+ CF2_Fixed* darkenAmount,
+ CF2_Fixed boldenAmount,
+ FT_Bool stemDarkened,
+ FT_Int* darkenParams )
+ {
+ /*
+ * Total darkening amount is computed in 1000 unit character space
+ * using the modified 5 part curve as Adobe's Avalon rasterizer.
+ * The darkening amount is smaller for thicker stems.
+ * It becomes zero when the stem is thicker than 2.333 pixels.
+ *
+ * By default, we use
+ *
+ * darkenAmount = 0.4 pixels if scaledStem <= 0.5 pixels,
+ * darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels,
+ * darkenAmount = 0 pixel if scaledStem >= 2.333 pixels,
+ *
+ * and piecewise linear in-between:
+ *
+ *
+ * darkening
+ * ^
+ * |
+ * | (x1,y1)
+ * |--------+
+ * | \
+ * | \
+ * | \ (x3,y3)
+ * | +----------+
+ * | (x2,y2) \
+ * | \
+ * | \
+ * | +-----------------
+ * | (x4,y4)
+ * +---------------------------------------------> stem
+ * thickness
+ *
+ *
+ * This corresponds to the following values for the
+ * `darkening-parameters' property:
+ *
+ * (x1, y1) = (500, 400)
+ * (x2, y2) = (1000, 275)
+ * (x3, y3) = (1667, 275)
+ * (x4, y4) = (2333, 0)
+ *
+ */
+
+ /* Internal calculations are done in units per thousand for */
+ /* convenience. The x axis is scaled stem width in */
+ /* thousandths of a pixel. That is, 1000 is 1 pixel. */
+ /* The y axis is darkening amount in thousandths of a pixel.*/
+ /* In the code, below, dividing by ppem and */
+ /* adjusting for emRatio converts darkenAmount to character */
+ /* space (font units). */
+ CF2_Fixed stemWidthPer1000, scaledStem;
+ FT_Int logBase2;
+
+
+ *darkenAmount = 0;
+
+ if ( boldenAmount == 0 && !stemDarkened )
+ return;
+
+ /* protect against range problems and divide by zero */
+ if ( emRatio < cf2_doubleToFixed( .01 ) )
+ return;
+
+ if ( stemDarkened )
+ {
+ FT_Int x1 = darkenParams[0];
+ FT_Int y1 = darkenParams[1];
+ FT_Int x2 = darkenParams[2];
+ FT_Int y2 = darkenParams[3];
+ FT_Int x3 = darkenParams[4];
+ FT_Int y3 = darkenParams[5];
+ FT_Int x4 = darkenParams[6];
+ FT_Int y4 = darkenParams[7];
+
+
+ /* convert from true character space to 1000 unit character space; */
+ /* add synthetic emboldening effect */
+
+ /* `stemWidthPer1000' will not overflow for a legitimate font */
+
+ stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );
+
+ /* `scaledStem' can easily overflow, so we must clamp its maximum */
+ /* value; the test doesn't need to be precise, but must be */
+ /* conservative. The clamp value (default 2333) where */
+ /* `darkenAmount' is zero is well below the overflow value of */
+ /* 32767. */
+ /* */
+ /* FT_MSB computes the integer part of the base 2 logarithm. The */
+ /* number of bits for the product is 1 or 2 more than the sum of */
+ /* logarithms; remembering that the 16 lowest bits of the fraction */
+ /* are dropped this is correct to within a factor of almost 4. */
+ /* For example, 0x80.0000 * 0x80.0000 = 0x4000.0000 is 23+23 and */
+ /* is flagged as possible overflow because 0xFF.FFFF * 0xFF.FFFF = */
+ /* 0xFFFF.FE00 is also 23+23. */
+
+ logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) +
+ FT_MSB( (FT_UInt32)ppem );
+
+ if ( logBase2 >= 46 )
+ /* possible overflow */
+ scaledStem = cf2_intToFixed( x4 );
+ else
+ scaledStem = FT_MulFix( stemWidthPer1000, ppem );
+
+ /* now apply the darkening parameters */
+
+ if ( scaledStem < cf2_intToFixed( x1 ) )
+ *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem );
+
+ else if ( scaledStem < cf2_intToFixed( x2 ) )
+ {
+ FT_Int xdelta = x2 - x1;
+ FT_Int ydelta = y2 - y1;
+ FT_Int x = stemWidthPer1000 -
+ FT_DivFix( cf2_intToFixed( x1 ), ppem );
+
+
+ if ( !xdelta )
+ goto Try_x3;
+
+ *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+ FT_DivFix( cf2_intToFixed( y1 ), ppem );
+ }
+
+ else if ( scaledStem < cf2_intToFixed( x3 ) )
+ {
+ Try_x3:
+ {
+ FT_Int xdelta = x3 - x2;
+ FT_Int ydelta = y3 - y2;
+ FT_Int x = stemWidthPer1000 -
+ FT_DivFix( cf2_intToFixed( x2 ), ppem );
+
+
+ if ( !xdelta )
+ goto Try_x4;
+
+ *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+ FT_DivFix( cf2_intToFixed( y2 ), ppem );
+ }
+ }
+
+ else if ( scaledStem < cf2_intToFixed( x4 ) )
+ {
+ Try_x4:
+ {
+ FT_Int xdelta = x4 - x3;
+ FT_Int ydelta = y4 - y3;
+ FT_Int x = stemWidthPer1000 -
+ FT_DivFix( cf2_intToFixed( x3 ), ppem );
+
+
+ if ( !xdelta )
+ goto Use_y4;
+
+ *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
+ FT_DivFix( cf2_intToFixed( y3 ), ppem );
+ }
+ }
+
+ else
+ {
+ Use_y4:
+ *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem );
+ }
+
+ /* use half the amount on each side and convert back to true */
+ /* character space */
+ *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
+ }
+
+ /* add synthetic emboldening effect in character space */
+ *darkenAmount += boldenAmount / 2;
+ }
+
+
+ /* set up values for the current FontDict and matrix; */
+ /* called for each glyph to be rendered */
+
+ /* caller's transform is adjusted for subpixel positioning */
+ static void
+ cf2_font_setup( CF2_Font font,
+ const CF2_Matrix* transform )
+ {
+ /* pointer to parsed font object */
+ PS_Decoder* decoder = font->decoder;
+
+ FT_Bool needExtraSetup = FALSE;
+
+ CFF_VStoreRec* vstore;
+ FT_Bool hasVariations = FALSE;
+
+ /* character space units */
+ CF2_Fixed boldenX = font->syntheticEmboldeningAmountX;
+ CF2_Fixed boldenY = font->syntheticEmboldeningAmountY;
+
+ CFF_SubFont subFont;
+ CF2_Fixed ppem;
+
+ CF2_UInt lenNormalizedV = 0;
+ FT_Fixed* normalizedV = NULL;
+
+ /* clear previous error */
+ font->error = FT_Err_Ok;
+
+ /* if a CID fontDict has changed, we need to recompute some cached */
+ /* data */
+ subFont = cf2_getSubfont( decoder );
+ if ( font->lastSubfont != subFont )
+ {
+ font->lastSubfont = subFont;
+ needExtraSetup = TRUE;
+ }
+
+ if ( !font->isT1 )
+ {
+ /* check for variation vectors */
+ vstore = cf2_getVStore( decoder );
+ hasVariations = ( vstore->dataCount != 0 );
+
+ if ( hasVariations )
+ {
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_Service_CFFLoad cffload = (FT_Service_CFFLoad)font->cffload;
+
+
+ /* check whether Private DICT in this subfont needs to be reparsed */
+ font->error = cf2_getNormalizedVector( decoder,
+ &lenNormalizedV,
+ &normalizedV );
+ if ( font->error )
+ return;
+
+ if ( cffload->blend_check_vector( &subFont->blend,
+ subFont->private_dict.vsindex,
+ lenNormalizedV,
+ normalizedV ) )
+ {
+ /* blend has changed, reparse */
+ cffload->load_private_dict( decoder->cff,
+ subFont,
+ lenNormalizedV,
+ normalizedV );
+ needExtraSetup = TRUE;
+ }
+#endif
+
+ /* copy from subfont */
+ font->blend.font = subFont->blend.font;
+
+ /* clear state of charstring blend */
+ font->blend.usedBV = FALSE;
+
+ /* initialize value for charstring */
+ font->vsindex = subFont->private_dict.vsindex;
+
+ /* store vector inputs for blends in charstring */
+ font->lenNDV = lenNormalizedV;
+ font->NDV = normalizedV;
+ }
+ }
+
+ /* if ppem has changed, we need to recompute some cached data */
+ /* note: because of CID font matrix concatenation, ppem and transform */
+ /* do not necessarily track. */
+ ppem = cf2_getPpemY( decoder );
+ if ( font->ppem != ppem )
+ {
+ font->ppem = ppem;
+ needExtraSetup = TRUE;
+ }
+
+ /* copy hinted flag on each call */
+ font->hinted = FT_BOOL( font->renderingFlags & CF2_FlagsHinted );
+
+ /* determine if transform has changed; */
+ /* include Fontmatrix but ignore translation */
+ if ( ft_memcmp( transform,
+ &font->currentTransform,
+ 4 * sizeof ( CF2_Fixed ) ) != 0 )
+ {
+ /* save `key' information for `cache of one' matrix data; */
+ /* save client transform, without the translation */
+ font->currentTransform = *transform;
+ font->currentTransform.tx =
+ font->currentTransform.ty = cf2_intToFixed( 0 );
+
+ /* TODO: FreeType transform is simple scalar; for now, use identity */
+ /* for outer */
+ font->innerTransform = *transform;
+ font->outerTransform.a =
+ font->outerTransform.d = cf2_intToFixed( 1 );
+ font->outerTransform.b =
+ font->outerTransform.c = cf2_intToFixed( 0 );
+
+ needExtraSetup = TRUE;
+ }
+
+ /*
+ * font->darkened is set to true if there is a stem darkening request or
+ * the font is synthetic emboldened.
+ * font->darkened controls whether to adjust blue zones, winding order,
+ * and hinting.
+ *
+ */
+ if ( font->stemDarkened != ( font->renderingFlags & CF2_FlagsDarkened ) )
+ {
+ font->stemDarkened =
+ FT_BOOL( font->renderingFlags & CF2_FlagsDarkened );
+
+ /* blue zones depend on darkened flag */
+ needExtraSetup = TRUE;
+ }
+
+ /* recompute variables that are dependent on transform or FontDict or */
+ /* darken flag */
+ if ( needExtraSetup )
+ {
+ /* StdVW is found in the private dictionary; */
+ /* recompute darkening amounts whenever private dictionary or */
+ /* transform change */
+ /* Note: a rendering flag turns darkening on or off, so we want to */
+ /* store the `on' amounts; */
+ /* darkening amount is computed in character space */
+ /* TODO: testing size-dependent darkening here; */
+ /* what to do for rotations? */
+
+ CF2_Fixed emRatio;
+ CF2_Fixed stdHW;
+ CF2_Int unitsPerEm = font->unitsPerEm;
+
+
+ if ( unitsPerEm == 0 )
+ unitsPerEm = 1000;
+
+ ppem = FT_MAX( cf2_intToFixed( 4 ),
+ font->ppem ); /* use minimum ppem of 4 */
+
+#if 0
+ /* since vstem is measured in the x-direction, we use the `a' member */
+ /* of the fontMatrix */
+ emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->a );
+#endif
+
+ /* Freetype does not preserve the fontMatrix when parsing; use */
+ /* unitsPerEm instead. */
+ /* TODO: check precision of this */
+ emRatio = cf2_intToFixed( 1000 ) / unitsPerEm;
+ font->stdVW = cf2_getStdVW( decoder );
+
+ if ( font->stdVW <= 0 )
+ font->stdVW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
+
+ if ( boldenX > 0 )
+ {
+ /* Ensure that boldenX is at least 1 pixel for synthetic bold font */
+ /* (similar to what Avalon does) */
+ boldenX = FT_MAX( boldenX,
+ FT_DivFix( cf2_intToFixed( unitsPerEm ), ppem ) );
+
+ /* Synthetic emboldening adds at least 1 pixel to darkenX, while */
+ /* stem darkening adds at most half pixel. Since the purpose of */
+ /* stem darkening (readability at small sizes) is met with */
+ /* synthetic emboldening, no need to add stem darkening for a */
+ /* synthetic bold font. */
+ cf2_computeDarkening( emRatio,
+ ppem,
+ font->stdVW,
+ &font->darkenX,
+ boldenX,
+ FALSE,
+ font->darkenParams );
+ }
+ else
+ cf2_computeDarkening( emRatio,
+ ppem,
+ font->stdVW,
+ &font->darkenX,
+ 0,
+ font->stemDarkened,
+ font->darkenParams );
+
+#if 0
+ /* since hstem is measured in the y-direction, we use the `d' member */
+ /* of the fontMatrix */
+ /* TODO: use the same units per em as above; check this */
+ emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->d );
+#endif
+
+ /* set the default stem width, because it must be the same for all */
+ /* family members; */
+ /* choose a constant for StdHW that depends on font contrast */
+ stdHW = cf2_getStdHW( decoder );
+
+ if ( stdHW > 0 && font->stdVW > MUL_INT32( 2, stdHW ) )
+ font->stdHW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
+ else
+ {
+ /* low contrast font gets less hstem darkening */
+ font->stdHW = FT_DivFix( cf2_intToFixed( 110 ), emRatio );
+ }
+
+ cf2_computeDarkening( emRatio,
+ ppem,
+ font->stdHW,
+ &font->darkenY,
+ boldenY,
+ font->stemDarkened,
+ font->darkenParams );
+
+ if ( font->darkenX != 0 || font->darkenY != 0 )
+ font->darkened = TRUE;
+ else
+ font->darkened = FALSE;
+
+ font->reverseWinding = FALSE; /* initial expectation is CCW */
+
+ /* compute blue zones for this instance */
+ cf2_blues_init( &font->blues, font );
+
+ } /* needExtraSetup */
+ }
+
+
+ /* equivalent to AdobeGetOutline */
+ FT_LOCAL_DEF( FT_Error )
+ cf2_getGlyphOutline( CF2_Font font,
+ CF2_Buffer charstring,
+ const CF2_Matrix* transform,
+ CF2_F16Dot16* glyphWidth )
+ {
+ FT_Error lastError = FT_Err_Ok;
+
+ FT_Vector translation;
+
+#if 0
+ FT_Vector advancePoint;
+#endif
+
+ CF2_Fixed advWidth = 0;
+ FT_Bool needWinding;
+
+
+ /* Note: use both integer and fraction for outlines. This allows bbox */
+ /* to come out directly. */
+
+ translation.x = transform->tx;
+ translation.y = transform->ty;
+
+ /* set up values based on transform */
+ cf2_font_setup( font, transform );
+ if ( font->error )
+ goto exit; /* setup encountered an error */
+
+ /* reset darken direction */
+ font->reverseWinding = FALSE;
+
+ /* winding order only affects darkening */
+ needWinding = font->darkened;
+
+ while ( 1 )
+ {
+ /* reset output buffer */
+ cf2_outline_reset( &font->outline );
+
+ /* build the outline, passing the full translation */
+ cf2_interpT2CharString( font,
+ charstring,
+ (CF2_OutlineCallbacks)&font->outline,
+ &translation,
+ FALSE,
+ 0,
+ 0,
+ &advWidth );
+
+ if ( font->error )
+ goto exit;
+
+ if ( !needWinding )
+ break;
+
+ /* check winding order */
+ if ( font->outline.root.windingMomentum >= 0 ) /* CFF is CCW */
+ break;
+
+ /* invert darkening and render again */
+ /* TODO: this should be a parameter to getOutline-computeOffset */
+ font->reverseWinding = TRUE;
+
+ needWinding = FALSE; /* exit after next iteration */
+ }
+
+ /* finish storing client outline */
+ cf2_outline_close( &font->outline );
+
+ exit:
+ /* FreeType just wants the advance width; there is no translation */
+ *glyphWidth = advWidth;
+
+ /* free resources and collect errors from objects we've used */
+ cf2_setError( &font->error, lastError );
+
+ return font->error;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psfont.h b/modules/freetype2/src/psaux/psfont.h
new file mode 100644
index 0000000000..836fce4e4d
--- /dev/null
+++ b/modules/freetype2/src/psaux/psfont.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+ *
+ * psfont.h
+ *
+ * Adobe's code for font instances (specification).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSFONT_H_
+#define PSFONT_H_
+
+
+#include <freetype/internal/services/svcfftl.h>
+
+#include "psft.h"
+#include "psblues.h"
+
+
+FT_BEGIN_HEADER
+
+
+#define CF2_OPERAND_STACK_SIZE 48
+#define CF2_MAX_SUBR 16 /* maximum subroutine nesting; */
+ /* only 10 are allowed but there exist */
+ /* fonts like `HiraKakuProN-W3.ttf' */
+ /* (Hiragino Kaku Gothic ProN W3; */
+ /* 8.2d6e1; 2014-12-19) that exceed */
+ /* this limit */
+#define CF2_STORAGE_SIZE 32
+
+
+ /* typedef is in `cf2glue.h' */
+ struct CF2_FontRec_
+ {
+ FT_Memory memory;
+ FT_Error error; /* shared error for this instance */
+
+ FT_Bool isT1;
+ FT_Bool isCFF2;
+ CF2_RenderingFlags renderingFlags;
+
+ /* variables that depend on Transform: */
+ /* the following have zero translation; */
+ /* inner * outer = font * original */
+
+ CF2_Matrix currentTransform; /* original client matrix */
+ CF2_Matrix innerTransform; /* for hinting; erect, scaled */
+ CF2_Matrix outerTransform; /* post hinting; includes rotations */
+ CF2_Fixed ppem; /* transform-dependent */
+
+ /* variation data */
+ CFF_BlendRec blend; /* cached charstring blend vector */
+ CF2_UInt vsindex; /* current vsindex */
+ CF2_UInt lenNDV; /* current length NDV or zero */
+ FT_Fixed* NDV; /* ptr to current NDV or NULL */
+
+ CF2_Int unitsPerEm;
+
+ CF2_Fixed syntheticEmboldeningAmountX; /* character space units */
+ CF2_Fixed syntheticEmboldeningAmountY; /* character space units */
+
+ /* FreeType related members */
+ CF2_OutlineRec outline; /* freetype glyph outline functions */
+ PS_Decoder* decoder;
+ CFF_SubFont lastSubfont; /* FreeType parsed data; */
+ /* top font or subfont */
+
+ /* these flags can vary from one call to the next */
+ FT_Bool hinted;
+ FT_Bool darkened; /* true if stemDarkened or synthetic bold */
+ /* i.e. darkenX != 0 || darkenY != 0 */
+ FT_Bool stemDarkened;
+
+ FT_Int darkenParams[8]; /* 1000 unit character space */
+
+ /* variables that depend on both FontDict and Transform */
+ CF2_Fixed stdVW; /* in character space; depends on dict entry */
+ CF2_Fixed stdHW; /* in character space; depends on dict entry */
+ CF2_Fixed darkenX; /* character space units */
+ CF2_Fixed darkenY; /* depends on transform */
+ /* and private dict (StdVW) */
+ FT_Bool reverseWinding; /* darken assuming */
+ /* counterclockwise winding */
+
+ CF2_BluesRec blues; /* computed zone data */
+
+ FT_Service_CFFLoad cffload; /* pointer to cff functions */
+ };
+
+
+ FT_LOCAL( FT_Error )
+ cf2_getGlyphOutline( CF2_Font font,
+ CF2_Buffer charstring,
+ const CF2_Matrix* transform,
+ CF2_F16Dot16* glyphWidth );
+
+
+FT_END_HEADER
+
+
+#endif /* PSFONT_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psft.c b/modules/freetype2/src/psaux/psft.c
new file mode 100644
index 0000000000..618864e6e0
--- /dev/null
+++ b/modules/freetype2/src/psaux/psft.c
@@ -0,0 +1,895 @@
+/****************************************************************************
+ *
+ * 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 <freetype/internal/ftdebug.h>
+
+#include "psfont.h"
+#include "pserror.h"
+#include "psobjs.h"
+#include "cffdecode.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include <freetype/ftmm.h>
+#include <freetype/internal/services/svmm.h>
+#endif
+
+#include <freetype/internal/services/svcfftl.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;
+
+
+ if ( transform->a <= 0 || transform->d <= 0 )
+ return FT_THROW( Invalid_Size_Handle );
+
+ FT_ASSERT( unitsPerEm > 0 );
+ 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 );
+
+ if ( !decoder->builder.is_t1 )
+ *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 );
+
+ 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 = FT_OFFSET( 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 = FT_OFFSET( 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 = type1->charstrings_len[glyph_index];
+ }
+
+ if ( !error )
+ {
+ FT_Byte* charstring_base = (FT_Byte*)glyph_data.pointer;
+ FT_ULong charstring_len = 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 )
+ {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ T1_Face face;
+ FT_Data data;
+
+
+ FT_ASSERT( decoder );
+
+ face = (T1_Face)decoder->builder.face;
+
+ data.pointer = buf->start;
+ data.length = (FT_UInt)( 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 );
+
+#else /* !FT_CONFIG_OPTION_INCREMENTAL */
+
+ FT_UNUSED( decoder );
+ FT_UNUSED( buf );
+
+#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 >= 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 = FT_OFFSET( 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 */
diff --git a/modules/freetype2/src/psaux/psft.h b/modules/freetype2/src/psaux/psft.h
new file mode 100644
index 0000000000..3da454e601
--- /dev/null
+++ b/modules/freetype2/src/psaux/psft.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+ *
+ * psft.h
+ *
+ * FreeType Glue Component to Adobe's Interpreter (specification).
+ *
+ * Copyright 2013 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.
+ *
+ */
+
+
+#ifndef PSFT_H_
+#define PSFT_H_
+
+
+#include <freetype/internal/compiler-macros.h>
+#include "pstypes.h"
+
+ /* TODO: disable asserts for now */
+#define CF2_NDEBUG
+
+
+#include <freetype/ftsystem.h>
+
+#include "psglue.h"
+#include <freetype/internal/psaux.h> /* for PS_Decoder */
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ cf2_decoder_parse_charstrings( PS_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len );
+
+ FT_LOCAL( CFF_SubFont )
+ cf2_getSubfont( PS_Decoder* decoder );
+
+ FT_LOCAL( CFF_VStore )
+ cf2_getVStore( PS_Decoder* decoder );
+
+ FT_LOCAL( FT_UInt )
+ cf2_getMaxstack( PS_Decoder* decoder );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_LOCAL( FT_Error )
+ cf2_getNormalizedVector( PS_Decoder* decoder,
+ CF2_UInt *len,
+ FT_Fixed* *vec );
+#endif
+
+ FT_LOCAL( CF2_Fixed )
+ cf2_getPpemY( PS_Decoder* decoder );
+ FT_LOCAL( CF2_Fixed )
+ cf2_getStdVW( PS_Decoder* decoder );
+ FT_LOCAL( CF2_Fixed )
+ cf2_getStdHW( PS_Decoder* decoder );
+
+ FT_LOCAL( void )
+ cf2_getBlueMetrics( PS_Decoder* decoder,
+ CF2_Fixed* blueScale,
+ CF2_Fixed* blueShift,
+ CF2_Fixed* blueFuzz );
+ FT_LOCAL( void )
+ cf2_getBlueValues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+ FT_LOCAL( void )
+ cf2_getOtherBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+ FT_LOCAL( void )
+ cf2_getFamilyBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+ FT_LOCAL( void )
+ cf2_getFamilyOtherBlues( PS_Decoder* decoder,
+ size_t* count,
+ FT_Pos* *data );
+
+ FT_LOCAL( CF2_Int )
+ cf2_getLanguageGroup( PS_Decoder* decoder );
+
+ FT_LOCAL( CF2_Int )
+ cf2_initGlobalRegionBuffer( PS_Decoder* decoder,
+ CF2_Int subrNum,
+ CF2_Buffer buf );
+ FT_LOCAL( FT_Error )
+ cf2_getSeacComponent( PS_Decoder* decoder,
+ CF2_Int code,
+ CF2_Buffer buf );
+ FT_LOCAL( void )
+ cf2_freeSeacComponent( PS_Decoder* decoder,
+ CF2_Buffer buf );
+ FT_LOCAL( CF2_Int )
+ cf2_initLocalRegionBuffer( PS_Decoder* decoder,
+ CF2_Int subrNum,
+ CF2_Buffer buf );
+
+ FT_LOCAL( CF2_Fixed )
+ cf2_getDefaultWidthX( PS_Decoder* decoder );
+ FT_LOCAL( CF2_Fixed )
+ cf2_getNominalWidthX( PS_Decoder* decoder );
+
+
+ FT_LOCAL( FT_Error )
+ cf2_getT1SeacComponent( PS_Decoder* decoder,
+ FT_UInt glyph_index,
+ CF2_Buffer buf );
+ FT_LOCAL( void )
+ cf2_freeT1SeacComponent( PS_Decoder* decoder,
+ CF2_Buffer buf );
+
+ /*
+ * FreeType client outline
+ *
+ * process output from the charstring interpreter
+ */
+ typedef struct CF2_OutlineRec_
+ {
+ CF2_OutlineCallbacksRec root; /* base class must be first */
+ PS_Decoder* decoder;
+
+ } CF2_OutlineRec, *CF2_Outline;
+
+
+ FT_LOCAL( void )
+ cf2_outline_reset( CF2_Outline outline );
+ FT_LOCAL( void )
+ cf2_outline_close( CF2_Outline outline );
+
+
+FT_END_HEADER
+
+
+#endif /* PSFT_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psglue.h b/modules/freetype2/src/psaux/psglue.h
new file mode 100644
index 0000000000..63085d71cf
--- /dev/null
+++ b/modules/freetype2/src/psaux/psglue.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+ *
+ * psglue.h
+ *
+ * Adobe's code for shared stuff (specification only).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSGLUE_H_
+#define PSGLUE_H_
+
+
+/* common includes for other modules */
+#include "pserror.h"
+#include "psfixed.h"
+#include "psarrst.h"
+#include "psread.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* rendering parameters */
+
+ /* apply hints to rendered glyphs */
+#define CF2_FlagsHinted 1
+ /* for testing */
+#define CF2_FlagsDarkened 2
+
+ /* type for holding the flags */
+ typedef CF2_Int CF2_RenderingFlags;
+
+
+ /* elements of a glyph outline */
+ typedef enum CF2_PathOp_
+ {
+ CF2_PathOpMoveTo = 1, /* change the current point */
+ CF2_PathOpLineTo = 2, /* line */
+ CF2_PathOpQuadTo = 3, /* quadratic curve */
+ CF2_PathOpCubeTo = 4 /* cubic curve */
+
+ } CF2_PathOp;
+
+
+ /* a matrix of fixed-point values */
+ typedef struct CF2_Matrix_
+ {
+ CF2_F16Dot16 a;
+ CF2_F16Dot16 b;
+ CF2_F16Dot16 c;
+ CF2_F16Dot16 d;
+ CF2_F16Dot16 tx;
+ CF2_F16Dot16 ty;
+
+ } CF2_Matrix;
+
+
+ /* these typedefs are needed by more than one header file */
+ /* and gcc compiler doesn't allow redefinition */
+ typedef struct CF2_FontRec_ CF2_FontRec, *CF2_Font;
+ typedef struct CF2_HintRec_ CF2_HintRec, *CF2_Hint;
+
+
+ /* A common structure for all callback parameters. */
+ /* */
+ /* Some members may be unused. For example, `pt0' is not used for */
+ /* `moveTo' and `pt3' is not used for `quadTo'. The initial point `pt0' */
+ /* is included for each path element for generality; curve conversions */
+ /* need it. The `op' parameter allows one function to handle multiple */
+ /* element types. */
+
+ typedef struct CF2_CallbackParamsRec_
+ {
+ FT_Vector pt0;
+ FT_Vector pt1;
+ FT_Vector pt2;
+ FT_Vector pt3;
+
+ CF2_Int op;
+
+ } CF2_CallbackParamsRec, *CF2_CallbackParams;
+
+
+ /* forward reference */
+ typedef struct CF2_OutlineCallbacksRec_ CF2_OutlineCallbacksRec,
+ *CF2_OutlineCallbacks;
+
+ /* callback function pointers */
+ typedef void
+ (*CF2_Callback_Type)( CF2_OutlineCallbacks callbacks,
+ const CF2_CallbackParams params );
+
+
+ struct CF2_OutlineCallbacksRec_
+ {
+ CF2_Callback_Type moveTo;
+ CF2_Callback_Type lineTo;
+ CF2_Callback_Type quadTo;
+ CF2_Callback_Type cubeTo;
+
+ CF2_Int windingMomentum; /* for winding order detection */
+
+ FT_Memory memory;
+ FT_Error* error;
+ };
+
+
+FT_END_HEADER
+
+
+#endif /* PSGLUE_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/pshints.c b/modules/freetype2/src/psaux/pshints.c
new file mode 100644
index 0000000000..6f44d0adbb
--- /dev/null
+++ b/modules/freetype2/src/psaux/pshints.c
@@ -0,0 +1,1952 @@
+/****************************************************************************
+ *
+ * pshints.c
+ *
+ * Adobe's code for handling CFF hints (body).
+ *
+ * Copyright 2007-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 <freetype/internal/ftdebug.h>
+
+#include "psglue.h"
+#include "psfont.h"
+#include "pshints.h"
+#include "psintrp.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 cf2hints
+
+
+ typedef struct CF2_HintMoveRec_
+ {
+ size_t j; /* index of upper hint map edge */
+ CF2_Fixed moveUp; /* adjustment to optimum position */
+
+ } CF2_HintMoveRec, *CF2_HintMove;
+
+
+ /* Compute angular momentum for winding order detection. It is called */
+ /* for all lines and curves, but not necessarily in element order. */
+ static CF2_Int
+ cf2_getWindingMomentum( CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2 )
+ {
+ /* cross product of pt1 position from origin with pt2 position from */
+ /* pt1; we reduce the precision so that the result fits into 32 bits */
+
+ return ( x1 >> 16 ) * ( SUB_INT32( y2, y1 ) >> 16 ) -
+ ( y1 >> 16 ) * ( SUB_INT32( x2, x1 ) >> 16 );
+ }
+
+
+ /*
+ * Construct from a StemHint; this is used as a parameter to
+ * `cf2_blues_capture'.
+ * `hintOrigin' is the character space displacement of a seac accent.
+ * Adjust stem hint for darkening here.
+ *
+ */
+ static void
+ cf2_hint_init( CF2_Hint hint,
+ const CF2_ArrStack stemHintArray,
+ size_t indexStemHint,
+ const CF2_Font font,
+ CF2_Fixed hintOrigin,
+ CF2_Fixed scale,
+ FT_Bool bottom )
+ {
+ CF2_Fixed width;
+ const CF2_StemHintRec* stemHint;
+
+
+ FT_ZERO( hint );
+
+ stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer(
+ stemHintArray,
+ indexStemHint );
+
+ width = SUB_INT32( stemHint->max, stemHint->min );
+
+ if ( width == cf2_intToFixed( -21 ) )
+ {
+ /* ghost bottom */
+
+ if ( bottom )
+ {
+ hint->csCoord = stemHint->max;
+ hint->flags = CF2_GhostBottom;
+ }
+ else
+ hint->flags = 0;
+ }
+
+ else if ( width == cf2_intToFixed( -20 ) )
+ {
+ /* ghost top */
+
+ if ( bottom )
+ hint->flags = 0;
+ else
+ {
+ hint->csCoord = stemHint->min;
+ hint->flags = CF2_GhostTop;
+ }
+ }
+
+ else if ( width < 0 )
+ {
+ /* inverted pair */
+
+ /*
+ * Hints with negative widths were produced by an early version of a
+ * non-Adobe font tool. The Type 2 spec allows edge (ghost) hints
+ * with negative widths, but says
+ *
+ * All other negative widths have undefined meaning.
+ *
+ * CoolType has a silent workaround that negates the hint width; for
+ * permissive mode, we do the same here.
+ *
+ * Note: Such fonts cannot use ghost hints, but should otherwise work.
+ * Note: Some poor hints in our faux fonts can produce negative
+ * widths at some blends. For example, see a light weight of
+ * `u' in ASerifMM.
+ *
+ */
+ if ( bottom )
+ {
+ hint->csCoord = stemHint->max;
+ hint->flags = CF2_PairBottom;
+ }
+ else
+ {
+ hint->csCoord = stemHint->min;
+ hint->flags = CF2_PairTop;
+ }
+ }
+
+ else
+ {
+ /* normal pair */
+
+ if ( bottom )
+ {
+ hint->csCoord = stemHint->min;
+ hint->flags = CF2_PairBottom;
+ }
+ else
+ {
+ hint->csCoord = stemHint->max;
+ hint->flags = CF2_PairTop;
+ }
+ }
+
+ /* Now that ghost hints have been detected, adjust this edge for */
+ /* darkening. Bottoms are not changed; tops are incremented by twice */
+ /* `darkenY'. */
+ if ( cf2_hint_isTop( hint ) )
+ hint->csCoord = ADD_INT32( hint->csCoord, 2 * font->darkenY );
+
+ hint->csCoord = ADD_INT32( hint->csCoord, hintOrigin );
+ hint->scale = scale;
+ hint->index = indexStemHint; /* index in original stem hint array */
+
+ /* if original stem hint has been used, use the same position */
+ if ( hint->flags != 0 && stemHint->used )
+ {
+ if ( cf2_hint_isTop( hint ) )
+ hint->dsCoord = stemHint->maxDS;
+ else
+ hint->dsCoord = stemHint->minDS;
+
+ cf2_hint_lock( hint );
+ }
+ else
+ hint->dsCoord = FT_MulFix( hint->csCoord, scale );
+ }
+
+
+ /* initialize an invalid hint map element */
+ static void
+ cf2_hint_initZero( CF2_Hint hint )
+ {
+ FT_ZERO( hint );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hint_isValid( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isPair( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags & ( CF2_PairBottom | CF2_PairTop ) );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isPairTop( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags & CF2_PairTop );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hint_isTop( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags & ( CF2_PairTop | CF2_GhostTop ) );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hint_isBottom( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags & ( CF2_PairBottom | CF2_GhostBottom ) );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isLocked( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags & CF2_Locked );
+ }
+
+
+ static FT_Bool
+ cf2_hint_isSynthetic( const CF2_Hint hint )
+ {
+ return FT_BOOL( hint->flags & CF2_Synthetic );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hint_lock( CF2_Hint hint )
+ {
+ hint->flags |= CF2_Locked;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmap_init( CF2_HintMap hintmap,
+ CF2_Font font,
+ CF2_HintMap initialMap,
+ CF2_ArrStack hintMoves,
+ CF2_Fixed scale )
+ {
+ FT_ZERO( hintmap );
+
+ /* copy parameters from font instance */
+ hintmap->hinted = font->hinted;
+ hintmap->scale = scale;
+ hintmap->font = font;
+ hintmap->initialHintMap = initialMap;
+ /* will clear in `cf2_hintmap_adjustHints' */
+ hintmap->hintMoves = hintMoves;
+ }
+
+
+ static FT_Bool
+ cf2_hintmap_isValid( const CF2_HintMap hintmap )
+ {
+ return hintmap->isValid;
+ }
+
+
+ static void
+ cf2_hintmap_dump( CF2_HintMap hintmap )
+ {
+#ifdef FT_DEBUG_LEVEL_TRACE
+ CF2_UInt i;
+
+
+ FT_TRACE6(( " index csCoord dsCoord scale flags\n" ));
+
+ for ( i = 0; i < hintmap->count; i++ )
+ {
+ CF2_Hint hint = &hintmap->edge[i];
+
+
+ FT_TRACE6(( " %3ld %7.2f %7.2f %5d %s%s%s%s\n",
+ hint->index,
+ hint->csCoord / 65536.0,
+ hint->dsCoord / ( hint->scale * 1.0 ),
+ hint->scale,
+ ( cf2_hint_isPair( hint ) ? "p" : "g" ),
+ ( cf2_hint_isTop( hint ) ? "t" : "b" ),
+ ( cf2_hint_isLocked( hint ) ? "L" : ""),
+ ( cf2_hint_isSynthetic( hint ) ? "S" : "" ) ));
+ }
+#else
+ FT_UNUSED( hintmap );
+#endif
+ }
+
+
+ /* transform character space coordinate to device space using hint map */
+ static CF2_Fixed
+ cf2_hintmap_map( CF2_HintMap hintmap,
+ CF2_Fixed csCoord )
+ {
+ if ( hintmap->count == 0 || !hintmap->hinted )
+ {
+ /* there are no hints; use uniform scale and zero offset */
+ return FT_MulFix( csCoord, hintmap->scale );
+ }
+ else
+ {
+ /* start linear search from last hit */
+ CF2_UInt i = hintmap->lastIndex;
+
+
+ FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES );
+
+ /* search up */
+ while ( i < hintmap->count - 1 &&
+ csCoord >= hintmap->edge[i + 1].csCoord )
+ i += 1;
+
+ /* search down */
+ while ( i > 0 && csCoord < hintmap->edge[i].csCoord )
+ i -= 1;
+
+ hintmap->lastIndex = i;
+
+ if ( i == 0 && csCoord < hintmap->edge[0].csCoord )
+ {
+ /* special case for points below first edge: use uniform scale */
+ return ADD_INT32( FT_MulFix( SUB_INT32( csCoord,
+ hintmap->edge[0].csCoord ),
+ hintmap->scale ),
+ hintmap->edge[0].dsCoord );
+ }
+ else
+ {
+ /*
+ * Note: entries with duplicate csCoord are allowed.
+ * Use edge[i], the highest entry where csCoord >= entry[i].csCoord
+ */
+ return ADD_INT32( FT_MulFix( SUB_INT32( csCoord,
+ hintmap->edge[i].csCoord ),
+ hintmap->edge[i].scale ),
+ hintmap->edge[i].dsCoord );
+ }
+ }
+ }
+
+
+ /*
+ * This hinting policy moves a hint pair in device space so that one of
+ * its two edges is on a device pixel boundary (its fractional part is
+ * zero). `cf2_hintmap_insertHint' guarantees no overlap in CS
+ * space. Ensure here that there is no overlap in DS.
+ *
+ * In the first pass, edges are adjusted relative to adjacent hints.
+ * Those that are below have already been adjusted. Those that are
+ * above have not yet been adjusted. If a hint above blocks an
+ * adjustment to an optimal position, we will try again in a second
+ * pass. The second pass is top-down.
+ *
+ */
+
+ static void
+ cf2_hintmap_adjustHints( CF2_HintMap hintmap )
+ {
+ size_t i, j;
+
+
+ cf2_arrstack_clear( hintmap->hintMoves ); /* working storage */
+
+ /*
+ * First pass is bottom-up (font hint order) without look-ahead.
+ * Locked edges are already adjusted.
+ * Unlocked edges begin with dsCoord from `initialHintMap'.
+ * Save edges that are not optimally adjusted in `hintMoves' array,
+ * and process them in second pass.
+ */
+
+ for ( i = 0; i < hintmap->count; i++ )
+ {
+ FT_Bool isPair = cf2_hint_isPair( &hintmap->edge[i] );
+
+ /* final amount to move edge or edge pair */
+ CF2_Fixed move = 0;
+
+ CF2_Fixed dsCoord_i;
+ CF2_Fixed dsCoord_j;
+
+
+ /* index of upper edge (same value for ghost hint) */
+ j = isPair ? i + 1 : i;
+
+ FT_ASSERT( j < hintmap->count );
+ FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) );
+ FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) );
+ FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) ==
+ cf2_hint_isLocked( &hintmap->edge[j] ) );
+
+ dsCoord_i = hintmap->edge[i].dsCoord;
+ dsCoord_j = hintmap->edge[j].dsCoord;
+
+ if ( !cf2_hint_isLocked( &hintmap->edge[i] ) )
+ {
+ /* hint edge is not locked, we can adjust it */
+ CF2_Fixed fracDown = cf2_fixedFraction( dsCoord_i );
+ CF2_Fixed fracUp = cf2_fixedFraction( dsCoord_j );
+
+ /* calculate all four possibilities; moves down are negative */
+ CF2_Fixed downMoveDown = 0 - fracDown;
+ CF2_Fixed upMoveDown = 0 - fracUp;
+ CF2_Fixed downMoveUp = ( fracDown == 0 )
+ ? 0
+ : cf2_intToFixed( 1 ) - fracDown;
+ CF2_Fixed upMoveUp = ( fracUp == 0 )
+ ? 0
+ : cf2_intToFixed( 1 ) - fracUp;
+
+ /* smallest move up */
+ CF2_Fixed moveUp = FT_MIN( downMoveUp, upMoveUp );
+ /* smallest move down */
+ CF2_Fixed moveDown = FT_MAX( downMoveDown, upMoveDown );
+
+ CF2_Fixed downMinCounter = CF2_MIN_COUNTER;
+ CF2_Fixed upMinCounter = CF2_MIN_COUNTER;
+ FT_Bool saveEdge = FALSE;
+
+
+ /* minimum counter constraint doesn't apply when adjacent edges */
+ /* are synthetic */
+ /* TODO: doesn't seem a big effect; for now, reduce the code */
+#if 0
+ if ( i == 0 ||
+ cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) )
+ downMinCounter = 0;
+
+ if ( j >= hintmap->count - 1 ||
+ cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) )
+ upMinCounter = 0;
+#endif
+
+ /* is there room to move up? */
+ /* there is if we are at top of array or the next edge is at or */
+ /* beyond proposed move up? */
+ if ( j >= hintmap->count - 1 ||
+ hintmap->edge[j + 1].dsCoord >=
+ ADD_INT32( dsCoord_j, moveUp + upMinCounter ) )
+ {
+ /* there is room to move up; is there also room to move down? */
+ if ( i == 0 ||
+ hintmap->edge[i - 1].dsCoord <=
+ ADD_INT32( dsCoord_i, moveDown - downMinCounter ) )
+ {
+ /* move smaller absolute amount */
+ move = ( -moveDown < moveUp ) ? moveDown : moveUp; /* optimum */
+ }
+ else
+ move = moveUp;
+ }
+ else
+ {
+ /* is there room to move down? */
+ if ( i == 0 ||
+ hintmap->edge[i - 1].dsCoord <=
+ ADD_INT32( dsCoord_i, moveDown - downMinCounter ) )
+ {
+ move = moveDown;
+ /* true if non-optimum move */
+ saveEdge = FT_BOOL( moveUp < -moveDown );
+ }
+ else
+ {
+ /* no room to move either way without overlapping or reducing */
+ /* the counter too much */
+ move = 0;
+ saveEdge = TRUE;
+ }
+ }
+
+ /* Identify non-moves and moves down that aren't optimal, and save */
+ /* them for second pass. */
+ /* Do this only if there is an unlocked edge above (which could */
+ /* possibly move). */
+ if ( saveEdge &&
+ j < hintmap->count - 1 &&
+ !cf2_hint_isLocked( &hintmap->edge[j + 1] ) )
+ {
+ CF2_HintMoveRec savedMove;
+
+
+ savedMove.j = j;
+ /* desired adjustment in second pass */
+ savedMove.moveUp = moveUp - move;
+
+ cf2_arrstack_push( hintmap->hintMoves, &savedMove );
+ }
+
+ /* move the edge(s) */
+ hintmap->edge[i].dsCoord = ADD_INT32( dsCoord_i, move );
+ if ( isPair )
+ hintmap->edge[j].dsCoord = ADD_INT32( dsCoord_j, move );
+ }
+
+ /* assert there are no overlaps in device space; */
+ /* ignore tests if there was overflow (that is, if */
+ /* operands have the same sign but the sum does not) */
+ FT_ASSERT( i == 0 ||
+ ( ( dsCoord_i ^ move ) >= 0 &&
+ ( dsCoord_i ^ hintmap->edge[i].dsCoord ) < 0 ) ||
+ hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord );
+ FT_ASSERT( i < j ||
+ ( ( dsCoord_j ^ move ) >= 0 &&
+ ( dsCoord_j ^ hintmap->edge[j].dsCoord ) < 0 ) ||
+ hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord );
+
+ /* adjust the scales, avoiding divide by zero */
+ if ( i > 0 )
+ {
+ if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord )
+ hintmap->edge[i - 1].scale =
+ FT_DivFix( SUB_INT32( hintmap->edge[i].dsCoord,
+ hintmap->edge[i - 1].dsCoord ),
+ SUB_INT32( hintmap->edge[i].csCoord,
+ hintmap->edge[i - 1].csCoord ) );
+ }
+
+ if ( isPair )
+ {
+ if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord )
+ hintmap->edge[j - 1].scale =
+ FT_DivFix( SUB_INT32( hintmap->edge[j].dsCoord,
+ hintmap->edge[j - 1].dsCoord ),
+ SUB_INT32( hintmap->edge[j].csCoord,
+ hintmap->edge[j - 1].csCoord ) );
+
+ i += 1; /* skip upper edge on next loop */
+ }
+ }
+
+ /* second pass tries to move non-optimal hints up, in case there is */
+ /* room now */
+ for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- )
+ {
+ CF2_HintMove hintMove = (CF2_HintMove)
+ cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 );
+
+
+ j = hintMove->j;
+
+ /* this was tested before the push, above */
+ FT_ASSERT( j < hintmap->count - 1 );
+
+ /* is there room to move up? */
+ if ( hintmap->edge[j + 1].dsCoord >=
+ ADD_INT32( hintmap->edge[j].dsCoord,
+ hintMove->moveUp + CF2_MIN_COUNTER ) )
+ {
+ /* there is more room now, move edge up */
+ hintmap->edge[j].dsCoord = ADD_INT32( hintmap->edge[j].dsCoord,
+ hintMove->moveUp );
+
+ if ( cf2_hint_isPair( &hintmap->edge[j] ) )
+ {
+ FT_ASSERT( j > 0 );
+ hintmap->edge[j - 1].dsCoord =
+ ADD_INT32( hintmap->edge[j - 1].dsCoord, hintMove->moveUp );
+ }
+ }
+ }
+ }
+
+
+ /* insert hint edges into map, sorted by csCoord */
+ static void
+ cf2_hintmap_insertHint( CF2_HintMap hintmap,
+ CF2_Hint bottomHintEdge,
+ CF2_Hint topHintEdge )
+ {
+ CF2_UInt indexInsert;
+
+ /* set default values, then check for edge hints */
+ FT_Bool isPair = TRUE;
+ CF2_Hint firstHintEdge = bottomHintEdge;
+ CF2_Hint secondHintEdge = topHintEdge;
+
+
+ /* one or none of the input params may be invalid when dealing with */
+ /* edge hints; at least one edge must be valid */
+ FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) ||
+ cf2_hint_isValid( topHintEdge ) );
+
+ /* determine how many and which edges to insert */
+ if ( !cf2_hint_isValid( bottomHintEdge ) )
+ {
+ /* insert only the top edge */
+ firstHintEdge = topHintEdge;
+ isPair = FALSE;
+ }
+ else if ( !cf2_hint_isValid( topHintEdge ) )
+ {
+ /* insert only the bottom edge */
+ isPair = FALSE;
+ }
+
+ /* paired edges must be in proper order */
+ if ( isPair &&
+ topHintEdge->csCoord < bottomHintEdge->csCoord )
+ return;
+
+ /* linear search to find index value of insertion point */
+ indexInsert = 0;
+ for ( ; indexInsert < hintmap->count; indexInsert++ )
+ {
+ if ( hintmap->edge[indexInsert].csCoord >= firstHintEdge->csCoord )
+ break;
+ }
+
+ FT_TRACE7(( " Got hint at %.2f (%.2f)\n",
+ firstHintEdge->csCoord / 65536.0,
+ firstHintEdge->dsCoord / 65536.0 ));
+ if ( isPair )
+ FT_TRACE7(( " Got hint at %.2f (%.2f)\n",
+ secondHintEdge->csCoord / 65536.0,
+ secondHintEdge->dsCoord / 65536.0 ));
+
+ /*
+ * Discard any hints that overlap in character space. Most often, this
+ * is while building the initial map, where captured hints from all
+ * zones are combined. Define overlap to include hints that `touch'
+ * (overlap zero). Hiragino Sans/Gothic fonts have numerous hints that
+ * touch. Some fonts have non-ideographic glyphs that overlap our
+ * synthetic hints.
+ *
+ * Overlap also occurs when darkening stem hints that are close.
+ *
+ */
+ if ( indexInsert < hintmap->count )
+ {
+ /* we are inserting before an existing edge: */
+ /* verify that an existing edge is not the same */
+ if ( hintmap->edge[indexInsert].csCoord == firstHintEdge->csCoord )
+ return; /* ignore overlapping stem hint */
+
+ /* verify that a new pair does not straddle the next edge */
+ if ( isPair &&
+ hintmap->edge[indexInsert].csCoord <= secondHintEdge->csCoord )
+ return; /* ignore overlapping stem hint */
+
+ /* verify that we are not inserting between paired edges */
+ if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) )
+ return; /* ignore overlapping stem hint */
+ }
+
+ /* recompute device space locations using initial hint map */
+ if ( cf2_hintmap_isValid( hintmap->initialHintMap ) &&
+ !cf2_hint_isLocked( firstHintEdge ) )
+ {
+ if ( isPair )
+ {
+ /* Use hint map to position the center of stem, and nominal scale */
+ /* to position the two edges. This preserves the stem width. */
+ CF2_Fixed midpoint =
+ cf2_hintmap_map(
+ hintmap->initialHintMap,
+ ADD_INT32(
+ firstHintEdge->csCoord,
+ SUB_INT32 ( secondHintEdge->csCoord,
+ firstHintEdge->csCoord ) / 2 ) );
+ CF2_Fixed halfWidth =
+ FT_MulFix( SUB_INT32( secondHintEdge->csCoord,
+ firstHintEdge->csCoord ) / 2,
+ hintmap->scale );
+
+
+ firstHintEdge->dsCoord = SUB_INT32( midpoint, halfWidth );
+ secondHintEdge->dsCoord = ADD_INT32( midpoint, halfWidth );
+ }
+ else
+ firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap,
+ firstHintEdge->csCoord );
+ }
+
+ /*
+ * Discard any hints that overlap in device space; this can occur
+ * because locked hints have been moved to align with blue zones.
+ *
+ * TODO: Although we might correct this later during adjustment, we
+ * don't currently have a way to delete a conflicting hint once it has
+ * been inserted. See v2.030 MinionPro-Regular, 12 ppem darkened,
+ * initial hint map for second path, glyph 945 (the perispomeni (tilde)
+ * in U+1F6E, Greek omega with psili and perispomeni). Darkening is
+ * 25. Pair 667,747 initially conflicts in design space with top edge
+ * 660. This is because 667 maps to 7.87, and the top edge was
+ * captured by a zone at 8.0. The pair is later successfully inserted
+ * in a zone without the top edge. In this zone it is adjusted to 8.0,
+ * and no longer conflicts with the top edge in design space. This
+ * means it can be included in yet a later zone which does have the top
+ * edge hint. This produces a small mismatch between the first and
+ * last points of this path, even though the hint masks are the same.
+ * The density map difference is tiny (1/256).
+ *
+ */
+
+ if ( indexInsert > 0 )
+ {
+ /* we are inserting after an existing edge */
+ if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord )
+ return;
+ }
+
+ if ( indexInsert < hintmap->count )
+ {
+ /* we are inserting before an existing edge */
+ if ( isPair )
+ {
+ if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
+ return;
+ }
+ else
+ {
+ if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
+ return;
+ }
+ }
+
+ /* make room to insert */
+ {
+ CF2_UInt iSrc = hintmap->count - 1;
+ CF2_UInt iDst = isPair ? hintmap->count + 1 : hintmap->count;
+
+ CF2_UInt count = hintmap->count - indexInsert;
+
+
+ if ( iDst >= CF2_MAX_HINT_EDGES )
+ {
+ FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" ));
+ return;
+ }
+
+ while ( count-- )
+ hintmap->edge[iDst--] = hintmap->edge[iSrc--];
+
+ /* insert first edge */
+ hintmap->edge[indexInsert] = *firstHintEdge; /* copy struct */
+ hintmap->count += 1;
+
+ FT_TRACE7(( " Inserting hint %.2f (%.2f)\n",
+ firstHintEdge->csCoord / 65536.0,
+ firstHintEdge->dsCoord / 65536.0 ));
+
+ if ( isPair )
+ {
+ /* insert second edge */
+ hintmap->edge[indexInsert + 1] = *secondHintEdge; /* copy struct */
+ hintmap->count += 1;
+
+ FT_TRACE7(( " Inserting hint %.2f (%.2f)\n",
+ secondHintEdge->csCoord / 65536.0,
+ secondHintEdge->dsCoord / 65536.0 ));
+
+ }
+ }
+
+ return;
+ }
+
+
+ /*
+ * Build a map from hints and mask.
+ *
+ * This function may recur one level if `hintmap->initialHintMap' is not yet
+ * valid.
+ * If `initialMap' is true, simply build initial map.
+ *
+ * Synthetic hints are used in two ways. A hint at zero is inserted, if
+ * needed, in the initial hint map, to prevent translations from
+ * propagating across the origin. If synthetic em box hints are enabled
+ * for ideographic dictionaries, then they are inserted in all hint
+ * maps, including the initial one.
+ *
+ */
+ FT_LOCAL_DEF( void )
+ cf2_hintmap_build( CF2_HintMap hintmap,
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOrigin,
+ FT_Bool initialMap )
+ {
+ FT_Byte* maskPtr;
+
+ CF2_Font font = hintmap->font;
+ CF2_HintMaskRec tempHintMask;
+
+ size_t bitCount, i;
+ FT_Byte maskByte;
+
+
+ /* check whether initial map is constructed */
+ if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) )
+ {
+ /* make recursive call with initialHintMap and temporary mask; */
+ /* temporary mask will get all bits set, below */
+ cf2_hintmask_init( &tempHintMask, hintMask->error );
+ cf2_hintmap_build( hintmap->initialHintMap,
+ hStemHintArray,
+ vStemHintArray,
+ &tempHintMask,
+ hintOrigin,
+ TRUE );
+ }
+
+ if ( !cf2_hintmask_isValid( hintMask ) )
+ {
+ /* without a hint mask, assume all hints are active */
+ cf2_hintmask_setAll( hintMask,
+ cf2_arrstack_size( hStemHintArray ) +
+ cf2_arrstack_size( vStemHintArray ) );
+ if ( !cf2_hintmask_isValid( hintMask ) )
+ {
+ if ( font->isT1 )
+ {
+ /* no error, just continue unhinted */
+ *hintMask->error = FT_Err_Ok;
+ hintmap->hinted = FALSE;
+ }
+ return; /* too many stem hints */
+ }
+ }
+
+ /* begin by clearing the map */
+ hintmap->count = 0;
+ hintmap->lastIndex = 0;
+
+ /* make a copy of the hint mask so we can modify it */
+ tempHintMask = *hintMask;
+ maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask );
+
+ /* use the hStem hints only, which are first in the mask */
+ bitCount = cf2_arrstack_size( hStemHintArray );
+
+ /* Defense-in-depth. Should never return here. */
+ if ( bitCount > hintMask->bitCount )
+ return;
+
+ /* synthetic embox hints get highest priority */
+ if ( font->blues.doEmBoxHints )
+ {
+ CF2_HintRec dummy;
+
+
+ cf2_hint_initZero( &dummy ); /* invalid hint map element */
+
+ /* ghost bottom */
+ cf2_hintmap_insertHint( hintmap,
+ &font->blues.emBoxBottomEdge,
+ &dummy );
+ /* ghost top */
+ cf2_hintmap_insertHint( hintmap,
+ &dummy,
+ &font->blues.emBoxTopEdge );
+ }
+
+ /* insert hints captured by a blue zone or already locked (higher */
+ /* priority) */
+ for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
+ {
+ if ( maskByte & *maskPtr )
+ {
+ /* expand StemHint into two `CF2_Hint' elements */
+ CF2_HintRec bottomHintEdge, topHintEdge;
+
+
+ cf2_hint_init( &bottomHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ TRUE /* bottom */ );
+ cf2_hint_init( &topHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ FALSE /* top */ );
+
+ if ( cf2_hint_isLocked( &bottomHintEdge ) ||
+ cf2_hint_isLocked( &topHintEdge ) ||
+ cf2_blues_capture( &font->blues,
+ &bottomHintEdge,
+ &topHintEdge ) )
+ {
+ /* insert captured hint into map */
+ cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
+
+ *maskPtr &= ~maskByte; /* turn off the bit for this hint */
+ }
+ }
+
+ if ( ( i & 7 ) == 7 )
+ {
+ /* move to next mask byte */
+ maskPtr++;
+ maskByte = 0x80;
+ }
+ else
+ maskByte >>= 1;
+ }
+
+ /* initial hint map includes only captured hints plus maybe one at 0 */
+
+ /*
+ * TODO: There is a problem here because we are trying to build a
+ * single hint map containing all captured hints. It is
+ * possible for there to be conflicts between captured hints,
+ * either because of darkening or because the hints are in
+ * separate hint zones (we are ignoring hint zones for the
+ * initial map). An example of the latter is MinionPro-Regular
+ * v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem.
+ * A stem hint for the psili conflicts with the top edge hint
+ * for the base character. The stem hint gets priority because
+ * of its sort order. In glyph 884 (Greek Capital Alpha with
+ * Psili and Oxia), the top of the base character gets a stem
+ * hint, and the psili does not. This creates different initial
+ * maps for the two glyphs resulting in different renderings of
+ * the base character. Will probably defer this either as not
+ * worth the cost or as a font bug. I don't think there is any
+ * good reason for an accent to be captured by an alignment
+ * zone. -darnold 2/12/10
+ */
+
+ if ( initialMap )
+ {
+ /* Apply a heuristic that inserts a point for (0,0), unless it's */
+ /* already covered by a mapping. This locks the baseline for glyphs */
+ /* that have no baseline hints. */
+
+ if ( hintmap->count == 0 ||
+ hintmap->edge[0].csCoord > 0 ||
+ hintmap->edge[hintmap->count - 1].csCoord < 0 )
+ {
+ /* all edges are above 0 or all edges are below 0; */
+ /* construct a locked edge hint at 0 */
+
+ CF2_HintRec edge, invalid;
+
+
+ cf2_hint_initZero( &edge );
+
+ edge.flags = CF2_GhostBottom |
+ CF2_Locked |
+ CF2_Synthetic;
+ edge.scale = hintmap->scale;
+
+ cf2_hint_initZero( &invalid );
+ cf2_hintmap_insertHint( hintmap, &edge, &invalid );
+ }
+ }
+ else
+ {
+ /* insert remaining hints */
+
+ maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask );
+
+ for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
+ {
+ if ( maskByte & *maskPtr )
+ {
+ CF2_HintRec bottomHintEdge, topHintEdge;
+
+
+ cf2_hint_init( &bottomHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ TRUE /* bottom */ );
+ cf2_hint_init( &topHintEdge,
+ hStemHintArray,
+ i,
+ font,
+ hintOrigin,
+ hintmap->scale,
+ FALSE /* top */ );
+
+ cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
+ }
+
+ if ( ( i & 7 ) == 7 )
+ {
+ /* move to next mask byte */
+ maskPtr++;
+ maskByte = 0x80;
+ }
+ else
+ maskByte >>= 1;
+ }
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( initialMap )
+ {
+ FT_TRACE6(( "flags: [p]air [g]host [t]op"
+ " [b]ottom [L]ocked [S]ynthetic\n" ));
+ FT_TRACE6(( "Initial hintmap:\n" ));
+ }
+ else
+ FT_TRACE6(( "Hints:\n" ));
+#endif
+
+ cf2_hintmap_dump( hintmap );
+
+ /*
+ * Note: The following line is a convenient place to break when
+ * debugging hinting. Examine `hintmap->edge' for the list of
+ * enabled hints, then step over the call to see the effect of
+ * adjustment. We stop here first on the recursive call that
+ * creates the initial map, and then on each counter group and
+ * hint zone.
+ */
+
+ /* adjust positions of hint edges that are not locked to blue zones */
+ cf2_hintmap_adjustHints( hintmap );
+
+ FT_TRACE6(( "Hints adjusted:\n" ));
+ cf2_hintmap_dump( hintmap );
+
+ /* save the position of all hints that were used in this hint map; */
+ /* if we use them again, we'll locate them in the same position */
+ if ( !initialMap )
+ {
+ for ( i = 0; i < hintmap->count; i++ )
+ {
+ if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) )
+ {
+ /* Note: include both valid and invalid edges */
+ /* Note: top and bottom edges are copied back separately */
+ CF2_StemHint stemhint = (CF2_StemHint)
+ cf2_arrstack_getPointer( hStemHintArray,
+ hintmap->edge[i].index );
+
+
+ if ( cf2_hint_isTop( &hintmap->edge[i] ) )
+ stemhint->maxDS = hintmap->edge[i].dsCoord;
+ else
+ stemhint->minDS = hintmap->edge[i].dsCoord;
+
+ stemhint->used = TRUE;
+ }
+ }
+ }
+
+ /* hint map is ready to use */
+ hintmap->isValid = TRUE;
+
+ /* remember this mask has been used */
+ cf2_hintmask_setNew( hintMask, FALSE );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_init( CF2_GlyphPath glyphpath,
+ CF2_Font font,
+ CF2_OutlineCallbacks callbacks,
+ CF2_Fixed scaleY,
+ /* CF2_Fixed hShift, */
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOriginY,
+ const CF2_Blues blues,
+ const FT_Vector* fractionalTranslation )
+ {
+ FT_ZERO( glyphpath );
+
+ glyphpath->font = font;
+ glyphpath->callbacks = callbacks;
+
+ cf2_arrstack_init( &glyphpath->hintMoves,
+ font->memory,
+ &font->error,
+ sizeof ( CF2_HintMoveRec ) );
+
+ cf2_hintmap_init( &glyphpath->initialHintMap,
+ font,
+ &glyphpath->initialHintMap,
+ &glyphpath->hintMoves,
+ scaleY );
+ cf2_hintmap_init( &glyphpath->firstHintMap,
+ font,
+ &glyphpath->initialHintMap,
+ &glyphpath->hintMoves,
+ scaleY );
+ cf2_hintmap_init( &glyphpath->hintMap,
+ font,
+ &glyphpath->initialHintMap,
+ &glyphpath->hintMoves,
+ scaleY );
+
+ glyphpath->scaleX = font->innerTransform.a;
+ glyphpath->scaleC = font->innerTransform.c;
+ glyphpath->scaleY = font->innerTransform.d;
+
+ glyphpath->fractionalTranslation = *fractionalTranslation;
+
+#if 0
+ glyphpath->hShift = hShift; /* for fauxing */
+#endif
+
+ glyphpath->hStemHintArray = hStemHintArray;
+ glyphpath->vStemHintArray = vStemHintArray;
+ glyphpath->hintMask = hintMask; /* ptr to current mask */
+ glyphpath->hintOriginY = hintOriginY;
+ glyphpath->blues = blues;
+ glyphpath->darken = font->darkened; /* TODO: should we make copies? */
+ glyphpath->xOffset = font->darkenX;
+ glyphpath->yOffset = font->darkenY;
+ glyphpath->miterLimit = 2 * FT_MAX(
+ cf2_fixedAbs( glyphpath->xOffset ),
+ cf2_fixedAbs( glyphpath->yOffset ) );
+
+ /* .1 character space unit */
+ glyphpath->snapThreshold = cf2_doubleToFixed( 0.1 );
+
+ glyphpath->moveIsPending = TRUE;
+ glyphpath->pathIsOpen = FALSE;
+ glyphpath->pathIsClosing = FALSE;
+ glyphpath->elemIsQueued = FALSE;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_finalize( CF2_GlyphPath glyphpath )
+ {
+ cf2_arrstack_finalize( &glyphpath->hintMoves );
+ }
+
+
+ /*
+ * Hint point in y-direction and apply outerTransform.
+ * Input `current' hint map (which is actually delayed by one element).
+ * Input x,y point in Character Space.
+ * Output x,y point in Device Space, including translation.
+ */
+ static void
+ cf2_glyphpath_hintPoint( CF2_GlyphPath glyphpath,
+ CF2_HintMap hintmap,
+ FT_Vector* ppt,
+ CF2_Fixed x,
+ CF2_Fixed y )
+ {
+ FT_Vector pt; /* hinted point in upright DS */
+
+
+ pt.x = ADD_INT32( FT_MulFix( glyphpath->scaleX, x ),
+ FT_MulFix( glyphpath->scaleC, y ) );
+ pt.y = cf2_hintmap_map( hintmap, y );
+
+ ppt->x = ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.a, pt.x ),
+ ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.c, pt.y ),
+ glyphpath->fractionalTranslation.x ) );
+ ppt->y = ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.b, pt.x ),
+ ADD_INT32(
+ FT_MulFix( glyphpath->font->outerTransform.d, pt.y ),
+ glyphpath->fractionalTranslation.y ) );
+ }
+
+
+ /*
+ * From two line segments, (u1,u2) and (v1,v2), compute a point of
+ * intersection on the corresponding lines.
+ * Return false if no intersection is found, or if the intersection is
+ * too far away from the ends of the line segments, u2 and v1.
+ *
+ */
+ static FT_Bool
+ cf2_glyphpath_computeIntersection( CF2_GlyphPath glyphpath,
+ const FT_Vector* u1,
+ const FT_Vector* u2,
+ const FT_Vector* v1,
+ const FT_Vector* v2,
+ FT_Vector* intersection )
+ {
+ /*
+ * Let `u' be a zero-based vector from the first segment, `v' from the
+ * second segment.
+ * Let `w 'be the zero-based vector from `u1' to `v1'.
+ * `perp' is the `perpendicular dot product'; see
+ * https://mathworld.wolfram.com/PerpDotProduct.html.
+ * `s' is the parameter for the parametric line for the first segment
+ * (`u').
+ *
+ * See notation in
+ * http://geomalgorithms.com/a05-_intersect-1.html.
+ * Calculations are done in 16.16, but must handle the squaring of
+ * line lengths in character space. We scale all vectors by 1/32 to
+ * avoid overflow. This allows values up to 4095 to be squared. The
+ * scale factor cancels in the divide.
+ *
+ * TODO: the scale factor could be computed from UnitsPerEm.
+ *
+ */
+
+#define cf2_perp( a, b ) \
+ ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) )
+
+ /* round and divide by 32 */
+#define CF2_CS_SCALE( x ) \
+ ( ( (x) + 0x10 ) >> 5 )
+
+ FT_Vector u, v, w; /* scaled vectors */
+ CF2_Fixed denominator, s;
+
+
+ u.x = CF2_CS_SCALE( SUB_INT32( u2->x, u1->x ) );
+ u.y = CF2_CS_SCALE( SUB_INT32( u2->y, u1->y ) );
+ v.x = CF2_CS_SCALE( SUB_INT32( v2->x, v1->x ) );
+ v.y = CF2_CS_SCALE( SUB_INT32( v2->y, v1->y ) );
+ w.x = CF2_CS_SCALE( SUB_INT32( v1->x, u1->x ) );
+ w.y = CF2_CS_SCALE( SUB_INT32( v1->y, u1->y ) );
+
+ denominator = cf2_perp( u, v );
+
+ if ( denominator == 0 )
+ return FALSE; /* parallel or coincident lines */
+
+ s = FT_DivFix( cf2_perp( w, v ), denominator );
+
+ intersection->x = ADD_INT32( u1->x,
+ FT_MulFix( s, SUB_INT32( u2->x, u1->x ) ) );
+ intersection->y = ADD_INT32( u1->y,
+ FT_MulFix( s, SUB_INT32( u2->y, u1->y ) ) );
+
+
+ /*
+ * Special case snapping for horizontal and vertical lines.
+ * This cleans up intersections and reduces problems with winding
+ * order detection.
+ * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685.
+ * Note: these calculations are in character space.
+ *
+ */
+
+ if ( u1->x == u2->x &&
+ cf2_fixedAbs( SUB_INT32( intersection->x,
+ u1->x ) ) < glyphpath->snapThreshold )
+ intersection->x = u1->x;
+ if ( u1->y == u2->y &&
+ cf2_fixedAbs( SUB_INT32( intersection->y,
+ u1->y ) ) < glyphpath->snapThreshold )
+ intersection->y = u1->y;
+
+ if ( v1->x == v2->x &&
+ cf2_fixedAbs( SUB_INT32( intersection->x,
+ v1->x ) ) < glyphpath->snapThreshold )
+ intersection->x = v1->x;
+ if ( v1->y == v2->y &&
+ cf2_fixedAbs( SUB_INT32( intersection->y,
+ v1->y ) ) < glyphpath->snapThreshold )
+ intersection->y = v1->y;
+
+ /* limit the intersection distance from midpoint of u2 and v1 */
+ if ( cf2_fixedAbs( intersection->x - ADD_INT32( u2->x, v1->x ) / 2 ) >
+ glyphpath->miterLimit ||
+ cf2_fixedAbs( intersection->y - ADD_INT32( u2->y, v1->y ) / 2 ) >
+ glyphpath->miterLimit )
+ return FALSE;
+
+ return TRUE;
+ }
+
+
+ /*
+ * Push the cached element (glyphpath->prevElem*) to the outline
+ * consumer. When a darkening offset is used, the end point of the
+ * cached element may be adjusted to an intersection point or we may
+ * synthesize a connecting line to the current element. If we are
+ * closing a subpath, we may also generate a connecting line to the start
+ * point.
+ *
+ * This is where Character Space (CS) is converted to Device Space (DS)
+ * using a hint map. This calculation must use a HintMap that was valid
+ * at the time the element was saved. For the first point in a subpath,
+ * that is a saved HintMap. For most elements, it just means the caller
+ * has delayed building a HintMap from the current HintMask.
+ *
+ * Transform each point with outerTransform and call the outline
+ * callbacks. This is a general 3x3 transform:
+ *
+ * x' = a*x + c*y + tx, y' = b*x + d*y + ty
+ *
+ * but it uses 4 elements from CF2_Font and the translation part
+ * from CF2_GlyphPath.
+ *
+ */
+ static void
+ cf2_glyphpath_pushPrevElem( CF2_GlyphPath glyphpath,
+ CF2_HintMap hintmap,
+ FT_Vector* nextP0,
+ FT_Vector nextP1,
+ FT_Bool close )
+ {
+ CF2_CallbackParamsRec params;
+
+ FT_Vector* prevP0;
+ FT_Vector* prevP1;
+
+ FT_Vector intersection = { 0, 0 };
+ FT_Bool useIntersection = FALSE;
+
+
+ FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo ||
+ glyphpath->prevElemOp == CF2_PathOpCubeTo );
+
+ if ( glyphpath->prevElemOp == CF2_PathOpLineTo )
+ {
+ prevP0 = &glyphpath->prevElemP0;
+ prevP1 = &glyphpath->prevElemP1;
+ }
+ else
+ {
+ prevP0 = &glyphpath->prevElemP2;
+ prevP1 = &glyphpath->prevElemP3;
+ }
+
+ /* optimization: if previous and next elements are offset by the same */
+ /* amount, then there will be no gap, and no need to compute an */
+ /* intersection. */
+ if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y )
+ {
+ /* previous element does not join next element: */
+ /* adjust end point of previous element to the intersection */
+ useIntersection = cf2_glyphpath_computeIntersection( glyphpath,
+ prevP0,
+ prevP1,
+ nextP0,
+ &nextP1,
+ &intersection );
+ if ( useIntersection )
+ {
+ /* modify the last point of the cached element (either line or */
+ /* curve) */
+ *prevP1 = intersection;
+ }
+ }
+
+ params.pt0 = glyphpath->currentDS;
+
+ switch( glyphpath->prevElemOp )
+ {
+ case CF2_PathOpLineTo:
+ params.op = CF2_PathOpLineTo;
+
+ /* note: pt2 and pt3 are unused */
+
+ if ( close )
+ {
+ /* use first hint map if closing */
+ cf2_glyphpath_hintPoint( glyphpath,
+ &glyphpath->firstHintMap,
+ &params.pt1,
+ glyphpath->prevElemP1.x,
+ glyphpath->prevElemP1.y );
+ }
+ else
+ {
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt1,
+ glyphpath->prevElemP1.x,
+ glyphpath->prevElemP1.y );
+ }
+
+ /* output only non-zero length lines */
+ if ( params.pt0.x != params.pt1.x || params.pt0.y != params.pt1.y )
+ {
+ glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt1;
+ }
+ break;
+
+ case CF2_PathOpCubeTo:
+ params.op = CF2_PathOpCubeTo;
+
+ /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt1,
+ glyphpath->prevElemP1.x,
+ glyphpath->prevElemP1.y );
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt2,
+ glyphpath->prevElemP2.x,
+ glyphpath->prevElemP2.y );
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt3,
+ glyphpath->prevElemP3.x,
+ glyphpath->prevElemP3.y );
+
+ glyphpath->callbacks->cubeTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt3;
+
+ break;
+ }
+
+ if ( !useIntersection || close )
+ {
+ /* insert connecting line between end of previous element and start */
+ /* of current one */
+ /* note: at the end of a subpath, we might do both, so use `nextP0' */
+ /* before we change it, below */
+
+ if ( close )
+ {
+ /* if we are closing the subpath, then nextP0 is in the first */
+ /* hint zone */
+ cf2_glyphpath_hintPoint( glyphpath,
+ &glyphpath->firstHintMap,
+ &params.pt1,
+ nextP0->x,
+ nextP0->y );
+ }
+ else
+ {
+ cf2_glyphpath_hintPoint( glyphpath,
+ hintmap,
+ &params.pt1,
+ nextP0->x,
+ nextP0->y );
+ }
+
+ if ( params.pt1.x != glyphpath->currentDS.x ||
+ params.pt1.y != glyphpath->currentDS.y )
+ {
+ /* length is nonzero */
+ params.op = CF2_PathOpLineTo;
+ params.pt0 = glyphpath->currentDS;
+
+ /* note: pt2 and pt3 are unused */
+ glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt1;
+ }
+ }
+
+ if ( useIntersection )
+ {
+ /* return intersection point to caller */
+ *nextP0 = intersection;
+ }
+ }
+
+
+ /* push a MoveTo element based on current point and offset of current */
+ /* element */
+ static void
+ cf2_glyphpath_pushMove( CF2_GlyphPath glyphpath,
+ FT_Vector start )
+ {
+ CF2_CallbackParamsRec params;
+
+
+ params.op = CF2_PathOpMoveTo;
+ params.pt0 = glyphpath->currentDS;
+
+ /* Test if move has really happened yet; it would have called */
+ /* `cf2_hintmap_build' to set `isValid'. */
+ if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) )
+ {
+ /* we are here iff first subpath is missing a moveto operator: */
+ /* synthesize first moveTo to finish initialization of hintMap */
+ cf2_glyphpath_moveTo( glyphpath,
+ glyphpath->start.x,
+ glyphpath->start.y );
+ }
+
+ cf2_glyphpath_hintPoint( glyphpath,
+ &glyphpath->hintMap,
+ &params.pt1,
+ start.x,
+ start.y );
+
+ /* note: pt2 and pt3 are unused */
+ glyphpath->callbacks->moveTo( glyphpath->callbacks, &params );
+
+ glyphpath->currentDS = params.pt1;
+ glyphpath->offsetStart0 = start;
+ }
+
+
+ /*
+ * All coordinates are in character space.
+ * On input, (x1, y1) and (x2, y2) give line segment.
+ * On output, (x, y) give offset vector.
+ * We use a piecewise approximation to trig functions.
+ *
+ * TODO: Offset true perpendicular and proper length
+ * supply the y-translation for hinting here, too,
+ * that adds yOffset unconditionally to *y.
+ */
+ static void
+ cf2_glyphpath_computeOffset( CF2_GlyphPath glyphpath,
+ CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2,
+ CF2_Fixed* x,
+ CF2_Fixed* y )
+ {
+ CF2_Fixed dx = SUB_INT32( x2, x1 );
+ CF2_Fixed dy = SUB_INT32( y2, y1 );
+
+
+ /* note: negative offsets don't work here; negate deltas to change */
+ /* quadrants, below */
+ if ( glyphpath->font->reverseWinding )
+ {
+ dx = NEG_INT32( dx );
+ dy = NEG_INT32( dy );
+ }
+
+ *x = *y = 0;
+
+ if ( !glyphpath->darken )
+ return;
+
+ /* add momentum for this path element */
+ glyphpath->callbacks->windingMomentum =
+ ADD_INT32( glyphpath->callbacks->windingMomentum,
+ cf2_getWindingMomentum( x1, y1, x2, y2 ) );
+
+ /* note: allow mixed integer and fixed multiplication here */
+ if ( dx >= 0 )
+ {
+ if ( dy >= 0 )
+ {
+ /* first quadrant, +x +y */
+
+ if ( dx > MUL_INT32( 2, dy ) )
+ {
+ /* +x */
+ *x = 0;
+ *y = 0;
+ }
+ else if ( dy > MUL_INT32( 2, dx ) )
+ {
+ /* +y */
+ *x = glyphpath->xOffset;
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* +x +y */
+ *x = FT_MulFix( cf2_doubleToFixed( 0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 - 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ else
+ {
+ /* fourth quadrant, +x -y */
+
+ if ( dx > MUL_INT32( -2, dy ) )
+ {
+ /* +x */
+ *x = 0;
+ *y = 0;
+ }
+ else if ( NEG_INT32( dy ) > MUL_INT32( 2, dx ) )
+ {
+ /* -y */
+ *x = NEG_INT32( glyphpath->xOffset );
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* +x -y */
+ *x = FT_MulFix( cf2_doubleToFixed( -0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 - 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ }
+ else
+ {
+ if ( dy >= 0 )
+ {
+ /* second quadrant, -x +y */
+
+ if ( NEG_INT32( dx ) > MUL_INT32( 2, dy ) )
+ {
+ /* -x */
+ *x = 0;
+ *y = MUL_INT32( 2, glyphpath->yOffset );
+ }
+ else if ( dy > MUL_INT32( -2, dx ) )
+ {
+ /* +y */
+ *x = glyphpath->xOffset;
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* -x +y */
+ *x = FT_MulFix( cf2_doubleToFixed( 0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 + 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ else
+ {
+ /* third quadrant, -x -y */
+
+ if ( NEG_INT32( dx ) > MUL_INT32( -2, dy ) )
+ {
+ /* -x */
+ *x = 0;
+ *y = MUL_INT32( 2, glyphpath->yOffset );
+ }
+ else if ( NEG_INT32( dy ) > MUL_INT32( -2, dx ) )
+ {
+ /* -y */
+ *x = NEG_INT32( glyphpath->xOffset );
+ *y = glyphpath->yOffset;
+ }
+ else
+ {
+ /* -x -y */
+ *x = FT_MulFix( cf2_doubleToFixed( -0.7 ),
+ glyphpath->xOffset );
+ *y = FT_MulFix( cf2_doubleToFixed( 1.0 + 0.7 ),
+ glyphpath->yOffset );
+ }
+ }
+ }
+ }
+
+
+ /*
+ * The functions cf2_glyphpath_{moveTo,lineTo,curveTo,closeOpenPath} are
+ * called by the interpreter with Character Space (CS) coordinates. Each
+ * path element is placed into a queue of length one to await the
+ * calculation of the following element. At that time, the darkening
+ * offset of the following element is known and joins can be computed,
+ * including possible modification of this element, before mapping to
+ * Device Space (DS) and passing it on to the outline consumer.
+ *
+ */
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y )
+ {
+ cf2_glyphpath_closeOpenPath( glyphpath );
+
+ /* save the parameters of the move for later, when we'll know how to */
+ /* offset it; */
+ /* also save last move point */
+ glyphpath->currentCS.x = glyphpath->start.x = x;
+ glyphpath->currentCS.y = glyphpath->start.y = y;
+
+ glyphpath->moveIsPending = TRUE;
+
+ /* ensure we have a valid map with current mask */
+ if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+ cf2_hintmask_isNew( glyphpath->hintMask ) )
+ cf2_hintmap_build( &glyphpath->hintMap,
+ glyphpath->hStemHintArray,
+ glyphpath->vStemHintArray,
+ glyphpath->hintMask,
+ glyphpath->hintOriginY,
+ FALSE );
+
+ /* save a copy of current HintMap to use when drawing initial point */
+ glyphpath->firstHintMap = glyphpath->hintMap; /* structure copy */
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y )
+ {
+ CF2_Fixed xOffset, yOffset;
+ FT_Vector P0, P1;
+ FT_Bool newHintMap;
+
+ /*
+ * New hints will be applied after cf2_glyphpath_pushPrevElem has run.
+ * In case this is a synthesized closing line, any new hints should be
+ * delayed until this path is closed (`cf2_hintmask_isNew' will be
+ * called again before the next line or curve).
+ */
+
+ /* true if new hint map not on close */
+ newHintMap = cf2_hintmask_isNew( glyphpath->hintMask ) &&
+ !glyphpath->pathIsClosing;
+
+ /*
+ * Zero-length lines may occur in the charstring. Because we cannot
+ * compute darkening offsets or intersections from zero-length lines,
+ * it is best to remove them and avoid artifacts. However, zero-length
+ * lines in CS at the start of a new hint map can generate non-zero
+ * lines in DS due to hint substitution. We detect a change in hint
+ * map here and pass those zero-length lines along.
+ */
+
+ /*
+ * Note: Find explicitly closed paths here with a conditional
+ * breakpoint using
+ *
+ * !gp->pathIsClosing && gp->start.x == x && gp->start.y == y
+ *
+ */
+
+ if ( glyphpath->currentCS.x == x &&
+ glyphpath->currentCS.y == y &&
+ !newHintMap )
+ /*
+ * Ignore zero-length lines in CS where the hint map is the same
+ * because the line in DS will also be zero length.
+ *
+ * Ignore zero-length lines when we synthesize a closing line because
+ * the close will be handled in cf2_glyphPath_pushPrevElem.
+ */
+ return;
+
+ cf2_glyphpath_computeOffset( glyphpath,
+ glyphpath->currentCS.x,
+ glyphpath->currentCS.y,
+ x,
+ y,
+ &xOffset,
+ &yOffset );
+
+ /* construct offset points */
+ P0.x = ADD_INT32( glyphpath->currentCS.x, xOffset );
+ P0.y = ADD_INT32( glyphpath->currentCS.y, yOffset );
+ P1.x = ADD_INT32( x, xOffset );
+ P1.y = ADD_INT32( y, yOffset );
+
+ if ( glyphpath->moveIsPending )
+ {
+ /* emit offset 1st point as MoveTo */
+ cf2_glyphpath_pushMove( glyphpath, P0 );
+
+ glyphpath->moveIsPending = FALSE; /* adjust state machine */
+ glyphpath->pathIsOpen = TRUE;
+
+ glyphpath->offsetStart1 = P1; /* record second point */
+ }
+
+ if ( glyphpath->elemIsQueued )
+ {
+ FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+ glyphpath->hintMap.count == 0 );
+
+ cf2_glyphpath_pushPrevElem( glyphpath,
+ &glyphpath->hintMap,
+ &P0,
+ P1,
+ FALSE );
+ }
+
+ /* queue the current element with offset points */
+ glyphpath->elemIsQueued = TRUE;
+ glyphpath->prevElemOp = CF2_PathOpLineTo;
+ glyphpath->prevElemP0 = P0;
+ glyphpath->prevElemP1 = P1;
+
+ /* update current map */
+ if ( newHintMap )
+ cf2_hintmap_build( &glyphpath->hintMap,
+ glyphpath->hStemHintArray,
+ glyphpath->vStemHintArray,
+ glyphpath->hintMask,
+ glyphpath->hintOriginY,
+ FALSE );
+
+ glyphpath->currentCS.x = x; /* pre-offset current point */
+ glyphpath->currentCS.y = y;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2,
+ CF2_Fixed x3,
+ CF2_Fixed y3 )
+ {
+ CF2_Fixed xOffset1, yOffset1, xOffset3, yOffset3;
+ FT_Vector P0, P1, P2, P3;
+
+
+ /* TODO: ignore zero length portions of curve?? */
+ cf2_glyphpath_computeOffset( glyphpath,
+ glyphpath->currentCS.x,
+ glyphpath->currentCS.y,
+ x1,
+ y1,
+ &xOffset1,
+ &yOffset1 );
+ cf2_glyphpath_computeOffset( glyphpath,
+ x2,
+ y2,
+ x3,
+ y3,
+ &xOffset3,
+ &yOffset3 );
+
+ /* add momentum from the middle segment */
+ glyphpath->callbacks->windingMomentum =
+ ADD_INT32( glyphpath->callbacks->windingMomentum,
+ cf2_getWindingMomentum( x1, y1, x2, y2 ) );
+
+ /* construct offset points */
+ P0.x = ADD_INT32( glyphpath->currentCS.x, xOffset1 );
+ P0.y = ADD_INT32( glyphpath->currentCS.y, yOffset1 );
+ P1.x = ADD_INT32( x1, xOffset1 );
+ P1.y = ADD_INT32( y1, yOffset1 );
+ /* note: preserve angle of final segment by using offset3 at both ends */
+ P2.x = ADD_INT32( x2, xOffset3 );
+ P2.y = ADD_INT32( y2, yOffset3 );
+ P3.x = ADD_INT32( x3, xOffset3 );
+ P3.y = ADD_INT32( y3, yOffset3 );
+
+ if ( glyphpath->moveIsPending )
+ {
+ /* emit offset 1st point as MoveTo */
+ cf2_glyphpath_pushMove( glyphpath, P0 );
+
+ glyphpath->moveIsPending = FALSE;
+ glyphpath->pathIsOpen = TRUE;
+
+ glyphpath->offsetStart1 = P1; /* record second point */
+ }
+
+ if ( glyphpath->elemIsQueued )
+ {
+ FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+ glyphpath->hintMap.count == 0 );
+
+ cf2_glyphpath_pushPrevElem( glyphpath,
+ &glyphpath->hintMap,
+ &P0,
+ P1,
+ FALSE );
+ }
+
+ /* queue the current element with offset points */
+ glyphpath->elemIsQueued = TRUE;
+ glyphpath->prevElemOp = CF2_PathOpCubeTo;
+ glyphpath->prevElemP0 = P0;
+ glyphpath->prevElemP1 = P1;
+ glyphpath->prevElemP2 = P2;
+ glyphpath->prevElemP3 = P3;
+
+ /* update current map */
+ if ( cf2_hintmask_isNew( glyphpath->hintMask ) )
+ cf2_hintmap_build( &glyphpath->hintMap,
+ glyphpath->hStemHintArray,
+ glyphpath->vStemHintArray,
+ glyphpath->hintMask,
+ glyphpath->hintOriginY,
+ FALSE );
+
+ glyphpath->currentCS.x = x3; /* pre-offset current point */
+ glyphpath->currentCS.y = y3;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath )
+ {
+ if ( glyphpath->pathIsOpen )
+ {
+ /*
+ * A closing line in Character Space line is always generated below
+ * with `cf2_glyphPath_lineTo'. It may be ignored later if it turns
+ * out to be zero length in Device Space.
+ */
+ glyphpath->pathIsClosing = TRUE;
+
+ cf2_glyphpath_lineTo( glyphpath,
+ glyphpath->start.x,
+ glyphpath->start.y );
+
+ /* empty the final element from the queue and close the path */
+ if ( glyphpath->elemIsQueued )
+ cf2_glyphpath_pushPrevElem( glyphpath,
+ &glyphpath->hintMap,
+ &glyphpath->offsetStart0,
+ glyphpath->offsetStart1,
+ TRUE );
+
+ /* reset state machine */
+ glyphpath->moveIsPending = TRUE;
+ glyphpath->pathIsOpen = FALSE;
+ glyphpath->pathIsClosing = FALSE;
+ glyphpath->elemIsQueued = FALSE;
+ }
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/pshints.h b/modules/freetype2/src/psaux/pshints.h
new file mode 100644
index 0000000000..31a8230364
--- /dev/null
+++ b/modules/freetype2/src/psaux/pshints.h
@@ -0,0 +1,288 @@
+/****************************************************************************
+ *
+ * pshints.h
+ *
+ * Adobe's code for handling CFF hints (body).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSHINT_H_
+#define PSHINT_H_
+
+FT_BEGIN_HEADER
+
+
+ enum
+ {
+ CF2_MAX_HINTS = 96 /* maximum # of hints */
+ };
+
+
+ /*
+ * A HintMask object stores a bit mask that specifies which hints in the
+ * charstring are active at a given time. Hints in CFF must be declared
+ * at the start, before any drawing operators, with horizontal hints
+ * preceding vertical hints. The HintMask is ordered the same way, with
+ * horizontal hints immediately followed by vertical hints. Clients are
+ * responsible for knowing how many of each type are present.
+ *
+ * The maximum total number of hints is 96, as specified by the CFF
+ * specification.
+ *
+ * A HintMask is built 0 or more times while interpreting a charstring, by
+ * the HintMask operator. There is only one HintMask, but it is built or
+ * rebuilt each time there is a hint substitution (HintMask operator) in
+ * the charstring. A default HintMask with all bits set is built if there
+ * has been no HintMask operator prior to the first drawing operator.
+ *
+ */
+
+ typedef struct CF2_HintMaskRec_
+ {
+ FT_Error* error;
+
+ FT_Bool isValid;
+ FT_Bool isNew;
+
+ size_t bitCount;
+ size_t byteCount;
+
+ FT_Byte mask[( CF2_MAX_HINTS + 7 ) / 8];
+
+ } CF2_HintMaskRec, *CF2_HintMask;
+
+
+ typedef struct CF2_StemHintRec_
+ {
+ FT_Bool used; /* DS positions are valid */
+
+ CF2_Fixed min; /* original character space value */
+ CF2_Fixed max;
+
+ CF2_Fixed minDS; /* DS position after first use */
+ CF2_Fixed maxDS;
+
+ } CF2_StemHintRec, *CF2_StemHint;
+
+
+ /*
+ * A HintMap object stores a piecewise linear function for mapping
+ * y-coordinates from character space to device space, providing
+ * appropriate pixel alignment to stem edges.
+ *
+ * The map is implemented as an array of `CF2_Hint' elements, each
+ * representing an edge. When edges are paired, as from stem hints, the
+ * bottom edge must immediately precede the top edge in the array.
+ * Element character space AND device space positions must both increase
+ * monotonically in the array. `CF2_Hint' elements are also used as
+ * parameters to `cf2_blues_capture'.
+ *
+ * The `cf2_hintmap_build' method must be called before any drawing
+ * operation (beginning with a Move operator) and at each hint
+ * substitution (HintMask operator).
+ *
+ * The `cf2_hintmap_map' method is called to transform y-coordinates at
+ * each drawing operation (move, line, curve).
+ *
+ */
+
+ /* TODO: make this a CF2_ArrStack and add a deep copy method */
+ enum
+ {
+ CF2_MAX_HINT_EDGES = CF2_MAX_HINTS * 2
+ };
+
+
+ typedef struct CF2_HintMapRec_
+ {
+ CF2_Font font;
+
+ /* initial map based on blue zones */
+ struct CF2_HintMapRec_* initialHintMap;
+
+ /* working storage for 2nd pass adjustHints */
+ CF2_ArrStack hintMoves;
+
+ FT_Bool isValid;
+ FT_Bool hinted;
+
+ CF2_Fixed scale;
+ CF2_UInt count;
+
+ /* start search from this index */
+ CF2_UInt lastIndex;
+
+ CF2_HintRec edge[CF2_MAX_HINT_EDGES]; /* 192 */
+
+ } CF2_HintMapRec, *CF2_HintMap;
+
+
+ FT_LOCAL( FT_Bool )
+ cf2_hint_isValid( const CF2_Hint hint );
+ FT_LOCAL( FT_Bool )
+ cf2_hint_isTop( const CF2_Hint hint );
+ FT_LOCAL( FT_Bool )
+ cf2_hint_isBottom( const CF2_Hint hint );
+ FT_LOCAL( void )
+ cf2_hint_lock( CF2_Hint hint );
+
+
+ FT_LOCAL( void )
+ cf2_hintmap_init( CF2_HintMap hintmap,
+ CF2_Font font,
+ CF2_HintMap initialMap,
+ CF2_ArrStack hintMoves,
+ CF2_Fixed scale );
+ FT_LOCAL( void )
+ cf2_hintmap_build( CF2_HintMap hintmap,
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOrigin,
+ FT_Bool initialMap );
+
+
+ /*
+ * GlyphPath is a wrapper for drawing operations that scales the
+ * coordinates according to the render matrix and HintMap. It also tracks
+ * open paths to control ClosePath and to insert MoveTo for broken fonts.
+ *
+ */
+ typedef struct CF2_GlyphPathRec_
+ {
+ /* TODO: gather some of these into a hinting context */
+
+ CF2_Font font; /* font instance */
+ CF2_OutlineCallbacks callbacks; /* outline consumer */
+
+
+ CF2_HintMapRec hintMap; /* current hint map */
+ CF2_HintMapRec firstHintMap; /* saved copy */
+ CF2_HintMapRec initialHintMap; /* based on all captured hints */
+
+ CF2_ArrStackRec hintMoves; /* list of hint moves for 2nd pass */
+
+ CF2_Fixed scaleX; /* matrix a */
+ CF2_Fixed scaleC; /* matrix c */
+ CF2_Fixed scaleY; /* matrix d */
+
+ FT_Vector fractionalTranslation; /* including deviceXScale */
+#if 0
+ CF2_Fixed hShift; /* character space horizontal shift */
+ /* (for fauxing) */
+#endif
+
+ FT_Bool pathIsOpen; /* true after MoveTo */
+ FT_Bool pathIsClosing; /* true when synthesizing closepath line */
+ FT_Bool darken; /* true if stem darkening */
+ FT_Bool moveIsPending; /* true between MoveTo and offset MoveTo */
+
+ /* references used to call `cf2_hintmap_build', if necessary */
+ CF2_ArrStack hStemHintArray;
+ CF2_ArrStack vStemHintArray;
+ CF2_HintMask hintMask; /* ptr to the current mask */
+ CF2_Fixed hintOriginY; /* copy of current origin */
+ const CF2_BluesRec* blues;
+
+ CF2_Fixed xOffset; /* character space offsets */
+ CF2_Fixed yOffset;
+
+ /* character space miter limit threshold */
+ CF2_Fixed miterLimit;
+ /* vertical/horizontal snap distance in character space */
+ CF2_Fixed snapThreshold;
+
+ FT_Vector offsetStart0; /* first and second points of first */
+ FT_Vector offsetStart1; /* element with offset applied */
+
+ /* current point, character space, before offset */
+ FT_Vector currentCS;
+ /* current point, device space */
+ FT_Vector currentDS;
+ /* start point of subpath, character space */
+ FT_Vector start;
+
+ /* the following members constitute the `queue' of one element */
+ FT_Bool elemIsQueued;
+ CF2_Int prevElemOp;
+
+ FT_Vector prevElemP0;
+ FT_Vector prevElemP1;
+ FT_Vector prevElemP2;
+ FT_Vector prevElemP3;
+
+ } CF2_GlyphPathRec, *CF2_GlyphPath;
+
+
+ FT_LOCAL( void )
+ cf2_glyphpath_init( CF2_GlyphPath glyphpath,
+ CF2_Font font,
+ CF2_OutlineCallbacks callbacks,
+ CF2_Fixed scaleY,
+ /* CF2_Fixed hShift, */
+ CF2_ArrStack hStemHintArray,
+ CF2_ArrStack vStemHintArray,
+ CF2_HintMask hintMask,
+ CF2_Fixed hintOrigin,
+ const CF2_Blues blues,
+ const FT_Vector* fractionalTranslation );
+ FT_LOCAL( void )
+ cf2_glyphpath_finalize( CF2_GlyphPath glyphpath );
+
+ FT_LOCAL( void )
+ cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y );
+ FT_LOCAL( void )
+ cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x,
+ CF2_Fixed y );
+ FT_LOCAL( void )
+ cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath,
+ CF2_Fixed x1,
+ CF2_Fixed y1,
+ CF2_Fixed x2,
+ CF2_Fixed y2,
+ CF2_Fixed x3,
+ CF2_Fixed y3 );
+ FT_LOCAL( void )
+ cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath );
+
+
+FT_END_HEADER
+
+
+#endif /* PSHINT_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psintrp.c b/modules/freetype2/src/psaux/psintrp.c
new file mode 100644
index 0000000000..6c640eebd5
--- /dev/null
+++ b/modules/freetype2/src/psaux/psintrp.c
@@ -0,0 +1,3059 @@
+/****************************************************************************
+ *
+ * psintrp.c
+ *
+ * Adobe's CFF Interpreter (body).
+ *
+ * Copyright 2007-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 <freetype/internal/ftdebug.h>
+#include <freetype/internal/services/svcfftl.h>
+
+#include "psglue.h"
+#include "psfont.h"
+#include "psstack.h"
+#include "pshints.h"
+#include "psintrp.h"
+
+#include "pserror.h"
+
+#include "psobjs.h" /* for cff_random */
+#include "t1decode.h" /* for t1 seac */
+
+
+ /**************************************************************************
+ *
+ * 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 cf2interp
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmask_init( CF2_HintMask hintmask,
+ FT_Error* error )
+ {
+ FT_ZERO( hintmask );
+
+ hintmask->error = error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hintmask_isValid( const CF2_HintMask hintmask )
+ {
+ return hintmask->isValid;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_hintmask_isNew( const CF2_HintMask hintmask )
+ {
+ return hintmask->isNew;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmask_setNew( CF2_HintMask hintmask,
+ FT_Bool val )
+ {
+ hintmask->isNew = val;
+ }
+
+
+ /* clients call `getMaskPtr' in order to iterate */
+ /* through hint mask */
+
+ FT_LOCAL_DEF( FT_Byte* )
+ cf2_hintmask_getMaskPtr( CF2_HintMask hintmask )
+ {
+ return hintmask->mask;
+ }
+
+
+ static size_t
+ cf2_hintmask_setCounts( CF2_HintMask hintmask,
+ size_t bitCount )
+ {
+ if ( bitCount > CF2_MAX_HINTS )
+ {
+ /* total of h and v stems must be <= 96 */
+ CF2_SET_ERROR( hintmask->error, Invalid_Glyph_Format );
+ return 0;
+ }
+
+ hintmask->bitCount = bitCount;
+ hintmask->byteCount = ( hintmask->bitCount + 7 ) / 8;
+
+ hintmask->isValid = TRUE;
+ hintmask->isNew = TRUE;
+
+ return bitCount;
+ }
+
+
+ /* consume the hintmask bytes from the charstring, advancing the src */
+ /* pointer */
+ static void
+ cf2_hintmask_read( CF2_HintMask hintmask,
+ CF2_Buffer charstring,
+ size_t bitCount )
+ {
+ size_t i;
+
+#ifndef CF2_NDEBUG
+ /* these are the bits in the final mask byte that should be zero */
+ /* Note: this variable is only used in an assert expression below */
+ /* and then only if CF2_NDEBUG is not defined */
+ CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
+#endif
+
+
+ /* initialize counts and isValid */
+ if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
+ return;
+
+ FT_ASSERT( hintmask->byteCount > 0 );
+
+ FT_TRACE4(( " (maskbytes:" ));
+
+ /* set mask and advance interpreter's charstring pointer */
+ for ( i = 0; i < hintmask->byteCount; i++ )
+ {
+ hintmask->mask[i] = (FT_Byte)cf2_buf_readByte( charstring );
+ FT_TRACE4(( " 0x%02X", hintmask->mask[i] ));
+ }
+
+ FT_TRACE4(( ")\n" ));
+
+ /* assert any unused bits in last byte are zero unless there's a prior */
+ /* error */
+ /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */
+#ifndef CF2_NDEBUG
+ FT_ASSERT( ( hintmask->mask[hintmask->byteCount - 1] & mask ) == 0 ||
+ *hintmask->error );
+#endif
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_hintmask_setAll( CF2_HintMask hintmask,
+ size_t bitCount )
+ {
+ size_t i;
+ CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
+
+
+ /* initialize counts and isValid */
+ if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
+ return;
+
+ FT_ASSERT( hintmask->byteCount > 0 );
+ FT_ASSERT( hintmask->byteCount <=
+ sizeof ( hintmask->mask ) / sizeof ( hintmask->mask[0] ) );
+
+ /* set mask to all ones */
+ for ( i = 0; i < hintmask->byteCount; i++ )
+ hintmask->mask[i] = 0xFF;
+
+ /* clear unused bits */
+ /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */
+ hintmask->mask[hintmask->byteCount - 1] &= ~mask;
+ }
+
+
+ /* Type2 charstring opcodes */
+ enum
+ {
+ cf2_cmdRESERVED_0, /* 0 */
+ cf2_cmdHSTEM, /* 1 */
+ cf2_cmdRESERVED_2, /* 2 */
+ cf2_cmdVSTEM, /* 3 */
+ cf2_cmdVMOVETO, /* 4 */
+ cf2_cmdRLINETO, /* 5 */
+ cf2_cmdHLINETO, /* 6 */
+ cf2_cmdVLINETO, /* 7 */
+ cf2_cmdRRCURVETO, /* 8 */
+ cf2_cmdCLOSEPATH, /* 9 T1 only */
+ cf2_cmdCALLSUBR, /* 10 */
+ cf2_cmdRETURN, /* 11 */
+ cf2_cmdESC, /* 12 */
+ cf2_cmdHSBW, /* 13 T1 only */
+ cf2_cmdENDCHAR, /* 14 */
+ cf2_cmdVSINDEX, /* 15 */
+ cf2_cmdBLEND, /* 16 */
+ cf2_cmdRESERVED_17, /* 17 */
+ cf2_cmdHSTEMHM, /* 18 */
+ cf2_cmdHINTMASK, /* 19 */
+ cf2_cmdCNTRMASK, /* 20 */
+ cf2_cmdRMOVETO, /* 21 */
+ cf2_cmdHMOVETO, /* 22 */
+ cf2_cmdVSTEMHM, /* 23 */
+ cf2_cmdRCURVELINE, /* 24 */
+ cf2_cmdRLINECURVE, /* 25 */
+ cf2_cmdVVCURVETO, /* 26 */
+ cf2_cmdHHCURVETO, /* 27 */
+ cf2_cmdEXTENDEDNMBR, /* 28 */
+ cf2_cmdCALLGSUBR, /* 29 */
+ cf2_cmdVHCURVETO, /* 30 */
+ cf2_cmdHVCURVETO /* 31 */
+ };
+
+ enum
+ {
+ cf2_escDOTSECTION, /* 0 */
+ cf2_escVSTEM3, /* 1 T1 only */
+ cf2_escHSTEM3, /* 2 T1 only */
+ cf2_escAND, /* 3 */
+ cf2_escOR, /* 4 */
+ cf2_escNOT, /* 5 */
+ cf2_escSEAC, /* 6 T1 only */
+ cf2_escSBW, /* 7 T1 only */
+ cf2_escRESERVED_8, /* 8 */
+ cf2_escABS, /* 9 */
+ cf2_escADD, /* 10 like otherADD */
+ cf2_escSUB, /* 11 like otherSUB */
+ cf2_escDIV, /* 12 */
+ cf2_escRESERVED_13, /* 13 */
+ cf2_escNEG, /* 14 */
+ cf2_escEQ, /* 15 */
+ cf2_escCALLOTHERSUBR,/* 16 T1 only */
+ cf2_escPOP, /* 17 T1 only */
+ cf2_escDROP, /* 18 */
+ cf2_escRESERVED_19, /* 19 */
+ cf2_escPUT, /* 20 like otherPUT */
+ cf2_escGET, /* 21 like otherGET */
+ cf2_escIFELSE, /* 22 like otherIFELSE */
+ cf2_escRANDOM, /* 23 like otherRANDOM */
+ cf2_escMUL, /* 24 like otherMUL */
+ cf2_escRESERVED_25, /* 25 */
+ cf2_escSQRT, /* 26 */
+ cf2_escDUP, /* 27 like otherDUP */
+ cf2_escEXCH, /* 28 like otherEXCH */
+ cf2_escINDEX, /* 29 */
+ cf2_escROLL, /* 30 */
+ cf2_escRESERVED_31, /* 31 */
+ cf2_escRESERVED_32, /* 32 */
+ cf2_escSETCURRENTPT, /* 33 T1 only */
+ cf2_escHFLEX, /* 34 */
+ cf2_escFLEX, /* 35 */
+ cf2_escHFLEX1, /* 36 */
+ cf2_escFLEX1, /* 37 */
+ cf2_escRESERVED_38 /* 38 & all higher */
+ };
+
+
+ /* `stemHintArray' does not change once we start drawing the outline. */
+ static void
+ cf2_doStems( const CF2_Font font,
+ CF2_Stack opStack,
+ CF2_ArrStack stemHintArray,
+ CF2_Fixed* width,
+ FT_Bool* haveWidth,
+ CF2_Fixed hintOffset )
+ {
+ CF2_UInt i;
+ CF2_UInt count = cf2_stack_count( opStack );
+ FT_Bool hasWidthArg = FT_BOOL( count & 1 );
+
+ /* variable accumulates delta values from operand stack */
+ CF2_Fixed position = hintOffset;
+
+ if ( font->isT1 && !font->decoder->flex_state && !*haveWidth )
+ FT_ERROR(( "cf2_doStems (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( !font->isT1 && hasWidthArg && !*haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ cf2_getNominalWidthX( font->decoder ) );
+
+ if ( font->decoder->width_only )
+ goto exit;
+
+ for ( i = hasWidthArg ? 1 : 0; i < count; i += 2 )
+ {
+ /* construct a CF2_StemHint and push it onto the list */
+ CF2_StemHintRec stemhint;
+
+
+ stemhint.min =
+ position = ADD_INT32( position,
+ cf2_stack_getReal( opStack, i ) );
+ stemhint.max =
+ position = ADD_INT32( position,
+ cf2_stack_getReal( opStack, i + 1 ) );
+
+ stemhint.used = FALSE;
+ stemhint.maxDS =
+ stemhint.minDS = 0;
+
+ cf2_arrstack_push( stemHintArray, &stemhint ); /* defer error check */
+ }
+
+ cf2_stack_clear( opStack );
+
+ exit:
+ /* cf2_doStems must define a width (may be default) */
+ *haveWidth = TRUE;
+ }
+
+
+ static void
+ cf2_doFlex( CF2_Stack opStack,
+ CF2_Fixed* curX,
+ CF2_Fixed* curY,
+ CF2_GlyphPath glyphPath,
+ const FT_Bool* readFromStack,
+ FT_Bool doConditionalLastRead )
+ {
+ CF2_Fixed vals[14];
+ CF2_UInt idx;
+ FT_Bool isHFlex;
+ CF2_Int top, i, j;
+
+
+ vals[0] = *curX;
+ vals[1] = *curY;
+ idx = 0;
+ isHFlex = FT_BOOL( readFromStack[9] == FALSE );
+ top = isHFlex ? 9 : 10;
+
+ for ( i = 0; i < top; i++ )
+ {
+ vals[i + 2] = vals[i];
+ if ( readFromStack[i] )
+ vals[i + 2] = ADD_INT32( vals[i + 2], cf2_stack_getReal( opStack,
+ idx++ ) );
+ }
+
+ if ( isHFlex )
+ vals[9 + 2] = *curY;
+
+ if ( doConditionalLastRead )
+ {
+ FT_Bool lastIsX = FT_BOOL(
+ cf2_fixedAbs( SUB_INT32( vals[10], *curX ) ) >
+ cf2_fixedAbs( SUB_INT32( vals[11], *curY ) ) );
+ CF2_Fixed lastVal = cf2_stack_getReal( opStack, idx );
+
+
+ if ( lastIsX )
+ {
+ vals[12] = ADD_INT32( vals[10], lastVal );
+ vals[13] = *curY;
+ }
+ else
+ {
+ vals[12] = *curX;
+ vals[13] = ADD_INT32( vals[11], lastVal );
+ }
+ }
+ else
+ {
+ if ( readFromStack[10] )
+ vals[12] = ADD_INT32( vals[10],
+ cf2_stack_getReal( opStack, idx++ ) );
+ else
+ vals[12] = *curX;
+
+ if ( readFromStack[11] )
+ vals[13] = ADD_INT32( vals[11],
+ cf2_stack_getReal( opStack, idx ) );
+ else
+ vals[13] = *curY;
+ }
+
+ for ( j = 0; j < 2; j++ )
+ cf2_glyphpath_curveTo( glyphPath, vals[j * 6 + 2],
+ vals[j * 6 + 3],
+ vals[j * 6 + 4],
+ vals[j * 6 + 5],
+ vals[j * 6 + 6],
+ vals[j * 6 + 7] );
+
+ cf2_stack_clear( opStack );
+
+ *curX = vals[12];
+ *curY = vals[13];
+ }
+
+
+ /* Blend numOperands on the stack, */
+ /* store results into the first numBlends values, */
+ /* then pop remaining arguments. */
+ static void
+ cf2_doBlend( const CFF_Blend blend,
+ CF2_Stack opStack,
+ CF2_UInt numBlends )
+ {
+ CF2_UInt delta;
+ CF2_UInt base;
+ CF2_UInt i, j;
+ CF2_UInt numOperands = (CF2_UInt)( numBlends * blend->lenBV );
+
+
+ base = cf2_stack_count( opStack ) - numOperands;
+ delta = base + numBlends;
+
+ for ( i = 0; i < numBlends; i++ )
+ {
+ const CF2_Fixed* weight = &blend->BV[1];
+
+ /* start with first term */
+ CF2_Fixed sum = cf2_stack_getReal( opStack, i + base );
+
+
+ for ( j = 1; j < blend->lenBV; j++ )
+ sum = ADD_INT32( sum,
+ FT_MulFix( *weight++,
+ cf2_stack_getReal( opStack,
+ delta++ ) ) );
+
+ /* store blended result */
+ cf2_stack_setReal( opStack, i + base, sum );
+ }
+
+ /* leave only `numBlends' results on stack */
+ cf2_stack_pop( opStack, numOperands - numBlends );
+ }
+
+
+ /*
+ * `error' is a shared error code used by many objects in this
+ * routine. Before the code continues from an error, it must check and
+ * record the error in `*error'. The idea is that this shared
+ * error code will record the first error encountered. If testing
+ * for an error anyway, the cost of `goto exit' is small, so we do it,
+ * even if continuing would be safe. In this case, `lastError' is
+ * set, so the testing and storing can be done in one place, at `exit'.
+ *
+ * Continuing after an error is intended for objects which do their own
+ * testing of `*error', e.g., array stack functions. This allows us to
+ * avoid an extra test after the call.
+ *
+ * Unimplemented opcodes are ignored.
+ *
+ */
+ FT_LOCAL_DEF( void )
+ cf2_interpT2CharString( CF2_Font font,
+ const CF2_Buffer buf,
+ CF2_OutlineCallbacks callbacks,
+ const FT_Vector* translation,
+ FT_Bool doingSeac,
+ CF2_Fixed curX,
+ CF2_Fixed curY,
+ CF2_Fixed* width )
+ {
+ /* lastError is used for errors that are immediately tested */
+ FT_Error lastError = FT_Err_Ok;
+
+ /* pointer to parsed font object */
+ PS_Decoder* decoder = font->decoder;
+
+ FT_Error* error = &font->error;
+ FT_Memory memory = font->memory;
+
+ CF2_Fixed scaleY = font->innerTransform.d;
+ CF2_Fixed nominalWidthX = cf2_getNominalWidthX( decoder );
+
+ /* stuff for Type 1 */
+ FT_Int known_othersubr_result_cnt = 0;
+ FT_Bool large_int = FALSE;
+ FT_Bool initial_map_ready = FALSE;
+
+#define PS_STORAGE_SIZE 3
+ CF2_F16Dot16 results[PS_STORAGE_SIZE]; /* for othersubr results */
+ FT_Int result_cnt = 0;
+
+ /* save this for hinting seac accents */
+ CF2_Fixed hintOriginY = curY;
+
+ CF2_Stack opStack = NULL;
+ FT_UInt stackSize;
+ FT_Byte op1; /* first opcode byte */
+
+ CF2_F16Dot16 storage[CF2_STORAGE_SIZE]; /* for `put' and `get' */
+ CF2_F16Dot16 flexStore[6]; /* for Type 1 flex */
+
+ /* instruction limit; 20,000,000 matches Avalon */
+ FT_UInt32 instructionLimit = 20000000UL;
+
+ CF2_ArrStackRec subrStack;
+
+ FT_Bool haveWidth;
+ CF2_Buffer charstring = NULL;
+
+ CF2_Int charstringIndex = -1; /* initialize to empty */
+
+ /* TODO: placeholders for hint structures */
+
+ /* objects used for hinting */
+ CF2_ArrStackRec hStemHintArray;
+ CF2_ArrStackRec vStemHintArray;
+
+ CF2_HintMaskRec hintMask;
+ CF2_GlyphPathRec glyphPath;
+
+
+ FT_ZERO( &storage );
+ FT_ZERO( &results );
+ FT_ZERO( &flexStore );
+
+ /* initialize the remaining objects */
+ cf2_arrstack_init( &subrStack,
+ memory,
+ error,
+ sizeof ( CF2_BufferRec ) );
+ cf2_arrstack_init( &hStemHintArray,
+ memory,
+ error,
+ sizeof ( CF2_StemHintRec ) );
+ cf2_arrstack_init( &vStemHintArray,
+ memory,
+ error,
+ sizeof ( CF2_StemHintRec ) );
+
+ /* initialize CF2_StemHint arrays */
+ cf2_hintmask_init( &hintMask, error );
+
+ /* initialize path map to manage drawing operations */
+
+ /* Note: last 4 params are used to handle `MoveToPermissive', which */
+ /* may need to call `hintMap.Build' */
+ /* TODO: MoveToPermissive is gone; are these still needed? */
+ cf2_glyphpath_init( &glyphPath,
+ font,
+ callbacks,
+ scaleY,
+ /* hShift, */
+ &hStemHintArray,
+ &vStemHintArray,
+ &hintMask,
+ hintOriginY,
+ &font->blues,
+ translation );
+
+ /*
+ * Initialize state for width parsing. From the CFF Spec:
+ *
+ * The first stack-clearing operator, which must be one of hstem,
+ * hstemhm, vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto,
+ * rmoveto, or endchar, takes an additional argument - the width (as
+ * described earlier), which may be expressed as zero or one numeric
+ * argument.
+ *
+ * What we implement here uses the first validly specified width, but
+ * does not detect errors for specifying more than one width.
+ *
+ * If one of the above operators occurs without explicitly specifying
+ * a width, we assume the default width.
+ *
+ * CFF2 charstrings always return the default width (0).
+ *
+ */
+ haveWidth = font->isCFF2 ? TRUE : FALSE;
+ *width = cf2_getDefaultWidthX( decoder );
+
+ /*
+ * Note: At this point, all pointers to resources must be NULL
+ * and all local objects must be initialized.
+ * There must be no branches to `exit:' above this point.
+ *
+ */
+
+ /* allocate an operand stack */
+ stackSize = font->isCFF2 ? cf2_getMaxstack( decoder )
+ : CF2_OPERAND_STACK_SIZE;
+ opStack = cf2_stack_init( memory, error, stackSize );
+
+ if ( !opStack )
+ {
+ lastError = FT_THROW( Out_Of_Memory );
+ goto exit;
+ }
+
+ /* initialize subroutine stack by placing top level charstring as */
+ /* first element (max depth plus one for the charstring) */
+ /* Note: Caller owns and must finalize the first charstring. */
+ /* Our copy of it does not change that requirement. */
+ cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 );
+
+ charstring = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack );
+
+ /* catch errors so far */
+ if ( *error )
+ goto exit;
+
+ *charstring = *buf; /* structure copy */
+ charstringIndex = 0; /* entry is valid now */
+
+ /* main interpreter loop */
+ while ( 1 )
+ {
+ if ( font->isT1 )
+ FT_ASSERT( known_othersubr_result_cnt == 0 ||
+ result_cnt == 0 );
+
+ if ( cf2_buf_isEnd( charstring ) )
+ {
+ /* If we've reached the end of the charstring, simulate a */
+ /* cf2_cmdRETURN or cf2_cmdENDCHAR. */
+ /* We do this for both CFF and CFF2. */
+ if ( charstringIndex )
+ op1 = cf2_cmdRETURN; /* end of buffer for subroutine */
+ else
+ op1 = cf2_cmdENDCHAR; /* end of buffer for top level charstring */
+ }
+ else
+ {
+ op1 = (FT_Byte)cf2_buf_readByte( charstring );
+
+ /* Explicit RETURN and ENDCHAR in CFF2 should be ignored. */
+ /* Note: Trace message will report 0 instead of 11 or 14. */
+ if ( ( op1 == cf2_cmdRETURN || op1 == cf2_cmdENDCHAR ) &&
+ font->isCFF2 )
+ op1 = cf2_cmdRESERVED_0;
+ }
+
+ if ( font->isT1 )
+ {
+ if ( !initial_map_ready &&
+ !( op1 == cf2_cmdHSTEM ||
+ op1 == cf2_cmdVSTEM ||
+ op1 == cf2_cmdHSBW ||
+ op1 == cf2_cmdCALLSUBR ||
+ op1 == cf2_cmdRETURN ||
+ op1 == cf2_cmdESC ||
+ op1 == cf2_cmdENDCHAR ||
+ op1 >= 32 /* Numbers */ ) )
+ {
+ /* Skip outline commands first time round. */
+ /* `endchar' will trigger initial hintmap build */
+ /* and rewind the charstring. */
+ FT_TRACE4(( " <outline command skipped>\n" ));
+ cf2_stack_clear( opStack );
+ continue;
+ }
+
+ if ( result_cnt > 0 &&
+ !( op1 == cf2_cmdCALLSUBR ||
+ op1 == cf2_cmdRETURN ||
+ op1 == cf2_cmdESC ||
+ op1 >= 32 /* Numbers */ ) )
+ {
+ /* all operands have been transferred by previous pops */
+ result_cnt = 0;
+ }
+
+ if ( large_int && !( op1 >= 32 || op1 == cf2_escDIV ) )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " no `div' after large integer\n" ));
+
+ large_int = FALSE;
+ }
+ }
+
+ /* check for errors once per loop */
+ if ( *error )
+ goto exit;
+
+ instructionLimit--;
+ if ( instructionLimit == 0 )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ switch( op1 )
+ {
+ case cf2_cmdRESERVED_0:
+ case cf2_cmdRESERVED_2:
+ case cf2_cmdRESERVED_17:
+ /* we may get here if we have a prior error */
+ FT_TRACE4(( " unknown op (%d)\n", op1 ));
+ break;
+
+ case cf2_cmdVSINDEX:
+ FT_TRACE4(( " vsindex\n" ));
+
+ if ( !font->isCFF2 )
+ break; /* clear stack & ignore */
+
+ if ( font->blend.usedBV )
+ {
+ /* vsindex not allowed after blend */
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ {
+ FT_Int temp = cf2_stack_popInt( opStack );
+
+
+ if ( temp >= 0 )
+ font->vsindex = (FT_UInt)temp;
+ }
+ break;
+
+ case cf2_cmdBLEND:
+ {
+ FT_UInt numBlends;
+
+
+ FT_TRACE4(( " blend\n" ));
+
+ if ( !font->isCFF2 )
+ break; /* clear stack & ignore */
+
+ /* do we have a `blend' op in a non-variant font? */
+ if ( !font->blend.font )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* check cached blend vector */
+ if ( font->cffload->blend_check_vector( &font->blend,
+ font->vsindex,
+ font->lenNDV,
+ font->NDV ) )
+ {
+ lastError = font->cffload->blend_build_vector( &font->blend,
+ font->vsindex,
+ font->lenNDV,
+ font->NDV );
+ if ( lastError )
+ goto exit;
+ }
+
+ /* do the blend */
+ numBlends = (FT_UInt)cf2_stack_popInt( opStack );
+ if ( numBlends > stackSize )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ cf2_doBlend( &font->blend, opStack, numBlends );
+
+ font->blend.usedBV = TRUE;
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_cmdHSTEMHM:
+ case cf2_cmdHSTEM:
+ FT_TRACE4(( "%s\n", op1 == cf2_cmdHSTEMHM ? " hstemhm"
+ : " hstem" ));
+
+ if ( !font->isT1 )
+ {
+ /* never add hints after the mask is computed */
+ /* except if in Type 1 mode (no hintmask op) */
+ if ( cf2_hintmask_isValid( &hintMask ) )
+ {
+ FT_TRACE4(( "cf2_interpT2CharString:"
+ " invalid horizontal hint mask\n" ));
+ break;
+ }
+ }
+
+ /* add left-sidebearing correction in Type 1 mode */
+ cf2_doStems( font,
+ opStack,
+ &hStemHintArray,
+ width,
+ &haveWidth,
+ font->isT1 ? decoder->builder.left_bearing->y
+ : 0 );
+
+ if ( decoder->width_only )
+ goto exit;
+
+ break;
+
+ case cf2_cmdVSTEMHM:
+ case cf2_cmdVSTEM:
+ FT_TRACE4(( "%s\n", op1 == cf2_cmdVSTEMHM ? " vstemhm"
+ : " vstem" ));
+
+ if ( !font->isT1 )
+ {
+ /* never add hints after the mask is computed */
+ /* except if in Type 1 mode (no hintmask op) */
+ if ( cf2_hintmask_isValid( &hintMask ) )
+ {
+ FT_TRACE4(( "cf2_interpT2CharString:"
+ " invalid vertical hint mask\n" ));
+ break;
+ }
+ }
+
+ /* add left-sidebearing correction in Type 1 mode */
+ cf2_doStems( font,
+ opStack,
+ &vStemHintArray,
+ width,
+ &haveWidth,
+ font->isT1 ? decoder->builder.left_bearing->x
+ : 0 );
+
+ if ( decoder->width_only )
+ goto exit;
+
+ break;
+
+ case cf2_cmdVMOVETO:
+ FT_TRACE4(( " vmoveto\n" ));
+
+ if ( font->isT1 && !decoder->flex_state && !haveWidth )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( cf2_stack_count( opStack ) > 1 && !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ curY = ADD_INT32( curY, cf2_stack_popFixed( opStack ) );
+
+ if ( !decoder->flex_state )
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ break;
+
+ case cf2_cmdRLINETO:
+ {
+ CF2_UInt idx;
+ CF2_UInt count = cf2_stack_count( opStack );
+
+
+ FT_TRACE4(( " rlineto\n" ));
+
+ for ( idx = 0; idx < count; idx += 2 )
+ {
+ curX = ADD_INT32( curX, cf2_stack_getReal( opStack,
+ idx + 0 ) );
+ curY = ADD_INT32( curY, cf2_stack_getReal( opStack,
+ idx + 1 ) );
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdHLINETO:
+ case cf2_cmdVLINETO:
+ {
+ CF2_UInt idx;
+ CF2_UInt count = cf2_stack_count( opStack );
+
+ FT_Bool isX = FT_BOOL( op1 == cf2_cmdHLINETO );
+
+
+ FT_TRACE4(( "%s\n", isX ? " hlineto" : " vlineto" ));
+
+ for ( idx = 0; idx < count; idx++ )
+ {
+ CF2_Fixed v = cf2_stack_getReal( opStack, idx );
+
+
+ if ( isX )
+ curX = ADD_INT32( curX, v );
+ else
+ curY = ADD_INT32( curY, v );
+
+ isX = !isX;
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue;
+
+ case cf2_cmdRCURVELINE:
+ case cf2_cmdRRCURVETO:
+ {
+ CF2_UInt count = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ FT_TRACE4(( "%s\n", op1 == cf2_cmdRCURVELINE ? " rcurveline"
+ : " rrcurveto" ));
+
+ while ( idx + 6 <= count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), x2 );
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 5 ), y2 );
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 6;
+ }
+
+ if ( op1 == cf2_cmdRCURVELINE )
+ {
+ curX = ADD_INT32( curX, cf2_stack_getReal( opStack,
+ idx + 0 ) );
+ curY = ADD_INT32( curY, cf2_stack_getReal( opStack,
+ idx + 1 ) );
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdCLOSEPATH:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (%d)\n", op1 ));
+ else
+ {
+ FT_TRACE4(( " closepath\n" ));
+
+ /* if there is no path, `closepath' is a no-op */
+ cf2_glyphpath_closeOpenPath( &glyphPath );
+
+ haveWidth = TRUE;
+ }
+ break;
+
+ case cf2_cmdCALLGSUBR:
+ case cf2_cmdCALLSUBR:
+ {
+ CF2_Int subrNum;
+
+
+ FT_TRACE4(( "%s", op1 == cf2_cmdCALLGSUBR ? " callgsubr"
+ : " callsubr" ));
+
+ if ( ( !font->isT1 && charstringIndex > CF2_MAX_SUBR ) ||
+ ( font->isT1 && charstringIndex > T1_MAX_SUBRS_CALLS ) )
+ {
+ /* max subr plus one for charstring */
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* overflow of stack */
+ }
+
+ /* push our current CFF charstring region on subrStack */
+ charstring = (CF2_Buffer)
+ cf2_arrstack_getPointer(
+ &subrStack,
+ (size_t)charstringIndex + 1 );
+
+ /* set up the new CFF region and pointer */
+ subrNum = cf2_stack_popInt( opStack );
+
+ if ( font->isT1 && decoder->locals_hash )
+ {
+ size_t* val = ft_hash_num_lookup( subrNum,
+ decoder->locals_hash );
+
+
+ if ( val )
+ subrNum = *val;
+ else
+ subrNum = -1;
+ }
+
+ switch ( op1 )
+ {
+ case cf2_cmdCALLGSUBR:
+ FT_TRACE4(( " (idx %d, entering level %d)\n",
+ subrNum + decoder->globals_bias,
+ charstringIndex + 1 ));
+
+ if ( cf2_initGlobalRegionBuffer( decoder,
+ subrNum,
+ charstring ) )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* subroutine lookup or stream error */
+ }
+ break;
+
+ default:
+ /* cf2_cmdCALLSUBR */
+ FT_TRACE4(( " (idx %d, entering level %d)\n",
+ subrNum + decoder->locals_bias,
+ charstringIndex + 1 ));
+
+ if ( cf2_initLocalRegionBuffer( decoder,
+ subrNum,
+ charstring ) )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* subroutine lookup or stream error */
+ }
+ }
+
+ charstringIndex += 1; /* entry is valid now */
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_cmdRETURN:
+ FT_TRACE4(( " return (leaving level %d)\n", charstringIndex ));
+
+ if ( charstringIndex < 1 )
+ {
+ /* Note: cannot return from top charstring */
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* underflow of stack */
+ }
+
+ /* restore position in previous charstring */
+ charstring = (CF2_Buffer)
+ cf2_arrstack_getPointer(
+ &subrStack,
+ (CF2_UInt)--charstringIndex );
+ continue; /* do not clear the stack */
+
+ case cf2_cmdESC:
+ {
+ FT_Byte op2 = (FT_Byte)cf2_buf_readByte( charstring );
+
+
+ /* first switch for 2-byte operators handles CFF2 */
+ /* and opcodes that are reserved for both CFF and CFF2 */
+ switch ( op2 )
+ {
+ case cf2_escHFLEX:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, FALSE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, FALSE /* dy3 */,
+ TRUE /* dx4 */, FALSE /* dy4 */,
+ TRUE /* dx5 */, FALSE /* dy5 */,
+ TRUE /* dx6 */, FALSE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " hflex\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ FALSE /* doConditionalLastRead */ );
+ }
+ continue;
+
+ case cf2_escFLEX:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, TRUE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, TRUE /* dy3 */,
+ TRUE /* dx4 */, TRUE /* dy4 */,
+ TRUE /* dx5 */, TRUE /* dy5 */,
+ TRUE /* dx6 */, TRUE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " flex\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ FALSE /* doConditionalLastRead */ );
+ }
+ break; /* TODO: why is this not a continue? */
+
+ case cf2_escHFLEX1:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, TRUE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, FALSE /* dy3 */,
+ TRUE /* dx4 */, FALSE /* dy4 */,
+ TRUE /* dx5 */, TRUE /* dy5 */,
+ TRUE /* dx6 */, FALSE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " hflex1\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ FALSE /* doConditionalLastRead */ );
+ }
+ continue;
+
+ case cf2_escFLEX1:
+ {
+ static const FT_Bool readFromStack[12] =
+ {
+ TRUE /* dx1 */, TRUE /* dy1 */,
+ TRUE /* dx2 */, TRUE /* dy2 */,
+ TRUE /* dx3 */, TRUE /* dy3 */,
+ TRUE /* dx4 */, TRUE /* dy4 */,
+ TRUE /* dx5 */, TRUE /* dy5 */,
+ FALSE /* dx6 */, FALSE /* dy6 */
+ };
+
+
+ FT_TRACE4(( " flex1\n" ));
+
+ cf2_doFlex( opStack,
+ &curX,
+ &curY,
+ &glyphPath,
+ readFromStack,
+ TRUE /* doConditionalLastRead */ );
+ }
+ continue;
+
+ /* these opcodes are always reserved */
+ case cf2_escRESERVED_8:
+ case cf2_escRESERVED_13:
+ case cf2_escRESERVED_19:
+ case cf2_escRESERVED_25:
+ case cf2_escRESERVED_31:
+ case cf2_escRESERVED_32:
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ break;
+
+ default:
+ {
+ if ( font->isCFF2 || op2 >= cf2_escRESERVED_38 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else if ( font->isT1 && result_cnt > 0 && op2 != cf2_escPOP )
+ {
+ /* all operands have been transferred by previous pops */
+ result_cnt = 0;
+ }
+ else
+ {
+ /* second switch for 2-byte operators handles */
+ /* CFF and Type 1 */
+ switch ( op2 )
+ {
+
+ case cf2_escDOTSECTION:
+ /* something about `flip type of locking' -- ignore it */
+ FT_TRACE4(( " dotsection\n" ));
+
+ break;
+
+ case cf2_escVSTEM3:
+ case cf2_escHSTEM3:
+ /*
+ * Type 1: Type 2:
+ * x0 dx0 x1 dx1 x2 dx2 vstem3 x dx {dxa dxb}* vstem
+ * y0 dy0 y1 dy1 y2 dy2 hstem3 y dy {dya dyb}* hstem
+ * relative to lsb point relative to zero
+ *
+ */
+ {
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ CF2_F16Dot16 v0, v1, v2;
+
+ FT_Bool isV = FT_BOOL( op2 == cf2_escVSTEM3 );
+
+
+ FT_TRACE4(( "%s\n", isV ? " vstem3"
+ : " hstem3" ));
+
+ FT_ASSERT( cf2_stack_count( opStack ) == 6 );
+
+ v0 = cf2_stack_getReal( opStack, 0 );
+ v1 = cf2_stack_getReal( opStack, 2 );
+ v2 = cf2_stack_getReal( opStack, 4 );
+
+ cf2_stack_setReal(
+ opStack, 2,
+ SUB_INT32( SUB_INT32( v1, v0 ),
+ cf2_stack_getReal( opStack, 1 ) ) );
+ cf2_stack_setReal(
+ opStack, 4,
+ SUB_INT32( SUB_INT32( v2, v1 ),
+ cf2_stack_getReal( opStack, 3 ) ) );
+
+ /* add left-sidebearing correction */
+ cf2_doStems( font,
+ opStack,
+ isV ? &vStemHintArray : &hStemHintArray,
+ width,
+ &haveWidth,
+ isV ? decoder->builder.left_bearing->x
+ : decoder->builder.left_bearing->y );
+
+ if ( decoder->width_only )
+ goto exit;
+ }
+ }
+ break;
+
+ case cf2_escAND:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " and\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, arg1 && arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escOR:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " or\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, arg1 || arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escNOT:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " not\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, !arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSEAC:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ FT_Error error2;
+ CF2_Int bchar_index, achar_index;
+ FT_Vector left_bearing, advance;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ T1_Face face = (T1_Face)decoder->builder.face;
+#endif
+ CF2_BufferRec component;
+ CF2_Fixed dummyWidth;
+
+ CF2_Int achar = cf2_stack_popInt( opStack );
+ CF2_Int bchar = cf2_stack_popInt( opStack );
+
+ FT_Pos ady = cf2_stack_popFixed ( opStack );
+ FT_Pos adx = cf2_stack_popFixed ( opStack );
+ FT_Pos asb = cf2_stack_popFixed ( opStack );
+
+
+ FT_TRACE4(( " seac\n" ));
+
+ if ( doingSeac )
+ {
+ FT_ERROR(( " nested seac\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* nested seac */
+ }
+
+ if ( decoder->builder.metrics_only )
+ {
+ FT_ERROR(( " unexpected seac\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* unexpected seac */
+ }
+
+ /* `glyph_names' is set to 0 for CID fonts which do */
+ /* not include an encoding. How can we deal with */
+ /* these? */
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( decoder->glyph_names == 0 &&
+ !face->root.internal->incremental_interface )
+#else
+ if ( decoder->glyph_names == 0 )
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ FT_ERROR(( "cf2_interpT2CharString:\n" ));
+ FT_ERROR(( " (Type 1 seac) glyph names table"
+ " not available in this font\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* seac weirdness */
+ adx += decoder->builder.left_bearing->x;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( face->root.internal->incremental_interface )
+ {
+ /* the caller must handle the font encoding also */
+ bchar_index = bchar;
+ achar_index = achar;
+ }
+ else
+#endif
+ {
+ bchar_index = t1_lookup_glyph_by_stdcharcode_ps(
+ decoder, bchar );
+ achar_index = t1_lookup_glyph_by_stdcharcode_ps(
+ decoder, achar );
+ }
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR(( "cf2_interpT2CharString:\n" ));
+ FT_ERROR(( " (Type 1 seac) invalid"
+ " seac character code arguments\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* if we are trying to load a composite glyph, */
+ /* do not load the accent character and return */
+ /* the array of subglyphs. */
+ if ( decoder->builder.no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error2 = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)FIXED_TO_INT( adx - asb );
+ subg->arg2 = (FT_Int)FIXED_TO_INT( ady );
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+
+ goto exit;
+ }
+
+ /* First load `bchar' in builder */
+ /* now load the unscaled outline */
+
+ /* prepare loader */
+ FT_GlyphLoader_Prepare( decoder->builder.loader );
+
+ error2 = cf2_getT1SeacComponent( decoder,
+ (FT_UInt)bchar_index,
+ &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+
+ /* save the left bearing and width of the SEAC */
+ /* glyph as they will be erased by the next load */
+
+ left_bearing = *decoder->builder.left_bearing;
+ advance = *decoder->builder.advance;
+
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ 0,
+ 0,
+ &dummyWidth );
+ cf2_freeT1SeacComponent( decoder, &component );
+
+ /* If the SEAC glyph doesn't have a (H)SBW of its */
+ /* own use the values from the base glyph. */
+
+ if ( !haveWidth )
+ {
+ left_bearing = *decoder->builder.left_bearing;
+ advance = *decoder->builder.advance;
+ }
+
+ decoder->builder.left_bearing->x = 0;
+ decoder->builder.left_bearing->y = 0;
+
+ /* Now load `achar' on top of */
+ /* the base outline */
+
+ error2 = cf2_getT1SeacComponent( decoder,
+ (FT_UInt)achar_index,
+ &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ adx - asb,
+ ady,
+ &dummyWidth );
+ cf2_freeT1SeacComponent( decoder, &component );
+
+ /* restore the left side bearing and advance width */
+ /* of the SEAC glyph or base character (saved above) */
+
+ *decoder->builder.left_bearing = left_bearing;
+ *decoder->builder.advance = advance;
+
+ goto exit;
+ }
+ break;
+
+ case cf2_escSBW:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ CF2_Fixed lsb_x, lsb_y;
+ PS_Builder* builder;
+
+
+ FT_TRACE4(( " sbw" ));
+
+ builder = &decoder->builder;
+
+ builder->advance->y = cf2_stack_popFixed( opStack );
+ builder->advance->x = cf2_stack_popFixed( opStack );
+
+ lsb_y = cf2_stack_popFixed( opStack );
+ lsb_x = cf2_stack_popFixed( opStack );
+
+ builder->left_bearing->x =
+ ADD_INT32( builder->left_bearing->x, lsb_x );
+ builder->left_bearing->y =
+ ADD_INT32( builder->left_bearing->y, lsb_y );
+
+ haveWidth = TRUE;
+
+ /* the `metrics_only' indicates that we only want */
+ /* to compute the glyph's metrics (lsb + advance */
+ /* width), not load the rest of it; so exit */
+ /* immediately */
+ if ( builder->metrics_only )
+ goto exit;
+
+ if ( initial_map_ready )
+ {
+ curX = ADD_INT32( curX, lsb_x );
+ curY = ADD_INT32( curY, lsb_y );
+ }
+ }
+ break;
+
+ case cf2_escABS:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " abs\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ if ( arg < -CF2_FIXED_MAX )
+ cf2_stack_pushFixed( opStack, CF2_FIXED_MAX );
+ else
+ cf2_stack_pushFixed( opStack, FT_ABS( arg ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escADD:
+ {
+ CF2_F16Dot16 summand1;
+ CF2_F16Dot16 summand2;
+
+
+ FT_TRACE4(( " add\n" ));
+
+ summand2 = cf2_stack_popFixed( opStack );
+ summand1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ ADD_INT32( summand1,
+ summand2 ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSUB:
+ {
+ CF2_F16Dot16 minuend;
+ CF2_F16Dot16 subtrahend;
+
+
+ FT_TRACE4(( " sub\n" ));
+
+ subtrahend = cf2_stack_popFixed( opStack );
+ minuend = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ SUB_INT32( minuend, subtrahend ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escDIV:
+ {
+ CF2_F16Dot16 dividend;
+ CF2_F16Dot16 divisor;
+
+
+ FT_TRACE4(( " div\n" ));
+
+ if ( font->isT1 && large_int )
+ {
+ divisor = (CF2_F16Dot16)cf2_stack_popInt( opStack );
+ dividend = (CF2_F16Dot16)cf2_stack_popInt( opStack );
+
+ large_int = FALSE;
+ }
+ else
+ {
+ divisor = cf2_stack_popFixed( opStack );
+ dividend = cf2_stack_popFixed( opStack );
+ }
+
+ cf2_stack_pushFixed( opStack,
+ FT_DivFix( dividend, divisor ) );
+
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escNEG:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " neg\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ if ( arg < -CF2_FIXED_MAX )
+ cf2_stack_pushFixed( opStack, CF2_FIXED_MAX );
+ else
+ cf2_stack_pushFixed( opStack, -arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escEQ:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " eq\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushInt( opStack, arg1 == arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escCALLOTHERSUBR:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ CF2_Int subr_no;
+ CF2_Int arg_cnt;
+ CF2_UInt count;
+ CF2_UInt opIdx = 0;
+
+
+ FT_TRACE4(( " callothersubr\n" ));
+
+ subr_no = cf2_stack_popInt( opStack );
+ arg_cnt = cf2_stack_popInt( opStack );
+
+ /********************************************************
+ *
+ * remove all operands to callothersubr from the stack
+ *
+ * for handled othersubrs, where we know the number of
+ * arguments, we increase the stack by the value of
+ * known_othersubr_result_cnt
+ *
+ * for unhandled othersubrs the following pops adjust
+ * the stack pointer as necessary
+ */
+
+ count = cf2_stack_count( opStack );
+ if ( (CF2_UInt)arg_cnt > count )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " stack underflow\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ opIdx += count - (CF2_UInt)arg_cnt;
+
+ known_othersubr_result_cnt = 0;
+ result_cnt = 0;
+
+ /* XXX TODO: The checks to `arg_count == <whatever>' */
+ /* might not be correct; an othersubr expects a */
+ /* certain number of operands on the PostScript stack */
+ /* (as opposed to the T1 stack) but it doesn't have to */
+ /* put them there by itself; previous othersubrs might */
+ /* have left the operands there if they were not */
+ /* followed by an appropriate number of pops */
+ /* */
+ /* On the other hand, Adobe Reader 7.0.8 for Linux */
+ /* doesn't accept a font that contains charstrings */
+ /* like */
+ /* */
+ /* 100 200 2 20 callothersubr */
+ /* 300 1 20 callothersubr pop */
+ /* */
+ /* Perhaps this is the reason why BuildCharArray */
+ /* exists. */
+
+ switch ( subr_no )
+ {
+ case 0: /* end flex feature */
+ if ( arg_cnt != 3 )
+ goto Unexpected_OtherSubr;
+
+ if ( initial_map_ready &&
+ ( !decoder->flex_state ||
+ decoder->num_flex_vectors != 7 ) )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " unexpected flex end\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* the two `results' are popped */
+ /* by the following setcurrentpoint */
+ cf2_stack_pushFixed( opStack, curX );
+ cf2_stack_pushFixed( opStack, curY );
+ known_othersubr_result_cnt = 2;
+ break;
+
+ case 1: /* start flex feature */
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ if ( !initial_map_ready )
+ break;
+
+ if ( ps_builder_check_points( &decoder->builder, 6 ) )
+ goto exit;
+
+ decoder->flex_state = 1;
+ decoder->num_flex_vectors = 0;
+ break;
+
+ case 2: /* add flex vectors */
+ {
+ FT_Int idx;
+ FT_Int idx2;
+
+
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ if ( !initial_map_ready )
+ break;
+
+ if ( !decoder->flex_state )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " missing flex start\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* note that we should not add a point for */
+ /* index 0; this will move our current position */
+ /* to the flex point without adding any point */
+ /* to the outline */
+ idx = decoder->num_flex_vectors++;
+ if ( idx > 0 && idx < 7 )
+ {
+ /* in malformed fonts it is possible to have */
+ /* other opcodes in the middle of a flex (which */
+ /* don't increase `num_flex_vectors'); we thus */
+ /* have to check whether we can add a point */
+
+ if ( ps_builder_check_points( &decoder->builder,
+ 1 ) )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* map: 1->2 2->4 3->6 4->2 5->4 6->6 */
+ idx2 = ( idx > 3 ? idx - 3 : idx ) * 2;
+
+ flexStore[idx2 - 2] = curX;
+ flexStore[idx2 - 1] = curY;
+
+ if ( idx == 3 || idx == 6 )
+ cf2_glyphpath_curveTo( &glyphPath,
+ flexStore[0],
+ flexStore[1],
+ flexStore[2],
+ flexStore[3],
+ flexStore[4],
+ flexStore[5] );
+ }
+ }
+ break;
+
+ case 3: /* change hints */
+ if ( arg_cnt != 1 )
+ goto Unexpected_OtherSubr;
+
+ if ( initial_map_ready )
+ {
+ /* do not clear hints if initial hintmap */
+ /* is not ready - we need to collate all */
+ cf2_arrstack_clear( &vStemHintArray );
+ cf2_arrstack_clear( &hStemHintArray );
+
+ cf2_hintmask_init( &hintMask, error );
+ hintMask.isValid = FALSE;
+ hintMask.isNew = TRUE;
+ }
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 12:
+ case 13:
+ /* counter control hints, clear stack */
+ cf2_stack_clear( opStack );
+ break;
+
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18: /* multiple masters */
+ {
+ PS_Blend blend = decoder->blend;
+ FT_UInt num_points, nn, mm;
+ CF2_UInt delta;
+ CF2_UInt values;
+
+
+ if ( !blend )
+ {
+ FT_ERROR((
+ "cf2_interpT2CharString:"
+ " unexpected multiple masters operator\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ num_points = (FT_UInt)subr_no - 13 +
+ ( subr_no == 18 );
+ if ( arg_cnt != (FT_Int)( num_points *
+ blend->num_designs ) )
+ {
+ FT_ERROR((
+ "cf2_interpT2CharString:"
+ " incorrect number of multiple masters arguments\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ /* We want to compute */
+ /* */
+ /* a0*w0 + a1*w1 + ... + ak*wk */
+ /* */
+ /* but we only have a0, a1-a0, a2-a0, ..., ak-a0. */
+ /* */
+ /* However, given that w0 + w1 + ... + wk == 1, we */
+ /* can rewrite it easily as */
+ /* */
+ /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk */
+ /* */
+ /* where k == num_designs-1. */
+ /* */
+ /* I guess that's why it's written in this `compact' */
+ /* form. */
+ /* */
+ delta = opIdx + num_points;
+ values = opIdx;
+ for ( nn = 0; nn < num_points; nn++ )
+ {
+ CF2_Fixed tmp = cf2_stack_getReal( opStack,
+ values );
+
+
+ for ( mm = 1; mm < blend->num_designs; mm++ )
+ tmp = ADD_INT32( tmp,
+ FT_MulFix(
+ cf2_stack_getReal( opStack,
+ delta++ ),
+ blend->weight_vector[mm] ) );
+
+ cf2_stack_setReal( opStack, values++, tmp );
+ }
+ cf2_stack_pop( opStack,
+ (CF2_UInt)arg_cnt - num_points );
+
+ known_othersubr_result_cnt = (FT_Int)num_points;
+ break;
+ }
+
+ case 19:
+ /* <idx> 1 19 callothersubr */
+ /* ==> replace elements starting from index */
+ /* cvi( <idx> ) of BuildCharArray with */
+ /* WeightVector */
+ {
+ FT_UInt idx;
+ PS_Blend blend = decoder->blend;
+ FT_UInt len_buildchar = decoder->len_buildchar;
+
+
+ if ( arg_cnt != 1 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = (FT_UInt)cf2_stack_popInt( opStack );
+
+ if ( len_buildchar < blend->num_designs ||
+ len_buildchar - blend->num_designs < idx )
+ goto Unexpected_OtherSubr;
+
+ if ( decoder->buildchar && blend->weight_vector )
+ ft_memcpy( &decoder->buildchar[idx],
+ blend->weight_vector,
+ blend->num_designs *
+ sizeof ( blend->weight_vector[0] ) );
+ }
+ break;
+
+ case 20:
+ /* <arg1> <arg2> 2 20 callothersubr pop */
+ /* ==> push <arg1> + <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 summand1;
+ CF2_F16Dot16 summand2;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ summand2 = cf2_stack_popFixed( opStack );
+ summand1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ ADD_INT32( summand1,
+ summand2 ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 21:
+ /* <arg1> <arg2> 2 21 callothersubr pop */
+ /* ==> push <arg1> - <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 minuend;
+ CF2_F16Dot16 subtrahend;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ subtrahend = cf2_stack_popFixed( opStack );
+ minuend = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ SUB_INT32( minuend,
+ subtrahend ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 22:
+ /* <arg1> <arg2> 2 22 callothersubr pop */
+ /* ==> push <arg1> * <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 factor1;
+ CF2_F16Dot16 factor2;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ factor2 = cf2_stack_popFixed( opStack );
+ factor1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ FT_MulFix( factor1, factor2 ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 23:
+ /* <arg1> <arg2> 2 23 callothersubr pop */
+ /* ==> push <arg1> / <arg2> onto T1 stack */
+ {
+ CF2_F16Dot16 dividend;
+ CF2_F16Dot16 divisor;
+
+
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ divisor = cf2_stack_popFixed( opStack );
+ dividend = cf2_stack_popFixed( opStack );
+
+ if ( divisor == 0 )
+ goto Unexpected_OtherSubr;
+
+ cf2_stack_pushFixed( opStack,
+ FT_DivFix( dividend,
+ divisor ) );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 24:
+ /* <val> <idx> 2 24 callothersubr */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ {
+ CF2_UInt idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 2 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = (CF2_UInt)cf2_stack_popInt( opStack );
+
+ if ( idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ decoder->buildchar[idx] =
+ cf2_stack_popFixed( opStack );
+ }
+ break;
+
+ case 25:
+ /* <idx> 1 25 callothersubr pop */
+ /* ==> push BuildCharArray[cvi( idx )] */
+ /* onto T1 stack */
+ {
+ CF2_UInt idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 1 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = (CF2_UInt)cf2_stack_popInt( opStack );
+
+ if ( idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ cf2_stack_pushFixed( opStack,
+ decoder->buildchar[idx] );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+#if 0
+ case 26:
+ /* <val> mark <idx> */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val>, */
+ /* leave mark on T1 stack */
+ /* <val> <idx> */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ XXX which routine has left its mark on the
+ XXX (PostScript) stack?;
+ break;
+#endif
+
+ case 27:
+ /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
+ /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
+ /* otherwise push <res2> */
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+ CF2_F16Dot16 cond1;
+ CF2_F16Dot16 cond2;
+
+
+ if ( arg_cnt != 4 )
+ goto Unexpected_OtherSubr;
+
+ cond2 = cf2_stack_popFixed( opStack );
+ cond1 = cf2_stack_popFixed( opStack );
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ cond1 <= cond2 ? arg1 : arg2 );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ case 28:
+ /* 0 28 callothersubr pop */
+ /* ==> push random value from interval [0, 1) */
+ /* onto stack */
+ {
+ CF2_F16Dot16 r;
+
+
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ /* only use the lower 16 bits of `random' */
+ /* to generate a number in the range (0;1] */
+ r = (CF2_F16Dot16)
+ ( ( decoder->current_subfont->random & 0xFFFF ) + 1 );
+
+ decoder->current_subfont->random =
+ cff_random( decoder->current_subfont->random );
+
+ cf2_stack_pushFixed( opStack, r );
+ known_othersubr_result_cnt = 1;
+ }
+ break;
+
+ default:
+ if ( arg_cnt >= 0 && subr_no >= 0 )
+ {
+ FT_Int i;
+
+
+ FT_ERROR((
+ "cf2_interpT2CharString (Type 1 mode):"
+ " unknown othersubr [%d %d], wish me luck\n",
+ arg_cnt, subr_no ));
+
+ /* store the unused args */
+ /* for this unhandled OtherSubr */
+
+ if ( arg_cnt > PS_STORAGE_SIZE )
+ arg_cnt = PS_STORAGE_SIZE;
+ result_cnt = arg_cnt;
+
+ for ( i = 1; i <= arg_cnt; i++ )
+ results[result_cnt - i] =
+ cf2_stack_popFixed( opStack );
+
+ break;
+ }
+ /* fall through */
+
+ Unexpected_OtherSubr:
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " invalid othersubr [%d %d]\n",
+ arg_cnt, subr_no ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escPOP:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ FT_TRACE4(( " pop" ));
+
+ if ( known_othersubr_result_cnt > 0 )
+ {
+ known_othersubr_result_cnt--;
+ /* ignore, we pushed the operands ourselves */
+ continue;
+ }
+
+ if ( result_cnt == 0 )
+ {
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " no more operands for othersubr\n" ));
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit;
+ }
+
+ result_cnt--;
+ cf2_stack_pushFixed( opStack, results[result_cnt] );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escDROP:
+ FT_TRACE4(( " drop\n" ));
+
+ (void)cf2_stack_popFixed( opStack );
+ continue; /* do not clear the stack */
+
+ case cf2_escPUT:
+ {
+ CF2_F16Dot16 val;
+ CF2_UInt idx;
+
+
+ FT_TRACE4(( " put\n" ));
+
+ idx = (CF2_UInt)cf2_stack_popInt( opStack );
+ val = cf2_stack_popFixed( opStack );
+
+ if ( idx < CF2_STORAGE_SIZE )
+ storage[idx] = val;
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escGET:
+ {
+ CF2_UInt idx;
+
+
+ FT_TRACE4(( " get\n" ));
+
+ idx = (CF2_UInt)cf2_stack_popInt( opStack );
+
+ if ( idx < CF2_STORAGE_SIZE )
+ cf2_stack_pushFixed( opStack, storage[idx] );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escIFELSE:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+ CF2_F16Dot16 cond1;
+ CF2_F16Dot16 cond2;
+
+
+ FT_TRACE4(( " ifelse\n" ));
+
+ cond2 = cf2_stack_popFixed( opStack );
+ cond1 = cf2_stack_popFixed( opStack );
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ cond1 <= cond2 ? arg1 : arg2 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escRANDOM: /* in spec */
+ {
+ CF2_F16Dot16 r;
+
+
+ FT_TRACE4(( " random\n" ));
+
+ /* only use the lower 16 bits of `random' */
+ /* to generate a number in the range (0;1] */
+ r = (CF2_F16Dot16)
+ ( ( decoder->current_subfont->random & 0xFFFF ) + 1 );
+
+ decoder->current_subfont->random =
+ cff_random( decoder->current_subfont->random );
+
+ cf2_stack_pushFixed( opStack, r );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escMUL:
+ {
+ CF2_F16Dot16 factor1;
+ CF2_F16Dot16 factor2;
+
+
+ FT_TRACE4(( " mul\n" ));
+
+ factor2 = cf2_stack_popFixed( opStack );
+ factor1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack,
+ FT_MulFix( factor1, factor2 ) );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSQRT:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " sqrt\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+ if ( arg > 0 )
+ {
+ /* use a start value that doesn't make */
+ /* the algorithm's addition overflow */
+ FT_Fixed root = arg < 10 ? arg : arg >> 1;
+ FT_Fixed new_root;
+
+
+ /* Babylonian method */
+ for (;;)
+ {
+ new_root = ( root + FT_DivFix( arg, root ) + 1 ) >> 1;
+ if ( new_root == root )
+ break;
+ root = new_root;
+ }
+ arg = new_root;
+ }
+ else
+ arg = 0;
+
+ cf2_stack_pushFixed( opStack, arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escDUP:
+ {
+ CF2_F16Dot16 arg;
+
+
+ FT_TRACE4(( " dup\n" ));
+
+ arg = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack, arg );
+ cf2_stack_pushFixed( opStack, arg );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escEXCH:
+ {
+ CF2_F16Dot16 arg1;
+ CF2_F16Dot16 arg2;
+
+
+ FT_TRACE4(( " exch\n" ));
+
+ arg2 = cf2_stack_popFixed( opStack );
+ arg1 = cf2_stack_popFixed( opStack );
+
+ cf2_stack_pushFixed( opStack, arg2 );
+ cf2_stack_pushFixed( opStack, arg1 );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escINDEX:
+ {
+ CF2_Int idx;
+ CF2_UInt size;
+
+
+ FT_TRACE4(( " index\n" ));
+
+ idx = cf2_stack_popInt( opStack );
+ size = cf2_stack_count( opStack );
+
+ if ( size > 0 )
+ {
+ /* for `cf2_stack_getReal', */
+ /* index 0 is bottom of stack */
+ CF2_UInt gr_idx;
+
+
+ if ( idx < 0 )
+ gr_idx = size - 1;
+ else if ( (CF2_UInt)idx >= size )
+ gr_idx = 0;
+ else
+ gr_idx = size - 1 - (CF2_UInt)idx;
+
+ cf2_stack_pushFixed( opStack,
+ cf2_stack_getReal( opStack,
+ gr_idx ) );
+ }
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escROLL:
+ {
+ CF2_Int idx;
+ CF2_Int count;
+
+
+ FT_TRACE4(( " roll\n" ));
+
+ idx = cf2_stack_popInt( opStack );
+ count = cf2_stack_popInt( opStack );
+
+ cf2_stack_roll( opStack, count, idx );
+ }
+ continue; /* do not clear the stack */
+
+ case cf2_escSETCURRENTPT:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+ else
+ {
+ FT_TRACE4(( " setcurrentpoint" ));
+
+ if ( !initial_map_ready )
+ break;
+
+ /* From the T1 specification, section 6.4: */
+ /* */
+ /* The setcurrentpoint command is used only in */
+ /* conjunction with results from OtherSubrs */
+ /* procedures. */
+
+ /* known_othersubr_result_cnt != 0 is already handled */
+ /* above. */
+
+ /* Note, however, that both Ghostscript and Adobe */
+ /* Distiller handle this situation by silently */
+ /* ignoring the inappropriate `setcurrentpoint' */
+ /* instruction. So we do the same. */
+#if 0
+
+ if ( decoder->flex_state != 1 )
+ {
+ FT_ERROR(( "cf2_interpT2CharString:"
+ " unexpected `setcurrentpoint'\n" ));
+ goto Syntax_Error;
+ }
+ else
+ ...
+#endif
+
+ curY = cf2_stack_popFixed( opStack );
+ curX = cf2_stack_popFixed( opStack );
+
+ decoder->flex_state = 0;
+ }
+ break;
+
+ } /* end of 2nd switch checking op2 */
+ }
+ }
+ } /* end of 1st switch checking op2 */
+ } /* case cf2_cmdESC */
+
+ break;
+
+ case cf2_cmdHSBW:
+ if ( !font->isT1 )
+ FT_TRACE4(( " unknown op (%d)\n", op1 ));
+ else
+ {
+ CF2_Fixed lsb_x;
+ PS_Builder* builder;
+
+
+ FT_TRACE4(( " hsbw\n" ));
+
+ builder = &decoder->builder;
+
+ builder->advance->x = cf2_stack_popFixed( opStack );
+ builder->advance->y = 0;
+
+ lsb_x = cf2_stack_popFixed( opStack );
+
+ builder->left_bearing->x = ADD_INT32( builder->left_bearing->x,
+ lsb_x );
+
+ haveWidth = TRUE;
+
+ /* the `metrics_only' indicates that we only want to compute */
+ /* the glyph's metrics (lsb + advance width), not load the */
+ /* rest of it; so exit immediately */
+ if ( builder->metrics_only )
+ goto exit;
+
+ if ( initial_map_ready )
+ curX = ADD_INT32( curX, lsb_x );
+ }
+ break;
+
+ case cf2_cmdENDCHAR:
+ FT_TRACE4(( " endchar\n" ));
+
+ if ( font->isT1 && !initial_map_ready )
+ {
+ FT_TRACE5(( "cf2_interpT2CharString (Type 1 mode): "
+ "Build initial hintmap, rewinding...\n" ));
+
+ /* trigger initial hintmap build */
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ initial_map_ready = TRUE;
+
+ /* change hints routine - clear for rewind */
+ cf2_arrstack_clear( &vStemHintArray );
+ cf2_arrstack_clear( &hStemHintArray );
+
+ cf2_hintmask_init( &hintMask, error );
+ hintMask.isValid = FALSE;
+ hintMask.isNew = TRUE;
+
+ /* rewind charstring */
+ /* some charstrings use endchar from a final subroutine call */
+ /* without returning, detect these and exit to the top level */
+ /* charstring */
+ while ( charstringIndex > 0 )
+ {
+ FT_TRACE4(( " return (leaving level %d)\n", charstringIndex ));
+
+ /* restore position in previous charstring */
+ charstring = (CF2_Buffer)
+ cf2_arrstack_getPointer(
+ &subrStack,
+ (CF2_UInt)--charstringIndex );
+ }
+ charstring->ptr = charstring->start;
+
+ break;
+ }
+
+ if ( cf2_stack_count( opStack ) == 1 ||
+ cf2_stack_count( opStack ) == 5 )
+ {
+ if ( !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+ }
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ /* close path if still open */
+ cf2_glyphpath_closeOpenPath( &glyphPath );
+
+ /* disable seac for CFF2 and Type1 */
+ /* (charstring ending with args on stack) */
+ if ( !font->isCFF2 && !font->isT1 && cf2_stack_count( opStack ) > 1 )
+ {
+ /* must be either 4 or 5 -- */
+ /* this is a (deprecated) implied `seac' operator */
+
+ CF2_Int achar;
+ CF2_Int bchar;
+ CF2_BufferRec component;
+ CF2_Fixed dummyWidth; /* ignore component width */
+ FT_Error error2;
+
+
+ if ( doingSeac )
+ {
+ lastError = FT_THROW( Invalid_Glyph_Format );
+ goto exit; /* nested seac */
+ }
+
+ achar = cf2_stack_popInt( opStack );
+ bchar = cf2_stack_popInt( opStack );
+
+ curY = cf2_stack_popFixed( opStack );
+ curX = cf2_stack_popFixed( opStack );
+
+ error2 = cf2_getSeacComponent( decoder, achar, &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ curX,
+ curY,
+ &dummyWidth );
+ cf2_freeSeacComponent( decoder, &component );
+
+ error2 = cf2_getSeacComponent( decoder, bchar, &component );
+ if ( error2 )
+ {
+ lastError = error2; /* pass FreeType error through */
+ goto exit;
+ }
+ cf2_interpT2CharString( font,
+ &component,
+ callbacks,
+ translation,
+ TRUE,
+ 0,
+ 0,
+ &dummyWidth );
+ cf2_freeSeacComponent( decoder, &component );
+ }
+ goto exit;
+
+ case cf2_cmdCNTRMASK:
+ case cf2_cmdHINTMASK:
+ /* the final \n in the tracing message gets added in */
+ /* `cf2_hintmask_read' (which also traces the mask bytes) */
+ FT_TRACE4(( "%s", op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask" ));
+
+ /* never add hints after the mask is computed */
+ if ( cf2_stack_count( opStack ) > 1 &&
+ cf2_hintmask_isValid( &hintMask ) )
+ {
+ FT_TRACE4(( "cf2_interpT2CharString: invalid hint mask\n" ));
+ break;
+ }
+
+ /* if there are arguments on the stack, there this is an */
+ /* implied cf2_cmdVSTEMHM */
+ cf2_doStems( font,
+ opStack,
+ &vStemHintArray,
+ width,
+ &haveWidth,
+ 0 );
+
+ if ( decoder->width_only )
+ goto exit;
+
+ if ( op1 == cf2_cmdHINTMASK )
+ {
+ /* consume the hint mask bytes which follow the operator */
+ cf2_hintmask_read( &hintMask,
+ charstring,
+ cf2_arrstack_size( &hStemHintArray ) +
+ cf2_arrstack_size( &vStemHintArray ) );
+ }
+ else
+ {
+ /*
+ * Consume the counter mask bytes which follow the operator:
+ * Build a temporary hint map, just to place and lock those
+ * stems participating in the counter mask. These are most
+ * likely the dominant hstems, and are grouped together in a
+ * few counter groups, not necessarily in correspondence
+ * with the hint groups. This reduces the chances of
+ * conflicts between hstems that are initially placed in
+ * separate hint groups and then brought together. The
+ * positions are copied back to `hStemHintArray', so we can
+ * discard `counterMask' and `counterHintMap'.
+ *
+ */
+ CF2_HintMapRec counterHintMap;
+ CF2_HintMaskRec counterMask;
+
+
+ cf2_hintmap_init( &counterHintMap,
+ font,
+ &glyphPath.initialHintMap,
+ &glyphPath.hintMoves,
+ scaleY );
+ cf2_hintmask_init( &counterMask, error );
+
+ cf2_hintmask_read( &counterMask,
+ charstring,
+ cf2_arrstack_size( &hStemHintArray ) +
+ cf2_arrstack_size( &vStemHintArray ) );
+ cf2_hintmap_build( &counterHintMap,
+ &hStemHintArray,
+ &vStemHintArray,
+ &counterMask,
+ 0,
+ FALSE );
+ }
+ break;
+
+ case cf2_cmdRMOVETO:
+ FT_TRACE4(( " rmoveto\n" ));
+
+ if ( font->isT1 && !decoder->flex_state && !haveWidth )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( cf2_stack_count( opStack ) > 2 && !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ curY = ADD_INT32( curY, cf2_stack_popFixed( opStack ) );
+ curX = ADD_INT32( curX, cf2_stack_popFixed( opStack ) );
+
+ if ( !decoder->flex_state )
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ break;
+
+ case cf2_cmdHMOVETO:
+ FT_TRACE4(( " hmoveto\n" ));
+
+ if ( font->isT1 && !decoder->flex_state && !haveWidth )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " No width. Use hsbw/sbw as first op\n" ));
+
+ if ( cf2_stack_count( opStack ) > 1 && !haveWidth )
+ *width = ADD_INT32( cf2_stack_getReal( opStack, 0 ),
+ nominalWidthX );
+
+ /* width is defined or default after this */
+ haveWidth = TRUE;
+
+ if ( decoder->width_only )
+ goto exit;
+
+ curX = ADD_INT32( curX, cf2_stack_popFixed( opStack ) );
+
+ if ( !decoder->flex_state )
+ cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+ break;
+
+ case cf2_cmdRLINECURVE:
+ {
+ CF2_UInt count = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ FT_TRACE4(( " rlinecurve\n" ));
+
+ while ( idx + 6 < count )
+ {
+ curX = ADD_INT32( curX, cf2_stack_getReal( opStack,
+ idx + 0 ) );
+ curY = ADD_INT32( curY, cf2_stack_getReal( opStack,
+ idx + 1 ) );
+
+ cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+ idx += 2;
+ }
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), x2 );
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 5 ), y2 );
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 6;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdVVCURVETO:
+ {
+ CF2_UInt count, count1 = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ /* if `cf2_stack_count' isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+ /* (and sorting the stack indexing to suit) */
+ count = count1 & ~2U;
+ idx += count1 - count;
+
+ FT_TRACE4(( " vvcurveto\n" ));
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ if ( ( count - idx ) & 1 )
+ {
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx ), curX );
+
+ idx++;
+ }
+ else
+ x1 = curX;
+
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ x3 = x2;
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y2 );
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 4;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdHHCURVETO:
+ {
+ CF2_UInt count, count1 = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+
+ /* if `cf2_stack_count' isn't of the form 4n or 4n+1, */
+ /* we enforce it by clearing the second bit */
+ /* (and sorting the stack indexing to suit) */
+ count = count1 & ~2U;
+ idx += count1 - count;
+
+ FT_TRACE4(( " hhcurveto\n" ));
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, y1, x2, y2, x3, y3;
+
+
+ if ( ( count - idx ) & 1 )
+ {
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx ), curY );
+
+ idx++;
+ }
+ else
+ y1 = curY;
+
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), x2 );
+ y3 = y2;
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 4;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdVHCURVETO:
+ case cf2_cmdHVCURVETO:
+ {
+ CF2_UInt count, count1 = cf2_stack_count( opStack );
+ CF2_UInt idx = 0;
+
+ FT_Bool alternate = FT_BOOL( op1 == cf2_cmdHVCURVETO );
+
+
+ /* if `cf2_stack_count' isn't of the form 8n, 8n+1, */
+ /* 8n+4, or 8n+5, we enforce it by clearing the */
+ /* second bit */
+ /* (and sorting the stack indexing to suit) */
+ count = count1 & ~2U;
+ idx += count1 - count;
+
+ FT_TRACE4(( "%s\n", alternate ? " hvcurveto" : " vhcurveto" ));
+
+ while ( idx < count )
+ {
+ CF2_Fixed x1, x2, x3, y1, y2, y3;
+
+
+ if ( alternate )
+ {
+ x1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curX );
+ y1 = curY;
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), y2 );
+
+ if ( count - idx == 5 )
+ {
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), x2 );
+
+ idx++;
+ }
+ else
+ x3 = x2;
+
+ alternate = FALSE;
+ }
+ else
+ {
+ x1 = curX;
+ y1 = ADD_INT32( cf2_stack_getReal( opStack, idx + 0 ), curY );
+ x2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 1 ), x1 );
+ y2 = ADD_INT32( cf2_stack_getReal( opStack, idx + 2 ), y1 );
+ x3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 3 ), x2 );
+
+ if ( count - idx == 5 )
+ {
+ y3 = ADD_INT32( cf2_stack_getReal( opStack, idx + 4 ), y2 );
+
+ idx++;
+ }
+ else
+ y3 = y2;
+
+ alternate = TRUE;
+ }
+
+ cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+ curX = x3;
+ curY = y3;
+ idx += 4;
+ }
+
+ cf2_stack_clear( opStack );
+ }
+ continue; /* no need to clear stack again */
+
+ case cf2_cmdEXTENDEDNMBR:
+ {
+ CF2_Int v;
+
+ CF2_Int byte1 = cf2_buf_readByte( charstring );
+ CF2_Int byte2 = cf2_buf_readByte( charstring );
+
+
+ v = (FT_Short)( ( byte1 << 8 ) |
+ byte2 );
+
+ FT_TRACE4(( " %d", v ));
+
+ cf2_stack_pushInt( opStack, v );
+ }
+ continue;
+
+ default:
+ /* numbers */
+ {
+ if ( /* op1 >= 32 && */ op1 <= 246 )
+ {
+ CF2_Int v;
+
+
+ v = op1 - 139;
+
+ FT_TRACE4(( " %d", v ));
+
+ /* -107 .. 107 */
+ cf2_stack_pushInt( opStack, v );
+ }
+
+ else if ( /* op1 >= 247 && */ op1 <= 250 )
+ {
+ CF2_Int v;
+
+
+ v = op1;
+ v -= 247;
+ v *= 256;
+ v += cf2_buf_readByte( charstring );
+ v += 108;
+
+ FT_TRACE4(( " %d", v ));
+
+ /* 108 .. 1131 */
+ cf2_stack_pushInt( opStack, v );
+ }
+
+ else if ( /* op1 >= 251 && */ op1 <= 254 )
+ {
+ CF2_Int v;
+
+
+ v = op1;
+ v -= 251;
+ v *= 256;
+ v += cf2_buf_readByte( charstring );
+ v = -v - 108;
+
+ FT_TRACE4(( " %d", v ));
+
+ /* -1131 .. -108 */
+ cf2_stack_pushInt( opStack, v );
+ }
+
+ else /* op1 == 255 */
+ {
+ CF2_Fixed v;
+
+ FT_UInt32 byte1 = (FT_UInt32)cf2_buf_readByte( charstring );
+ FT_UInt32 byte2 = (FT_UInt32)cf2_buf_readByte( charstring );
+ FT_UInt32 byte3 = (FT_UInt32)cf2_buf_readByte( charstring );
+ FT_UInt32 byte4 = (FT_UInt32)cf2_buf_readByte( charstring );
+
+
+ v = (CF2_Fixed)( ( byte1 << 24 ) |
+ ( byte2 << 16 ) |
+ ( byte3 << 8 ) |
+ byte4 );
+
+ /*
+ * For Type 1:
+ *
+ * According to the specification, values > 32000 or < -32000
+ * must be followed by a `div' operator to make the result be
+ * in the range [-32000;32000]. We expect that the second
+ * argument of `div' is not a large number. Additionally, we
+ * don't handle stuff like `<large1> <large2> <num> div <num>
+ * div' or <large1> <large2> <num> div div'. This is probably
+ * not allowed anyway.
+ *
+ * <large> <num> <num>+ div is not checked but should not be
+ * allowed as the large value remains untouched.
+ *
+ */
+ if ( font->isT1 )
+ {
+ if ( v > 32000 || v < -32000 )
+ {
+ if ( large_int )
+ FT_ERROR(( "cf2_interpT2CharString (Type 1 mode):"
+ " no `div' after large integer\n" ));
+ else
+ large_int = TRUE;
+ }
+
+ FT_TRACE4(( " %d", v ));
+
+ cf2_stack_pushInt( opStack, (CF2_Int)v );
+ }
+ else
+ {
+ FT_TRACE4(( " %.5fF", v / 65536.0 ));
+
+ cf2_stack_pushFixed( opStack, v );
+ }
+ }
+ }
+ continue; /* don't clear stack */
+
+ } /* end of switch statement checking `op1' */
+
+ cf2_stack_clear( opStack );
+
+ } /* end of main interpreter loop */
+
+ /* we get here if the charstring ends without cf2_cmdENDCHAR */
+ FT_TRACE4(( "cf2_interpT2CharString:"
+ " charstring ends without ENDCHAR\n" ));
+
+ exit:
+ /* check whether last error seen is also the first one */
+ cf2_setError( error, lastError );
+
+ if ( *error )
+ FT_TRACE4(( "charstring error %d\n", *error ));
+
+ /* free resources from objects we've used */
+ cf2_glyphpath_finalize( &glyphPath );
+ cf2_arrstack_finalize( &vStemHintArray );
+ cf2_arrstack_finalize( &hStemHintArray );
+ cf2_arrstack_finalize( &subrStack );
+ cf2_stack_free( opStack );
+
+ FT_TRACE4(( "\n" ));
+
+ return;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psintrp.h b/modules/freetype2/src/psaux/psintrp.h
new file mode 100644
index 0000000000..d8b9342ecb
--- /dev/null
+++ b/modules/freetype2/src/psaux/psintrp.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+ *
+ * psintrp.h
+ *
+ * Adobe's CFF Interpreter (specification).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSINTRP_H_
+#define PSINTRP_H_
+
+
+#include "psft.h"
+#include "pshints.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ cf2_hintmask_init( CF2_HintMask hintmask,
+ FT_Error* error );
+ FT_LOCAL( FT_Bool )
+ cf2_hintmask_isValid( const CF2_HintMask hintmask );
+ FT_LOCAL( FT_Bool )
+ cf2_hintmask_isNew( const CF2_HintMask hintmask );
+ FT_LOCAL( void )
+ cf2_hintmask_setNew( CF2_HintMask hintmask,
+ FT_Bool val );
+ FT_LOCAL( FT_Byte* )
+ cf2_hintmask_getMaskPtr( CF2_HintMask hintmask );
+ FT_LOCAL( void )
+ cf2_hintmask_setAll( CF2_HintMask hintmask,
+ size_t bitCount );
+
+ FT_LOCAL( void )
+ cf2_interpT2CharString( CF2_Font font,
+ const CF2_Buffer buf,
+ CF2_OutlineCallbacks callbacks,
+ const FT_Vector* translation,
+ FT_Bool doingSeac,
+ CF2_Fixed curX,
+ CF2_Fixed curY,
+ CF2_Fixed* width );
+
+
+FT_END_HEADER
+
+
+#endif /* PSINTRP_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psobjs.c b/modules/freetype2/src/psaux/psobjs.c
new file mode 100644
index 0000000000..8da755d0e5
--- /dev/null
+++ b/modules/freetype2/src/psaux/psobjs.c
@@ -0,0 +1,2562 @@
+/****************************************************************************
+ *
+ * psobjs.c
+ *
+ * Auxiliary functions for PostScript fonts (body).
+ *
+ * Copyright (C) 1996-2023 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 <freetype/internal/psaux.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftcalc.h>
+#include <freetype/ftdriver.h>
+
+#include "psobjs.h"
+#include "psconv.h"
+
+#include "psauxerr.h"
+#include "psauxmod.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 psobjs
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS_TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ps_table_new
+ *
+ * @Description:
+ * Initializes a PS_Table.
+ *
+ * @InOut:
+ * table ::
+ * The address of the target table.
+ *
+ * @Input:
+ * count ::
+ * The table size = the maximum number of elements.
+ *
+ * memory ::
+ * The memory object to use for all subsequent
+ * reallocations.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ ps_table_new( PS_Table table,
+ FT_Int count,
+ FT_Memory memory )
+ {
+ FT_Error error;
+
+
+ table->memory = memory;
+ if ( FT_NEW_ARRAY( table->elements, count ) ||
+ FT_NEW_ARRAY( table->lengths, count ) )
+ goto Exit;
+
+ table->max_elems = count;
+ table->init = 0xDEADBEEFUL;
+ table->block = NULL;
+ table->capacity = 0;
+ table->cursor = 0;
+
+ *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs;
+
+ Exit:
+ if ( error )
+ FT_FREE( table->elements );
+
+ return error;
+ }
+
+
+ static FT_Error
+ ps_table_realloc( PS_Table table,
+ FT_Offset new_size )
+ {
+ FT_Memory memory = table->memory;
+ FT_Byte* old_base = table->block;
+ FT_Error error;
+
+
+ /* (re)allocate the base block */
+ if ( FT_REALLOC( table->block, table->capacity, new_size ) )
+ return error;
+
+ /* rebase offsets if necessary */
+ if ( old_base && table->block != old_base )
+ {
+ FT_Byte** offset = table->elements;
+ FT_Byte** limit = offset + table->max_elems;
+
+
+ for ( ; offset < limit; offset++ )
+ {
+ if ( *offset )
+ *offset = table->block + ( *offset - old_base );
+ }
+ }
+
+ table->capacity = new_size;
+
+ return FT_Err_Ok;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ps_table_add
+ *
+ * @Description:
+ * Adds an object to a PS_Table, possibly growing its memory block.
+ *
+ * @InOut:
+ * table ::
+ * The target table.
+ *
+ * @Input:
+ * idx ::
+ * The index of the object in the table.
+ *
+ * object ::
+ * The address of the object to copy in memory.
+ *
+ * length ::
+ * The length in bytes of the source object.
+ *
+ * @Return:
+ * FreeType error code. 0 means success. An error is returned if a
+ * reallocation fails.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ ps_table_add( PS_Table table,
+ FT_Int idx,
+ const void* object,
+ FT_UInt length )
+ {
+ if ( idx < 0 || idx >= table->max_elems )
+ {
+ FT_ERROR(( "ps_table_add: invalid index\n" ));
+ return FT_THROW( Invalid_Argument );
+ }
+
+ /* grow the base block if needed */
+ if ( table->cursor + length > table->capacity )
+ {
+ FT_Error error;
+ FT_Offset new_size = table->capacity;
+ FT_PtrDist in_offset;
+
+
+ in_offset = (FT_Byte*)object - table->block;
+ if ( in_offset < 0 || (FT_Offset)in_offset >= table->capacity )
+ in_offset = -1;
+
+ while ( new_size < table->cursor + length )
+ {
+ /* increase size by 25% and round up to the nearest multiple
+ of 1024 */
+ new_size += ( new_size >> 2 ) + 1;
+ new_size = FT_PAD_CEIL( new_size, 1024 );
+ }
+
+ error = ps_table_realloc( table, new_size );
+ if ( error )
+ return error;
+
+ if ( in_offset >= 0 )
+ object = table->block + in_offset;
+ }
+
+ /* add the object to the base block and adjust offset */
+ table->elements[idx] = FT_OFFSET( table->block, table->cursor );
+ table->lengths [idx] = length;
+ FT_MEM_COPY( table->block + table->cursor, object, length );
+
+ table->cursor += length;
+ return FT_Err_Ok;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ps_table_done
+ *
+ * @Description:
+ * Finalizes a PS_TableRec (i.e., reallocate it to its current
+ * cursor).
+ *
+ * @InOut:
+ * table ::
+ * The target table.
+ */
+ FT_LOCAL_DEF( void )
+ ps_table_done( PS_Table table )
+ {
+ /* no problem if shrinking fails */
+ ps_table_realloc( table, table->cursor );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ps_table_release( PS_Table table )
+ {
+ FT_Memory memory = table->memory;
+
+
+ if ( table->init == 0xDEADBEEFUL )
+ {
+ FT_FREE( table->block );
+ FT_FREE( table->elements );
+ FT_FREE( table->lengths );
+ table->init = 0;
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 PARSER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* first character must be already part of the comment */
+
+ static void
+ skip_comment( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+
+
+ while ( cur < limit )
+ {
+ if ( IS_PS_NEWLINE( *cur ) )
+ break;
+ cur++;
+ }
+
+ *acur = cur;
+ }
+
+
+ static void
+ skip_spaces( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+
+
+ while ( cur < limit )
+ {
+ if ( !IS_PS_SPACE( *cur ) )
+ {
+ if ( *cur == '%' )
+ /* According to the PLRM, a comment is equal to a space. */
+ skip_comment( &cur, limit );
+ else
+ break;
+ }
+ cur++;
+ }
+
+ *acur = cur;
+ }
+
+
+#define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' )
+
+
+ /* first character must be `('; */
+ /* *acur is positioned at the character after the closing `)' */
+
+ static FT_Error
+ skip_literal_string( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+ FT_Int embed = 0;
+ FT_Error error = FT_ERR( Invalid_File_Format );
+ unsigned int i;
+
+
+ while ( cur < limit )
+ {
+ FT_Byte c = *cur;
+
+
+ cur++;
+
+ if ( c == '\\' )
+ {
+ /* Red Book 3rd ed., section `Literal Text Strings', p. 29: */
+ /* A backslash can introduce three different types */
+ /* of escape sequences: */
+ /* - a special escaped char like \r, \n, etc. */
+ /* - a one-, two-, or three-digit octal number */
+ /* - none of the above in which case the backslash is ignored */
+
+ if ( cur == limit )
+ /* error (or to be ignored?) */
+ break;
+
+ switch ( *cur )
+ {
+ /* skip `special' escape */
+ case 'n':
+ case 'r':
+ case 't':
+ case 'b':
+ case 'f':
+ case '\\':
+ case '(':
+ case ')':
+ cur++;
+ break;
+
+ default:
+ /* skip octal escape or ignore backslash */
+ for ( i = 0; i < 3 && cur < limit; i++ )
+ {
+ if ( !IS_OCTAL_DIGIT( *cur ) )
+ break;
+
+ cur++;
+ }
+ }
+ }
+ else if ( c == '(' )
+ embed++;
+ else if ( c == ')' )
+ {
+ embed--;
+ if ( embed == 0 )
+ {
+ error = FT_Err_Ok;
+ break;
+ }
+ }
+ }
+
+ *acur = cur;
+
+ return error;
+ }
+
+
+ /* first character must be `<' */
+
+ static FT_Error
+ skip_string( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+ FT_Error err = FT_Err_Ok;
+
+
+ while ( ++cur < limit )
+ {
+ /* All whitespace characters are ignored. */
+ skip_spaces( &cur, limit );
+ if ( cur >= limit )
+ break;
+
+ if ( !IS_PS_XDIGIT( *cur ) )
+ break;
+ }
+
+ if ( cur < limit && *cur != '>' )
+ {
+ FT_ERROR(( "skip_string: missing closing delimiter `>'\n" ));
+ err = FT_THROW( Invalid_File_Format );
+ }
+ else
+ cur++;
+
+ *acur = cur;
+ return err;
+ }
+
+
+ /* first character must be the opening brace that */
+ /* starts the procedure */
+
+ /* NB: [ and ] need not match: */
+ /* `/foo {[} def' is a valid PostScript fragment, */
+ /* even within a Type1 font */
+
+ static FT_Error
+ skip_procedure( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur;
+ FT_Int embed = 0;
+ FT_Error error = FT_Err_Ok;
+
+
+ FT_ASSERT( **acur == '{' );
+
+ for ( cur = *acur; cur < limit && error == FT_Err_Ok; cur++ )
+ {
+ switch ( *cur )
+ {
+ case '{':
+ embed++;
+ break;
+
+ case '}':
+ embed--;
+ if ( embed == 0 )
+ {
+ cur++;
+ goto end;
+ }
+ break;
+
+ case '(':
+ error = skip_literal_string( &cur, limit );
+ break;
+
+ case '<':
+ error = skip_string( &cur, limit );
+ break;
+
+ case '%':
+ skip_comment( &cur, limit );
+ break;
+ }
+ }
+
+ end:
+ if ( embed != 0 )
+ error = FT_THROW( Invalid_File_Format );
+
+ *acur = cur;
+
+ return error;
+ }
+
+
+ /************************************************************************
+ *
+ * All exported parsing routines handle leading whitespace and stop at
+ * the first character which isn't part of the just handled token.
+ *
+ */
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_skip_PS_token( PS_Parser parser )
+ {
+ /* Note: PostScript allows any non-delimiting, non-whitespace */
+ /* character in a name (PS Ref Manual, 3rd ed, p31). */
+ /* PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */
+
+ FT_Byte* cur = parser->cursor;
+ FT_Byte* limit = parser->limit;
+ FT_Error error = FT_Err_Ok;
+
+
+ skip_spaces( &cur, limit ); /* this also skips comments */
+ if ( cur >= limit )
+ goto Exit;
+
+ /* self-delimiting, single-character tokens */
+ if ( *cur == '[' || *cur == ']' )
+ {
+ cur++;
+ goto Exit;
+ }
+
+ /* skip balanced expressions (procedures and strings) */
+
+ if ( *cur == '{' ) /* {...} */
+ {
+ error = skip_procedure( &cur, limit );
+ goto Exit;
+ }
+
+ if ( *cur == '(' ) /* (...) */
+ {
+ error = skip_literal_string( &cur, limit );
+ goto Exit;
+ }
+
+ if ( *cur == '<' ) /* <...> */
+ {
+ if ( cur + 1 < limit && *( cur + 1 ) == '<' ) /* << */
+ {
+ cur++;
+ cur++;
+ }
+ else
+ error = skip_string( &cur, limit );
+
+ goto Exit;
+ }
+
+ if ( *cur == '>' )
+ {
+ cur++;
+ if ( cur >= limit || *cur != '>' ) /* >> */
+ {
+ FT_ERROR(( "ps_parser_skip_PS_token:"
+ " unexpected closing delimiter `>'\n" ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+ cur++;
+ goto Exit;
+ }
+
+ if ( *cur == '/' )
+ cur++;
+
+ /* anything else */
+ while ( cur < limit )
+ {
+ /* *cur might be invalid (e.g., ')' or '}'), but this */
+ /* is handled by the test `cur == parser->cursor' below */
+ if ( IS_PS_DELIM( *cur ) )
+ break;
+
+ cur++;
+ }
+
+ Exit:
+ if ( cur < limit && cur == parser->cursor )
+ {
+ FT_ERROR(( "ps_parser_skip_PS_token:"
+ " current token is `%c' which is self-delimiting\n",
+ *cur ));
+ FT_ERROR(( " "
+ " but invalid at this point\n" ));
+
+ error = FT_THROW( Invalid_File_Format );
+ }
+
+ if ( cur > limit )
+ cur = limit;
+
+ parser->error = error;
+ parser->cursor = cur;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_skip_spaces( PS_Parser parser )
+ {
+ skip_spaces( &parser->cursor, parser->limit );
+ }
+
+
+ /* `token' here means either something between balanced delimiters */
+ /* or the next token; the delimiters are not removed. */
+
+ FT_LOCAL_DEF( void )
+ ps_parser_to_token( PS_Parser parser,
+ T1_Token token )
+ {
+ FT_Byte* cur;
+ FT_Byte* limit;
+ FT_Int embed;
+
+
+ token->type = T1_TOKEN_TYPE_NONE;
+ token->start = NULL;
+ token->limit = NULL;
+
+ /* first of all, skip leading whitespace */
+ ps_parser_skip_spaces( parser );
+
+ cur = parser->cursor;
+ limit = parser->limit;
+
+ if ( cur >= limit )
+ return;
+
+ switch ( *cur )
+ {
+ /************* check for literal string *****************/
+ case '(':
+ token->type = T1_TOKEN_TYPE_STRING;
+ token->start = cur;
+
+ if ( skip_literal_string( &cur, limit ) == FT_Err_Ok )
+ token->limit = cur;
+ break;
+
+ /************* check for programs/array *****************/
+ case '{':
+ token->type = T1_TOKEN_TYPE_ARRAY;
+ token->start = cur;
+
+ if ( skip_procedure( &cur, limit ) == FT_Err_Ok )
+ token->limit = cur;
+ break;
+
+ /************* check for table/array ********************/
+ /* XXX: in theory we should also look for "<<" */
+ /* since this is semantically equivalent to "["; */
+ /* in practice it doesn't matter (?) */
+ case '[':
+ token->type = T1_TOKEN_TYPE_ARRAY;
+ embed = 1;
+ token->start = cur++;
+
+ /* we need this to catch `[ ]' */
+ parser->cursor = cur;
+ ps_parser_skip_spaces( parser );
+ cur = parser->cursor;
+
+ while ( cur < limit && !parser->error )
+ {
+ /* XXX: this is wrong because it does not */
+ /* skip comments, procedures, and strings */
+ if ( *cur == '[' )
+ embed++;
+ else if ( *cur == ']' )
+ {
+ embed--;
+ if ( embed <= 0 )
+ {
+ token->limit = ++cur;
+ break;
+ }
+ }
+
+ parser->cursor = cur;
+ ps_parser_skip_PS_token( parser );
+ /* we need this to catch `[XXX ]' */
+ ps_parser_skip_spaces ( parser );
+ cur = parser->cursor;
+ }
+ break;
+
+ /* ************ otherwise, it is any token **************/
+ default:
+ token->start = cur;
+ token->type = ( *cur == '/' ) ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY;
+ ps_parser_skip_PS_token( parser );
+ cur = parser->cursor;
+ if ( !parser->error )
+ token->limit = cur;
+ }
+
+ if ( !token->limit )
+ {
+ token->start = NULL;
+ token->type = T1_TOKEN_TYPE_NONE;
+ }
+
+ parser->cursor = cur;
+ }
+
+
+ /* NB: `tokens' can be NULL if we only want to count */
+ /* the number of array elements */
+
+ FT_LOCAL_DEF( void )
+ ps_parser_to_token_array( PS_Parser parser,
+ T1_Token tokens,
+ FT_UInt max_tokens,
+ FT_Int* pnum_tokens )
+ {
+ T1_TokenRec master;
+
+
+ *pnum_tokens = -1;
+
+ /* this also handles leading whitespace */
+ ps_parser_to_token( parser, &master );
+
+ if ( master.type == T1_TOKEN_TYPE_ARRAY )
+ {
+ FT_Byte* old_cursor = parser->cursor;
+ FT_Byte* old_limit = parser->limit;
+ T1_Token cur = tokens;
+ T1_Token limit = cur + max_tokens;
+
+
+ /* don't include outermost delimiters */
+ parser->cursor = master.start + 1;
+ parser->limit = master.limit - 1;
+
+ while ( parser->cursor < parser->limit )
+ {
+ T1_TokenRec token;
+
+
+ ps_parser_to_token( parser, &token );
+ if ( !token.type )
+ break;
+
+ if ( tokens && cur < limit )
+ *cur = token;
+
+ cur++;
+ }
+
+ *pnum_tokens = (FT_Int)( cur - tokens );
+
+ parser->cursor = old_cursor;
+ parser->limit = old_limit;
+ }
+ }
+
+
+ /* first character must be a delimiter or a part of a number */
+ /* NB: `coords' can be NULL if we just want to skip the */
+ /* array; in this case we ignore `max_coords' */
+
+ static FT_Int
+ ps_tocoordarray( FT_Byte* *acur,
+ FT_Byte* limit,
+ FT_Int max_coords,
+ FT_Short* coords )
+ {
+ FT_Byte* cur = *acur;
+ FT_Int count = 0;
+ FT_Byte c, ender;
+
+
+ if ( cur >= limit )
+ goto Exit;
+
+ /* check for the beginning of an array; otherwise, only one number */
+ /* will be read */
+ c = *cur;
+ ender = 0;
+
+ if ( c == '[' )
+ ender = ']';
+ else if ( c == '{' )
+ ender = '}';
+
+ if ( ender )
+ cur++;
+
+ /* now, read the coordinates */
+ while ( cur < limit )
+ {
+ FT_Short dummy;
+ FT_Byte* old_cur;
+
+
+ /* skip whitespace in front of data */
+ skip_spaces( &cur, limit );
+ if ( cur >= limit )
+ goto Exit;
+
+ if ( *cur == ender )
+ {
+ cur++;
+ break;
+ }
+
+ old_cur = cur;
+
+ if ( coords && count >= max_coords )
+ break;
+
+ /* call PS_Conv_ToFixed() even if coords == NULL */
+ /* to properly parse number at `cur' */
+ *( coords ? &coords[count] : &dummy ) =
+ (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
+
+ if ( old_cur == cur )
+ {
+ count = -1;
+ goto Exit;
+ }
+ else
+ count++;
+
+ if ( !ender )
+ break;
+ }
+
+ Exit:
+ *acur = cur;
+ return count;
+ }
+
+
+ /* first character must be a delimiter or a part of a number */
+ /* NB: `values' can be NULL if we just want to skip the */
+ /* array; in this case we ignore `max_values' */
+ /* */
+ /* return number of successfully parsed values */
+
+ static FT_Int
+ ps_tofixedarray( FT_Byte* *acur,
+ FT_Byte* limit,
+ FT_Int max_values,
+ FT_Fixed* values,
+ FT_Int power_ten )
+ {
+ FT_Byte* cur = *acur;
+ FT_Int count = 0;
+ FT_Byte c, ender;
+
+
+ if ( cur >= limit )
+ goto Exit;
+
+ /* Check for the beginning of an array. Otherwise, only one number */
+ /* will be read. */
+ c = *cur;
+ ender = 0;
+
+ if ( c == '[' )
+ ender = ']';
+ else if ( c == '{' )
+ ender = '}';
+
+ if ( ender )
+ cur++;
+
+ /* now, read the values */
+ while ( cur < limit )
+ {
+ FT_Fixed dummy;
+ FT_Byte* old_cur;
+
+
+ /* skip whitespace in front of data */
+ skip_spaces( &cur, limit );
+ if ( cur >= limit )
+ goto Exit;
+
+ if ( *cur == ender )
+ {
+ cur++;
+ break;
+ }
+
+ old_cur = cur;
+
+ if ( values && count >= max_values )
+ break;
+
+ /* call PS_Conv_ToFixed() even if coords == NULL */
+ /* to properly parse number at `cur' */
+ *( values ? &values[count] : &dummy ) =
+ PS_Conv_ToFixed( &cur, limit, power_ten );
+
+ if ( old_cur == cur )
+ {
+ count = -1;
+ goto Exit;
+ }
+ else
+ count++;
+
+ if ( !ender )
+ break;
+ }
+
+ Exit:
+ *acur = cur;
+ return count;
+ }
+
+
+#if 0
+
+ static FT_String*
+ ps_tostring( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Memory memory )
+ {
+ FT_Byte* cur = *cursor;
+ FT_UInt len = 0;
+ FT_Int count;
+ FT_String* result;
+ FT_Error error;
+
+
+ /* XXX: some stupid fonts have a `Notice' or `Copyright' string */
+ /* that simply doesn't begin with an opening parenthesis, even */
+ /* though they have a closing one! E.g. "amuncial.pfb" */
+ /* */
+ /* We must deal with these ill-fated cases there. Note that */
+ /* these fonts didn't work with the old Type 1 driver as the */
+ /* notice/copyright was not recognized as a valid string token */
+ /* and made the old token parser commit errors. */
+
+ while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) )
+ cur++;
+ if ( cur + 1 >= limit )
+ return 0;
+
+ if ( *cur == '(' )
+ cur++; /* skip the opening parenthesis, if there is one */
+
+ *cursor = cur;
+ count = 0;
+
+ /* then, count its length */
+ for ( ; cur < limit; cur++ )
+ {
+ if ( *cur == '(' )
+ count++;
+
+ else if ( *cur == ')' )
+ {
+ count--;
+ if ( count < 0 )
+ break;
+ }
+ }
+
+ len = (FT_UInt)( cur - *cursor );
+ if ( cur >= limit || FT_QALLOC( result, len + 1 ) )
+ return 0;
+
+ /* now copy the string */
+ FT_MEM_COPY( result, *cursor, len );
+ result[len] = '\0';
+ *cursor = cur;
+ return result;
+ }
+
+#endif /* 0 */
+
+
+ static int
+ ps_tobool( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+ FT_Bool result = 0;
+
+
+ /* return 1 if we find `true', 0 otherwise */
+ if ( cur + 3 < limit &&
+ cur[0] == 't' &&
+ cur[1] == 'r' &&
+ cur[2] == 'u' &&
+ cur[3] == 'e' )
+ {
+ result = 1;
+ cur += 5;
+ }
+ else if ( cur + 4 < limit &&
+ cur[0] == 'f' &&
+ cur[1] == 'a' &&
+ cur[2] == 'l' &&
+ cur[3] == 's' &&
+ cur[4] == 'e' )
+ {
+ result = 0;
+ cur += 6;
+ }
+
+ *acur = cur;
+ return result;
+ }
+
+
+ /* load a simple field (i.e. non-table) into the current list of objects */
+
+ FT_LOCAL_DEF( FT_Error )
+ ps_parser_load_field( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags )
+ {
+ T1_TokenRec token;
+ FT_Byte* cur;
+ FT_Byte* limit;
+ FT_UInt count;
+ FT_UInt idx;
+ FT_Error error;
+ T1_FieldType type;
+
+
+ /* this also skips leading whitespace */
+ ps_parser_to_token( parser, &token );
+ if ( !token.type )
+ goto Fail;
+
+ count = 1;
+ idx = 0;
+ cur = token.start;
+ limit = token.limit;
+
+ type = field->type;
+
+ /* we must detect arrays in /FontBBox */
+ if ( type == T1_FIELD_TYPE_BBOX )
+ {
+ T1_TokenRec token2;
+ FT_Byte* old_cur = parser->cursor;
+ FT_Byte* old_limit = parser->limit;
+
+
+ /* don't include delimiters */
+ parser->cursor = token.start + 1;
+ parser->limit = token.limit - 1;
+
+ ps_parser_to_token( parser, &token2 );
+ parser->cursor = old_cur;
+ parser->limit = old_limit;
+
+ if ( token2.type == T1_TOKEN_TYPE_ARRAY )
+ {
+ type = T1_FIELD_TYPE_MM_BBOX;
+ goto FieldArray;
+ }
+ }
+ else if ( token.type == T1_TOKEN_TYPE_ARRAY )
+ {
+ count = max_objects;
+
+ FieldArray:
+ /* if this is an array and we have no blend, an error occurs */
+ if ( max_objects == 0 )
+ goto Fail;
+
+ idx = 1;
+
+ /* don't include delimiters */
+ cur++;
+ limit--;
+ }
+
+ for ( ; count > 0; count--, idx++ )
+ {
+ FT_Byte* q = (FT_Byte*)objects[idx] + field->offset;
+ FT_Long val;
+
+
+ skip_spaces( &cur, limit );
+
+ switch ( type )
+ {
+ case T1_FIELD_TYPE_BOOL:
+ val = ps_tobool( &cur, limit );
+ FT_TRACE4(( " %s", val ? "true" : "false" ));
+ goto Store_Integer;
+
+ case T1_FIELD_TYPE_FIXED:
+ val = PS_Conv_ToFixed( &cur, limit, 0 );
+ FT_TRACE4(( " %f", (double)val / 65536 ));
+ goto Store_Integer;
+
+ case T1_FIELD_TYPE_FIXED_1000:
+ val = PS_Conv_ToFixed( &cur, limit, 3 );
+ FT_TRACE4(( " %f", (double)val / 65536 / 1000 ));
+ goto Store_Integer;
+
+ case T1_FIELD_TYPE_INTEGER:
+ val = PS_Conv_ToInt( &cur, limit );
+ FT_TRACE4(( " %ld", val ));
+ /* fall through */
+
+ Store_Integer:
+ switch ( field->size )
+ {
+ case (8 / FT_CHAR_BIT):
+ *(FT_Byte*)q = (FT_Byte)val;
+ break;
+
+ case (16 / FT_CHAR_BIT):
+ *(FT_UShort*)q = (FT_UShort)val;
+ break;
+
+ case (32 / FT_CHAR_BIT):
+ *(FT_UInt32*)q = (FT_UInt32)val;
+ break;
+
+ default: /* for 64-bit systems */
+ *(FT_Long*)q = val;
+ }
+ break;
+
+ case T1_FIELD_TYPE_STRING:
+ case T1_FIELD_TYPE_KEY:
+ {
+ FT_Memory memory = parser->memory;
+ FT_UInt len = (FT_UInt)( limit - cur );
+ FT_String* string = NULL;
+
+
+ if ( cur >= limit )
+ break;
+
+ /* we allow both a string or a name */
+ /* for cases like /FontName (foo) def */
+ if ( token.type == T1_TOKEN_TYPE_KEY )
+ {
+ /* don't include leading `/' */
+ len--;
+ cur++;
+ }
+ else if ( token.type == T1_TOKEN_TYPE_STRING )
+ {
+ /* don't include delimiting parentheses */
+ /* XXX we don't handle <<...>> here */
+ /* XXX should we convert octal escapes? */
+ /* if so, what encoding should we use? */
+ cur++;
+ len -= 2;
+ }
+ else
+ {
+ FT_ERROR(( "ps_parser_load_field:"
+ " expected a name or string\n" ));
+ FT_ERROR(( " "
+ " but found token of type %d instead\n",
+ token.type ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ /* for this to work (FT_String**)q must have been */
+ /* initialized to NULL */
+ if ( *(FT_String**)q )
+ {
+ FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n",
+ field->ident ));
+ FT_FREE( *(FT_String**)q );
+ }
+
+ if ( FT_QALLOC( string, len + 1 ) )
+ goto Exit;
+
+ FT_MEM_COPY( string, cur, len );
+ string[len] = 0;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( token.type == T1_TOKEN_TYPE_STRING )
+ FT_TRACE4(( " (%s)", string ));
+ else
+ FT_TRACE4(( " /%s", string ));
+#endif
+
+ *(FT_String**)q = string;
+ }
+ break;
+
+ case T1_FIELD_TYPE_BBOX:
+ {
+ FT_Fixed temp[4];
+ FT_BBox* bbox = (FT_BBox*)q;
+ FT_Int result;
+
+
+ result = ps_tofixedarray( &cur, limit, 4, temp, 0 );
+
+ if ( result < 4 )
+ {
+ FT_ERROR(( "ps_parser_load_field:"
+ " expected four integers in bounding box\n" ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ bbox->xMin = FT_RoundFix( temp[0] );
+ bbox->yMin = FT_RoundFix( temp[1] );
+ bbox->xMax = FT_RoundFix( temp[2] );
+ bbox->yMax = FT_RoundFix( temp[3] );
+
+ FT_TRACE4(( " [%ld %ld %ld %ld]",
+ bbox->xMin / 65536,
+ bbox->yMin / 65536,
+ bbox->xMax / 65536,
+ bbox->yMax / 65536 ));
+ }
+ break;
+
+ case T1_FIELD_TYPE_MM_BBOX:
+ {
+ FT_Memory memory = parser->memory;
+ FT_Fixed* temp = NULL;
+ FT_Int result;
+ FT_UInt i;
+
+
+ if ( FT_QNEW_ARRAY( temp, max_objects * 4 ) )
+ goto Exit;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ result = ps_tofixedarray( &cur, limit, (FT_Int)max_objects,
+ temp + i * max_objects, 0 );
+ if ( result < 0 || (FT_UInt)result < max_objects )
+ {
+ FT_ERROR(( "ps_parser_load_field:"
+ " expected %d integer%s in the %s subarray\n",
+ max_objects, max_objects > 1 ? "s" : "",
+ i == 0 ? "first"
+ : ( i == 1 ? "second"
+ : ( i == 2 ? "third"
+ : "fourth" ) ) ));
+ FT_ERROR(( " "
+ " of /FontBBox in the /Blend dictionary\n" ));
+ error = FT_THROW( Invalid_File_Format );
+
+ FT_FREE( temp );
+ goto Exit;
+ }
+
+ skip_spaces( &cur, limit );
+ }
+
+ FT_TRACE4(( " [" ));
+ for ( i = 0; i < max_objects; i++ )
+ {
+ FT_BBox* bbox = (FT_BBox*)objects[i];
+
+
+ bbox->xMin = FT_RoundFix( temp[i ] );
+ bbox->yMin = FT_RoundFix( temp[i + max_objects] );
+ bbox->xMax = FT_RoundFix( temp[i + 2 * max_objects] );
+ bbox->yMax = FT_RoundFix( temp[i + 3 * max_objects] );
+
+ FT_TRACE4(( " [%ld %ld %ld %ld]",
+ bbox->xMin / 65536,
+ bbox->yMin / 65536,
+ bbox->xMax / 65536,
+ bbox->yMax / 65536 ));
+ }
+ FT_TRACE4(( "]" ));
+
+ FT_FREE( temp );
+ }
+ break;
+
+ default:
+ /* an error occurred */
+ goto Fail;
+ }
+ }
+
+#if 0 /* obsolete -- keep for reference */
+ if ( pflags )
+ *pflags |= 1L << field->flag_bit;
+#else
+ FT_UNUSED( pflags );
+#endif
+
+ error = FT_Err_Ok;
+
+ Exit:
+ return error;
+
+ Fail:
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+
+#define T1_MAX_TABLE_ELEMENTS 32
+
+
+ FT_LOCAL_DEF( FT_Error )
+ ps_parser_load_field_table( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags )
+ {
+ T1_TokenRec elements[T1_MAX_TABLE_ELEMENTS];
+ T1_Token token;
+ FT_Int num_elements;
+ FT_Error error = FT_Err_Ok;
+ FT_Byte* old_cursor;
+ FT_Byte* old_limit;
+ T1_FieldRec fieldrec = *(T1_Field)field;
+
+
+ fieldrec.type = T1_FIELD_TYPE_INTEGER;
+ if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY ||
+ field->type == T1_FIELD_TYPE_BBOX )
+ fieldrec.type = T1_FIELD_TYPE_FIXED;
+
+ ps_parser_to_token_array( parser, elements,
+ T1_MAX_TABLE_ELEMENTS, &num_elements );
+ if ( num_elements < 0 )
+ {
+ error = FT_ERR( Ignore );
+ goto Exit;
+ }
+ if ( (FT_UInt)num_elements > field->array_max )
+ num_elements = (FT_Int)field->array_max;
+
+ old_cursor = parser->cursor;
+ old_limit = parser->limit;
+
+ /* we store the elements count if necessary; */
+ /* we further assume that `count_offset' can't be zero */
+ if ( field->type != T1_FIELD_TYPE_BBOX && field->count_offset != 0 )
+ *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) =
+ (FT_Byte)num_elements;
+
+ FT_TRACE4(( " [" ));
+
+ /* we now load each element, adjusting the field.offset on each one */
+ token = elements;
+ for ( ; num_elements > 0; num_elements--, token++ )
+ {
+ parser->cursor = token->start;
+ parser->limit = token->limit;
+
+ error = ps_parser_load_field( parser,
+ &fieldrec,
+ objects,
+ max_objects,
+ 0 );
+ if ( error )
+ break;
+
+ fieldrec.offset += fieldrec.size;
+ }
+
+ FT_TRACE4(( "]" ));
+
+#if 0 /* obsolete -- keep for reference */
+ if ( pflags )
+ *pflags |= 1L << field->flag_bit;
+#else
+ FT_UNUSED( pflags );
+#endif
+
+ parser->cursor = old_cursor;
+ parser->limit = old_limit;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Long )
+ ps_parser_to_int( PS_Parser parser )
+ {
+ ps_parser_skip_spaces( parser );
+ return PS_Conv_ToInt( &parser->cursor, parser->limit );
+ }
+
+
+ /* first character must be `<' if `delimiters' is non-zero */
+
+ FT_LOCAL_DEF( FT_Error )
+ ps_parser_to_bytes( PS_Parser parser,
+ FT_Byte* bytes,
+ FT_Offset max_bytes,
+ FT_ULong* pnum_bytes,
+ FT_Bool delimiters )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Byte* cur;
+
+
+ ps_parser_skip_spaces( parser );
+ cur = parser->cursor;
+
+ if ( cur >= parser->limit )
+ goto Exit;
+
+ if ( delimiters )
+ {
+ if ( *cur != '<' )
+ {
+ FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ cur++;
+ }
+
+ *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
+ parser->limit,
+ bytes,
+ max_bytes );
+
+ parser->cursor = cur;
+
+ if ( delimiters )
+ {
+ if ( cur < parser->limit && *cur != '>' )
+ {
+ FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" ));
+ error = FT_THROW( Invalid_File_Format );
+ goto Exit;
+ }
+
+ parser->cursor++;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Fixed )
+ ps_parser_to_fixed( PS_Parser parser,
+ FT_Int power_ten )
+ {
+ ps_parser_skip_spaces( parser );
+ return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ ps_parser_to_coord_array( PS_Parser parser,
+ FT_Int max_coords,
+ FT_Short* coords )
+ {
+ ps_parser_skip_spaces( parser );
+ return ps_tocoordarray( &parser->cursor, parser->limit,
+ max_coords, coords );
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ ps_parser_to_fixed_array( PS_Parser parser,
+ FT_Int max_values,
+ FT_Fixed* values,
+ FT_Int power_ten )
+ {
+ ps_parser_skip_spaces( parser );
+ return ps_tofixedarray( &parser->cursor, parser->limit,
+ max_values, values, power_ten );
+ }
+
+
+#if 0
+
+ FT_LOCAL_DEF( FT_String* )
+ T1_ToString( PS_Parser parser )
+ {
+ return ps_tostring( &parser->cursor, parser->limit, parser->memory );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ T1_ToBool( PS_Parser parser )
+ {
+ return ps_tobool( &parser->cursor, parser->limit );
+ }
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_init( PS_Parser parser,
+ FT_Byte* base,
+ FT_Byte* limit,
+ FT_Memory memory )
+ {
+ parser->error = FT_Err_Ok;
+ parser->base = base;
+ parser->limit = limit;
+ parser->cursor = base;
+ parser->memory = memory;
+ parser->funcs = ps_parser_funcs;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_done( PS_Parser parser )
+ {
+ FT_UNUSED( parser );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1_builder_init
+ *
+ * @Description:
+ * Initializes a given glyph builder.
+ *
+ * @InOut:
+ * builder ::
+ * A pointer to the glyph builder to initialize.
+ *
+ * @Input:
+ * face ::
+ * The current face object.
+ *
+ * size ::
+ * The current size object.
+ *
+ * glyph ::
+ * The current glyph object.
+ *
+ * hinting ::
+ * Whether hinting should be applied.
+ */
+ FT_LOCAL_DEF( void )
+ t1_builder_init( T1_Builder builder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot glyph,
+ FT_Bool hinting )
+ {
+ builder->parse_state = T1_Parse_Start;
+ builder->load_points = 1;
+
+ builder->face = face;
+ builder->glyph = glyph;
+ builder->memory = face->memory;
+
+ if ( glyph )
+ {
+ FT_GlyphLoader loader = glyph->internal->loader;
+
+
+ builder->loader = loader;
+ builder->base = &loader->base.outline;
+ builder->current = &loader->current.outline;
+ FT_GlyphLoader_Rewind( loader );
+
+ builder->hints_globals = size->internal->module_data;
+ builder->hints_funcs = NULL;
+
+ if ( hinting )
+ builder->hints_funcs = glyph->internal->glyph_hints;
+ }
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+ builder->advance.x = 0;
+ builder->advance.y = 0;
+
+ builder->funcs = t1_builder_funcs;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1_builder_done
+ *
+ * @Description:
+ * Finalizes a given glyph builder. Its contents can still be used
+ * after the call, but the function saves important information
+ * within the corresponding glyph slot.
+ *
+ * @Input:
+ * builder ::
+ * A pointer to the glyph builder to finalize.
+ */
+ FT_LOCAL_DEF( void )
+ t1_builder_done( T1_Builder builder )
+ {
+ FT_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->outline = *builder->base;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_check_points( T1_Builder builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ FT_LOCAL_DEF( void )
+ t1_builder_add_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+
+ point->x = FIXED_TO_INT( x );
+ point->y = FIXED_TO_INT( y );
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+ }
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_add_point1( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = t1_builder_check_points( builder, 1 );
+ if ( !error )
+ t1_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_add_contour( T1_Builder builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ /* this might happen in invalid fonts */
+ if ( !outline )
+ {
+ FT_ERROR(( "t1_builder_add_contour: no outline to add points to\n" ));
+ return FT_THROW( Invalid_File_Format );
+ }
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return FT_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_start_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = FT_ERR( Invalid_File_Format );
+
+
+ /* test whether we are building a new contour */
+
+ if ( builder->parse_state == T1_Parse_Have_Path )
+ error = FT_Err_Ok;
+ else
+ {
+ builder->parse_state = T1_Parse_Have_Path;
+ error = t1_builder_add_contour( builder );
+ if ( !error )
+ error = t1_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ FT_LOCAL_DEF( void )
+ t1_builder_close_contour( T1_Builder builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Int first;
+
+
+ if ( !outline )
+ return;
+
+ first = outline->n_contours <= 1
+ ? 0 : outline->contours[outline->n_contours - 2] + 1;
+
+ /* in malformed fonts it can happen that a contour was started */
+ /* but no points were added */
+ if ( outline->n_contours && first == outline->n_points )
+ {
+ outline->n_contours--;
+ return;
+ }
+
+ /* We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ {
+ /* Don't add contours only consisting of one point, i.e., */
+ /* check whether the first and the last point is the same. */
+ if ( first == outline->n_points - 1 )
+ {
+ outline->n_contours--;
+ outline->n_points--;
+ }
+ else
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CFF BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * cff_builder_init
+ *
+ * @Description:
+ * Initializes a given glyph builder.
+ *
+ * @InOut:
+ * builder ::
+ * A pointer to the glyph builder to initialize.
+ *
+ * @Input:
+ * face ::
+ * The current face object.
+ *
+ * size ::
+ * The current size object.
+ *
+ * glyph ::
+ * The current glyph object.
+ *
+ * hinting ::
+ * Whether hinting is active.
+ */
+ FT_LOCAL_DEF( void )
+ cff_builder_init( CFF_Builder* builder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot glyph,
+ FT_Bool hinting )
+ {
+ builder->path_begun = 0;
+ builder->load_points = 1;
+
+ builder->face = face;
+ builder->glyph = glyph;
+ builder->memory = face->root.memory;
+
+ if ( glyph )
+ {
+ FT_GlyphLoader loader = glyph->root.internal->loader;
+
+
+ builder->loader = loader;
+ builder->base = &loader->base.outline;
+ builder->current = &loader->current.outline;
+ FT_GlyphLoader_Rewind( loader );
+
+ builder->hints_globals = NULL;
+ builder->hints_funcs = NULL;
+
+ if ( hinting && size )
+ {
+ FT_Size ftsize = FT_SIZE( size );
+ CFF_Internal internal = (CFF_Internal)ftsize->internal->module_data;
+
+ if ( internal )
+ {
+ builder->hints_globals = (void *)internal->topfont;
+ builder->hints_funcs = glyph->root.internal->glyph_hints;
+ }
+ }
+ }
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+ builder->advance.x = 0;
+ builder->advance.y = 0;
+
+ builder->funcs = cff_builder_funcs;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * cff_builder_done
+ *
+ * @Description:
+ * Finalizes a given glyph builder. Its contents can still be used
+ * after the call, but the function saves important information
+ * within the corresponding glyph slot.
+ *
+ * @Input:
+ * builder ::
+ * A pointer to the glyph builder to finalize.
+ */
+ FT_LOCAL_DEF( void )
+ cff_builder_done( CFF_Builder* builder )
+ {
+ CFF_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->root.outline = *builder->base;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ FT_LOCAL_DEF( FT_Error )
+ cff_check_points( CFF_Builder* builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ FT_LOCAL_DEF( void )
+ cff_builder_add_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+
+
+ if ( driver->hinting_engine == FT_HINTING_FREETYPE )
+ {
+ point->x = x >> 16;
+ point->y = y >> 16;
+ }
+ else
+#endif
+ {
+ /* cf2_decoder_parse_charstrings uses 16.16 coordinates */
+ point->x = x >> 10;
+ point->y = y >> 10;
+ }
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+ }
+
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ cff_builder_add_point1( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = cff_check_points( builder, 1 );
+ if ( !error )
+ cff_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ cff_builder_add_contour( CFF_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return FT_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ FT_LOCAL_DEF( FT_Error )
+ cff_builder_start_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ /* test whether we are building a new contour */
+ if ( !builder->path_begun )
+ {
+ builder->path_begun = 1;
+ error = cff_builder_add_contour( builder );
+ if ( !error )
+ error = cff_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ FT_LOCAL_DEF( void )
+ cff_builder_close_contour( CFF_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Int first;
+
+
+ if ( !outline )
+ return;
+
+ first = outline->n_contours <= 1
+ ? 0 : outline->contours[outline->n_contours - 2] + 1;
+
+ /* in malformed fonts it can happen that a contour was started */
+ /* but no points were added */
+ if ( outline->n_contours && first == outline->n_points )
+ {
+ outline->n_contours--;
+ return;
+ }
+
+ /* We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and if it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ {
+ /* Don't add contours only consisting of one point, i.e., */
+ /* check whether begin point and last point are the same. */
+ if ( first == outline->n_points - 1 )
+ {
+ outline->n_contours--;
+ outline->n_points--;
+ }
+ else
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ps_builder_init
+ *
+ * @Description:
+ * Initializes a given glyph builder.
+ *
+ * @InOut:
+ * builder ::
+ * A pointer to the glyph builder to initialize.
+ *
+ * @Input:
+ * face ::
+ * The current face object.
+ *
+ * size ::
+ * The current size object.
+ *
+ * glyph ::
+ * The current glyph object.
+ *
+ * hinting ::
+ * Whether hinting should be applied.
+ */
+ FT_LOCAL_DEF( void )
+ ps_builder_init( PS_Builder* ps_builder,
+ void* builder,
+ FT_Bool is_t1 )
+ {
+ FT_ZERO( ps_builder );
+
+ if ( is_t1 )
+ {
+ T1_Builder t1builder = (T1_Builder)builder;
+
+
+ ps_builder->memory = t1builder->memory;
+ ps_builder->face = (FT_Face)t1builder->face;
+ ps_builder->glyph = (CFF_GlyphSlot)t1builder->glyph;
+ ps_builder->loader = t1builder->loader;
+ ps_builder->base = t1builder->base;
+ ps_builder->current = t1builder->current;
+
+ ps_builder->pos_x = &t1builder->pos_x;
+ ps_builder->pos_y = &t1builder->pos_y;
+
+ ps_builder->left_bearing = &t1builder->left_bearing;
+ ps_builder->advance = &t1builder->advance;
+
+ ps_builder->bbox = &t1builder->bbox;
+ ps_builder->path_begun = 0;
+ ps_builder->load_points = t1builder->load_points;
+ ps_builder->no_recurse = t1builder->no_recurse;
+
+ ps_builder->metrics_only = t1builder->metrics_only;
+ }
+ else
+ {
+ CFF_Builder* cffbuilder = (CFF_Builder*)builder;
+
+
+ ps_builder->memory = cffbuilder->memory;
+ ps_builder->face = (FT_Face)cffbuilder->face;
+ ps_builder->glyph = cffbuilder->glyph;
+ ps_builder->loader = cffbuilder->loader;
+ ps_builder->base = cffbuilder->base;
+ ps_builder->current = cffbuilder->current;
+
+ ps_builder->pos_x = &cffbuilder->pos_x;
+ ps_builder->pos_y = &cffbuilder->pos_y;
+
+ ps_builder->left_bearing = &cffbuilder->left_bearing;
+ ps_builder->advance = &cffbuilder->advance;
+
+ ps_builder->bbox = &cffbuilder->bbox;
+ ps_builder->path_begun = cffbuilder->path_begun;
+ ps_builder->load_points = cffbuilder->load_points;
+ ps_builder->no_recurse = cffbuilder->no_recurse;
+
+ ps_builder->metrics_only = cffbuilder->metrics_only;
+ }
+
+ ps_builder->is_t1 = is_t1;
+ ps_builder->funcs = ps_builder_funcs;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ps_builder_done
+ *
+ * @Description:
+ * Finalizes a given glyph builder. Its contents can still be used
+ * after the call, but the function saves important information
+ * within the corresponding glyph slot.
+ *
+ * @Input:
+ * builder ::
+ * A pointer to the glyph builder to finalize.
+ */
+ FT_LOCAL_DEF( void )
+ ps_builder_done( PS_Builder* builder )
+ {
+ CFF_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->root.outline = *builder->base;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_check_points( PS_Builder* builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ FT_LOCAL_DEF( void )
+ ps_builder_add_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+
+
+ if ( !builder->is_t1 &&
+ driver->hinting_engine == FT_HINTING_FREETYPE )
+ {
+ point->x = x >> 16;
+ point->y = y >> 16;
+ }
+ else
+#endif
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+#ifndef CFF_CONFIG_OPTION_OLD_ENGINE
+ PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( builder->face );
+#endif
+ if ( builder->is_t1 &&
+ driver->hinting_engine == FT_HINTING_FREETYPE )
+ {
+ point->x = FIXED_TO_INT( x );
+ point->y = FIXED_TO_INT( y );
+ }
+ else
+#endif
+ {
+ /* cf2_decoder_parse_charstrings uses 16.16 coordinates */
+ point->x = x >> 10;
+ point->y = y >> 10;
+ }
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+ }
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_add_point1( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = ps_builder_check_points( builder, 1 );
+ if ( !error )
+ ps_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_add_contour( PS_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ /* this might happen in invalid fonts */
+ if ( !outline )
+ {
+ FT_ERROR(( "ps_builder_add_contour: no outline to add points to\n" ));
+ return FT_THROW( Invalid_File_Format );
+ }
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return FT_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ FT_LOCAL_DEF( FT_Error )
+ ps_builder_start_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ /* test whether we are building a new contour */
+ if ( !builder->path_begun )
+ {
+ builder->path_begun = 1;
+ error = ps_builder_add_contour( builder );
+ if ( !error )
+ error = ps_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ FT_LOCAL_DEF( void )
+ ps_builder_close_contour( PS_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Int first;
+
+
+ if ( !outline )
+ return;
+
+ first = outline->n_contours <= 1
+ ? 0 : outline->contours[outline->n_contours - 2] + 1;
+
+ /* in malformed fonts it can happen that a contour was started */
+ /* but no points were added */
+ if ( outline->n_contours && first == outline->n_points )
+ {
+ outline->n_contours--;
+ return;
+ }
+
+ /* We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ {
+ /* Don't add contours only consisting of one point, i.e., */
+ /* check whether the first and the last point is the same. */
+ if ( first == outline->n_points - 1 )
+ {
+ outline->n_contours--;
+ outline->n_points--;
+ }
+ else
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** OTHER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * ps_decoder_init
+ *
+ * @Description:
+ * Creates a wrapper decoder for use in the combined
+ * Type 1 / CFF interpreter.
+ *
+ * @InOut:
+ * ps_decoder ::
+ * A pointer to the decoder to initialize.
+ *
+ * @Input:
+ * decoder ::
+ * A pointer to the original decoder.
+ *
+ * is_t1 ::
+ * Flag indicating Type 1 or CFF
+ */
+ FT_LOCAL_DEF( void )
+ ps_decoder_init( PS_Decoder* ps_decoder,
+ void* decoder,
+ FT_Bool is_t1 )
+ {
+ FT_ZERO( ps_decoder );
+
+ if ( is_t1 )
+ {
+ T1_Decoder t1_decoder = (T1_Decoder)decoder;
+
+
+ ps_builder_init( &ps_decoder->builder,
+ &t1_decoder->builder,
+ is_t1 );
+
+ ps_decoder->cf2_instance = &t1_decoder->cf2_instance;
+ ps_decoder->psnames = t1_decoder->psnames;
+
+ ps_decoder->num_glyphs = t1_decoder->num_glyphs;
+ ps_decoder->glyph_names = t1_decoder->glyph_names;
+ ps_decoder->hint_mode = t1_decoder->hint_mode;
+ ps_decoder->blend = t1_decoder->blend;
+
+ ps_decoder->num_locals = (FT_UInt)t1_decoder->num_subrs;
+ ps_decoder->locals = t1_decoder->subrs;
+ ps_decoder->locals_len = t1_decoder->subrs_len;
+ ps_decoder->locals_hash = t1_decoder->subrs_hash;
+
+ ps_decoder->buildchar = t1_decoder->buildchar;
+ ps_decoder->len_buildchar = t1_decoder->len_buildchar;
+
+ ps_decoder->lenIV = t1_decoder->lenIV;
+ }
+ else
+ {
+ CFF_Decoder* cff_decoder = (CFF_Decoder*)decoder;
+
+
+ ps_builder_init( &ps_decoder->builder,
+ &cff_decoder->builder,
+ is_t1 );
+
+ ps_decoder->cff = cff_decoder->cff;
+ ps_decoder->cf2_instance = &cff_decoder->cff->cf2_instance;
+ ps_decoder->current_subfont = cff_decoder->current_subfont;
+
+ ps_decoder->num_globals = cff_decoder->num_globals;
+ ps_decoder->globals = cff_decoder->globals;
+ ps_decoder->globals_bias = cff_decoder->globals_bias;
+ ps_decoder->num_locals = cff_decoder->num_locals;
+ ps_decoder->locals = cff_decoder->locals;
+ ps_decoder->locals_bias = cff_decoder->locals_bias;
+
+ ps_decoder->glyph_width = &cff_decoder->glyph_width;
+ ps_decoder->width_only = cff_decoder->width_only;
+
+ ps_decoder->hint_mode = cff_decoder->hint_mode;
+
+ ps_decoder->get_glyph_callback = cff_decoder->get_glyph_callback;
+ ps_decoder->free_glyph_callback = cff_decoder->free_glyph_callback;
+ }
+ }
+
+
+ /* Synthesize a SubFont object for Type 1 fonts, for use in the */
+ /* new interpreter to access Private dict data. */
+ FT_LOCAL_DEF( void )
+ t1_make_subfont( FT_Face face,
+ PS_Private priv,
+ CFF_SubFont subfont )
+ {
+ CFF_Private cpriv = &subfont->private_dict;
+ FT_UInt n, count;
+
+
+ FT_ZERO( subfont );
+ FT_ZERO( cpriv );
+
+ count = cpriv->num_blue_values = priv->num_blue_values;
+ for ( n = 0; n < count; n++ )
+ cpriv->blue_values[n] = (FT_Pos)priv->blue_values[n];
+
+ count = cpriv->num_other_blues = priv->num_other_blues;
+ for ( n = 0; n < count; n++ )
+ cpriv->other_blues[n] = (FT_Pos)priv->other_blues[n];
+
+ count = cpriv->num_family_blues = priv->num_family_blues;
+ for ( n = 0; n < count; n++ )
+ cpriv->family_blues[n] = (FT_Pos)priv->family_blues[n];
+
+ count = cpriv->num_family_other_blues = priv->num_family_other_blues;
+ for ( n = 0; n < count; n++ )
+ cpriv->family_other_blues[n] = (FT_Pos)priv->family_other_blues[n];
+
+ cpriv->blue_scale = priv->blue_scale;
+ cpriv->blue_shift = (FT_Pos)priv->blue_shift;
+ cpriv->blue_fuzz = (FT_Pos)priv->blue_fuzz;
+
+ cpriv->standard_width = (FT_Pos)priv->standard_width[0];
+ cpriv->standard_height = (FT_Pos)priv->standard_height[0];
+
+ count = cpriv->num_snap_widths = priv->num_snap_widths;
+ for ( n = 0; n < count; n++ )
+ cpriv->snap_widths[n] = (FT_Pos)priv->snap_widths[n];
+
+ count = cpriv->num_snap_heights = priv->num_snap_heights;
+ for ( n = 0; n < count; n++ )
+ cpriv->snap_heights[n] = (FT_Pos)priv->snap_heights[n];
+
+ cpriv->force_bold = priv->force_bold;
+ cpriv->lenIV = priv->lenIV;
+ cpriv->language_group = priv->language_group;
+ cpriv->expansion_factor = priv->expansion_factor;
+
+ cpriv->subfont = subfont;
+
+
+ /* Initialize the random number generator. */
+ if ( face->internal->random_seed != -1 )
+ {
+ /* If we have a face-specific seed, use it. */
+ /* If non-zero, update it to a positive value. */
+ subfont->random = (FT_UInt32)face->internal->random_seed;
+ if ( face->internal->random_seed )
+ {
+ do
+ {
+ face->internal->random_seed = (FT_Int32)cff_random(
+ (FT_UInt32)face->internal->random_seed );
+
+ } while ( face->internal->random_seed < 0 );
+ }
+ }
+ if ( !subfont->random )
+ {
+ FT_UInt32 seed;
+
+
+ /* compute random seed from some memory addresses */
+ seed = (FT_UInt32)( (FT_Offset)(char*)&seed ^
+ (FT_Offset)(char*)&face ^
+ (FT_Offset)(char*)&subfont );
+ seed = seed ^ ( seed >> 10 ) ^ ( seed >> 20 );
+ if ( seed == 0 )
+ seed = 0x7384;
+
+ subfont->random = seed;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ t1_decrypt( FT_Byte* buffer,
+ FT_Offset length,
+ FT_UShort seed )
+ {
+ PS_Conv_EexecDecode( &buffer,
+ FT_OFFSET( buffer, length ),
+ buffer,
+ length,
+ &seed );
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt32 )
+ cff_random( FT_UInt32 r )
+ {
+ /* a 32bit version of the `xorshift' algorithm */
+ r ^= r << 13;
+ r ^= r >> 17;
+ r ^= r << 5;
+
+ return r;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psobjs.h b/modules/freetype2/src/psaux/psobjs.h
new file mode 100644
index 0000000000..d5bce54108
--- /dev/null
+++ b/modules/freetype2/src/psaux/psobjs.h
@@ -0,0 +1,312 @@
+/****************************************************************************
+ *
+ * psobjs.h
+ *
+ * Auxiliary functions for PostScript fonts (specification).
+ *
+ * Copyright (C) 1996-2023 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.
+ *
+ */
+
+
+#ifndef PSOBJS_H_
+#define PSOBJS_H_
+
+
+#include <freetype/internal/psaux.h>
+#include <freetype/internal/cffotypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1_TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_TABLE
+ const PS_Table_FuncsRec ps_table_funcs;
+
+ FT_CALLBACK_TABLE
+ const PS_Parser_FuncsRec ps_parser_funcs;
+
+ FT_CALLBACK_TABLE
+ const T1_Builder_FuncsRec t1_builder_funcs;
+
+
+ FT_LOCAL( FT_Error )
+ ps_table_new( PS_Table table,
+ FT_Int count,
+ FT_Memory memory );
+
+ FT_LOCAL( FT_Error )
+ ps_table_add( PS_Table table,
+ FT_Int idx,
+ const void* object,
+ FT_UInt length );
+
+ FT_LOCAL( void )
+ ps_table_done( PS_Table table );
+
+
+ FT_LOCAL( void )
+ ps_table_release( PS_Table table );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 PARSER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL( void )
+ ps_parser_skip_spaces( PS_Parser parser );
+
+ FT_LOCAL( void )
+ ps_parser_skip_PS_token( PS_Parser parser );
+
+ FT_LOCAL( void )
+ ps_parser_to_token( PS_Parser parser,
+ T1_Token token );
+
+ FT_LOCAL( void )
+ ps_parser_to_token_array( PS_Parser parser,
+ T1_Token tokens,
+ FT_UInt max_tokens,
+ FT_Int* pnum_tokens );
+
+ FT_LOCAL( FT_Error )
+ ps_parser_load_field( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags );
+
+ FT_LOCAL( FT_Error )
+ ps_parser_load_field_table( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags );
+
+ FT_LOCAL( FT_Long )
+ ps_parser_to_int( PS_Parser parser );
+
+
+ FT_LOCAL( FT_Error )
+ ps_parser_to_bytes( PS_Parser parser,
+ FT_Byte* bytes,
+ FT_Offset max_bytes,
+ FT_ULong* pnum_bytes,
+ FT_Bool delimiters );
+
+
+ FT_LOCAL( FT_Fixed )
+ ps_parser_to_fixed( PS_Parser parser,
+ FT_Int power_ten );
+
+
+ FT_LOCAL( FT_Int )
+ ps_parser_to_coord_array( PS_Parser parser,
+ FT_Int max_coords,
+ FT_Short* coords );
+
+ FT_LOCAL( FT_Int )
+ ps_parser_to_fixed_array( PS_Parser parser,
+ FT_Int max_values,
+ FT_Fixed* values,
+ FT_Int power_ten );
+
+
+ FT_LOCAL( void )
+ ps_parser_init( PS_Parser parser,
+ FT_Byte* base,
+ FT_Byte* limit,
+ FT_Memory memory );
+
+ FT_LOCAL( void )
+ ps_parser_done( PS_Parser parser );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ t1_builder_init( T1_Builder builder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot glyph,
+ FT_Bool hinting );
+
+ FT_LOCAL( void )
+ t1_builder_done( T1_Builder builder );
+
+ FT_LOCAL( FT_Error )
+ t1_builder_check_points( T1_Builder builder,
+ FT_Int count );
+
+ FT_LOCAL( void )
+ t1_builder_add_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+
+ FT_LOCAL( FT_Error )
+ t1_builder_add_point1( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y );
+
+ FT_LOCAL( FT_Error )
+ t1_builder_add_contour( T1_Builder builder );
+
+
+ FT_LOCAL( FT_Error )
+ t1_builder_start_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y );
+
+
+ FT_LOCAL( void )
+ t1_builder_close_contour( T1_Builder builder );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CFF BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ cff_builder_init( CFF_Builder* builder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot glyph,
+ FT_Bool hinting );
+
+ FT_LOCAL( void )
+ cff_builder_done( CFF_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ cff_check_points( CFF_Builder* builder,
+ FT_Int count );
+
+ FT_LOCAL( void )
+ cff_builder_add_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+ FT_LOCAL( FT_Error )
+ cff_builder_add_point1( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+ FT_LOCAL( FT_Error )
+ cff_builder_start_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+ FT_LOCAL( void )
+ cff_builder_close_contour( CFF_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ cff_builder_add_contour( CFF_Builder* builder );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ ps_builder_init( PS_Builder* ps_builder,
+ void* builder,
+ FT_Bool is_t1 );
+
+
+ FT_LOCAL( void )
+ ps_builder_done( PS_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_check_points( PS_Builder* builder,
+ FT_Int count );
+
+ FT_LOCAL( void )
+ ps_builder_add_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_add_point1( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_add_contour( PS_Builder* builder );
+
+ FT_LOCAL( FT_Error )
+ ps_builder_start_point( PS_Builder* builder,
+ FT_Pos x,
+ FT_Pos y );
+
+ FT_LOCAL( void )
+ ps_builder_close_contour( PS_Builder* builder );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** OTHER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ ps_decoder_init( PS_Decoder* ps_decoder,
+ void* decoder,
+ FT_Bool is_t1 );
+
+ FT_LOCAL( void )
+ t1_make_subfont( FT_Face face,
+ PS_Private priv,
+ CFF_SubFont subfont );
+
+ FT_LOCAL( void )
+ t1_decrypt( FT_Byte* buffer,
+ FT_Offset length,
+ FT_UShort seed );
+
+
+ FT_LOCAL( FT_UInt32 )
+ cff_random( FT_UInt32 r );
+
+
+FT_END_HEADER
+
+#endif /* PSOBJS_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psread.c b/modules/freetype2/src/psaux/psread.c
new file mode 100644
index 0000000000..7f657f2cdc
--- /dev/null
+++ b/modules/freetype2/src/psaux/psread.c
@@ -0,0 +1,112 @@
+/****************************************************************************
+ *
+ * psread.c
+ *
+ * Adobe's code for stream handling (body).
+ *
+ * Copyright 2007-2013 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 <freetype/internal/ftdebug.h>
+
+#include "psglue.h"
+
+#include "pserror.h"
+
+
+ /* Define CF2_IO_FAIL as 1 to enable random errors and random */
+ /* value errors in I/O. */
+#define CF2_IO_FAIL 0
+
+
+#if CF2_IO_FAIL
+
+ /* set the .00 value to a nonzero probability */
+ static int
+ randomError2( void )
+ {
+ /* for region buffer ReadByte (interp) function */
+ return (double)rand() / RAND_MAX < .00;
+ }
+
+ /* set the .00 value to a nonzero probability */
+ static CF2_Int
+ randomValue()
+ {
+ return (double)rand() / RAND_MAX < .00 ? rand() : 0;
+ }
+
+#endif /* CF2_IO_FAIL */
+
+
+ /* Region Buffer */
+ /* */
+ /* Can be constructed from a copied buffer managed by */
+ /* `FCM_getDatablock'. */
+ /* Reads bytes with check for end of buffer. */
+
+ /* reading past the end of the buffer sets error and returns zero */
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_buf_readByte( CF2_Buffer buf )
+ {
+ if ( buf->ptr < buf->end )
+ {
+#if CF2_IO_FAIL
+ if ( randomError2() )
+ {
+ CF2_SET_ERROR( buf->error, Invalid_Stream_Operation );
+ return 0;
+ }
+
+ return *(buf->ptr)++ + randomValue();
+#else
+ return *(buf->ptr)++;
+#endif
+ }
+ else
+ {
+ CF2_SET_ERROR( buf->error, Invalid_Stream_Operation );
+ return 0;
+ }
+ }
+
+
+ /* note: end condition can occur without error */
+ FT_LOCAL_DEF( FT_Bool )
+ cf2_buf_isEnd( CF2_Buffer buf )
+ {
+ return FT_BOOL( buf->ptr >= buf->end );
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psread.h b/modules/freetype2/src/psaux/psread.h
new file mode 100644
index 0000000000..9e55fe0447
--- /dev/null
+++ b/modules/freetype2/src/psaux/psread.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+ * psread.h
+ *
+ * Adobe's code for stream handling (specification).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSREAD_H_
+#define PSREAD_H_
+
+
+FT_BEGIN_HEADER
+
+
+ typedef struct CF2_BufferRec_
+ {
+ FT_Error* error;
+ const FT_Byte* start;
+ const FT_Byte* end;
+ const FT_Byte* ptr;
+
+ } CF2_BufferRec, *CF2_Buffer;
+
+
+ FT_LOCAL( CF2_Int )
+ cf2_buf_readByte( CF2_Buffer buf );
+ FT_LOCAL( FT_Bool )
+ cf2_buf_isEnd( CF2_Buffer buf );
+
+
+FT_END_HEADER
+
+
+#endif /* PSREAD_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psstack.c b/modules/freetype2/src/psaux/psstack.c
new file mode 100644
index 0000000000..797486588a
--- /dev/null
+++ b/modules/freetype2/src/psaux/psstack.c
@@ -0,0 +1,329 @@
+/****************************************************************************
+ *
+ * psstack.c
+ *
+ * Adobe's code for emulating a CFF stack (body).
+ *
+ * Copyright 2007-2013 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 <freetype/internal/ftdebug.h>
+
+#include "psglue.h"
+#include "psfont.h"
+#include "psstack.h"
+
+#include "pserror.h"
+
+
+ /* Allocate and initialize an instance of CF2_Stack. */
+ /* Note: This function returns NULL on error (does not set */
+ /* `error'). */
+ FT_LOCAL_DEF( CF2_Stack )
+ cf2_stack_init( FT_Memory memory,
+ FT_Error* e,
+ FT_UInt stackSize )
+ {
+ FT_Error error; /* for FT_QNEW */
+ CF2_Stack stack = NULL;
+
+
+ if ( FT_QNEW( stack ) )
+ return NULL;
+
+ stack->memory = memory;
+ stack->error = e;
+
+ /* allocate the stack buffer */
+ if ( FT_QNEW_ARRAY( stack->buffer, stackSize ) )
+ {
+ FT_FREE( stack );
+ return NULL;
+ }
+
+ stack->stackSize = stackSize;
+ stack->top = stack->buffer; /* empty stack */
+
+ return stack;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_free( CF2_Stack stack )
+ {
+ if ( stack )
+ {
+ FT_Memory memory = stack->memory;
+
+ /* free the buffer */
+ FT_FREE( stack->buffer );
+
+ /* free the main structure */
+ FT_FREE( stack );
+ }
+ }
+
+
+ FT_LOCAL_DEF( CF2_UInt )
+ cf2_stack_count( CF2_Stack stack )
+ {
+ return (CF2_UInt)( stack->top - stack->buffer );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_pushInt( CF2_Stack stack,
+ CF2_Int val )
+ {
+ if ( stack->top == stack->buffer + stack->stackSize )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return; /* stack overflow */
+ }
+
+ stack->top->u.i = val;
+ stack->top->type = CF2_NumberInt;
+ stack->top++;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_pushFixed( CF2_Stack stack,
+ CF2_Fixed val )
+ {
+ if ( stack->top == stack->buffer + stack->stackSize )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return; /* stack overflow */
+ }
+
+ stack->top->u.r = val;
+ stack->top->type = CF2_NumberFixed;
+ stack->top++;
+ }
+
+
+ /* this function is only allowed to pop an integer type */
+ FT_LOCAL_DEF( CF2_Int )
+ cf2_stack_popInt( CF2_Stack stack )
+ {
+ if ( stack->top == stack->buffer )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Underflow );
+ return 0; /* underflow */
+ }
+ if ( stack->top[-1].type != CF2_NumberInt )
+ {
+ CF2_SET_ERROR( stack->error, Syntax_Error );
+ return 0; /* type mismatch */
+ }
+
+ stack->top--;
+
+ return stack->top->u.i;
+ }
+
+
+ /* Note: type mismatch is silently cast */
+ /* TODO: check this */
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_stack_popFixed( CF2_Stack stack )
+ {
+ if ( stack->top == stack->buffer )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Underflow );
+ return cf2_intToFixed( 0 ); /* underflow */
+ }
+
+ stack->top--;
+
+ switch ( stack->top->type )
+ {
+ case CF2_NumberInt:
+ return cf2_intToFixed( stack->top->u.i );
+ case CF2_NumberFrac:
+ return cf2_fracToFixed( stack->top->u.f );
+ default:
+ return stack->top->u.r;
+ }
+ }
+
+
+ /* Note: type mismatch is silently cast */
+ /* TODO: check this */
+ FT_LOCAL_DEF( CF2_Fixed )
+ cf2_stack_getReal( CF2_Stack stack,
+ CF2_UInt idx )
+ {
+ FT_ASSERT( cf2_stack_count( stack ) <= stack->stackSize );
+
+ if ( idx >= cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return cf2_intToFixed( 0 ); /* bounds error */
+ }
+
+ switch ( stack->buffer[idx].type )
+ {
+ case CF2_NumberInt:
+ return cf2_intToFixed( stack->buffer[idx].u.i );
+ case CF2_NumberFrac:
+ return cf2_fracToFixed( stack->buffer[idx].u.f );
+ default:
+ return stack->buffer[idx].u.r;
+ }
+ }
+
+
+ /* provide random access to stack */
+ FT_LOCAL_DEF( void )
+ cf2_stack_setReal( CF2_Stack stack,
+ CF2_UInt idx,
+ CF2_Fixed val )
+ {
+ if ( idx > cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return;
+ }
+
+ stack->buffer[idx].u.r = val;
+ stack->buffer[idx].type = CF2_NumberFixed;
+ }
+
+
+ /* discard (pop) num values from stack */
+ FT_LOCAL_DEF( void )
+ cf2_stack_pop( CF2_Stack stack,
+ CF2_UInt num )
+ {
+ if ( num > cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Underflow );
+ return;
+ }
+ stack->top -= num;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_roll( CF2_Stack stack,
+ CF2_Int count,
+ CF2_Int shift )
+ {
+ /* we initialize this variable to avoid compiler warnings */
+ CF2_StackNumber last = { { 0 }, CF2_NumberInt };
+
+ CF2_Int start_idx, idx, i;
+
+
+ if ( count < 2 )
+ return; /* nothing to do (values 0 and 1), or undefined value */
+
+ if ( (CF2_UInt)count > cf2_stack_count( stack ) )
+ {
+ CF2_SET_ERROR( stack->error, Stack_Overflow );
+ return;
+ }
+
+ /* before C99 it is implementation-defined whether */
+ /* the result of `%' is negative if the first operand */
+ /* is negative */
+ if ( shift < 0 )
+ shift = -( ( -shift ) % count );
+ else
+ shift %= count;
+
+ if ( shift == 0 )
+ return; /* nothing to do */
+
+ /* We use the following algorithm to do the rolling, */
+ /* which needs two temporary variables only. */
+ /* */
+ /* Example: */
+ /* */
+ /* count = 8 */
+ /* shift = 2 */
+ /* */
+ /* stack indices before roll: 7 6 5 4 3 2 1 0 */
+ /* stack indices after roll: 1 0 7 6 5 4 3 2 */
+ /* */
+ /* The value of index 0 gets moved to index 2, while */
+ /* the old value of index 2 gets moved to index 4, */
+ /* and so on. We thus have the following copying */
+ /* chains for shift value 2. */
+ /* */
+ /* 0 -> 2 -> 4 -> 6 -> 0 */
+ /* 1 -> 3 -> 5 -> 7 -> 1 */
+ /* */
+ /* If `count' and `shift' are incommensurable, we */
+ /* have a single chain only. Otherwise, increase */
+ /* the start index by 1 after the first chain, then */
+ /* do the next chain until all elements in all */
+ /* chains are handled. */
+
+ start_idx = -1;
+ idx = -1;
+ for ( i = 0; i < count; i++ )
+ {
+ CF2_StackNumber tmp;
+
+
+ if ( start_idx == idx )
+ {
+ start_idx++;
+ idx = start_idx;
+ last = stack->buffer[idx];
+ }
+
+ idx += shift;
+ if ( idx >= count )
+ idx -= count;
+ else if ( idx < 0 )
+ idx += count;
+
+ tmp = stack->buffer[idx];
+ stack->buffer[idx] = last;
+ last = tmp;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cf2_stack_clear( CF2_Stack stack )
+ {
+ stack->top = stack->buffer;
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/psstack.h b/modules/freetype2/src/psaux/psstack.h
new file mode 100644
index 0000000000..907b424000
--- /dev/null
+++ b/modules/freetype2/src/psaux/psstack.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+ *
+ * psstack.h
+ *
+ * Adobe's code for emulating a CFF stack (specification).
+ *
+ * Copyright 2007-2013 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.
+ *
+ */
+
+
+#ifndef PSSTACK_H_
+#define PSSTACK_H_
+
+#include <freetype/internal/compiler-macros.h>
+
+FT_BEGIN_HEADER
+
+
+ /* CFF operand stack; specified maximum of 48 or 192 values */
+ typedef struct CF2_StackNumber_
+ {
+ union
+ {
+ CF2_Fixed r; /* 16.16 fixed-point */
+ CF2_Frac f; /* 2.30 fixed-point (for font matrix) */
+ CF2_Int i;
+ } u;
+
+ CF2_NumberType type;
+
+ } CF2_StackNumber;
+
+
+ typedef struct CF2_StackRec_
+ {
+ FT_Memory memory;
+ FT_Error* error;
+ CF2_StackNumber* buffer;
+ CF2_StackNumber* top;
+ FT_UInt stackSize;
+
+ } CF2_StackRec, *CF2_Stack;
+
+
+ FT_LOCAL( CF2_Stack )
+ cf2_stack_init( FT_Memory memory,
+ FT_Error* error,
+ FT_UInt stackSize );
+ FT_LOCAL( void )
+ cf2_stack_free( CF2_Stack stack );
+
+ FT_LOCAL( CF2_UInt )
+ cf2_stack_count( CF2_Stack stack );
+
+ FT_LOCAL( void )
+ cf2_stack_pushInt( CF2_Stack stack,
+ CF2_Int val );
+ FT_LOCAL( void )
+ cf2_stack_pushFixed( CF2_Stack stack,
+ CF2_Fixed val );
+
+ FT_LOCAL( CF2_Int )
+ cf2_stack_popInt( CF2_Stack stack );
+ FT_LOCAL( CF2_Fixed )
+ cf2_stack_popFixed( CF2_Stack stack );
+
+ FT_LOCAL( CF2_Fixed )
+ cf2_stack_getReal( CF2_Stack stack,
+ CF2_UInt idx );
+ FT_LOCAL( void )
+ cf2_stack_setReal( CF2_Stack stack,
+ CF2_UInt idx,
+ CF2_Fixed val );
+
+ FT_LOCAL( void )
+ cf2_stack_pop( CF2_Stack stack,
+ CF2_UInt num );
+
+ FT_LOCAL( void )
+ cf2_stack_roll( CF2_Stack stack,
+ CF2_Int count,
+ CF2_Int idx );
+
+ FT_LOCAL( void )
+ cf2_stack_clear( CF2_Stack stack );
+
+
+FT_END_HEADER
+
+
+#endif /* PSSTACK_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/pstypes.h b/modules/freetype2/src/psaux/pstypes.h
new file mode 100644
index 0000000000..435ef7e1fe
--- /dev/null
+++ b/modules/freetype2/src/psaux/pstypes.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+ *
+ * pstypes.h
+ *
+ * Adobe's code for defining data types (specification only).
+ *
+ * Copyright 2011-2013 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.
+ *
+ */
+
+
+#ifndef PSTYPES_H_
+#define PSTYPES_H_
+
+#include <freetype/freetype.h>
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * The data models that we expect to support are as follows:
+ *
+ * name char short int long long-long pointer example
+ * -----------------------------------------------------
+ * ILP32 8 16 32 32 64* 32 32-bit MacOS, x86
+ * LLP64 8 16 32 32 64 64 x64
+ * LP64 8 16 32 64 64 64 64-bit MacOS
+ *
+ * *) type may be supported by emulation on a 32-bit architecture
+ *
+ */
+
+
+ /* integers at least 32 bits wide */
+#define CF2_UInt FT_UFast
+#define CF2_Int FT_Fast
+
+
+ /* fixed-float numbers */
+ typedef FT_Int32 CF2_F16Dot16;
+
+
+FT_END_HEADER
+
+
+#endif /* PSTYPES_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/rules.mk b/modules/freetype2/src/psaux/rules.mk
new file mode 100644
index 0000000000..d542ab8ee8
--- /dev/null
+++ b/modules/freetype2/src/psaux/rules.mk
@@ -0,0 +1,89 @@
+#
+# FreeType 2 PSaux driver configuration rules
+#
+
+
+# Copyright (C) 1996-2023 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.
+
+
+# PSAUX driver directory
+#
+PSAUX_DIR := $(SRC_DIR)/psaux
+
+
+# compilation flags for the driver
+#
+PSAUX_COMPILE := $(CC) $(ANSIFLAGS) \
+ $I$(subst /,$(COMPILER_SEP),$(PSAUX_DIR)) \
+ $(INCLUDE_FLAGS) \
+ $(FT_CFLAGS)
+
+
+# PSAUX driver sources (i.e., C files)
+#
+PSAUX_DRV_SRC := $(PSAUX_DIR)/psobjs.c \
+ $(PSAUX_DIR)/t1decode.c \
+ $(PSAUX_DIR)/t1cmap.c \
+ $(PSAUX_DIR)/afmparse.c \
+ $(PSAUX_DIR)/psconv.c \
+ $(PSAUX_DIR)/psauxmod.c \
+ $(PSAUX_DIR)/psarrst.c \
+ $(PSAUX_DIR)/psblues.c \
+ $(PSAUX_DIR)/pserror.c \
+ $(PSAUX_DIR)/psfont.c \
+ $(PSAUX_DIR)/psft.c \
+ $(PSAUX_DIR)/pshints.c \
+ $(PSAUX_DIR)/psintrp.c \
+ $(PSAUX_DIR)/psread.c \
+ $(PSAUX_DIR)/psstack.c \
+ $(PSAUX_DIR)/cffdecode.c
+
+# PSAUX driver headers
+#
+PSAUX_DRV_H := $(PSAUX_DRV_SRC:%c=%h) \
+ $(PSAUX_DIR)/psauxerr.h \
+ $(PSAUX_DIR)/psfixed.h \
+ $(PSAUX_DIR)/psglue.h \
+ $(PSAUX_DIR)/pstypes.h
+
+
+# PSAUX driver object(s)
+#
+# PSAUX_DRV_OBJ_M is used during `multi' builds.
+# PSAUX_DRV_OBJ_S is used during `single' builds.
+#
+PSAUX_DRV_OBJ_M := $(PSAUX_DRV_SRC:$(PSAUX_DIR)/%.c=$(OBJ_DIR)/%.$O)
+PSAUX_DRV_OBJ_S := $(OBJ_DIR)/psaux.$O
+
+# PSAUX driver source file for single build
+#
+PSAUX_DRV_SRC_S := $(PSAUX_DIR)/psaux.c
+
+
+# PSAUX driver - single object
+#
+$(PSAUX_DRV_OBJ_S): $(PSAUX_DRV_SRC_S) $(PSAUX_DRV_SRC) \
+ $(FREETYPE_H) $(PSAUX_DRV_H)
+ $(PSAUX_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSAUX_DRV_SRC_S))
+
+
+# PSAUX driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(PSAUX_DIR)/%.c $(FREETYPE_H) $(PSAUX_DRV_H)
+ $(PSAUX_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(PSAUX_DRV_OBJ_S)
+DRV_OBJS_M += $(PSAUX_DRV_OBJ_M)
+
+
+# EOF
diff --git a/modules/freetype2/src/psaux/t1cmap.c b/modules/freetype2/src/psaux/t1cmap.c
new file mode 100644
index 0000000000..bf0a393b45
--- /dev/null
+++ b/modules/freetype2/src/psaux/t1cmap.c
@@ -0,0 +1,374 @@
+/****************************************************************************
+ *
+ * t1cmap.c
+ *
+ * Type 1 character map support (body).
+ *
+ * Copyright (C) 2002-2023 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 "t1cmap.h"
+
+#include <freetype/internal/ftdebug.h>
+
+#include "psauxerr.h"
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ t1_cmap_std_init( T1_CMapStd cmap,
+ FT_Int is_expert )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ cmap->num_glyphs = (FT_UInt)face->type1.num_glyphs;
+ cmap->glyph_names = (const char* const*)face->type1.glyph_names;
+ cmap->sid_to_string = psnames->adobe_std_strings;
+ cmap->code_to_sid = is_expert ? psnames->adobe_expert_encoding
+ : psnames->adobe_std_encoding;
+
+ FT_ASSERT( cmap->code_to_sid );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ t1_cmap_std_done( T1_CMapStd cmap )
+ {
+ cmap->num_glyphs = 0;
+ cmap->glyph_names = NULL;
+ cmap->sid_to_string = NULL;
+ cmap->code_to_sid = NULL;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_std_char_index( T1_CMapStd cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt result = 0;
+
+
+ if ( char_code < 256 )
+ {
+ FT_UInt code, n;
+ const char* glyph_name;
+
+
+ /* convert character code to Adobe SID string */
+ code = cmap->code_to_sid[char_code];
+ glyph_name = cmap->sid_to_string( code );
+
+ /* look for the corresponding glyph name */
+ for ( n = 0; n < cmap->num_glyphs; n++ )
+ {
+ const char* gname = cmap->glyph_names[n];
+
+
+ if ( gname && gname[0] == glyph_name[0] &&
+ ft_strcmp( gname, glyph_name ) == 0 )
+ {
+ result = n;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ t1_cmap_std_char_next( T1_CMapStd cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+
+
+ while ( char_code < 256 )
+ {
+ result = t1_cmap_std_char_index( cmap, char_code );
+ if ( result != 0 )
+ goto Exit;
+
+ char_code++;
+ }
+ char_code = 0;
+
+ Exit:
+ *pchar_code = char_code;
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_standard_init( T1_CMapStd cmap,
+ FT_Pointer pointer )
+ {
+ FT_UNUSED( pointer );
+
+
+ t1_cmap_std_init( cmap, 0 );
+ return 0;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_standard_class_rec =
+ {
+ sizeof ( T1_CMapStdRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_standard_init, /* init */
+ (FT_CMap_DoneFunc) t1_cmap_std_done, /* done */
+ (FT_CMap_CharIndexFunc)t1_cmap_std_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) t1_cmap_std_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL /* variantchar_list */
+ };
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_expert_init( T1_CMapStd cmap,
+ FT_Pointer pointer )
+ {
+ FT_UNUSED( pointer );
+
+
+ t1_cmap_std_init( cmap, 1 );
+ return 0;
+ }
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_expert_class_rec =
+ {
+ sizeof ( T1_CMapStdRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_expert_init, /* init */
+ (FT_CMap_DoneFunc) t1_cmap_std_done, /* done */
+ (FT_CMap_CharIndexFunc)t1_cmap_std_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) t1_cmap_std_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL /* variantchar_list */
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 CUSTOM ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_custom_init( T1_CMapCustom cmap,
+ FT_Pointer pointer )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ T1_Encoding encoding = &face->type1.encoding;
+
+ FT_UNUSED( pointer );
+
+
+ cmap->first = (FT_UInt)encoding->code_first;
+ cmap->count = (FT_UInt)encoding->code_last - cmap->first;
+ cmap->indices = encoding->char_index;
+
+ FT_ASSERT( cmap->indices );
+ FT_ASSERT( encoding->code_first <= encoding->code_last );
+
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ t1_cmap_custom_done( T1_CMapCustom cmap )
+ {
+ cmap->indices = NULL;
+ cmap->first = 0;
+ cmap->count = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_custom_char_index( T1_CMapCustom cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt result = 0;
+
+
+ if ( ( char_code >= cmap->first ) &&
+ ( char_code < ( cmap->first + cmap->count ) ) )
+ result = cmap->indices[char_code];
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ t1_cmap_custom_char_next( T1_CMapCustom cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code;
+
+
+ char_code++;
+
+ if ( char_code < cmap->first )
+ char_code = cmap->first;
+
+ for ( ; char_code < ( cmap->first + cmap->count ); char_code++ )
+ {
+ result = cmap->indices[char_code];
+ if ( result != 0 )
+ goto Exit;
+ }
+
+ char_code = 0;
+
+ Exit:
+ *pchar_code = char_code;
+ return result;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_custom_class_rec =
+ {
+ sizeof ( T1_CMapCustomRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_custom_init, /* init */
+ (FT_CMap_DoneFunc) t1_cmap_custom_done, /* done */
+ (FT_CMap_CharIndexFunc)t1_cmap_custom_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) t1_cmap_custom_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL /* variantchar_list */
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( const char * )
+ psaux_get_glyph_name( T1_Face face,
+ FT_UInt idx )
+ {
+ return face->type1.glyph_names[idx];
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_unicode_init( PS_Unicodes unicodes,
+ FT_Pointer pointer )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+ FT_UNUSED( pointer );
+
+
+ if ( !psnames->unicodes_init )
+ return FT_THROW( Unimplemented_Feature );
+
+ return psnames->unicodes_init( memory,
+ unicodes,
+ (FT_UInt)face->type1.num_glyphs,
+ (PS_GetGlyphNameFunc)&psaux_get_glyph_name,
+ (PS_FreeGlyphNameFunc)NULL,
+ (FT_Pointer)face );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ t1_cmap_unicode_done( PS_Unicodes unicodes )
+ {
+ FT_Face face = FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( unicodes->maps );
+ unicodes->num_maps = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_unicode_char_index( PS_Unicodes unicodes,
+ FT_UInt32 char_code )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_char_index( unicodes, char_code );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt32 )
+ t1_cmap_unicode_char_next( PS_Unicodes unicodes,
+ FT_UInt32 *pchar_code )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_char_next( unicodes, pchar_code );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_unicode_class_rec =
+ {
+ sizeof ( PS_UnicodesRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_unicode_init, /* init */
+ (FT_CMap_DoneFunc) t1_cmap_unicode_done, /* done */
+ (FT_CMap_CharIndexFunc)t1_cmap_unicode_char_index, /* char_index */
+ (FT_CMap_CharNextFunc) t1_cmap_unicode_char_next, /* char_next */
+
+ (FT_CMap_CharVarIndexFunc) NULL, /* char_var_index */
+ (FT_CMap_CharVarIsDefaultFunc)NULL, /* char_var_default */
+ (FT_CMap_VariantListFunc) NULL, /* variant_list */
+ (FT_CMap_CharVariantListFunc) NULL, /* charvariant_list */
+ (FT_CMap_VariantCharListFunc) NULL /* variantchar_list */
+ };
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/t1cmap.h b/modules/freetype2/src/psaux/t1cmap.h
new file mode 100644
index 0000000000..b3702498a5
--- /dev/null
+++ b/modules/freetype2/src/psaux/t1cmap.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+ *
+ * t1cmap.h
+ *
+ * Type 1 character map support (specification).
+ *
+ * Copyright (C) 2002-2023 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.
+ *
+ */
+
+
+#ifndef T1CMAP_H_
+#define T1CMAP_H_
+
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/t1types.h>
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* standard (and expert) encoding cmaps */
+ typedef struct T1_CMapStdRec_* T1_CMapStd;
+
+ typedef struct T1_CMapStdRec_
+ {
+ FT_CMapRec cmap;
+
+ const FT_UShort* code_to_sid;
+ PS_Adobe_Std_StringsFunc sid_to_string;
+
+ FT_UInt num_glyphs;
+ const char* const* glyph_names;
+
+ } T1_CMapStdRec;
+
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_standard_class_rec;
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_expert_class_rec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 CUSTOM ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct T1_CMapCustomRec_* T1_CMapCustom;
+
+ typedef struct T1_CMapCustomRec_
+ {
+ FT_CMapRec cmap;
+ FT_UInt first;
+ FT_UInt count;
+ FT_UShort* indices;
+
+ } T1_CMapCustomRec;
+
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_custom_class_rec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* unicode (synthetic) cmaps */
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_unicode_class_rec;
+
+ /* */
+
+
+FT_END_HEADER
+
+#endif /* T1CMAP_H_ */
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/t1decode.c b/modules/freetype2/src/psaux/t1decode.c
new file mode 100644
index 0000000000..bfed45b53a
--- /dev/null
+++ b/modules/freetype2/src/psaux/t1decode.c
@@ -0,0 +1,2159 @@
+/****************************************************************************
+ *
+ * t1decode.c
+ *
+ * PostScript Type 1 decoding routines (body).
+ *
+ * Copyright (C) 2000-2023 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 <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/pshints.h>
+#include <freetype/internal/fthash.h>
+#include <freetype/ftoutln.h>
+
+#include "t1decode.h"
+#include "psobjs.h"
+
+#include "psauxerr.h"
+
+
+/* ensure proper sign extension */
+#define Fix2Int( f ) ( (FT_Int) (FT_Short)( (f) >> 16 ) )
+#define Fix2UInt( f ) ( (FT_UInt)(FT_Short)( (f) >> 16 ) )
+
+
+ /**************************************************************************
+ *
+ * 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 t1decode
+
+
+ typedef enum T1_Operator_
+ {
+ op_none = 0,
+ op_endchar,
+ op_hsbw,
+ op_seac,
+ op_sbw,
+ op_closepath,
+ op_hlineto,
+ op_hmoveto,
+ op_hvcurveto,
+ op_rlineto,
+ op_rmoveto,
+ op_rrcurveto,
+ op_vhcurveto,
+ op_vlineto,
+ op_vmoveto,
+ op_dotsection,
+ op_hstem,
+ op_hstem3,
+ op_vstem,
+ op_vstem3,
+ op_div,
+ op_callothersubr,
+ op_callsubr,
+ op_pop,
+ op_return,
+ op_setcurrentpoint,
+ op_unknown15,
+
+ op_max /* never remove this one */
+
+ } T1_Operator;
+
+
+ static
+ const FT_Int t1_args_count[op_max] =
+ {
+ 0, /* none */
+ 0, /* endchar */
+ 2, /* hsbw */
+ 5, /* seac */
+ 4, /* sbw */
+ 0, /* closepath */
+ 1, /* hlineto */
+ 1, /* hmoveto */
+ 4, /* hvcurveto */
+ 2, /* rlineto */
+ 2, /* rmoveto */
+ 6, /* rrcurveto */
+ 4, /* vhcurveto */
+ 1, /* vlineto */
+ 1, /* vmoveto */
+ 0, /* dotsection */
+ 2, /* hstem */
+ 6, /* hstem3 */
+ 2, /* vstem */
+ 6, /* vstem3 */
+ 2, /* div */
+ -1, /* callothersubr */
+ 1, /* callsubr */
+ 0, /* pop */
+ 0, /* return */
+ 2, /* setcurrentpoint */
+ 2 /* opcode 15 (undocumented and obsolete) */
+ };
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1_lookup_glyph_by_stdcharcode_ps
+ *
+ * @Description:
+ * Looks up a given glyph by its StandardEncoding charcode. Used to
+ * implement the SEAC Type 1 operator in the Adobe engine
+ *
+ * @Input:
+ * face ::
+ * The current face object.
+ *
+ * charcode ::
+ * The character code to look for.
+ *
+ * @Return:
+ * A glyph index in the font face. Returns -1 if the corresponding
+ * glyph wasn't found.
+ */
+ FT_LOCAL_DEF( FT_Int )
+ t1_lookup_glyph_by_stdcharcode_ps( PS_Decoder* decoder,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ const FT_String* glyph_name;
+ FT_Service_PsCMaps psnames = decoder->psnames;
+
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+ glyph_name = psnames->adobe_std_strings(
+ psnames->adobe_std_encoding[charcode]);
+
+ for ( n = 0; n < decoder->num_glyphs; n++ )
+ {
+ FT_String* name = (FT_String*)decoder->glyph_names[n];
+
+
+ if ( name &&
+ name[0] == glyph_name[0] &&
+ ft_strcmp( name, glyph_name ) == 0 )
+ return (FT_Int)n;
+ }
+
+ return -1;
+ }
+
+
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1_lookup_glyph_by_stdcharcode
+ *
+ * @Description:
+ * Looks up a given glyph by its StandardEncoding charcode. Used to
+ * implement the SEAC Type 1 operator.
+ *
+ * @Input:
+ * face ::
+ * The current face object.
+ *
+ * charcode ::
+ * The character code to look for.
+ *
+ * @Return:
+ * A glyph index in the font face. Returns -1 if the corresponding
+ * glyph wasn't found.
+ */
+ static FT_Int
+ t1_lookup_glyph_by_stdcharcode( T1_Decoder decoder,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ const FT_String* glyph_name;
+ FT_Service_PsCMaps psnames = decoder->psnames;
+
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+ glyph_name = psnames->adobe_std_strings(
+ psnames->adobe_std_encoding[charcode]);
+
+ for ( n = 0; n < decoder->num_glyphs; n++ )
+ {
+ FT_String* name = (FT_String*)decoder->glyph_names[n];
+
+
+ if ( name &&
+ name[0] == glyph_name[0] &&
+ ft_strcmp( name, glyph_name ) == 0 )
+ return (FT_Int)n;
+ }
+
+ return -1;
+ }
+
+
+ /* parse a single Type 1 glyph */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_parse_glyph( T1_Decoder decoder,
+ FT_UInt glyph )
+ {
+ return decoder->parse_callback( decoder, glyph );
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1operator_seac
+ *
+ * @Description:
+ * Implements the `seac' Type 1 operator for a Type 1 decoder.
+ *
+ * @Input:
+ * decoder ::
+ * The current CID decoder.
+ *
+ * asb ::
+ * The accent's side bearing.
+ *
+ * adx ::
+ * The horizontal offset of the accent.
+ *
+ * ady ::
+ * The vertical offset of the accent.
+ *
+ * bchar ::
+ * The base character's StandardEncoding charcode.
+ *
+ * achar ::
+ * The accent character's StandardEncoding charcode.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ static FT_Error
+ t1operator_seac( T1_Decoder decoder,
+ FT_Pos asb,
+ FT_Pos adx,
+ FT_Pos ady,
+ FT_Int bchar,
+ FT_Int achar )
+ {
+ FT_Error error;
+ FT_Int bchar_index, achar_index;
+#if 0
+ FT_Int n_base_points;
+ FT_Outline* base = decoder->builder.base;
+#endif
+ FT_Vector left_bearing, advance;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ T1_Face face = (T1_Face)decoder->builder.face;
+#endif
+
+
+ if ( decoder->seac )
+ {
+ FT_ERROR(( "t1operator_seac: invalid nested seac\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ if ( decoder->builder.metrics_only )
+ {
+ FT_ERROR(( "t1operator_seac: unexpected seac\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ /* seac weirdness */
+ adx += decoder->builder.left_bearing.x;
+
+ /* `glyph_names' is set to 0 for CID fonts which do not */
+ /* include an encoding. How can we deal with these? */
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( decoder->glyph_names == 0 &&
+ !face->root.internal->incremental_interface )
+#else
+ if ( decoder->glyph_names == 0 )
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ FT_ERROR(( "t1operator_seac:"
+ " glyph names table not available in this font\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( face->root.internal->incremental_interface )
+ {
+ /* the caller must handle the font encoding also */
+ bchar_index = bchar;
+ achar_index = achar;
+ }
+ else
+#endif
+ {
+ bchar_index = t1_lookup_glyph_by_stdcharcode( decoder, bchar );
+ achar_index = t1_lookup_glyph_by_stdcharcode( decoder, achar );
+ }
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR(( "t1operator_seac:"
+ " invalid seac character code arguments\n" ));
+ return FT_THROW( Syntax_Error );
+ }
+
+ /* if we are trying to load a composite glyph, do not load the */
+ /* accent character and return the array of subglyphs. */
+ if ( decoder->builder.no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error )
+ goto Exit;
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)FIXED_TO_INT( adx - asb );
+ subg->arg2 = (FT_Int)FIXED_TO_INT( ady );
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+ goto Exit;
+ }
+
+ /* First load `bchar' in builder */
+ /* now load the unscaled outline */
+
+ FT_GlyphLoader_Prepare( decoder->builder.loader ); /* prepare loader */
+
+ /* save the left bearing and width of the SEAC */
+ /* glyph as they will be erased by the next load */
+
+ left_bearing = decoder->builder.left_bearing;
+ advance = decoder->builder.advance;
+
+ /* the seac operator must not be nested */
+ decoder->seac = TRUE;
+ error = t1_decoder_parse_glyph( decoder, (FT_UInt)bchar_index );
+ decoder->seac = FALSE;
+ if ( error )
+ goto Exit;
+
+ /* If the SEAC glyph doesn't have a (H)SBW of its */
+ /* own use the values from the base glyph. */
+
+ if ( decoder->builder.parse_state != T1_Parse_Have_Width )
+ {
+ left_bearing = decoder->builder.left_bearing;
+ advance = decoder->builder.advance;
+ }
+
+ decoder->builder.left_bearing.x = 0;
+ decoder->builder.left_bearing.y = 0;
+
+ decoder->builder.pos_x = adx - asb;
+ decoder->builder.pos_y = ady;
+
+ /* Now load `achar' on top of */
+ /* the base outline */
+
+ /* the seac operator must not be nested */
+ decoder->seac = TRUE;
+ error = t1_decoder_parse_glyph( decoder, (FT_UInt)achar_index );
+ decoder->seac = FALSE;
+ if ( error )
+ goto Exit;
+
+ /* restore the left side bearing and advance width */
+ /* of the SEAC glyph or base character (saved above) */
+
+ decoder->builder.left_bearing = left_bearing;
+ decoder->builder.advance = advance;
+
+ decoder->builder.pos_x = 0;
+ decoder->builder.pos_y = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1_decoder_parse_charstrings
+ *
+ * @Description:
+ * Parses a given Type 1 charstrings program.
+ *
+ * @Input:
+ * decoder ::
+ * The current Type 1 decoder.
+ *
+ * charstring_base ::
+ * The base address of the charstring stream.
+ *
+ * charstring_len ::
+ * The length in bytes of the charstring stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_parse_charstrings( T1_Decoder decoder,
+ FT_Byte* charstring_base,
+ FT_UInt charstring_len )
+ {
+ FT_Error error;
+ T1_Decoder_Zone zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ T1_Builder builder = &decoder->builder;
+ FT_Pos x, y, orig_x, orig_y;
+ FT_Int known_othersubr_result_cnt = 0;
+ FT_Int unknown_othersubr_result_cnt = 0;
+ FT_Bool large_int;
+ FT_Fixed seed;
+
+ T1_Hints_Funcs hinter;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_Bool bol = TRUE;
+#endif
+
+
+ /* compute random seed from stack address of parameter */
+ seed = (FT_Fixed)( ( (FT_Offset)(char*)&seed ^
+ (FT_Offset)(char*)&decoder ^
+ (FT_Offset)(char*)&charstring_base ) &
+ FT_ULONG_MAX );
+ seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL;
+ if ( seed == 0 )
+ seed = 0x7384;
+
+ /* First of all, initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+
+ builder->parse_state = T1_Parse_Start;
+
+ hinter = (T1_Hints_Funcs)builder->hints_funcs;
+
+ /* a font that reads BuildCharArray without setting */
+ /* its values first is buggy, but ... */
+ FT_ASSERT( ( decoder->len_buildchar == 0 ) ==
+ ( decoder->buildchar == NULL ) );
+
+ if ( decoder->buildchar && decoder->len_buildchar > 0 )
+ FT_ARRAY_ZERO( decoder->buildchar, decoder->len_buildchar );
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ error = FT_Err_Ok;
+
+ x = orig_x = builder->pos_x;
+ y = orig_y = builder->pos_y;
+
+ /* begin hints recording session, if any */
+ if ( hinter )
+ hinter->open( hinter->hints );
+
+ large_int = FALSE;
+
+ /* now, execute loop */
+ while ( ip < limit )
+ {
+ FT_Long* top = decoder->top;
+ T1_Operator op = op_none;
+ FT_Int32 value = 0;
+
+
+ FT_ASSERT( known_othersubr_result_cnt == 0 ||
+ unknown_othersubr_result_cnt == 0 );
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( bol )
+ {
+ FT_TRACE5(( " (%ld)", decoder->top - decoder->stack ));
+ bol = FALSE;
+ }
+#endif
+
+ /**********************************************************************
+ *
+ * Decode operator or operand
+ *
+ */
+
+ /* first of all, decompress operator or value */
+ switch ( *ip++ )
+ {
+ case 1:
+ op = op_hstem;
+ break;
+
+ case 3:
+ op = op_vstem;
+ break;
+ case 4:
+ op = op_vmoveto;
+ break;
+ case 5:
+ op = op_rlineto;
+ break;
+ case 6:
+ op = op_hlineto;
+ break;
+ case 7:
+ op = op_vlineto;
+ break;
+ case 8:
+ op = op_rrcurveto;
+ break;
+ case 9:
+ op = op_closepath;
+ break;
+ case 10:
+ op = op_callsubr;
+ break;
+ case 11:
+ op = op_return;
+ break;
+
+ case 13:
+ op = op_hsbw;
+ break;
+ case 14:
+ op = op_endchar;
+ break;
+
+ case 15: /* undocumented, obsolete operator */
+ op = op_unknown15;
+ break;
+
+ case 21:
+ op = op_rmoveto;
+ break;
+ case 22:
+ op = op_hmoveto;
+ break;
+
+ case 30:
+ op = op_vhcurveto;
+ break;
+ case 31:
+ op = op_hvcurveto;
+ break;
+
+ case 12:
+ if ( ip >= limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " invalid escape (12+EOF)\n" ));
+ goto Syntax_Error;
+ }
+
+ switch ( *ip++ )
+ {
+ case 0:
+ op = op_dotsection;
+ break;
+ case 1:
+ op = op_vstem3;
+ break;
+ case 2:
+ op = op_hstem3;
+ break;
+ case 6:
+ op = op_seac;
+ break;
+ case 7:
+ op = op_sbw;
+ break;
+ case 12:
+ op = op_div;
+ break;
+ case 16:
+ op = op_callothersubr;
+ break;
+ case 17:
+ op = op_pop;
+ break;
+ case 33:
+ op = op_setcurrentpoint;
+ break;
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " invalid escape (12+%d)\n",
+ ip[-1] ));
+ goto Syntax_Error;
+ }
+ break;
+
+ case 255: /* four bytes integer */
+ if ( ip + 4 > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
+ ( (FT_UInt32)ip[1] << 16 ) |
+ ( (FT_UInt32)ip[2] << 8 ) |
+ (FT_UInt32)ip[3] );
+ ip += 4;
+
+ /* According to the specification, values > 32000 or < -32000 must */
+ /* be followed by a `div' operator to make the result be in the */
+ /* range [-32000;32000]. We expect that the second argument of */
+ /* `div' is not a large number. Additionally, we don't handle */
+ /* stuff like `<large1> <large2> <num> div <num> div' or */
+ /* <large1> <large2> <num> div div'. This is probably not allowed */
+ /* anyway. */
+ if ( value > 32000 || value < -32000 )
+ {
+ if ( large_int )
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " no `div' after large integer\n" ));
+ else
+ large_int = TRUE;
+ }
+ else
+ {
+ if ( !large_int )
+ value = (FT_Int32)( (FT_UInt32)value << 16 );
+ }
+
+ break;
+
+ default:
+ if ( ip[-1] >= 32 )
+ {
+ if ( ip[-1] < 247 )
+ value = (FT_Int32)ip[-1] - 139;
+ else
+ {
+ if ( ++ip > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( ip[-2] < 251 )
+ value = ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108;
+ else
+ value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 );
+ }
+
+ if ( !large_int )
+ value = (FT_Int32)( (FT_UInt32)value << 16 );
+ }
+ else
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " invalid byte (%d)\n", ip[-1] ));
+ goto Syntax_Error;
+ }
+ }
+
+ if ( unknown_othersubr_result_cnt > 0 )
+ {
+ switch ( op )
+ {
+ case op_callsubr:
+ case op_return:
+ case op_none:
+ case op_pop:
+ break;
+
+ default:
+ /* all operands have been transferred by previous pops */
+ unknown_othersubr_result_cnt = 0;
+ break;
+ }
+ }
+
+ if ( large_int && !( op == op_none || op == op_div ) )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " no `div' after large integer\n" ));
+
+ large_int = FALSE;
+ }
+
+ /**********************************************************************
+ *
+ * Push value on stack, or process operator
+ *
+ */
+ if ( op == op_none )
+ {
+ if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow\n" ));
+ goto Syntax_Error;
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( large_int )
+ FT_TRACE4(( " %d", value ));
+ else
+ FT_TRACE4(( " %d", value / 65536 ));
+#endif
+
+ *top++ = value;
+ decoder->top = top;
+ }
+ else if ( op == op_callothersubr ) /* callothersubr */
+ {
+ FT_Int subr_no;
+ FT_Int arg_cnt;
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE4(( " callothersubr\n" ));
+ bol = TRUE;
+#endif
+
+ if ( top - decoder->stack < 2 )
+ goto Stack_Underflow;
+
+ top -= 2;
+
+ subr_no = Fix2Int( top[1] );
+ arg_cnt = Fix2Int( top[0] );
+
+ /************************************************************
+ *
+ * remove all operands to callothersubr from the stack
+ *
+ * for handled othersubrs, where we know the number of
+ * arguments, we increase the stack by the value of
+ * known_othersubr_result_cnt
+ *
+ * for unhandled othersubrs the following pops adjust the
+ * stack pointer as necessary
+ */
+
+ if ( arg_cnt > top - decoder->stack )
+ goto Stack_Underflow;
+
+ top -= arg_cnt;
+
+ known_othersubr_result_cnt = 0;
+ unknown_othersubr_result_cnt = 0;
+
+ /* XXX TODO: The checks to `arg_count == <whatever>' */
+ /* might not be correct; an othersubr expects a certain */
+ /* number of operands on the PostScript stack (as opposed */
+ /* to the T1 stack) but it doesn't have to put them there */
+ /* by itself; previous othersubrs might have left the */
+ /* operands there if they were not followed by an */
+ /* appropriate number of pops */
+ /* */
+ /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */
+ /* accept a font that contains charstrings like */
+ /* */
+ /* 100 200 2 20 callothersubr */
+ /* 300 1 20 callothersubr pop */
+ /* */
+ /* Perhaps this is the reason why BuildCharArray exists. */
+
+ switch ( subr_no )
+ {
+ case 0: /* end flex feature */
+ if ( arg_cnt != 3 )
+ goto Unexpected_OtherSubr;
+
+ if ( !decoder->flex_state ||
+ decoder->num_flex_vectors != 7 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unexpected flex end\n" ));
+ goto Syntax_Error;
+ }
+
+ /* the two `results' are popped by the following setcurrentpoint */
+ top[0] = x;
+ top[1] = y;
+ known_othersubr_result_cnt = 2;
+ break;
+
+ case 1: /* start flex feature */
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
+ FT_SET_ERROR( t1_builder_check_points( builder, 6 ) ) )
+ goto Fail;
+
+ decoder->flex_state = 1;
+ decoder->num_flex_vectors = 0;
+ break;
+
+ case 2: /* add flex vectors */
+ {
+ FT_Int idx;
+
+
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ if ( !decoder->flex_state )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " missing flex start\n" ));
+ goto Syntax_Error;
+ }
+
+ /* note that we should not add a point for index 0; */
+ /* this will move our current position to the flex */
+ /* point without adding any point to the outline */
+ idx = decoder->num_flex_vectors++;
+ if ( idx > 0 && idx < 7 )
+ {
+ /* in malformed fonts it is possible to have other */
+ /* opcodes in the middle of a flex (which don't */
+ /* increase `num_flex_vectors'); we thus have to */
+ /* check whether we can add a point */
+ if ( FT_SET_ERROR( t1_builder_check_points( builder, 1 ) ) )
+ goto Syntax_Error;
+
+ t1_builder_add_point( builder,
+ x,
+ y,
+ (FT_Byte)( idx == 3 || idx == 6 ) );
+ }
+ }
+ break;
+
+ case 3: /* change hints */
+ if ( arg_cnt != 1 )
+ goto Unexpected_OtherSubr;
+
+ known_othersubr_result_cnt = 1;
+
+ if ( hinter )
+ hinter->reset( hinter->hints,
+ (FT_UInt)builder->current->n_points );
+ break;
+
+ case 12:
+ case 13:
+ /* counter control hints, clear stack */
+ top = decoder->stack;
+ break;
+
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18: /* multiple masters */
+ {
+ PS_Blend blend = decoder->blend;
+ FT_UInt num_points, nn, mm;
+ FT_Long* delta;
+ FT_Long* values;
+
+
+ if ( !blend )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unexpected multiple masters operator\n" ));
+ goto Syntax_Error;
+ }
+
+ num_points = (FT_UInt)subr_no - 13 + ( subr_no == 18 );
+ if ( arg_cnt != (FT_Int)( num_points * blend->num_designs ) )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " incorrect number of multiple masters arguments\n" ));
+ goto Syntax_Error;
+ }
+
+ /* We want to compute */
+ /* */
+ /* a0*w0 + a1*w1 + ... + ak*wk */
+ /* */
+ /* but we only have a0, a1-a0, a2-a0, ..., ak-a0. */
+ /* */
+ /* However, given that w0 + w1 + ... + wk == 1, we can */
+ /* rewrite it easily as */
+ /* */
+ /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk */
+ /* */
+ /* where k == num_designs-1. */
+ /* */
+ /* I guess that's why it's written in this `compact' */
+ /* form. */
+ /* */
+ delta = top + num_points;
+ values = top;
+ for ( nn = 0; nn < num_points; nn++ )
+ {
+ FT_Long tmp = values[0];
+
+
+ for ( mm = 1; mm < blend->num_designs; mm++ )
+ tmp = ADD_LONG( tmp,
+ FT_MulFix( *delta++,
+ blend->weight_vector[mm] ) );
+
+ *values++ = tmp;
+ }
+
+ known_othersubr_result_cnt = (FT_Int)num_points;
+ break;
+ }
+
+ case 19:
+ /* <idx> 1 19 callothersubr */
+ /* => replace elements starting from index cvi( <idx> ) */
+ /* of BuildCharArray with WeightVector */
+ {
+ FT_Int idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 1 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = Fix2Int( top[0] );
+
+ if ( idx < 0 ||
+ (FT_UInt)idx + blend->num_designs > decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ ft_memcpy( &decoder->buildchar[idx],
+ blend->weight_vector,
+ blend->num_designs *
+ sizeof ( blend->weight_vector[0] ) );
+ }
+ break;
+
+ case 20:
+ /* <arg1> <arg2> 2 20 callothersubr pop */
+ /* ==> push <arg1> + <arg2> onto T1 stack */
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ top[0] = ADD_LONG( top[0], top[1] );
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 21:
+ /* <arg1> <arg2> 2 21 callothersubr pop */
+ /* ==> push <arg1> - <arg2> onto T1 stack */
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ top[0] = SUB_LONG( top[0], top[1] );
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 22:
+ /* <arg1> <arg2> 2 22 callothersubr pop */
+ /* ==> push <arg1> * <arg2> onto T1 stack */
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ top[0] = FT_MulFix( top[0], top[1] );
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 23:
+ /* <arg1> <arg2> 2 23 callothersubr pop */
+ /* ==> push <arg1> / <arg2> onto T1 stack */
+ if ( arg_cnt != 2 || top[1] == 0 )
+ goto Unexpected_OtherSubr;
+
+ top[0] = FT_DivFix( top[0], top[1] );
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 24:
+ /* <val> <idx> 2 24 callothersubr */
+ /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ {
+ FT_UInt idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 2 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = Fix2UInt( top[1] );
+
+ if ( idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ decoder->buildchar[idx] = top[0];
+ }
+ break;
+
+ case 25:
+ /* <idx> 1 25 callothersubr pop */
+ /* ==> push BuildCharArray[cvi( idx )] */
+ /* onto T1 stack */
+ {
+ FT_UInt idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 1 || !blend )
+ goto Unexpected_OtherSubr;
+
+ idx = Fix2UInt( top[0] );
+
+ if ( idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ top[0] = decoder->buildchar[idx];
+ }
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+#if 0
+ case 26:
+ /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */
+ /* leave mark on T1 stack */
+ /* <val> <idx> ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ XXX which routine has left its mark on the (PostScript) stack?;
+ break;
+#endif
+
+ case 27:
+ /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
+ /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
+ /* otherwise push <res2> */
+ if ( arg_cnt != 4 )
+ goto Unexpected_OtherSubr;
+
+ if ( top[2] > top[3] )
+ top[0] = top[1];
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 28:
+ /* 0 28 callothersubr pop */
+ /* => push random value from interval [0, 1) onto stack */
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ {
+ FT_Fixed Rand;
+
+
+ Rand = seed;
+ if ( Rand >= 0x8000L )
+ Rand++;
+
+ top[0] = Rand;
+
+ seed = FT_MulFix( seed, 0x10000L - seed );
+ if ( seed == 0 )
+ seed += 0x2873;
+ }
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ default:
+ if ( arg_cnt >= 0 && subr_no >= 0 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unknown othersubr [%d %d], wish me luck\n",
+ arg_cnt, subr_no ));
+ unknown_othersubr_result_cnt = arg_cnt;
+ break;
+ }
+ /* fall through */
+
+ Unexpected_OtherSubr:
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " invalid othersubr [%d %d]\n", arg_cnt, subr_no ));
+ goto Syntax_Error;
+ }
+
+ top += known_othersubr_result_cnt;
+
+ decoder->top = top;
+ }
+ else /* general operator */
+ {
+ FT_Int num_args = t1_args_count[op];
+
+
+ FT_ASSERT( num_args >= 0 );
+
+ if ( top - decoder->stack < num_args )
+ goto Stack_Underflow;
+
+ /* XXX Operators usually take their operands from the */
+ /* bottom of the stack, i.e., the operands are */
+ /* decoder->stack[0], ..., decoder->stack[num_args - 1]; */
+ /* only div, callsubr, and callothersubr are different. */
+ /* In practice it doesn't matter (?). */
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ switch ( op )
+ {
+ case op_callsubr:
+ case op_div:
+ case op_callothersubr:
+ case op_pop:
+ case op_return:
+ break;
+
+ default:
+ if ( top - decoder->stack != num_args )
+ FT_TRACE0(( "t1_decoder_parse_charstrings:"
+ " too much operands on the stack"
+ " (seen %ld, expected %d)\n",
+ top - decoder->stack, num_args ));
+ break;
+ }
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ top -= num_args;
+
+ switch ( op )
+ {
+ case op_endchar:
+ FT_TRACE4(( " endchar\n" ));
+
+ t1_builder_close_contour( builder );
+
+ /* close hints recording session */
+ if ( hinter )
+ {
+ if ( hinter->close( hinter->hints,
+ (FT_UInt)builder->current->n_points ) )
+ goto Syntax_Error;
+
+ /* apply hints to the loaded glyph outline now */
+ error = hinter->apply( hinter->hints,
+ builder->current,
+ (PSH_Globals)builder->hints_globals,
+ decoder->hint_mode );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+
+ /* the compiler should optimize away this empty loop but ... */
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ if ( decoder->len_buildchar > 0 )
+ {
+ FT_UInt i;
+
+
+ FT_TRACE4(( "BuildCharArray = [ " ));
+
+ for ( i = 0; i < decoder->len_buildchar; i++ )
+ FT_TRACE4(( "%ld ", decoder->buildchar[i] ));
+
+ FT_TRACE4(( "]\n" ));
+ }
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ FT_TRACE4(( "\n" ));
+
+ /* return now! */
+ return FT_Err_Ok;
+
+ case op_hsbw:
+ FT_TRACE4(( " hsbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
+ top[0] );
+
+ builder->advance.x = top[1];
+ builder->advance.y = 0;
+
+ orig_x = x = ADD_LONG( builder->pos_x, top[0] );
+ orig_y = y = builder->pos_y;
+
+ FT_UNUSED( orig_y );
+
+ /* `metrics_only' indicates that we only want to compute the */
+ /* glyph's metrics (lsb + advance width) without loading the */
+ /* rest of it; so exit immediately */
+ if ( builder->metrics_only )
+ {
+ FT_TRACE4(( "\n" ));
+ return FT_Err_Ok;
+ }
+
+ break;
+
+ case op_seac:
+ return t1operator_seac( decoder,
+ top[0],
+ top[1],
+ top[2],
+ Fix2Int( top[3] ),
+ Fix2Int( top[4] ) );
+
+ case op_sbw:
+ FT_TRACE4(( " sbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
+ top[0] );
+ builder->left_bearing.y = ADD_LONG( builder->left_bearing.y,
+ top[1] );
+
+ builder->advance.x = top[2];
+ builder->advance.y = top[3];
+
+ x = ADD_LONG( builder->pos_x, top[0] );
+ y = ADD_LONG( builder->pos_y, top[1] );
+
+ /* `metrics_only' indicates that we only want to compute the */
+ /* glyph's metrics (lsb + advance width) without loading the */
+ /* rest of it; so exit immediately */
+ if ( builder->metrics_only )
+ {
+ FT_TRACE4(( "\n" ));
+ return FT_Err_Ok;
+ }
+
+ break;
+
+ case op_closepath:
+ FT_TRACE4(( " closepath" ));
+
+ /* if there is no path, `closepath' is a no-op */
+ if ( builder->parse_state == T1_Parse_Have_Path ||
+ builder->parse_state == T1_Parse_Have_Moveto )
+ t1_builder_close_contour( builder );
+
+ builder->parse_state = T1_Parse_Have_Width;
+ break;
+
+ case op_hlineto:
+ FT_TRACE4(( " hlineto" ));
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) )
+ goto Fail;
+
+ x = ADD_LONG( x, top[0] );
+ goto Add_Line;
+
+ case op_hmoveto:
+ FT_TRACE4(( " hmoveto" ));
+
+ x = ADD_LONG( x, top[0] );
+
+ if ( !decoder->flex_state )
+ {
+ if ( builder->parse_state == T1_Parse_Start )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Moveto;
+ }
+ break;
+
+ case op_hvcurveto:
+ FT_TRACE4(( " hvcurveto" ));
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
+ FT_SET_ERROR( t1_builder_check_points( builder, 3 ) ) )
+ goto Fail;
+
+ x = ADD_LONG( x, top[0] );
+ t1_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, top[1] );
+ y = ADD_LONG( y, top[2] );
+ t1_builder_add_point( builder, x, y, 0 );
+
+ y = ADD_LONG( y, top[3] );
+ t1_builder_add_point( builder, x, y, 1 );
+ break;
+
+ case op_rlineto:
+ FT_TRACE4(( " rlineto" ));
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) )
+ goto Fail;
+
+ x = ADD_LONG( x, top[0] );
+ y = ADD_LONG( y, top[1] );
+
+ Add_Line:
+ if ( FT_SET_ERROR( t1_builder_add_point1( builder, x, y ) ) )
+ goto Fail;
+ break;
+
+ case op_rmoveto:
+ FT_TRACE4(( " rmoveto" ));
+
+ x = ADD_LONG( x, top[0] );
+ y = ADD_LONG( y, top[1] );
+
+ if ( !decoder->flex_state )
+ {
+ if ( builder->parse_state == T1_Parse_Start )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Moveto;
+ }
+ break;
+
+ case op_rrcurveto:
+ FT_TRACE4(( " rrcurveto" ));
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
+ FT_SET_ERROR( t1_builder_check_points( builder, 3 ) ) )
+ goto Fail;
+
+ x = ADD_LONG( x, top[0] );
+ y = ADD_LONG( y, top[1] );
+ t1_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, top[2] );
+ y = ADD_LONG( y, top[3] );
+ t1_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, top[4] );
+ y = ADD_LONG( y, top[5] );
+ t1_builder_add_point( builder, x, y, 1 );
+ break;
+
+ case op_vhcurveto:
+ FT_TRACE4(( " vhcurveto" ));
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
+ FT_SET_ERROR( t1_builder_check_points( builder, 3 ) ) )
+ goto Fail;
+
+ y = ADD_LONG( y, top[0] );
+ t1_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, top[1] );
+ y = ADD_LONG( y, top[2] );
+ t1_builder_add_point( builder, x, y, 0 );
+
+ x = ADD_LONG( x, top[3] );
+ t1_builder_add_point( builder, x, y, 1 );
+ break;
+
+ case op_vlineto:
+ FT_TRACE4(( " vlineto" ));
+
+ if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) )
+ goto Fail;
+
+ y = ADD_LONG( y, top[0] );
+ goto Add_Line;
+
+ case op_vmoveto:
+ FT_TRACE4(( " vmoveto" ));
+
+ y = ADD_LONG( y, top[0] );
+
+ if ( !decoder->flex_state )
+ {
+ if ( builder->parse_state == T1_Parse_Start )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Moveto;
+ }
+ break;
+
+ case op_div:
+ FT_TRACE4(( " div" ));
+
+ /* if `large_int' is set, we divide unscaled numbers; */
+ /* otherwise, we divide numbers in 16.16 format -- */
+ /* in both cases, it is the same operation */
+ *top = FT_DivFix( top[0], top[1] );
+ top++;
+
+ large_int = FALSE;
+ break;
+
+ case op_callsubr:
+ {
+ FT_Int idx;
+
+
+ FT_TRACE4(( " callsubr" ));
+
+ idx = Fix2Int( top[0] );
+
+ if ( decoder->subrs_hash )
+ {
+ size_t* val = ft_hash_num_lookup( idx,
+ decoder->subrs_hash );
+
+
+ if ( val )
+ idx = *val;
+ else
+ idx = -1;
+ }
+
+ if ( idx < 0 || idx >= decoder->num_subrs )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " invalid subrs index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+
+ /* 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. */
+ zone->base = decoder->subrs[idx];
+
+ if ( decoder->subrs_len )
+ zone->limit = zone->base + decoder->subrs_len[idx];
+ else
+ {
+ /* We are using subroutines from a CID font. We must adjust */
+ /* for the seed bytes. */
+ zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
+ zone->limit = decoder->subrs[idx + 1];
+ }
+
+ zone->cursor = zone->base;
+
+ if ( !zone->base )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " invoking empty subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ break;
+ }
+
+ case op_pop:
+ FT_TRACE4(( " pop" ));
+
+ if ( known_othersubr_result_cnt > 0 )
+ {
+ known_othersubr_result_cnt--;
+ /* ignore, we pushed the operands ourselves */
+ break;
+ }
+
+ if ( unknown_othersubr_result_cnt == 0 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " no more operands for othersubr\n" ));
+ goto Syntax_Error;
+ }
+
+ unknown_othersubr_result_cnt--;
+ top++; /* `push' the operand to callothersubr onto the stack */
+ break;
+
+ case op_return:
+ FT_TRACE4(( " return" ));
+
+ if ( zone <= decoder->zones )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unexpected return\n" ));
+ goto Syntax_Error;
+ }
+
+ zone--;
+ ip = zone->cursor;
+ limit = zone->limit;
+ decoder->zone = zone;
+ break;
+
+ case op_dotsection:
+ FT_TRACE4(( " dotsection" ));
+
+ break;
+
+ case op_hstem:
+ FT_TRACE4(( " hstem" ));
+
+ /* record horizontal hint */
+ if ( hinter )
+ {
+ /* top[0] += builder->left_bearing.y; */
+ hinter->stem( hinter->hints, 1, top );
+ }
+ break;
+
+ case op_hstem3:
+ FT_TRACE4(( " hstem3" ));
+
+ /* record horizontal counter-controlled hints */
+ if ( hinter )
+ hinter->stem3( hinter->hints, 1, top );
+ break;
+
+ case op_vstem:
+ FT_TRACE4(( " vstem" ));
+
+ /* record vertical hint */
+ if ( hinter )
+ {
+ top[0] = ADD_LONG( top[0], orig_x );
+ hinter->stem( hinter->hints, 0, top );
+ }
+ break;
+
+ case op_vstem3:
+ FT_TRACE4(( " vstem3" ));
+
+ /* record vertical counter-controlled hints */
+ if ( hinter )
+ {
+ FT_Pos dx = orig_x;
+
+
+ top[0] = ADD_LONG( top[0], dx );
+ top[2] = ADD_LONG( top[2], dx );
+ top[4] = ADD_LONG( top[4], dx );
+ hinter->stem3( hinter->hints, 0, top );
+ }
+ break;
+
+ case op_setcurrentpoint:
+ FT_TRACE4(( " setcurrentpoint" ));
+
+ /* From the T1 specification, section 6.4: */
+ /* */
+ /* The setcurrentpoint command is used only in */
+ /* conjunction with results from OtherSubrs procedures. */
+
+ /* known_othersubr_result_cnt != 0 is already handled */
+ /* above. */
+
+ /* Note, however, that both Ghostscript and Adobe */
+ /* Distiller handle this situation by silently ignoring */
+ /* the inappropriate `setcurrentpoint' instruction. So */
+ /* we do the same. */
+#if 0
+
+ if ( decoder->flex_state != 1 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unexpected `setcurrentpoint'\n" ));
+ goto Syntax_Error;
+ }
+ else
+ ...
+#endif
+
+ x = top[0];
+ y = top[1];
+ decoder->flex_state = 0;
+ break;
+
+ case op_unknown15:
+ FT_TRACE4(( " opcode_15" ));
+ /* nothing to do except to pop the two arguments */
+ break;
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_charstrings:"
+ " unhandled opcode %d\n", op ));
+ goto Syntax_Error;
+ }
+
+ /* XXX Operators usually clear the operand stack; */
+ /* only div, callsubr, callothersubr, pop, and */
+ /* return are different. */
+ /* In practice it doesn't matter (?). */
+
+ decoder->top = top;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_TRACE4(( "\n" ));
+ bol = TRUE;
+#endif
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n" ));
+ FT_TRACE4(( "\n" ));
+
+ Fail:
+ return error;
+
+ Syntax_Error:
+ return FT_THROW( Syntax_Error );
+
+ Stack_Underflow:
+ return FT_THROW( Stack_Underflow );
+ }
+
+
+#else /* !T1_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * t1_decoder_parse_metrics
+ *
+ * @Description:
+ * Parses a given Type 1 charstrings program to extract width
+ *
+ * @Input:
+ * decoder ::
+ * The current Type 1 decoder.
+ *
+ * charstring_base ::
+ * The base address of the charstring stream.
+ *
+ * charstring_len ::
+ * The length in bytes of the charstring stream.
+ *
+ * @Return:
+ * FreeType error code. 0 means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_parse_metrics( T1_Decoder decoder,
+ FT_Byte* charstring_base,
+ FT_UInt charstring_len )
+ {
+ T1_Decoder_Zone zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ T1_Builder builder = &decoder->builder;
+ FT_Bool large_int;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_Bool bol = TRUE;
+#endif
+
+
+ /* First of all, initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+
+ builder->parse_state = T1_Parse_Start;
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ large_int = FALSE;
+
+ /* now, execute loop */
+ while ( ip < limit )
+ {
+ FT_Long* top = decoder->top;
+ T1_Operator op = op_none;
+ FT_Int32 value = 0;
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( bol )
+ {
+ FT_TRACE5(( " (%ld)", decoder->top - decoder->stack ));
+ bol = FALSE;
+ }
+#endif
+
+ /**********************************************************************
+ *
+ * Decode operator or operand
+ *
+ */
+
+ /* first of all, decompress operator or value */
+ switch ( *ip++ )
+ {
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 14:
+ case 15:
+ case 21:
+ case 22:
+ case 30:
+ case 31:
+ goto No_Width;
+
+ case 10:
+ op = op_callsubr;
+ break;
+ case 11:
+ op = op_return;
+ break;
+
+ case 13:
+ op = op_hsbw;
+ break;
+
+ case 12:
+ if ( ip >= limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " invalid escape (12+EOF)\n" ));
+ goto Syntax_Error;
+ }
+
+ switch ( *ip++ )
+ {
+ case 7:
+ op = op_sbw;
+ break;
+ case 12:
+ op = op_div;
+ break;
+
+ default:
+ goto No_Width;
+ }
+ break;
+
+ case 255: /* four bytes integer */
+ if ( ip + 4 > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
+ ( (FT_UInt32)ip[1] << 16 ) |
+ ( (FT_UInt32)ip[2] << 8 ) |
+ (FT_UInt32)ip[3] );
+ ip += 4;
+
+ /* According to the specification, values > 32000 or < -32000 must */
+ /* be followed by a `div' operator to make the result be in the */
+ /* range [-32000;32000]. We expect that the second argument of */
+ /* `div' is not a large number. Additionally, we don't handle */
+ /* stuff like `<large1> <large2> <num> div <num> div' or */
+ /* <large1> <large2> <num> div div'. This is probably not allowed */
+ /* anyway. */
+ if ( value > 32000 || value < -32000 )
+ {
+ if ( large_int )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " no `div' after large integer\n" ));
+ goto Syntax_Error;
+ }
+ else
+ large_int = TRUE;
+ }
+ else
+ {
+ if ( !large_int )
+ value = (FT_Int32)( (FT_UInt32)value << 16 );
+ }
+
+ break;
+
+ default:
+ if ( ip[-1] >= 32 )
+ {
+ if ( ip[-1] < 247 )
+ value = (FT_Int32)ip[-1] - 139;
+ else
+ {
+ if ( ++ip > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( ip[-2] < 251 )
+ value = ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108;
+ else
+ value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 );
+ }
+
+ if ( !large_int )
+ value = (FT_Int32)( (FT_UInt32)value << 16 );
+ }
+ else
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " invalid byte (%d)\n", ip[-1] ));
+ goto Syntax_Error;
+ }
+ }
+
+ if ( large_int && !( op == op_none || op == op_div ) )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " no `div' after large integer\n" ));
+ goto Syntax_Error;
+ }
+
+ /**********************************************************************
+ *
+ * Push value on stack, or process operator
+ *
+ */
+ if ( op == op_none )
+ {
+ if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics: stack overflow\n" ));
+ goto Syntax_Error;
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( large_int )
+ FT_TRACE4(( " %d", value ));
+ else
+ FT_TRACE4(( " %d", value / 65536 ));
+#endif
+
+ *top++ = value;
+ decoder->top = top;
+ }
+ else /* general operator */
+ {
+ FT_Int num_args = t1_args_count[op];
+
+
+ FT_ASSERT( num_args >= 0 );
+
+ if ( top - decoder->stack < num_args )
+ goto Stack_Underflow;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ switch ( op )
+ {
+ case op_callsubr:
+ case op_div:
+ case op_return:
+ break;
+
+ default:
+ if ( top - decoder->stack != num_args )
+ FT_TRACE0(( "t1_decoder_parse_metrics:"
+ " too much operands on the stack"
+ " (seen %ld, expected %d)\n",
+ top - decoder->stack, num_args ));
+ break;
+ }
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ top -= num_args;
+
+ switch ( op )
+ {
+ case op_hsbw:
+ FT_TRACE4(( " hsbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
+ top[0] );
+
+ builder->advance.x = top[1];
+ builder->advance.y = 0;
+
+ /* we only want to compute the glyph's metrics */
+ /* (lsb + advance width) without loading the */
+ /* rest of it; so exit immediately */
+ FT_TRACE4(( "\n" ));
+ return FT_Err_Ok;
+
+ case op_sbw:
+ FT_TRACE4(( " sbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
+ top[0] );
+ builder->left_bearing.y = ADD_LONG( builder->left_bearing.y,
+ top[1] );
+
+ builder->advance.x = top[2];
+ builder->advance.y = top[3];
+
+ /* we only want to compute the glyph's metrics */
+ /* (lsb + advance width), without loading the */
+ /* rest of it; so exit immediately */
+ FT_TRACE4(( "\n" ));
+ return FT_Err_Ok;
+
+ case op_div:
+ FT_TRACE4(( " div" ));
+
+ /* if `large_int' is set, we divide unscaled numbers; */
+ /* otherwise, we divide numbers in 16.16 format -- */
+ /* in both cases, it is the same operation */
+ *top = FT_DivFix( top[0], top[1] );
+ top++;
+
+ large_int = FALSE;
+ break;
+
+ case op_callsubr:
+ {
+ FT_Int idx;
+
+
+ FT_TRACE4(( " callsubr" ));
+
+ idx = Fix2Int( top[0] );
+
+ if ( decoder->subrs_hash )
+ {
+ size_t* val = ft_hash_num_lookup( idx,
+ decoder->subrs_hash );
+
+
+ if ( val )
+ idx = *val;
+ else
+ idx = -1;
+ }
+
+ if ( idx < 0 || idx >= decoder->num_subrs )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " invalid subrs index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+
+ /* 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. */
+ zone->base = decoder->subrs[idx];
+
+ if ( decoder->subrs_len )
+ zone->limit = zone->base + decoder->subrs_len[idx];
+ else
+ {
+ /* We are using subroutines from a CID font. We must adjust */
+ /* for the seed bytes. */
+ zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
+ zone->limit = decoder->subrs[idx + 1];
+ }
+
+ zone->cursor = zone->base;
+
+ if ( !zone->base )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " invoking empty subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ break;
+ }
+
+ case op_return:
+ FT_TRACE4(( " return" ));
+
+ if ( zone <= decoder->zones )
+ {
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unexpected return\n" ));
+ goto Syntax_Error;
+ }
+
+ zone--;
+ ip = zone->cursor;
+ limit = zone->limit;
+ decoder->zone = zone;
+ break;
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " unhandled opcode %d\n", op ));
+ goto Syntax_Error;
+ }
+
+ decoder->top = top;
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n" ));
+ FT_TRACE4(( "\n" ));
+
+ No_Width:
+ FT_ERROR(( "t1_decoder_parse_metrics:"
+ " no width, found op %d instead\n",
+ ip[-1] ));
+ Syntax_Error:
+ return FT_THROW( Syntax_Error );
+
+ Stack_Underflow:
+ return FT_THROW( Stack_Underflow );
+ }
+
+#endif /* !T1_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /* initialize T1 decoder */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_init( T1_Decoder decoder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot slot,
+ FT_Byte** glyph_names,
+ PS_Blend blend,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ T1_Decoder_Callback parse_callback )
+ {
+ FT_ZERO( decoder );
+
+ /* retrieve `psnames' interface from list of current modules */
+ {
+ FT_Service_PsCMaps psnames;
+
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+ if ( !psnames )
+ {
+ FT_ERROR(( "t1_decoder_init:"
+ " the `psnames' module is not available\n" ));
+ return FT_THROW( Unimplemented_Feature );
+ }
+
+ decoder->psnames = psnames;
+ }
+
+ t1_builder_init( &decoder->builder, face, size, slot, hinting );
+
+ /* decoder->buildchar and decoder->len_buildchar have to be */
+ /* initialized by the caller since we cannot know the length */
+ /* of the BuildCharArray */
+
+ decoder->num_glyphs = (FT_UInt)face->num_glyphs;
+ decoder->glyph_names = glyph_names;
+ decoder->hint_mode = hint_mode;
+ decoder->blend = blend;
+ decoder->parse_callback = parse_callback;
+
+ decoder->funcs = t1_decoder_funcs;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* finalize T1 decoder */
+ FT_LOCAL_DEF( void )
+ t1_decoder_done( T1_Decoder decoder )
+ {
+ FT_Memory memory = decoder->builder.memory;
+
+
+ t1_builder_done( &decoder->builder );
+
+ if ( decoder->cf2_instance.finalizer )
+ {
+ decoder->cf2_instance.finalizer( decoder->cf2_instance.data );
+ FT_FREE( decoder->cf2_instance.data );
+ }
+ }
+
+
+/* END */
diff --git a/modules/freetype2/src/psaux/t1decode.h b/modules/freetype2/src/psaux/t1decode.h
new file mode 100644
index 0000000000..0970def960
--- /dev/null
+++ b/modules/freetype2/src/psaux/t1decode.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+ *
+ * t1decode.h
+ *
+ * PostScript Type 1 decoding routines (specification).
+ *
+ * Copyright (C) 2000-2023 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.
+ *
+ */
+
+
+#ifndef T1DECODE_H_
+#define T1DECODE_H_
+
+
+#include <freetype/internal/psaux.h>
+#include <freetype/internal/t1types.h>
+
+
+FT_BEGIN_HEADER
+
+
+ FT_CALLBACK_TABLE
+ const T1_Decoder_FuncsRec t1_decoder_funcs;
+
+ FT_LOCAL( FT_Int )
+ t1_lookup_glyph_by_stdcharcode_ps( PS_Decoder* decoder,
+ FT_Int charcode );
+
+#ifdef T1_CONFIG_OPTION_OLD_ENGINE
+ FT_LOCAL( FT_Error )
+ t1_decoder_parse_glyph( T1_Decoder decoder,
+ FT_UInt glyph_index );
+
+ FT_LOCAL( FT_Error )
+ t1_decoder_parse_charstrings( T1_Decoder decoder,
+ FT_Byte* base,
+ FT_UInt len );
+#else
+ FT_LOCAL( FT_Error )
+ t1_decoder_parse_metrics( T1_Decoder decoder,
+ FT_Byte* charstring_base,
+ FT_UInt charstring_len );
+#endif
+
+ FT_LOCAL( FT_Error )
+ t1_decoder_init( T1_Decoder decoder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot slot,
+ FT_Byte** glyph_names,
+ PS_Blend blend,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ T1_Decoder_Callback parse_glyph );
+
+ FT_LOCAL( void )
+ t1_decoder_done( T1_Decoder decoder );
+
+
+FT_END_HEADER
+
+#endif /* T1DECODE_H_ */
+
+
+/* END */