diff options
author | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2016-12-30 23:22:05 +0000 |
---|---|---|
committer | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2016-12-30 23:22:05 +0000 |
commit | c1c7df45902c28d68a07508d11224cef86edcb0d (patch) | |
tree | f00af2d1129c70381279ba7db2c699ede0738f51 /Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c | |
parent | 770c1f2b994c43dfec555e1411995aa3700d406d (diff) |
freetype2 2.7.1
git-svn-id: svn://tug.org/texlive/trunk@42811 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c')
-rw-r--r-- | Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c | 992 |
1 files changed, 903 insertions, 89 deletions
diff --git a/Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c b/Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c index c0d013c17fe..cf4f7b17a94 100644 --- a/Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c +++ b/Build/source/libs/freetype2/freetype-src/src/truetype/ttgxvar.c @@ -380,7 +380,7 @@ segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4; segment->correspondence[j].toCoord = FT_GET_SHORT() * 4; - FT_TRACE5(( " mapping %.4f to %.4f\n", + FT_TRACE5(( " mapping %.5f to %.5f\n", segment->correspondence[j].fromCoord / 65536.0, segment->correspondence[j].toCoord / 65536.0 )); } @@ -393,6 +393,514 @@ } + /* some macros we need */ + #define FT_FIXED_ONE ( (FT_Fixed)0x10000 ) + + #define FT_fdot14ToFixed( x ) \ + ( ( (FT_Fixed)( (FT_Int16)(x) ) ) << 2 ) + #define FT_intToFixed( i ) \ + ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) + #define FT_fixedToInt( x ) \ + ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_load_hvar */ + /* */ + /* <Description> */ + /* Parse the `HVAR' table and set `blend->hvar_loaded' to TRUE. */ + /* */ + /* On success, `blend->hvar_checked' is set to TRUE. */ + /* */ + /* Some memory may remain allocated on error; it is always freed in */ + /* `tt_done_blend', however. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + ft_var_load_hvar( TT_Face face ) + { + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; + + GX_Blend blend = face->blend; + + FT_Error error; + FT_UShort majorVersion; + FT_ULong table_len; + FT_ULong table_offset; + FT_ULong store_offset; + + FT_ULong* dataOffsetArray = NULL; + + + blend->hvar_loaded = TRUE; + + FT_TRACE2(( "HVAR " )); + + error = face->goto_table( face, TTAG_HVAR, stream, &table_len ); + if ( error ) + { + FT_TRACE2(( "is missing\n" )); + goto Exit; + } + + table_offset = FT_STREAM_POS(); + + /* skip minor version */ + if ( FT_READ_USHORT( majorVersion ) || + FT_STREAM_SKIP( 2 ) ) + goto Exit; + if ( majorVersion != 1 ) + { + FT_TRACE2(( "bad table version %d\n", majorVersion )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* skip map offset */ + if ( FT_READ_ULONG( store_offset ) || + FT_STREAM_SKIP( 4 ) ) + goto Exit; + + /* parse item variation store */ + { + FT_UShort format; + FT_ULong region_offset; + FT_UInt i, j, k; + FT_UInt shortDeltaCount; + + GX_HVStore itemStore; + GX_HVarTable hvarTable; + GX_HVarData hvarData; + + + if ( FT_STREAM_SEEK( table_offset + store_offset ) || + FT_READ_USHORT( format ) ) + goto Exit; + if ( format != 1 ) + { + FT_TRACE2(( "bad store format %d\n", format )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + if ( FT_NEW( blend->hvar_table ) ) /* allocate table at top level */ + goto Exit; + + hvarTable = blend->hvar_table; + itemStore = &hvarTable->itemStore; + + /* read top level fields */ + if ( FT_READ_ULONG( region_offset ) || + FT_READ_USHORT( itemStore->dataCount ) ) + goto Exit; + + /* make temporary copy of item variation data offsets; */ + /* we will parse region list first, then come back */ + if ( FT_NEW_ARRAY( dataOffsetArray, itemStore->dataCount ) ) + goto Exit; + + for ( i = 0; i < itemStore->dataCount; i++ ) + { + if ( FT_READ_ULONG( dataOffsetArray[i] ) ) + goto Exit; + } + + /* parse array of region records (region list) */ + if ( FT_STREAM_SEEK( table_offset + store_offset + region_offset ) ) + goto Exit; + + if ( FT_READ_USHORT( itemStore->axisCount ) || + FT_READ_USHORT( itemStore->regionCount ) ) + goto Exit; + + if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) ) + goto Exit; + + for ( i = 0; i < itemStore->regionCount; i++ ) + { + GX_AxisCoords axisCoords; + + + if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, + itemStore->axisCount ) ) + goto Exit; + + axisCoords = itemStore->varRegionList[i].axisList; + + for ( j = 0; j < itemStore->axisCount; j++ ) + { + FT_Short start, peak, end; + + + if ( FT_READ_SHORT( start ) || + FT_READ_SHORT( peak ) || + FT_READ_SHORT( end ) ) + goto Exit; + + axisCoords[j].startCoord = FT_fdot14ToFixed( start ); + axisCoords[j].peakCoord = FT_fdot14ToFixed( peak ); + axisCoords[j].endCoord = FT_fdot14ToFixed( end ); + } + } + + /* end of region list parse */ + + /* use dataOffsetArray now to parse varData items */ + if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) ) + goto Exit; + + for ( i = 0; i < itemStore->dataCount; i++ ) + { + hvarData = &itemStore->varData[i]; + + if ( FT_STREAM_SEEK( table_offset + + store_offset + + dataOffsetArray[i] ) ) + goto Exit; + + if ( FT_READ_USHORT( hvarData->itemCount ) || + FT_READ_USHORT( shortDeltaCount ) || + FT_READ_USHORT( hvarData->regionIdxCount ) ) + goto Exit; + + /* check some data consistency */ + if ( shortDeltaCount > hvarData->regionIdxCount ) + { + FT_TRACE2(( "bad short count %d or region count %d\n", + shortDeltaCount, + hvarData->regionIdxCount )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + if ( hvarData->regionIdxCount > itemStore->regionCount ) + { + FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n", + hvarData->regionIdxCount, + i )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* parse region indices */ + if ( FT_NEW_ARRAY( hvarData->regionIndices, + hvarData->regionIdxCount ) ) + goto Exit; + + for ( j = 0; j < hvarData->regionIdxCount; j++ ) + { + if ( FT_READ_USHORT( hvarData->regionIndices[j] ) ) + goto Exit; + + if ( hvarData->regionIndices[j] >= itemStore->regionCount ) + { + FT_TRACE2(( "bad region index %d\n", + hvarData->regionIndices[j] )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + } + + /* Parse delta set. */ + /* */ + /* On input, deltas are ( shortDeltaCount + regionIdxCount ) bytes */ + /* each; on output, deltas are expanded to `regionIdxCount' shorts */ + /* each. */ + if ( FT_NEW_ARRAY( hvarData->deltaSet, + hvarData->regionIdxCount * hvarData->itemCount ) ) + goto Exit; + + /* the delta set is stored as a 2-dimensional array of shorts; */ + /* sign-extend signed bytes to signed shorts */ + for ( j = 0; j < hvarData->itemCount * hvarData->regionIdxCount; ) + { + for ( k = 0; k < shortDeltaCount; k++, j++ ) + { + /* read the short deltas */ + FT_Short delta; + + + if ( FT_READ_SHORT( delta ) ) + goto Exit; + + hvarData->deltaSet[j] = delta; + } + + for ( ; k < hvarData->regionIdxCount; k++, j++ ) + { + /* read the (signed) byte deltas */ + FT_Char delta; + + + if ( FT_READ_CHAR( delta ) ) + goto Exit; + + hvarData->deltaSet[j] = delta; + } + } + } + } + + /* end parse item variation store */ + + /* parse width map */ + { + GX_WidthMap widthMap; + + FT_UShort format; + FT_UInt entrySize; + FT_UInt innerBitCount; + FT_UInt innerIndexMask; + FT_UInt i, j; + + + widthMap = &blend->hvar_table->widthMap; + + if ( FT_READ_USHORT( format ) || + FT_READ_USHORT( widthMap->mapCount ) ) + goto Exit; + + if ( format & 0xFFC0 ) + { + FT_TRACE2(( "bad map format %d\n", format )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* bytes per entry: 1, 2, 3, or 4 */ + entrySize = ( ( format & 0x0030 ) >> 4 ) + 1; + innerBitCount = ( format & 0x000F ) + 1; + innerIndexMask = ( 1 << innerBitCount ) - 1; + + if ( FT_NEW_ARRAY( widthMap->innerIndex, widthMap->mapCount ) ) + goto Exit; + + if ( FT_NEW_ARRAY( widthMap->outerIndex, widthMap->mapCount ) ) + goto Exit; + + for ( i = 0; i < widthMap->mapCount; i++ ) + { + FT_UInt mapData = 0; + FT_UInt outerIndex, innerIndex; + + + /* read map data one unsigned byte at a time, big endian */ + for ( j = 0; j < entrySize; j++ ) + { + FT_Byte data; + + + if ( FT_READ_BYTE( data ) ) + goto Exit; + + mapData = ( mapData << 8 ) | data; + } + + outerIndex = mapData >> innerBitCount; + + if ( outerIndex >= blend->hvar_table->itemStore.dataCount ) + { + FT_TRACE2(( "outerIndex[%d] == %d out of range\n", + i, + outerIndex )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + widthMap->outerIndex[i] = outerIndex; + + innerIndex = mapData & innerIndexMask; + + if ( innerIndex >= + blend->hvar_table->itemStore.varData[outerIndex].itemCount ) + { + FT_TRACE2(( "innerIndex[%d] == %d out of range\n", + i, + innerIndex )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + widthMap->innerIndex[i] = innerIndex; + } + } + + /* end parse width map */ + + FT_TRACE2(( "loaded\n" )); + error = FT_Err_Ok; + + Exit: + FT_FREE( dataOffsetArray ); + + if ( !error ) + { + blend->hvar_checked = TRUE; + + /* TODO: implement other HVAR stuff */ + face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE; + } + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_hadvance_adjust */ + /* */ + /* <Description> */ + /* Apply HVAR advance width adjustment of a given glyph. */ + /* */ + /* <Input> */ + /* gindex :: The glyph index. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + /* adelta :: Points to width value that gets modified. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_hadvance_adjust( TT_Face face, + FT_UInt gindex, + FT_Int *avalue ) + { + FT_Error error = FT_Err_Ok; + + GX_HVarData varData; + + FT_UInt innerIndex, outerIndex; + FT_UInt master, j; + FT_Fixed netAdjustment = 0; /* accumulated adjustment */ + FT_Fixed scaledDelta; + FT_Short* deltaSet; + FT_Fixed delta; + + + if ( !face->doblend || !face->blend ) + goto Exit; + + if ( !face->blend->hvar_loaded ) + { + /* initialize hvar table */ + face->blend->hvar_error = ft_var_load_hvar( face ); + } + + if ( !face->blend->hvar_checked ) + { + error = face->blend->hvar_error; + goto Exit; + } + + /* advance width adjustments are always present in an `HVAR' table, */ + /* so need to test for this capability */ + + if ( gindex >= face->blend->hvar_table->widthMap.mapCount ) + { + FT_TRACE2(( "gindex %d out of range\n", gindex )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* trust that HVAR parser has checked indices */ + outerIndex = face->blend->hvar_table->widthMap.outerIndex[gindex]; + innerIndex = face->blend->hvar_table->widthMap.innerIndex[gindex]; + varData = &face->blend->hvar_table->itemStore.varData[outerIndex]; + deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex]; + + /* See pseudo code from `Font Variations Overview' */ + /* in the OpenType specification. */ + + /* outer loop steps through master designs to be blended */ + for ( master = 0; master < varData->regionIdxCount; master++ ) + { + FT_Fixed scalar = FT_FIXED_ONE; + FT_UInt regionIndex = varData->regionIndices[master]; + + GX_AxisCoords axis = face->blend + ->hvar_table + ->itemStore.varRegionList[regionIndex] + .axisList; + + + /* inner loop steps through axes in this region */ + for ( j = 0; + j < face->blend->hvar_table->itemStore.axisCount; + j++, axis++ ) + { + FT_Fixed axisScalar; + + + /* compute the scalar contribution of this axis; */ + /* ignore invalid ranges */ + if ( axis->startCoord > axis->peakCoord || + axis->peakCoord > axis->endCoord ) + axisScalar = FT_FIXED_ONE; + + else if ( axis->startCoord < 0 && + axis->endCoord > 0 && + axis->peakCoord != 0 ) + axisScalar = FT_FIXED_ONE; + + /* peak of 0 means ignore this axis */ + else if ( axis->peakCoord == 0 ) + axisScalar = FT_FIXED_ONE; + + /* ignore this region if coords are out of range */ + else if ( face->blend->normalizedcoords[j] < axis->startCoord || + face->blend->normalizedcoords[j] > axis->endCoord ) + axisScalar = 0; + + /* calculate a proportional factor */ + else + { + if ( face->blend->normalizedcoords[j] == axis->peakCoord ) + axisScalar = FT_FIXED_ONE; + else if ( face->blend->normalizedcoords[j] < axis->peakCoord ) + axisScalar = + FT_DivFix( face->blend->normalizedcoords[j] - axis->startCoord, + axis->peakCoord - axis->startCoord ); + else + axisScalar = + FT_DivFix( axis->endCoord - face->blend->normalizedcoords[j], + axis->endCoord - axis->peakCoord ); + } + + /* take product of all the axis scalars */ + scalar = FT_MulFix( scalar, axisScalar ); + + } /* per-axis loop */ + + /* get the scaled delta for this region */ + delta = FT_intToFixed( deltaSet[master] ); + scaledDelta = FT_MulFix( scalar, delta ); + + /* accumulate the adjustments from each region */ + netAdjustment = netAdjustment + scaledDelta; + + } /* per-region loop */ + + /* apply the accumulated adjustment to derive the interpolated value */ + FT_TRACE5(( "horizontal width %d adjusted by %d units (HVAR)\n", + *avalue, + FT_fixedToInt( netAdjustment ) )); + + *avalue += FT_fixedToInt( netAdjustment ); + + Exit: + return error; + } + + typedef struct GX_GVar_Head_ { FT_Long version; @@ -454,10 +962,10 @@ FT_TRACE2(( "GVAR " )); - if ( ( error = face->goto_table( face, - TTAG_gvar, - stream, - &table_len ) ) != 0 ) + if ( FT_SET_ERROR( face->goto_table( face, + TTAG_gvar, + stream, + &table_len ) ) ) { FT_TRACE2(( "is missing\n" )); goto Exit; @@ -556,7 +1064,7 @@ { blend->tuplecoords[i * gvar_head.axisCount + j] = FT_GET_SHORT() * 4; /* convert to FT_Fixed */ - FT_TRACE5(( "%.4f ", + FT_TRACE5(( "%.5f ", blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 )); } FT_TRACE5(( "]\n" )); @@ -612,10 +1120,10 @@ for ( i = 0; i < blend->num_axis; i++ ) { - FT_TRACE6(( " axis coordinate %d (%.4f):\n", + FT_TRACE6(( " axis coordinate %d (%.5f):\n", i, blend->normalizedcoords[i] / 65536.0 )); if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) - FT_TRACE6(( " intermediate coordinates %d (%.4f, %.4f):\n", + FT_TRACE6(( " intermediate coordinates %d (%.5f, %.5f):\n", i, im_start_coords[i] / 65536.0, im_end_coords[i] / 65536.0 )); @@ -640,7 +1148,7 @@ if ( blend->normalizedcoords[i] == tuple_coords[i] ) { - FT_TRACE6(( " tuple coordinate value %.4f fits perfectly\n", + FT_TRACE6(( " tuple coordinate value %.5f fits perfectly\n", tuple_coords[i] / 65536.0 )); /* `apply' does not change */ continue; @@ -653,13 +1161,13 @@ if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) || blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) ) { - FT_TRACE6(( " tuple coordinate value %.4f is exceeded, stop\n", + FT_TRACE6(( " tuple coordinate value %.5f is exceeded, stop\n", tuple_coords[i] / 65536.0 )); apply = 0; break; } - FT_TRACE6(( " tuple coordinate value %.4f fits\n", + FT_TRACE6(( " tuple coordinate value %.5f fits\n", tuple_coords[i] / 65536.0 )); apply = FT_MulDiv( apply, blend->normalizedcoords[i], @@ -672,7 +1180,7 @@ if ( blend->normalizedcoords[i] < im_start_coords[i] || blend->normalizedcoords[i] > im_end_coords[i] ) { - FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] is exceeded," + FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] is exceeded," " stop\n", im_start_coords[i] / 65536.0, im_end_coords[i] / 65536.0 )); @@ -682,7 +1190,7 @@ else if ( blend->normalizedcoords[i] < tuple_coords[i] ) { - FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", + FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n", im_start_coords[i] / 65536.0, im_end_coords[i] / 65536.0 )); apply = FT_MulDiv( apply, @@ -692,7 +1200,7 @@ else { - FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", + FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n", im_start_coords[i] / 65536.0, im_end_coords[i] / 65536.0 )); apply = FT_MulDiv( apply, @@ -702,7 +1210,7 @@ } } - FT_TRACE6(( " apply factor is %.4f\n", apply / 65536.0 )); + FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 )); return apply; } @@ -778,6 +1286,7 @@ FT_Var_Axis* a; FT_Var_Named_Style* ns; GX_FVar_Head fvar_head; + FT_Bool usePsName; static const FT_Frame_Field fvar_fields[] = { @@ -816,21 +1325,26 @@ /* read the font data and set up the internal representation */ /* if not already done */ - if ( face->blend == NULL ) + if ( !face->blend ) { FT_TRACE2(( "FVAR " )); /* both `fvar' and `gvar' must be present */ - if ( ( error = face->goto_table( face, TTAG_gvar, - stream, &table_len ) ) != 0 ) + if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar, + stream, &table_len ) ) ) { - FT_TRACE1(( "\n" - "TT_Get_MM_Var: `gvar' table is missing\n" )); - goto Exit; + /* CFF2 is an alternate to gvar here */ + if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2, + stream, &table_len ) ) ) + { + FT_TRACE1(( "\n" + "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" )); + goto Exit; + } } - if ( ( error = face->goto_table( face, TTAG_fvar, - stream, &table_len ) ) != 0 ) + if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar, + stream, &table_len ) ) ) { FT_TRACE1(( "is missing\n" )); goto Exit; @@ -838,30 +1352,13 @@ fvar_start = FT_STREAM_POS( ); + /* the validity of the `fvar' header data was already checked */ + /* in function `sfnt_init_face' */ if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) goto Exit; - if ( fvar_head.version != (FT_Long)0x00010000L || -#if 0 - /* fonts like `JamRegular.ttf' have an incorrect value for */ - /* `countSizePairs'; since value 2 is hard-coded in `fvar' */ - /* version 1.0, we simply ignore it */ - fvar_head.countSizePairs != 2 || -#endif - fvar_head.axisSize != 20 || - /* axisCount limit implied by 16-bit instanceSize */ - fvar_head.axisCount > 0x3FFE || - fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || - /* instanceCount limit implied by limited range of name IDs */ - fvar_head.instanceCount > 0x7EFF || - fvar_head.offsetToData + fvar_head.axisCount * 20U + - fvar_head.instanceCount * fvar_head.instanceSize > table_len ) - { - FT_TRACE1(( "\n" - "TT_Get_MM_Var: invalid `fvar' header\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } + usePsName = FT_BOOL( fvar_head.instanceSize == + 6 + 4 * fvar_head.axisCount ); FT_TRACE2(( "loaded\n" )); @@ -938,7 +1435,18 @@ a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); a->name[4] = '\0'; - FT_TRACE5(( " \"%s\": minimum=%.4f, default=%.4f, maximum=%.4f\n", + if ( a->minimum > a->def || + a->def > a->maximum ) + { + FT_TRACE2(( "TT_Get_MM_Var:" + " invalid \"%s\" axis record; disabling\n", + a->name )); + + a->minimum = a->def; + a->maximum = a->def; + } + + FT_TRACE5(( " \"%s\": minimum=%.5f, default=%.5f, maximum=%.5f\n", a->name, a->minimum / 65536.0, a->def / 65536.0, @@ -952,7 +1460,9 @@ ns = mmvar->namedstyle; for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) { - if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) + /* PostScript names add 2 bytes to the instance record size */ + if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) + + 4L * fvar_head.axisCount ) ) goto Exit; ns->strid = FT_GET_USHORT(); @@ -961,13 +1471,16 @@ for ( j = 0; j < fvar_head.axisCount; j++ ) ns->coords[j] = FT_GET_LONG(); + if ( usePsName ) + ns->psid = FT_GET_USHORT(); + FT_FRAME_EXIT(); } } /* fill the output array if requested */ - if ( master != NULL ) + if ( master ) { FT_UInt n; @@ -1051,6 +1564,7 @@ GX_Blend blend; FT_MM_Var* mmvar; FT_UInt i; + FT_Bool is_default_instance = 1; FT_Memory memory = face->root.memory; enum @@ -1064,9 +1578,9 @@ face->doblend = FALSE; - if ( face->blend == NULL ) + if ( !face->blend ) { - if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) + if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) goto Exit; } @@ -1084,24 +1598,27 @@ for ( i = 0; i < num_coords; i++ ) { - FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); + FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) { - FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.4f\n" + FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n" " is out of range [-1;1]\n", coords[i] / 65536.0 )); error = FT_THROW( Invalid_Argument ); goto Exit; } + + if ( coords[i] != 0 ) + is_default_instance = 0; } FT_TRACE5(( "\n" )); - if ( blend->glyphoffsets == NULL ) - if ( ( error = ft_var_load_gvar( face ) ) != 0 ) + if ( !face->isCFF2 && !blend->glyphoffsets ) + if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) ) goto Exit; - if ( blend->normalizedcoords == NULL ) + if ( !blend->normalizedcoords ) { if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) goto Exit; @@ -1147,7 +1664,7 @@ face->doblend = TRUE; - if ( face->cvt != NULL ) + if ( face->cvt ) { switch ( manageCvt ) { @@ -1172,6 +1689,8 @@ } } + face->is_default_instance = is_default_instance; + Exit: return error; } @@ -1180,6 +1699,73 @@ /*************************************************************************/ /* */ /* <Function> */ + /* TT_Get_MM_Blend */ + /* */ + /* <Description> */ + /* Get the blend (normalized) coordinates for this instance of the */ + /* font. */ + /* */ + /* <InOut> */ + /* face :: The font. */ + /* Initialize the blend structure with `gvar' data. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available coordinates. If it is */ + /* larger than the number of axes, set the excess */ + /* values to 0. */ + /* */ + /* coords :: An array of `num_coords', each between [-1,1]. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Get_MM_Blend( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error = FT_Err_Ok; + GX_Blend blend; + FT_UInt i, nc; + + + if ( !face->blend ) + { + if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) + return error; + } + + blend = face->blend; + + nc = num_coords; + if ( num_coords > blend->num_axis ) + { + FT_TRACE2(( "TT_Get_MM_Blend: only using first %d of %d coordinates\n", + blend->num_axis, num_coords )); + nc = blend->num_axis; + } + + if ( face->doblend ) + { + for ( i = 0; i < nc; i++ ) + coords[i] = blend->normalizedcoords[i]; + } + else + { + for ( i = 0; i < nc; i++ ) + coords[i] = 0; + } + + for ( ; i < num_coords; i++ ) + coords[i] = 0; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ /* TT_Set_Var_Design */ /* */ /* <Description> */ @@ -1217,9 +1803,9 @@ FT_Memory memory = face->root.memory; - if ( face->blend == NULL ) + if ( !face->blend ) { - if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) + if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) goto Exit; } @@ -1234,7 +1820,7 @@ num_coords = mmvar->num_axis; } - /* Axis normalization is a two stage process. First we normalize */ + /* Axis normalization is a two-stage process. First we normalize */ /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ /* Then, if there's an `avar' table, we renormalize this range. */ @@ -1246,26 +1832,33 @@ a = mmvar->axis; for ( i = 0; i < num_coords; i++, a++ ) { - FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); - if ( coords[i] > a->maximum || coords[i] < a->minimum ) + FT_Fixed coord = coords[i]; + + + FT_TRACE5(( " %.5f\n", coord / 65536.0 )); + if ( coord > a->maximum || coord < a->minimum ) { - FT_TRACE1(( "TT_Set_Var_Design: normalized design coordinate %.4f\n" - " is out of range [%.4f;%.4f]\n", - coords[i] / 65536.0, - a->minimum / 65536.0, - a->maximum / 65536.0 )); - error = FT_THROW( Invalid_Argument ); - goto Exit; + FT_TRACE1(( + "TT_Set_Var_Design: design coordinate %.5f\n" + " is out of range [%.5f;%.5f]; clamping\n", + coord / 65536.0, + a->minimum / 65536.0, + a->maximum / 65536.0 )); + + if ( coord > a->maximum) + coord = a->maximum; + else + coord = a->minimum; } - if ( coords[i] < a->def ) + if ( coord < a->def ) normalized[i] = -FT_DivFix( coords[i] - a->def, a->minimum - a->def ); - else if ( a->maximum == a->def ) - normalized[i] = 0; - else + else if ( coord > a->def ) normalized[i] = FT_DivFix( coords[i] - a->def, a->maximum - a->def ); + else + normalized[i] = 0; } FT_TRACE5(( "\n" )); @@ -1276,7 +1869,7 @@ if ( !blend->avar_checked ) ft_var_load_avar( face ); - if ( blend->avar_segment != NULL ) + if ( blend->avar_segment ) { FT_TRACE5(( "normalized design coordinates" " before applying `avar' data:\n" )); @@ -1286,9 +1879,10 @@ { for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) { - FT_TRACE5(( " %.4f\n", normalized[i] / 65536.0 )); if ( normalized[i] < av->correspondence[j].fromCoord ) { + FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); + normalized[i] = FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, av->correspondence[j].toCoord - @@ -1311,6 +1905,123 @@ /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Get_Var_Design */ + /* */ + /* <Description> */ + /* Get the design coordinates of the currently selected interpolated */ + /* font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* num_coords :: The number of design coordinates to retrieve. If it */ + /* is larger than the number of axes, set the excess */ + /* values to~0. */ + /* */ + /* <Output> */ + /* coords :: The design coordinates array. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Get_Var_Design( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error = FT_Err_Ok; + + GX_Blend blend; + FT_MM_Var* mmvar; + FT_Var_Axis* a; + + FT_UInt i, j, nc; + + + if ( !face->blend ) + { + if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) + return error; + } + + blend = face->blend; + + nc = num_coords; + if ( num_coords > blend->num_axis ) + { + FT_TRACE2(( "TT_Get_Var_Design: only using first %d of %d coordinates\n", + blend->num_axis, num_coords )); + nc = blend->num_axis; + } + + if ( face->doblend ) + { + for ( i = 0; i < nc; i++ ) + coords[i] = blend->normalizedcoords[i]; + } + else + { + for ( i = 0; i < nc; i++ ) + coords[i] = 0; + } + + for ( ; i < num_coords; i++ ) + coords[i] = 0; + + if ( !blend->avar_checked ) + ft_var_load_avar( face ); + + if ( blend->avar_segment ) + { + GX_AVarSegment av = blend->avar_segment; + + + FT_TRACE5(( "design coordinates" + " after removing `avar' distortion:\n" )); + + for ( i = 0; i < nc; i++, av++ ) + { + for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) + { + if ( coords[i] < av->correspondence[j].toCoord ) + { + coords[i] = + FT_MulDiv( coords[i] - av->correspondence[j - 1].toCoord, + av->correspondence[j].fromCoord - + av->correspondence[j - 1].fromCoord, + av->correspondence[j].toCoord - + av->correspondence[j - 1].toCoord ) + + av->correspondence[j - 1].fromCoord; + + FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); + break; + } + } + } + } + + mmvar = blend->mmvar; + a = mmvar->axis; + + for ( i = 0; i < nc; i++, a++ ) + { + if ( coords[i] < 0 ) + coords[i] = a->def + FT_MulFix( coords[i], + a->def - a->minimum ); + else if ( coords[i] > 0 ) + coords[i] = a->def + FT_MulFix( coords[i], + a->maximum - a->def ); + else + coords[i] = a->def; + } + + return FT_Err_Ok; + } + + + /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GX VAR PARSING ROUTINES *****/ @@ -1363,7 +2074,7 @@ FT_TRACE2(( "CVAR " )); - if ( blend == NULL ) + if ( !blend ) { FT_TRACE2(( "\n" "tt_face_vary_cvt: no blend specified\n" )); @@ -1371,7 +2082,7 @@ goto Exit; } - if ( face->cvt == NULL ) + if ( !face->cvt ) { FT_TRACE2(( "\n" "tt_face_vary_cvt: no `cvt ' table\n" )); @@ -1414,7 +2125,8 @@ offsetToData = FT_GET_USHORT(); /* rough sanity test */ - if ( offsetToData + tupleCount * 4 > table_len ) + if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > + table_len ) { FT_TRACE2(( "tt_face_vary_cvt:" " invalid CVT variation array header\n" )); @@ -1498,7 +2210,7 @@ table_len, point_count == 0 ? face->cvt_size : point_count ); - if ( localpoints == NULL || deltas == NULL ) + if ( !localpoints || !deltas ) ; /* failure, ignore it */ else if ( localpoints == ALL_POINTS ) @@ -1546,10 +2258,15 @@ for ( j = 0; j < point_count; j++ ) { - int pindex = localpoints[j]; - FT_Long orig_cvt = face->cvt[pindex]; + int pindex; + FT_Long orig_cvt; + + pindex = localpoints[j]; + if ( (FT_ULong)pindex >= face->cvt_size ) + continue; + orig_cvt = face->cvt[pindex]; face->cvt[pindex] = (FT_Short)( orig_cvt + FT_MulFix( deltas[j], apply ) ); @@ -1858,7 +2575,7 @@ FT_Short *deltas_x, *deltas_y; - if ( !face->doblend || blend == NULL ) + if ( !face->doblend || !blend ) return FT_THROW( Invalid_Argument ); if ( glyph_index >= blend->gv_glyphcnt || @@ -2004,7 +2721,7 @@ point_count == 0 ? n_points : point_count ); - if ( points == NULL || deltas_y == NULL || deltas_x == NULL ) + if ( !points || !deltas_y || !deltas_x ) ; /* failure, ignore it */ else if ( points == ALL_POINTS ) @@ -2023,8 +2740,36 @@ FT_Pos delta_y = FT_MulFix( deltas_y[j], apply ); - outline->points[j].x += delta_x; - outline->points[j].y += delta_y; + if ( j < n_points - 3 ) + { + outline->points[j].x += delta_x; + outline->points[j].y += delta_y; + } + else + { + /* To avoid double adjustment of advance width or height, */ + /* adjust phantom points only if there is no HVAR or VVAR */ + /* support, respectively. */ + if ( j == ( n_points - 3 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_HADVANCE ) ) + outline->points[j].x += delta_x; + + else if ( j == ( n_points - 2 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_LSB ) ) + outline->points[j].x += delta_x; + + else if ( j == ( n_points - 1 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_VADVANCE ) ) + outline->points[j].y += delta_y; + + else if ( j == ( n_points - 0 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_TSB ) ) + outline->points[j].y += delta_y; + } #ifdef FT_DEBUG_LEVEL_TRACE if ( delta_x || delta_y ) @@ -2146,30 +2891,99 @@ /*************************************************************************/ /* */ /* <Function> */ + /* tt_get_var_blend */ + /* */ + /* <Description> */ + /* An extended internal version of `TT_Get_MM_Blend' that returns */ + /* pointers instead of copying data, without any initialization of */ + /* the MM machinery in case it isn't loaded yet. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_get_var_blend( TT_Face face, + FT_UInt *num_coords, + FT_Fixed* *coords, + FT_MM_Var* *mm_var ) + { + if ( face->blend ) + { + if ( num_coords ) + *num_coords = face->blend->num_axis; + if ( coords ) + *coords = face->blend->normalizedcoords; + if ( mm_var ) + *mm_var = face->blend->mmvar; + } + else + { + if ( num_coords ) + *num_coords = 0; + if ( coords ) + *coords = NULL; + if ( mm_var ) + *mm_var = NULL; + } + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ /* tt_done_blend */ /* */ /* <Description> */ /* Free the blend internal data structure. */ /* */ FT_LOCAL_DEF( void ) - tt_done_blend( FT_Memory memory, - GX_Blend blend ) + tt_done_blend( TT_Face face ) { - if ( blend != NULL ) + FT_Memory memory = FT_FACE_MEMORY( face ); + GX_Blend blend = face->blend; + + + if ( blend ) { - FT_UInt i; + FT_UInt i, num_axes; + + /* blend->num_axis might not be set up yet */ + num_axes = blend->mmvar->num_axis; FT_FREE( blend->normalizedcoords ); FT_FREE( blend->mmvar ); - if ( blend->avar_segment != NULL ) + if ( blend->avar_segment ) { - for ( i = 0; i < blend->num_axis; i++ ) + for ( i = 0; i < num_axes; i++ ) FT_FREE( blend->avar_segment[i].correspondence ); FT_FREE( blend->avar_segment ); } + if ( blend->hvar_table ) + { + if ( blend->hvar_table->itemStore.varData ) + { + for ( i = 0; i < blend->hvar_table->itemStore.dataCount; i++ ) + { + FT_FREE( blend->hvar_table->itemStore.varData[i].regionIndices ); + FT_FREE( blend->hvar_table->itemStore.varData[i].deltaSet ); + } + FT_FREE( blend->hvar_table->itemStore.varData ); + } + + if ( blend->hvar_table->itemStore.varRegionList ) + { + for ( i = 0; i < blend->hvar_table->itemStore.regionCount; i++ ) + FT_FREE( blend->hvar_table->itemStore.varRegionList[i].axisList ); + FT_FREE( blend->hvar_table->itemStore.varRegionList ); + } + + FT_FREE( blend->hvar_table->widthMap.innerIndex ); + FT_FREE( blend->hvar_table->widthMap.outerIndex ); + FT_FREE( blend->hvar_table ); + } + FT_FREE( blend->tuplecoords ); FT_FREE( blend->glyphoffsets ); FT_FREE( blend ); |