📄 ftxgsub.c
字号:
/******************************************************************* * * ftxgsub.c * * TrueType Open GSUB table support. * * Copyright 1996-2001 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. * ******************************************************************//* XXX There is *a lot* of duplicated code (cf. formats 5 and 6), but I don't care currently. I believe that it would be possible to save about 50% of TTO code by carefully designing the structures, sharing as much as possible with extensive use of macros. This is something for a volunteer :-) */#include "tttypes.h"#include "tttags.h"#include "ttload.h"#include "ttextend.h"#include "ttmemory.h"#include "ttfile.h"#include "ftxopen.h"#include "ftxopenf.h"#define ADD_String( in, num_in, out, num_out, glyph_data, component, ligID ) \ ( ( error = TT_GSUB_Add_String( (in), (num_in), \ (out), (num_out), \ (glyph_data), (component), (ligID) \ ) ) != TT_Err_Ok ) static TT_Error Do_Glyph_Lookup( TTO_GSUBHeader* gsub, UShort lookup_index, TTO_GSUB_String* in, TTO_GSUB_String* out, UShort context_length, int nesting_level ); /********************** * Auxiliary functions **********************/ /* The following function copies `num_out' elements from `glyph_data' to `out', advancing the array pointer in the `in' structure by `num_in' elements, and in `out' by `num_out' elements. If the string (resp. the properties) array in `out' is empty or too small, it allocates resp. reallocates the string (and properties) array. Finally, it sets the `length' field of `out' equal to `pos' of the `out' structure. If `component' is 0xFFFF, the value `in->component[in->pos]' will be copied `num_out' times, otherwise `component' itself will be used to fill `out->component'. If `ligID' is 0xFFFF, the value `in->lig_IDs[in->pos]' will be copied `num_out' times, otherwise `ligID' itself will be used to fill `out->ligIDs'. The properties (if defined) for all replaced glyphs are taken from the glyph at position `in->pos'. */ FT_EXPORT_FUNC( TT_Error ) TT_GSUB_Add_String( TTO_GSUB_String* in, UShort num_in, TTO_GSUB_String* out, UShort num_out, UShort* glyph_data, UShort component, UShort ligID ) { TT_Error error; TT_UInt i; UShort p_in; UShort* p_out; /* sanity check */ if ( !in || !out || in->length == 0 || in->pos >= in->length || in->length < in->pos + num_in ) return TT_Err_Invalid_Argument; if ( out->pos + num_out >= out->allocated ) { ULong size = out->pos + num_out + 256L; /* The following works because all fields in `out' must be initialized to zero (including the `string' field) for the first use. */ if ( REALLOC_ARRAY( out->string, size, UShort ) ) return error; if ( REALLOC_ARRAY( out->components, size, UShort ) ) return error; if ( REALLOC_ARRAY( out->ligIDs, size, UShort ) ) return error; if ( in->properties ) if ( REALLOC_ARRAY( out->properties, size, UShort ) ) return error; out->allocated = size; } if ( num_out ) { MEM_Copy( &out->string[out->pos], glyph_data, num_out * sizeof ( UShort ) ); if ( component == 0xFFFF ) component = in->components[in->pos]; p_out = out->components; for ( i = out->pos; i < out->pos + num_out; i++ ) p_out[i] = component; p_out = out->ligIDs; if ( ligID == 0xFFFF ) ligID = in->ligIDs[in->pos]; for ( i = out->pos; i < out->pos + num_out; i++ ) p_out[i] = ligID; if ( in->properties ) { p_in = in->properties[in->pos]; p_out = out->properties; for ( i = out->pos; i < out->pos + num_out; i++ ) p_out[i] = p_in; } } in->pos += num_in; out->pos += num_out; out->length = out->pos; return TT_Err_Ok; } /********************** * Extension Functions **********************/ static TT_Error GSUB_Create( void* ext, PFace face ) { DEFINE_LOAD_LOCALS( face->stream ); TTO_GSUBHeader* gsub = (TTO_GSUBHeader*)ext; Long table; /* by convention */ if ( !gsub ) return TT_Err_Ok; /* a null offset indicates that there is no GSUB table */ gsub->offset = 0; /* we store the start offset and the size of the subtable */ table = TT_LookUp_Table( face, TTAG_GSUB ); if ( table < 0 ) return TT_Err_Ok; /* The table is optional */ if ( FILE_Seek( face->dirTables[table].Offset ) || ACCESS_Frame( 4L ) ) return error; gsub->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */ gsub->Version = GET_ULong(); FORGET_Frame(); gsub->loaded = FALSE; return TT_Err_Ok; } static TT_Error GSUB_Destroy( void* ext, PFace face ) { TTO_GSUBHeader* gsub = (TTO_GSUBHeader*)ext; /* by convention */ if ( !gsub ) return TT_Err_Ok; if ( gsub->loaded ) { Free_LookupList( &gsub->LookupList, GSUB ); Free_FeatureList( &gsub->FeatureList ); Free_ScriptList( &gsub->ScriptList ); } return TT_Err_Ok; } FT_EXPORT_FUNC( TT_Error ) TT_Init_GSUB_Extension( TT_Engine engine ) { PEngine_Instance _engine = HANDLE_Engine( engine ); if ( !_engine ) return TT_Err_Invalid_Engine; return TT_Register_Extension( _engine, GSUB_ID, sizeof ( TTO_GSUBHeader ), GSUB_Create, GSUB_Destroy ); } FT_EXPORT_FUNC( TT_Error ) TT_Load_GSUB_Table( TT_Face face, TTO_GSUBHeader* retptr, TTO_GDEFHeader* gdef ) { ULong cur_offset, new_offset, base_offset; UShort i, num_lookups; TTO_GSUBHeader* gsub; TTO_GDEFHeader* gdef_reg; TTO_Lookup* lo; PFace faze = HANDLE_Face( face ); DEFINE_ALL_LOCALS; if ( !retptr ) return TT_Err_Invalid_Argument; if ( !faze ) return TT_Err_Invalid_Face_Handle; error = TT_Extension_Get( faze, GSUB_ID, (void**)&gsub ); if ( error ) return error; if ( gsub->offset == 0 ) return TT_Err_Table_Missing; /* no GSUB table; nothing to do */ /* now access stream */ if ( USE_Stream( faze->stream, stream ) ) return error; base_offset = gsub->offset; /* skip version */ if ( FILE_Seek( base_offset + 4L ) || ACCESS_Frame( 2L ) ) return error; new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_ScriptList( &gsub->ScriptList, faze ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 2L ) ) goto Fail3; new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_FeatureList( &gsub->FeatureList, faze ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 2L ) ) goto Fail2; new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_LookupList( &gsub->LookupList, faze, GSUB ) ) != TT_Err_Ok ) goto Fail2; gsub->gdef = gdef; /* can be NULL */ /* We now check the LookupFlags for values larger than 0xFF to find out whether we need to load the `MarkAttachClassDef' field of the GDEF table -- this hack is necessary for OpenType 1.2 tables since the version field of the GDEF table hasn't been incremented. For constructed GDEF tables, we only load it if `MarkAttachClassDef_offset' is not zero (nevertheless, a build of a constructed mark attach table is not supported currently). */ if ( gdef && gdef->MarkAttachClassDef_offset && !gdef->MarkAttachClassDef.loaded ) { lo = gsub->LookupList.Lookup; num_lookups = gsub->LookupList.LookupCount; for ( i = 0; i < num_lookups; i++ ) { if ( lo[i].LookupFlag & IGNORE_SPECIAL_MARKS ) { if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) || ACCESS_Frame( 2L ) ) goto Fail1; new_offset = GET_UShort(); FORGET_Frame(); if ( !new_offset ) return TTO_Err_Invalid_GDEF_SubTable; new_offset += gdef->offset; if ( FILE_Seek( new_offset ) || ( error = Load_ClassDefinition( &gdef->MarkAttachClassDef, 256, faze ) ) != TT_Err_Ok ) goto Fail1; /* copy the class definition pointer into the extension structure */ error = TT_Extension_Get( faze, GDEF_ID, (void**)&gdef_reg ); if ( error ) return error; *gdef_reg = *gdef; break; } } } gsub->loaded = TRUE; *retptr = *gsub; DONE_Stream( stream ); return TT_Err_Ok; Fail1: Free_LookupList( &gsub->LookupList, GSUB ); Fail2: Free_FeatureList( &gsub->FeatureList ); Fail3: Free_ScriptList( &gsub->ScriptList ); /* release stream */ DONE_Stream( stream ); return error; } /***************************** * SubTable related functions *****************************/ /* LookupType 1 */ /* SingleSubstFormat1 */ /* SingleSubstFormat2 */ TT_Error Load_SingleSubst( TTO_SingleSubst* ss, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; UShort* s; base_offset = FILE_Pos(); if ( ACCESS_Frame( 4L ) ) return error; ss->SubstFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &ss->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); switch ( ss->SubstFormat ) { case 1: if ( ACCESS_Frame( 2L ) ) goto Fail2; ss->ssf.ssf1.DeltaGlyphID = GET_UShort(); FORGET_Frame(); break; case 2: if ( ACCESS_Frame( 2L ) ) goto Fail2; count = ss->ssf.ssf2.GlyphCount = GET_UShort(); FORGET_Frame(); ss->ssf.ssf2.Substitute = NULL; if ( ALLOC_ARRAY( ss->ssf.ssf2.Substitute, count, UShort ) ) goto Fail2; s = ss->ssf.ssf2.Substitute; if ( ACCESS_Frame( count * 2L ) ) goto Fail1; for ( n = 0; n < count; n++ ) s[n] = GET_UShort(); FORGET_Frame(); break; default: return TTO_Err_Invalid_GSUB_SubTable_Format; } return TT_Err_Ok; Fail1: FREE( s ); Fail2: Free_Coverage( &ss->Coverage ); return error; } void Free_SingleSubst( TTO_SingleSubst* ss ) { switch ( ss->SubstFormat ) { case 1: break; case 2: FREE( ss->ssf.ssf2.Substitute ); break; } Free_Coverage( &ss->Coverage ); } static TT_Error Lookup_SingleSubst( TTO_SingleSubst* ss, TTO_GSUB_String* in, TTO_GSUB_String* out, UShort flags, UShort context_length, TTO_GDEFHeader* gdef ) { UShort index, value[1], property; TT_Error error; if ( context_length != 0xFFFF && context_length < 1 ) return TTO_Err_Not_Covered; if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) return error; error = Coverage_Index( &ss->Coverage, in->string[in->pos], &index ); if ( error ) return error; switch ( ss->SubstFormat ) { case 1: value[0] = ( in->string[in->pos] + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF; if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) ) return error; break; case 2: if ( index >= ss->ssf.ssf2.GlyphCount ) return TTO_Err_Invalid_GSUB_SubTable; value[0] = ss->ssf.ssf2.Substitute[index]; if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) ) return error; break; default: return TTO_Err_Invalid_GSUB_SubTable; } if ( gdef && gdef->NewGlyphClasses ) { /* we inherit the old glyph class to the substituted glyph */ error = Add_Glyph_Property( gdef, value[0], property ); if ( error && error != TTO_Err_Not_Covered ) return error; } return TT_Err_Ok; } /* LookupType 2 */ /* Sequence */ static TT_Error Load_Sequence( TTO_Sequence* s, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; UShort* sub; if ( ACCESS_Frame( 2L ) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -