mirror of https://github.com/freetype/freetype
[truetype] Add `HVAR' table parsing.
Note that this is not complete yet; it only handles advance width variation. Activation of the code follows in another commit. * include/freetype/ftmm.h (FT_Var_Named_Style): Add `psid' member. * src/truetype/ttgxvar.h (GX_HVarData, GX_AxisCoords, GX_HVarRegion, GX_HVStore, GX_WidthMap): New auxiliary structures for... (GX_HVarTable): ... HVAR main structure. (GX_BlendRec): Add data for HVAR loading. * src/truetype/ttgxvar.c (FT_FIXED_ONE, FT_fdot14ToFixed, FT_intToFixed, FT_fixedToInt): New macros. (ft_var_load_hvar): New function. (TT_Get_MM_Var): Updated. (tt_done_blend): Deallocate HVAR data.
This commit is contained in:
parent
abd5858102
commit
097cd874ee
23
ChangeLog
23
ChangeLog
|
@ -1,3 +1,26 @@
|
|||
2016-12-15 Dave Arnold <darnold@adobe.com>
|
||||
Werner Lemberg <wl@gnu.org>
|
||||
|
||||
[truetype] Add `HVAR' table parsing.
|
||||
|
||||
Note that this is not complete yet; it only handles advance width
|
||||
variation.
|
||||
|
||||
Activation of the code follows in another commit.
|
||||
|
||||
* include/freetype/ftmm.h (FT_Var_Named_Style): Add `psid' member.
|
||||
|
||||
* src/truetype/ttgxvar.h (GX_HVarData, GX_AxisCoords, GX_HVarRegion,
|
||||
GX_HVStore, GX_WidthMap): New auxiliary structures for...
|
||||
(GX_HVarTable): ... HVAR main structure.
|
||||
(GX_BlendRec): Add data for HVAR loading.
|
||||
|
||||
* src/truetype/ttgxvar.c (FT_FIXED_ONE, FT_fdot14ToFixed,
|
||||
FT_intToFixed, FT_fixedToInt): New macros.
|
||||
(ft_var_load_hvar): New function.
|
||||
(TT_Get_MM_Var): Updated.
|
||||
(tt_done_blend): Deallocate HVAR data.
|
||||
|
||||
2016-12-15 Dave Arnold <darnold@adobe.com>
|
||||
|
||||
[cff] Extend number parsing.
|
||||
|
|
|
@ -171,6 +171,7 @@ FT_BEGIN_HEADER
|
|||
{
|
||||
FT_Fixed* coords;
|
||||
FT_UInt strid;
|
||||
FT_UInt psid; /* since 2.7.1 */
|
||||
|
||||
} FT_Var_Named_Style;
|
||||
|
||||
|
|
|
@ -393,6 +393,361 @@
|
|||
}
|
||||
|
||||
|
||||
/* 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_UShort minorVersion;
|
||||
FT_ULong table_len;
|
||||
FT_ULong table_offset;
|
||||
FT_ULong store_offset;
|
||||
FT_ULong map_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();
|
||||
|
||||
if ( FT_READ_USHORT( majorVersion ) ||
|
||||
FT_READ_USHORT( minorVersion ) )
|
||||
goto Exit;
|
||||
if ( majorVersion != 1 )
|
||||
{
|
||||
FT_TRACE2(( "bad table version %d\n", majorVersion ));
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if ( FT_READ_ULONG( store_offset ) ||
|
||||
FT_READ_ULONG( map_offset ) )
|
||||
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;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
typedef struct GX_GVar_Head_
|
||||
{
|
||||
FT_Long version;
|
||||
|
@ -778,6 +1133,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[] =
|
||||
{
|
||||
|
@ -851,8 +1207,6 @@
|
|||
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 )
|
||||
|
@ -863,6 +1217,18 @@
|
|||
goto Exit;
|
||||
}
|
||||
|
||||
if ( fvar_head.instanceSize == 4 + 4 * fvar_head.axisCount )
|
||||
usePsName = FALSE;
|
||||
else if ( fvar_head.instanceSize == 6 + 4 * fvar_head.axisCount )
|
||||
usePsName = TRUE;
|
||||
else
|
||||
{
|
||||
FT_TRACE1(( "\n"
|
||||
"TT_Get_MM_Var: invalid `fvar' header\n" ));
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
FT_TRACE2(( "loaded\n" ));
|
||||
|
||||
FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
|
||||
|
@ -952,7 +1318,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,6 +1329,9 @@
|
|||
for ( j = 0; j < fvar_head.axisCount; j++ )
|
||||
ns->coords[j] = FT_GET_LONG();
|
||||
|
||||
if ( usePsName )
|
||||
ns->psid = FT_GET_USHORT();
|
||||
|
||||
FT_FRAME_EXIT();
|
||||
}
|
||||
}
|
||||
|
@ -2296,6 +2667,30 @@
|
|||
FT_FREE( blend->avar_segment );
|
||||
}
|
||||
|
||||
if ( blend->hvar_table != NULL )
|
||||
{
|
||||
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 );
|
||||
|
|
|
@ -61,6 +61,77 @@ FT_BEGIN_HEADER
|
|||
} GX_AVarSegmentRec, *GX_AVarSegment;
|
||||
|
||||
|
||||
typedef struct GX_HVarDataRec_
|
||||
{
|
||||
FT_UInt itemCount; /* number of delta sets per item */
|
||||
FT_UInt regionIdxCount; /* number of region indices in this data */
|
||||
FT_UInt* regionIndices; /* array of `regionCount' indices; */
|
||||
/* these index `varRegionList' */
|
||||
FT_Short* deltaSet; /* array of `itemCount' deltas */
|
||||
/* use `innerIndex' for this array */
|
||||
|
||||
} GX_HVarDataRec, *GX_HVarData;
|
||||
|
||||
|
||||
/* contribution of one axis to a region */
|
||||
typedef struct GX_AxisCoordsRec_
|
||||
{
|
||||
FT_Fixed startCoord;
|
||||
FT_Fixed peakCoord; /* zero means no effect (factor = 1) */
|
||||
FT_Fixed endCoord;
|
||||
|
||||
} GX_AxisCoordsRec, *GX_AxisCoords;
|
||||
|
||||
|
||||
typedef struct GX_HVarRegionRec_
|
||||
{
|
||||
GX_AxisCoords axisList; /* array of axisCount records */
|
||||
|
||||
} GX_HVarRegionRec, *GX_HVarRegion;
|
||||
|
||||
|
||||
/* HVAR item variation store */
|
||||
typedef struct GX_HVStoreRec_
|
||||
{
|
||||
FT_UInt dataCount;
|
||||
GX_HVarData varData; /* array of dataCount records; */
|
||||
/* use `outerIndex' for this array */
|
||||
FT_UShort axisCount;
|
||||
FT_UInt regionCount; /* total number of regions defined */
|
||||
GX_HVarRegion varRegionList;
|
||||
|
||||
} GX_HVStoreRec, *GX_HVStore;
|
||||
|
||||
|
||||
typedef struct GX_WidthMapRec_
|
||||
{
|
||||
FT_UInt mapCount;
|
||||
FT_UInt* outerIndex; /* indices to item var data */
|
||||
FT_UInt* innerIndex; /* indices to delta set */
|
||||
|
||||
} GX_WidthMapRec, *GX_WidthMap;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* GX_HVarTableRec */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* Data from the `HVAR' table. */
|
||||
/* */
|
||||
typedef struct GX_HVarTableRec_
|
||||
{
|
||||
GX_HVStoreRec itemStore; /* Item Variation Store */
|
||||
GX_WidthMapRec widthMap; /* Advance Width Mapping */
|
||||
#if 0
|
||||
GX_LSBMap LsbMap; /* not implemented */
|
||||
GX_RSBMap RsbMap; /* not implemented */
|
||||
#endif
|
||||
|
||||
} GX_HVarTableRec, *GX_HVarTable;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
|
@ -89,6 +160,11 @@ FT_BEGIN_HEADER
|
|||
FT_Bool avar_checked;
|
||||
GX_AVarSegment avar_segment;
|
||||
|
||||
FT_Bool hvar_loaded;
|
||||
FT_Bool hvar_checked;
|
||||
FT_Error hvar_error;
|
||||
GX_HVarTable hvar_table;
|
||||
|
||||
FT_UInt tuplecount; /* shared tuples in `gvar' */
|
||||
FT_Fixed* tuplecoords; /* tuplecoords[tuplecount][num_axis] */
|
||||
|
||||
|
|
Loading…
Reference in New Issue