ref: 28eee3636caa8b933aaaccf47102bd2e698f29f6
dir: /src/psaux/psobjs.c/
/**************************************************************************** * * psobjs.c * * Auxiliary functions for PostScript fonts (body). * * Copyright (C) 1996-2021 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->num_elems = 0; 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 void shift_elements( PS_Table table, FT_Byte* old_base ) { FT_PtrDist delta = table->block - old_base; FT_Byte** offset = table->elements; FT_Byte** limit = offset + table->max_elems; for ( ; offset < limit; offset++ ) { if ( offset[0] ) offset[0] += delta; } } static FT_Error reallocate_t1_table( PS_Table table, FT_Offset new_size ) { FT_Memory memory = table->memory; FT_Byte* old_base = table->block; FT_Error error; /* allocate new base block */ if ( FT_ALLOC( table->block, new_size ) ) { table->block = old_base; return error; } /* copy elements and shift offsets */ if ( old_base ) { FT_MEM_COPY( table->block, old_base, table->capacity ); shift_elements( table, old_base ); FT_FREE( 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 = reallocate_t1_table( 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. * * @Note: * This function does NOT release the heap's memory block. It is up * to the caller to clean it, or reference it in its own structures. */ FT_LOCAL_DEF( void ) ps_table_done( PS_Table table ) { FT_Memory memory = table->memory; FT_Error error; FT_Byte* old_base = table->block; /* should never fail, because rec.cursor <= rec.size */ if ( !old_base ) return; if ( FT_QALLOC( table->block, table->cursor ) ) return; FT_MEM_COPY( table->block, old_base, table->cursor ); shift_elements( table, old_base ); table->capacity = table->cursor; FT_FREE( old_base ); FT_UNUSED( error ); } FT_LOCAL_DEF( void ) ps_table_release( PS_Table table ) { FT_Memory memory = table->memory; if ( (FT_ULong)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; FT_String* string = NULL; 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 ); 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 ); *(FT_String**)q = NULL; } 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 */