Adds the functionality to load SVG documents.

SVG document corresponding to a glyphID is fetched and stored
in an `FT_SVG_Document' structure which is referenced in the
glyphslot.

* include/freetype/config/ftheader.h: Adds `FT_OTSVG_H'.

* include/freetype/ftimage.h: Adds `FT_GLYPH_FORMAT_SVG'.

* include/freetype/internal/ftobjs.h: Adds `FT_GLYPH_OWN_GZIP_SVG'.

* include/freetype/internal/sfnt.h: Adds `load_svg_doc' and its
function type `TT_Load_Svg_Doc_Func'.

* include/freetype/otsvg.h: Adds `FT_SVG_Document' and its struct.

* src/base/ftobjs.c: Adds code to allocate and free memory for
`FT_SVG_Document' in `slot->other'.

* src/cff/cffgload.c: Adds code to load SVG glyph if it's present.

* src/truetype/ttgload.c: Ditto.

* src/sfnt/sfdriver.c: Adds `tt_face_load_svg_doc'.

* src/sfnt/ttsvg.h: Ditto.

* src/sfnt/ttsvg.c: Adds implementation of `tt_face_load_svg_doc'
and its helper functions.
This commit is contained in:
Moazin Khatti 2019-08-10 14:49:59 +05:00
parent 92eeba75c4
commit ce64d9cbf1
11 changed files with 490 additions and 11 deletions

View File

@ -547,6 +547,19 @@
#define FT_BITMAP_H <freetype/ftbitmap.h>
/**************************************************************************
*
* @macro:
* FT_OTSVG_H
*
* @description:
* A macro used in `#include` statements to name the file containing the
* API of OT-SVG support related things.
*
*/
#define FT_OTSVG_H <freetype/otsvg.h>
/**************************************************************************
*
* @macro:

View File

@ -732,6 +732,9 @@ FT_BEGIN_HEADER
* contours. Some Type~1 fonts, like those in the Hershey family,
* contain glyphs in this format. These are described as @FT_Outline,
* but FreeType isn't currently capable of rendering them correctly.
*
* FT_GLYPH_FORMAT_SVG ::
* The glyph is represented by an SVG document in the SVG table.
*/
typedef enum FT_Glyph_Format_
{
@ -740,7 +743,8 @@ FT_BEGIN_HEADER
FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ),
FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ),
FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ),
FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' )
FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' ),
FT_IMAGE_TAG( FT_GLYPH_FORMAT_SVG, 's', 'v', 'g', ' ' )
} FT_Glyph_Format;
@ -752,6 +756,7 @@ FT_BEGIN_HEADER
#define ft_glyph_format_bitmap FT_GLYPH_FORMAT_BITMAP
#define ft_glyph_format_outline FT_GLYPH_FORMAT_OUTLINE
#define ft_glyph_format_plotter FT_GLYPH_FORMAT_PLOTTER
#define ft_glyph_format_svg FT_GLYPH_FORMAT_SVG
/*************************************************************************/

View File

@ -418,7 +418,8 @@ FT_BEGIN_HEADER
* initializing the glyph slot.
*/
#define FT_GLYPH_OWN_BITMAP 0x1U
#define FT_GLYPH_OWN_BITMAP 0x1U
#define FT_GLYPH_OWN_GZIP_SVG 0x2U
typedef struct FT_Slot_InternalRec_
{

View File

@ -312,6 +312,31 @@ FT_BEGIN_HEADER
TT_SBit_MetricsRec *ametrics );
/**************************************************************************
*
* @functype:
* TT_Load_Svg_Doc_Func
*
* @description:
* Scans the SVG documents list to find the document containing the glyph
* that has the id "glyph<glyph_index>".
*
* @input:
* glyph ::
* The glyph slot from which pointers to SVG documents list will be
* grabbed. The results will be stored back in the slot too.
*
* glyph_index ::
* The index of the glyph that is to be looked up.
*
* @return:
* FreeType error code. 0 means success.
*/
typedef FT_Error
(*TT_Load_Svg_Doc_Func)( FT_GlyphSlot glyph,
FT_UInt glyph_index );
/**************************************************************************
*
* @functype:
@ -781,6 +806,7 @@ FT_BEGIN_HEADER
/* OpenType SVG support */
TT_Load_Table_Func load_svg;
TT_Free_Table_Func free_svg;
TT_Load_Svg_Doc_Func load_svg_doc;
} SFNT_Interface;
@ -830,7 +856,8 @@ FT_BEGIN_HEADER
get_name_, \
get_name_id_, \
load_svg_, \
free_svg_ ) \
free_svg_, \
load_svg_doc_ ) \
static const SFNT_Interface class_ = \
{ \
goto_table_, \
@ -872,7 +899,8 @@ FT_BEGIN_HEADER
get_name_, \
get_name_id_, \
load_svg_, \
free_svg_ \
free_svg_, \
load_svg_doc_ \
};

91
include/freetype/otsvg.h Normal file
View File

@ -0,0 +1,91 @@
/****************************************************************************
*
* otsvg.h
*
* Interface for OT-SVG support related things (specification).
*
* Copyright (C) 2004-2019 by
* David Turner, Robert Wilhelm, Werner Lemberg and Moazin Khatti.
*
* This file is part of the FreeType project, and may only be used,
* modified, and distributed under the terms of the FreeType project
* license, LICENSE.TXT. By continuing to use, modify, or distribute
* this file you indicate that you have read the license and
* understand and accept it fully.
*
*/
#ifndef FTSVG_RENDERER_H_
#define FTSVG_RENDERER_H_
#include <ft2build.h>
#include FT_FREETYPE_H
#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif
FT_BEGIN_HEADER
/**************************************************************************
*
* @struct:
* FT_SVG_DocumentRec_
*
* @description:
* A structure that models one SVG document.
*
* @fields:
* svg_document ::
* A pointer to the SVG document string.
*
* svg_document_length ::
* The length of the SVG document string.
*
* metrics ::
* A metrics object storing the size information.
*
* units_per_EM ::
* The size of the EM square.
*
* start_glyph_id ::
* The starting glyph ID for the glyph range that this document has.
*
* end_glyph_id ::
* The ending glyph ID for the glyph range that this document has.
*
* @note:
* `metrics` and `units_per_EM` might look like repetitions since both
* fields are stored in face object, but they are not; When the slot is
* passed down to a renderer, the renderer can only access the `metrics`
* and `units_per_EM` by `slot->face`. However, when `FT_Glyph_To_Bitmap`
* sets up a dummy object, it has no way to set a `face` object. Thus,
* metrics information and units_per_EM (which is necessary for OT-SVG)
* has to be stored separately.
*/
typedef struct FT_SVG_DocumentRec_
{
FT_Byte* svg_document;
FT_ULong svg_document_length;
FT_Size_Metrics metrics;
FT_UShort units_per_EM;
FT_UShort start_glyph_id;
FT_UShort end_glyph_id;
} FT_SVG_DocumentRec;
/**************************************************************************
*
* @type:
* FT_SVG_Document
*
* @description:
* A handle to a FT_SVG_DocumentRec object.
*/
typedef struct FT_SVG_DocumentRec_* FT_SVG_Document;
FT_END_HEADER
#endif

View File

@ -40,6 +40,7 @@
#include FT_SERVICE_TT_CMAP_H
#include FT_SERVICE_KERNING_H
#include FT_SERVICE_TRUETYPE_ENGINE_H
#include FT_OTSVG_H
#include FT_DRIVER_H
@ -317,6 +318,17 @@
if ( !error && clazz->init_slot )
error = clazz->init_slot( slot );
#ifdef FT_CONFIG_OPTION_SVG
/* check if SVG table exists allocate the space in slot->other */
if ( slot->face->face_flags & FT_FACE_FLAG_SVG )
{
FT_SVG_Document document;
if ( FT_NEW( document ) )
goto Exit;
slot->other = document;
}
#endif
Exit:
return error;
}
@ -551,7 +563,23 @@
slot->subglyphs = NULL;
slot->control_data = NULL;
slot->control_len = 0;
#ifndef FT_CONFIG_OPTION_SVG
slot->other = NULL;
#else
if ( !( slot->face->face_flags & FT_FACE_FLAG_SVG ) )
slot->other = NULL;
else
{
if ( slot->internal->flags & FT_GLYPH_OWN_GZIP_SVG )
{
FT_Memory memory = slot->face->memory;
FT_SVG_Document doc = (FT_SVG_Document)slot->other;
FT_FREE( doc->svg_document );
slot->internal->load_flags &= ~FT_GLYPH_OWN_GZIP_SVG;
}
}
#endif
slot->format = FT_GLYPH_FORMAT_NONE;
slot->linearHoriAdvance = 0;
@ -569,6 +597,20 @@
FT_Memory memory = driver->root.memory;
#ifdef FT_CONFIG_OPTION_SVG
if ( slot->face->face_flags & FT_FACE_FLAG_SVG )
{
/* free memory in case svg was there */
if ( slot->internal->flags & FT_GLYPH_OWN_GZIP_SVG )
{
FT_SVG_Document doc = (FT_SVG_Document)slot->other;
FT_FREE( doc->svg_document );
slot->internal->flags &= ~FT_GLYPH_OWN_GZIP_SVG;
}
FT_FREE( slot->other );
}
#endif
if ( clazz->done_slot )
clazz->done_slot( slot );

