diff --git a/include/freetype/config/ftheader.h b/include/freetype/config/ftheader.h index 696d6ba90..55716c2e5 100644 --- a/include/freetype/config/ftheader.h +++ b/include/freetype/config/ftheader.h @@ -547,6 +547,19 @@ #define FT_BITMAP_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 + + /************************************************************************** * * @macro: diff --git a/include/freetype/ftimage.h b/include/freetype/ftimage.h index face34fe4..86fb04065 100644 --- a/include/freetype/ftimage.h +++ b/include/freetype/ftimage.h @@ -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 /*************************************************************************/ diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h index 0c1d3e5bf..2f2050921 100644 --- a/include/freetype/internal/ftobjs.h +++ b/include/freetype/internal/ftobjs.h @@ -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_ { diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index fbc29d3a2..d09b190aa 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -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". + * + * @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_ \ }; diff --git a/include/freetype/otsvg.h b/include/freetype/otsvg.h new file mode 100644 index 000000000..5569ce4d6 --- /dev/null +++ b/include/freetype/otsvg.h @@ -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 +#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 diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index e301f8f11..31fc23936 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -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 ); diff --git a/src/cff/cffgload.c b/src/cff/cffgload.c index 36aa7d1b9..33733896a 100644 --- a/src/cff/cffgload.c +++ b/src/cff/cffgload.c @@ -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) */ diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c index dec953c4b..8e9a8a005 100644 --- a/src/sfnt/sfdriver.c +++ b/src/sfnt/sfdriver.c @@ -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 */ ) diff --git a/src/sfnt/ttsvg.c b/src/sfnt/ttsvg.c index 546446b6f..211a63960 100644 --- a/src/sfnt/ttsvg.c +++ b/src/sfnt/ttsvg.c @@ -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 */ diff --git a/src/sfnt/ttsvg.h b/src/sfnt/ttsvg.h index 493d72aa0..c54e04832 100644 --- a/src/sfnt/ttsvg.h +++ b/src/sfnt/ttsvg.h @@ -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__ */ diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index 093eed839..a6ec379ed 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -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 );