View File

@ -347,6 +347,53 @@
if ( load_flags & FT_LOAD_SBITS_ONLY )
return FT_THROW( Invalid_Argument );
#ifdef FT_CONFIG_OPTION_SVG
/* check for OT-SVG */
if ( ( load_flags & FT_LOAD_COLOR ) &&
( ((TT_Face)glyph->root.face)->svg ) )
{
SFNT_Service sfnt;
FT_Short leftBearing;
FT_Short topBearing;
FT_UShort advanceX;
FT_UShort advanceY;
if ( ( size->root.metrics.x_ppem < 1 ||
size->root.metrics.y_ppem < 1 ) )
{
error = FT_THROW( Invalid_Size_Handle );
return error;
}
FT_TRACE3(( "Attemping to load SVG glyph\n" ));
sfnt = (SFNT_Service)((TT_Face)glyph->root.face)->sfnt;
error = sfnt->load_svg_doc( (FT_GlyphSlot)glyph, glyph_index );
if( error == FT_Err_Ok )
{
FT_TRACE3(( "Successfully loaded SVG glyph\n" ));
glyph->root.format = FT_GLYPH_FORMAT_SVG;
sfnt->get_metrics( face,
FALSE,
glyph_index,
&leftBearing,
&advanceX );
sfnt->get_metrics( face,
TRUE,
glyph_index,
&topBearing,
&advanceY );
advanceX *= ((float)glyph->root.face->size->metrics.x_ppem)/
((float)glyph->root.face->units_per_EM) * 64.0;
advanceY *= ((float)glyph->root.face->size->metrics.y_ppem)/
((float)glyph->root.face->units_per_EM) * 64.0;
glyph->root.metrics.horiAdvance = advanceX;
glyph->root.metrics.vertAdvance = advanceY;
return error;
}
FT_TRACE3(( "Failed to load SVG glyph\n" ));
}
#endif
/* if we have a CID subfont, use its matrix (which has already */
/* been multiplied with the root matrix) */

View File

@ -1212,18 +1212,18 @@
#define PUT_EMBEDDED_BITMAPS( a ) NULL
#endif
#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
#define PUT_COLOR_LAYERS( a ) a
#else
#define PUT_COLOR_LAYERS( a ) NULL
#endif
#ifdef FT_CONFIG_OPTION_SVG
#define PUT_SVG_SUPPORT( a ) a
#else
#define PUT_SVG_SUPPORT( a ) NULL
#endif
#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
#define PUT_COLOR_LAYERS( a ) a
#else
#define PUT_COLOR_LAYERS( a ) NULL
#endif
#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
#define PUT_PS_NAMES( a ) a
#else
@ -1308,8 +1308,10 @@
PUT_SVG_SUPPORT( tt_face_load_svg ),
/* TT_Load_Table_Func load_svg */
PUT_SVG_SUPPORT( tt_face_free_svg )
PUT_SVG_SUPPORT( tt_face_free_svg ),
/* TT_Free_Table_Func free_svg */
PUT_SVG_SUPPORT( tt_face_load_svg_doc )
/* TT_Load_Svg_Doc_Func load_svg_doc */
)

View File

@ -28,6 +28,8 @@
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_OBJECTS_H
#include FT_TRUETYPE_TAGS_H
#include FT_GZIP_H
#include FT_OTSVG_H
#ifdef FT_CONFIG_OPTION_SVG
@ -121,6 +123,211 @@
}
}
typedef struct Svg_doc_
{
FT_UShort start_glyph_id;
FT_UShort end_glyph_id;
FT_ULong offset;
FT_ULong length;
} Svg_doc;
static Svg_doc
extract_svg_doc( FT_Byte* stream )
{
Svg_doc doc;
doc.start_glyph_id = FT_NEXT_USHORT( stream );
doc.end_glyph_id = FT_NEXT_USHORT( stream );
doc.offset = FT_NEXT_ULONG( stream );
doc.length = FT_NEXT_ULONG( stream );
return doc;
}
static FT_Int
compare_svg_doc( Svg_doc doc,
FT_UInt glyph_index )
{
if ( glyph_index < doc.start_glyph_id )
return -1;
else if ( glyph_index > doc.end_glyph_id )
return 1;
else
return 0;
}
static FT_Error
find_doc( FT_Byte* stream,
FT_UShort num_entries,
FT_UInt glyph_index,
FT_ULong *doc_offset,
FT_ULong *doc_length,
FT_UShort *start_glyph,
FT_UShort *end_glyph )
{
FT_Error error;
Svg_doc start_doc;
Svg_doc mid_doc;
Svg_doc end_doc;
FT_Bool found = FALSE;
FT_UInt i = 0;
FT_UInt start_index = 0;
FT_UInt end_index = num_entries - 1;
FT_Int comp_res;
/* search algo */
if ( num_entries == 0 )
{
error = FT_THROW( Invalid_Table );
return error;
}
FT_TRACE6(( "--- binary search glyph id: %d ---\n", glyph_index ));
start_doc = extract_svg_doc( stream + start_index * 12 );
end_doc = extract_svg_doc( stream + end_index * 12 );
FT_TRACE6(( "--- start glyph ---\n" ));
FT_TRACE6(( "start_id: %d\n", start_doc.start_glyph_id ));
FT_TRACE6(( "end_id: %d\n", start_doc.end_glyph_id ));
FT_TRACE6(( "--- end glyph ---\n" ));
FT_TRACE6(( "start_id: %d\n", end_doc.start_glyph_id ));
FT_TRACE6(( "end_id: %d\n", end_doc.end_glyph_id ));
if ( ( compare_svg_doc( start_doc, glyph_index ) == -1 ) ||
( compare_svg_doc( end_doc, glyph_index ) == 1 ) )
{
error = FT_THROW( Invalid_Glyph_Index );
return error;
}
while ( start_index <= end_index )
{
i = ( start_index + end_index ) / 2;
mid_doc = extract_svg_doc( stream + i * 12 );
FT_TRACE6(( "--- current glyph ---\n" ));
FT_TRACE6(( "start_id: %d\n", mid_doc.start_glyph_id ));
FT_TRACE6(( "end_id: %d\n", mid_doc.end_glyph_id ));
comp_res = compare_svg_doc( mid_doc, glyph_index );
if ( comp_res == 1 )
{
start_index = i + 1;
start_doc = extract_svg_doc( stream + start_index * 4 );
FT_TRACE6(( "RIGHT\n" ));
}
else if ( comp_res == -1 )
{
end_index = i - 1;
end_doc = extract_svg_doc( stream + end_index * 4 );
FT_TRACE6(( "LEFT\n" ));
}
else
{
found = TRUE;
FT_TRACE5(( "FOUND\n" ));
break;
}
}
FT_TRACE5(( "--- binary search end ---\n" ));
/* search algo end */
if ( found != TRUE )
{
FT_TRACE5(( "NOT FOUND\n" ));
error = FT_THROW( Invalid_Glyph_Index );
}
else
{
*doc_offset = mid_doc.offset;
*doc_length = mid_doc.length;
*start_glyph = mid_doc.start_glyph_id;
*end_glyph = mid_doc.end_glyph_id;
error = FT_Err_Ok;
}
return error;
}
FT_LOCAL_DEF( FT_Error )
tt_face_load_svg_doc( FT_GlyphSlot glyph,
FT_UInt glyph_index )
{
FT_Byte* doc_list; /* Pointer to the Svg Document List */
FT_UShort num_entries; /* Total no of entires in doc list */
FT_ULong doc_offset;
FT_ULong doc_length;
FT_UShort start_glyph_id;
FT_UShort end_glyph_id;
FT_ULong uncomp_size;
FT_Byte* uncomp_buffer;
FT_Error error = FT_Err_Ok;
TT_Face face = (TT_Face)glyph->face;
FT_Memory memory = face->root.memory;
Svg* svg = face->svg;
FT_SVG_Document svg_document = glyph->other;
FT_ASSERT( !( svg == NULL ) );
doc_list = svg->svg_doc_list;
num_entries = FT_NEXT_USHORT( doc_list );
error = find_doc( doc_list, num_entries, glyph_index,
&doc_offset, &doc_length,
&start_glyph_id, &end_glyph_id );
if ( error != FT_Err_Ok )
goto Exit;
doc_list = svg->svg_doc_list; /* Reset to so we can use it again */
doc_list = (FT_Byte*)( doc_list + doc_offset );
if( ( doc_list[0] == 0x1F ) && ( doc_list[1] == 0x8B )
&& ( doc_list[2] == 0x08 ) )
{
/* get the size of the orignal document. This helps in alotting the
* buffer to accomodate the uncompressed version. The last 4 bytes
* of the compressed document are equal to orignal_size modulo 2^32.
* Since SVG docs will be lesser in size then 2^32, we can use this
* accurately. The four bytes are stored in little-endian format.
*/
FT_TRACE4(( "SVG document found is GZIP compressed\n" ));
uncomp_size = (FT_ULong)doc_list[doc_length - 1] << 24 |
(FT_ULong)doc_list[doc_length - 2] << 16 |
(FT_ULong)doc_list[doc_length - 3] << 8 |
(FT_ULong)doc_list[doc_length - 4];
uncomp_buffer = (FT_Byte*) memory->alloc( memory, uncomp_size );
error = FT_Gzip_Uncompress( memory, uncomp_buffer, &uncomp_size,
doc_list, doc_length );
if ( error != FT_Err_Ok )
{
memory->free( memory, uncomp_buffer );
error = FT_THROW( Invalid_Table );
goto Exit;
}
glyph->internal->flags |= FT_GLYPH_OWN_GZIP_SVG;
doc_list = uncomp_buffer;
doc_length = uncomp_size;
}
svg_document->svg_document = doc_list;
svg_document->svg_document_length = doc_length;
svg_document->metrics = glyph->face->size->metrics;
svg_document->units_per_EM = glyph->face->units_per_EM;
svg_document->start_glyph_id = start_glyph_id;
svg_document->end_glyph_id = end_glyph_id;
FT_TRACE5(( "start_glyph_id: %d\n", start_glyph_id ));
FT_TRACE5(( "end_glyph_id: %d\n", end_glyph_id ));
FT_TRACE5(( "svg_document:\n%.*s\n", doc_length, doc_list ));
glyph->other = svg_document;
Exit:
return error;
}
#else /* !FT_CONFIG_OPTION_SVG */
/* ANSI C doesn't like empty source files */

View File

@ -29,6 +29,10 @@ FT_BEGIN_HEADER
FT_LOCAL( void )
tt_face_free_svg( TT_Face face );
FT_LOCAL( FT_Error )
tt_face_load_svg_doc( FT_GlyphSlot glyph,
FT_UInt glyph_index );
FT_END_HEADER
#endif /* __TTSVG_H__ */

View File

@ -2907,6 +2907,45 @@
goto Exit;
}
#ifdef FT_CONFIG_OPTION_SVG
/* check for OT-SVG */
if ( ( load_flags & FT_LOAD_COLOR ) && ( ((TT_Face)glyph->face)->svg ) )
{
SFNT_Service sfnt;
FT_Short leftBearing;
FT_Short topBearing;
FT_UShort advanceX;
FT_UShort advanceY;
FT_TRACE3(( "Attemping to load SVG glyph\n" ));
sfnt = (SFNT_Service)((TT_Face)glyph->face)->sfnt;
error = sfnt->load_svg_doc( glyph, glyph_index );
if( error == FT_Err_Ok )
{
FT_TRACE3(( "Successfully loaded SVG glyph\n" ));
glyph->format = FT_GLYPH_FORMAT_SVG;
sfnt->get_metrics( (TT_Face)glyph->face,
FALSE,
glyph_index,
&leftBearing,
&advanceX );
sfnt->get_metrics( (TT_Face)glyph->face,
TRUE,
glyph_index,
&topBearing,
&advanceY );
advanceX *= ((float)glyph->face->size->metrics.x_ppem)/
((float)glyph->face->units_per_EM) * 64.0;
advanceY *= ((float)glyph->face->size->metrics.y_ppem)/
((float)glyph->face->units_per_EM) * 64.0;
glyph->metrics.horiAdvance = advanceX;
glyph->metrics.vertAdvance = advanceY;
return error;
}
FT_TRACE3(( "Failed to load SVG glyph\n" ));
}
#endif
if ( load_flags & FT_LOAD_SBITS_ONLY )
{
error = FT_THROW( Invalid_Argument );