* include/freetype/config/ftheader.h (FT_OTSVG_H): New macro. * include/freetype/freetype.h (FT_FACE_FLAG_SVG, FT_HAS_SVG): New macros. (FT_LOAD_SVG_ONLY): New internal macro. * include/freetype/ftimage.h (FT_Glyph_Format): New enumeration value `FT_GLYPH_FORMAT_SVG`. * include/freetype/internal/ftobjs.h (FT_GLYPH_OWN_GZIP_SVG): New macro. * include/freetype/internal/fttrace.h: Add `ttsvg` for `ttsvg.c`. * include/freetype/internal/sfnt.h(load_svg, free_svg, load_svg_doc): New functions. * include/freetype/internal/tttypes.h (TT_FaceRec): Add `svg` for the SVG table. * include/freetype/otsvg.h (FT_SVG_DocumentRec): New structure to hold the SVG document and other necessary information of an OT-SVG glyph in a glyph slot. * include/freetype/tttags.h (TTAG_SVG): New macro. * src/base/ftobjs.c: Include `otsvg.h`. (ft_glyphslot_init): Allocate `FT_SVG_DocumentRec` in `slot->other` if the SVG table exists. (ft_glyphslot_clear): Free it upon clean-up if it is a GZIP compressed glyph. (ft_glyphslot_done): Free the document data if it is a GZIP compressed glyph. (FT_Load_Glyph): Don't auto-hint SVG documents. * src/cache/ftcbasic.c (ftc_basic_family_load_glyph): Add support for FT_GLYPH_FORMAT_SVG. * src/sfnt/rules.mk (SFNT_DRV_SRC): Add `ttsvg.c`. * src/sfnt/sfdriver.c: Include `ttsvg.h`. (sfnt_interface): Add `tt_face_load_svg`, `tt_face_free_svg` and `tt_face_load_svg_doc`. * src/sfnt/sfnt.c: Include `ttsvg.c`. * src/sfnt/sfobjs.c (sfnt_load_face, sfnt_done_face): Add code to load and free data of the the SVG table. * src/sfnt/ttsvg.c: New file, implementing `tt_face_load_svg`, `tt_face_free_svg` and `tt_face_load_svg_doc`. * src/sfnt/ttsvg.h: Declarations of the SVG functions in `ttsvg.c`.fix-sdf-squared-distances
parent
06c1a25e63
commit
f93a897afe
18 changed files with 741 additions and 24 deletions
@ -0,0 +1,114 @@ |
||||
/****************************************************************************
|
||||
* |
||||
* otsvg.h |
||||
* |
||||
* Interface for OT-SVG support related things (specification). |
||||
* |
||||
* Copyright (C) 2022 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 OTSVG_H_ |
||||
#define OTSVG_H_ |
||||
|
||||
#include <freetype/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. |
||||
* |
||||
* svg_document_length :: |
||||
* The length of `svg_document`. |
||||
* |
||||
* metrics :: |
||||
* A metrics object storing the size information. |
||||
* |
||||
* units_per_EM :: |
||||
* The size of the EM square. |
||||
* |
||||
* start_glyph_id :: |
||||
* The first glyph ID in the glyph range covered by this document. |
||||
* |
||||
* end_glyph_id :: |
||||
* The last glyph ID in the glyph range covered by this document. |
||||
* |
||||
* transform :: |
||||
* A 2x2 transformation matrix to apply to the glyph while rendering |
||||
* it. |
||||
* |
||||
* delta :: |
||||
* The translation to apply to the glyph while rendering. |
||||
* |
||||
* @note: |
||||
* When the slot is passed down to a renderer, the renderer can only |
||||
* access the `metrics` and `units_per_EM` fields via `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. |
||||
* |
||||
* @since: |
||||
* 2.12 |
||||
*/ |
||||
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_Matrix transform; |
||||
FT_Vector delta; |
||||
|
||||
} FT_SVG_DocumentRec; |
||||
|
||||
|
||||
/**************************************************************************
|
||||
* |
||||
* @type: |
||||
* FT_SVG_Document |
||||
* |
||||
* @description: |
||||
* A handle to an @FT_SVG_DocumentRec object. |
||||
* |
||||
* @since: |
||||
* 2.12 |
||||
*/ |
||||
typedef struct FT_SVG_DocumentRec_* FT_SVG_Document; |
||||
|
||||
|
||||
FT_END_HEADER |
||||
|
||||
#endif /* OTSVG_H_ */ |
||||
|
||||
|
||||
/* END */ |
@ -0,0 +1,386 @@ |
||||
/****************************************************************************
|
||||
* |
||||
* ttsvg.c |
||||
* |
||||
* OpenType SVG Color (specification). |
||||
* |
||||
* Copyright (C) 2022 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. |
||||
* |
||||
*/ |
||||
|
||||
|
||||
/**************************************************************************
|
||||
* |
||||
* 'SVG' table specification: |
||||
* |
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/svg
|
||||
* |
||||
*/ |
||||
|
||||
#include <ft2build.h> |
||||
#include <freetype/internal/ftstream.h> |
||||
#include <freetype/internal/ftobjs.h> |
||||
#include <freetype/internal/ftdebug.h> |
||||
#include <freetype/tttags.h> |
||||
#include <freetype/ftgzip.h> |
||||
#include <freetype/otsvg.h> |
||||
|
||||
|
||||
#ifdef FT_CONFIG_OPTION_SVG |
||||
|
||||
#include "ttsvg.h" |
||||
|
||||
|
||||
typedef struct Svg_ |
||||
{ |
||||
FT_UShort version; /* table version (starting at 0) */ |
||||
FT_UShort num_entries; /* number of SVG document records */ |
||||
|
||||
FT_Byte* svg_doc_list; /* pointer to the start of SVG Document List */ |
||||
|
||||
void* table; /* memory that backs up SVG */ |
||||
FT_ULong table_size; |
||||
|
||||
} Svg; |
||||
|
||||
|
||||
/**************************************************************************
|
||||
* |
||||
* The macro FT_COMPONENT is used in trace mode. It is an implicit |
||||
* parameter of the FT_TRACE() and FT_ERROR() macros, usued to print/log |
||||
* messages during execution. |
||||
*/ |
||||
#undef FT_COMPONENT |
||||
#define FT_COMPONENT ttsvg |
||||
|
||||
|
||||
FT_LOCAL_DEF( FT_Error ) |
||||
tt_face_load_svg( TT_Face face, |
||||
FT_Stream stream ) |
||||
{ |
||||
FT_Error error; |
||||
FT_Memory memory = face->root.memory; |
||||
|
||||
FT_ULong table_size; |
||||
FT_Byte* table = NULL; |
||||
FT_Byte* p = NULL; |
||||
Svg* svg = NULL; |
||||
FT_ULong offsetToSVGDocumentList; |
||||
|
||||
|
||||
error = face->goto_table( face, TTAG_SVG, stream, &table_size ); |
||||
if ( error ) |
||||
goto NoSVG; |
||||
|
||||
if ( FT_FRAME_EXTRACT( table_size, table ) ) |
||||
goto NoSVG; |
||||
|
||||
/* Allocate memory for the SVG object */ |
||||
if ( FT_NEW( svg ) ) |
||||
goto NoSVG; |
||||
|
||||
p = table; |
||||
svg->version = FT_NEXT_USHORT( p ); |
||||
offsetToSVGDocumentList = FT_NEXT_ULONG( p ); |
||||
|
||||
if ( offsetToSVGDocumentList == 0 ) |
||||
goto InvalidTable; |
||||
|
||||
svg->svg_doc_list = (FT_Byte*)( table + offsetToSVGDocumentList ); |
||||
|
||||
p = svg->svg_doc_list; |
||||
svg->num_entries = FT_NEXT_USHORT( p ); |
||||
|
||||
FT_TRACE3(( "version: %d\n", svg->version )); |
||||
FT_TRACE3(( "number of entries: %d\n", svg->num_entries )); |
||||
|
||||
svg->table = table; |
||||
svg->table_size = table_size; |
||||
|
||||
face->svg = svg; |
||||
face->root.face_flags |= FT_FACE_FLAG_SVG; |
||||
|
||||
return FT_Err_Ok; |
||||
|
||||
InvalidTable: |
||||
error = FT_THROW( Invalid_Table ); |
||||
|
||||
NoSVG: |
||||
FT_FRAME_RELEASE( table ); |
||||
FT_FREE( svg ); |
||||
face->svg = NULL; |
||||
|
||||
return error; |
||||
} |
||||
|
||||
|
||||
FT_LOCAL_DEF( void ) |
||||
tt_face_free_svg( TT_Face face ) |
||||
{ |
||||
FT_Memory memory = face->root.memory; |
||||
FT_Stream stream = face->root.stream; |
||||
|
||||
Svg* svg = (Svg*)face->svg; |
||||
|
||||
|
||||
if ( svg ) |
||||
{ |
||||
FT_FRAME_RELEASE( svg->table ); |
||||
FT_FREE( svg ); |
||||
} |
||||
} |
||||
|
||||
|
||||
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 algorithm */ |
||||
if ( num_entries == 0 ) |
||||
{ |
||||
error = FT_THROW( Invalid_Table ); |
||||
return error; |
||||
} |
||||
|
||||
start_doc = extract_svg_doc( stream + start_index * 12 ); |
||||
end_doc = extract_svg_doc( stream + end_index * 12 ); |
||||
|
||||
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 ); |
||||
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 ); |
||||
} |
||||
else if ( comp_res == -1 ) |
||||
{ |
||||
end_index = i - 1; |
||||
end_doc = extract_svg_doc( stream + end_index * 4 ); |
||||
} |
||||
else |
||||
{ |
||||
found = TRUE; |
||||
break; |
||||
} |
||||
} |
||||
/* search algorithm end */ |
||||
|
||||
if ( found != TRUE ) |
||||
{ |
||||
FT_TRACE5(( "SVG glyph 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 doc list */ |
||||
FT_UShort num_entries; /* total number of entries in doc list */ |
||||
FT_ULong doc_offset; |
||||
FT_ULong doc_length; |
||||
|
||||
FT_UShort start_glyph_id; |
||||
FT_UShort end_glyph_id; |
||||
|
||||
FT_Error error = FT_Err_Ok; |
||||
TT_Face face = (TT_Face)glyph->face; |
||||
FT_Memory memory = face->root.memory; |
||||
Svg* svg = (Svg*)face->svg; |
||||
|
||||
FT_SVG_Document svg_document = (FT_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, 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 ) ) |
||||
{ |
||||
#ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB |
||||
|
||||
FT_ULong uncomp_size; |
||||
FT_Byte* uncomp_buffer; |
||||
|
||||
|
||||
/*
|
||||
* Get the size of the original document. This helps in allotting the |
||||
* buffer to accommodate the uncompressed version. The last 4 bytes |
||||
* of the compressed document are equal to the original size modulo |
||||
* 2^32. Since the size of SVG documents is less than 2^32 bytes we |
||||
* can use this accurately. The four bytes are stored in |
||||
* little-endian format. |
||||
*/ |
||||
FT_TRACE4(( "SVG document 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]; |
||||
|
||||
if ( FT_QALLOC( uncomp_buffer, uncomp_size ) ) |
||||
goto Exit; |
||||
|
||||
error = FT_Gzip_Uncompress( memory, |
||||
uncomp_buffer, |
||||
&uncomp_size, |
||||
doc_list, |
||||
doc_length ); |
||||
if ( error ) |
||||
{ |
||||
FT_FREE( 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; |
||||
|
||||
#else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */ |
||||
|
||||
error = FT_THROW( Unimplemented_Feature ); |
||||
goto Exit; |
||||
|
||||
#endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */ |
||||
} |
||||
|
||||
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; |
||||
|
||||
svg_document->transform.xx = 0x10000; |
||||
svg_document->transform.xy = 0; |
||||
svg_document->transform.yx = 0; |
||||
svg_document->transform.yy = 0x10000; |
||||
|
||||
svg_document->delta.x = 0; |
||||
svg_document->delta.y = 0; |
||||
|
||||
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" )); |
||||
FT_TRACE5(( " %.*s\n", (FT_UInt)doc_length, doc_list )); |
||||
|
||||
glyph->other = svg_document; |
||||
|
||||
Exit: |
||||
return error; |
||||
} |
||||
|
||||
#else /* !FT_CONFIG_OPTION_SVG */ |
||||
|
||||
/* ANSI C doesn't like empty source files */ |
||||
typedef int _tt_svg_dummy; |
||||
|
||||
#endif /* !FT_CONFIG_OPTION_SVG */ |
||||
|
||||
|
||||
/* END */ |
@ -0,0 +1,43 @@ |
||||
/****************************************************************************
|
||||
* |
||||
* ttsvg.h |
||||
* |
||||
* OpenType SVG Color (specification). |
||||
* |
||||
* Copyright (C) 2022 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 TTSVG_H_ |
||||
#define TTSVG_H_ |
||||
|
||||
#include <freetype/internal/ftstream.h> |
||||
#include <freetype/internal/tttypes.h> |
||||
|
||||
|
||||
FT_BEGIN_HEADER |
||||
|
||||
FT_LOCAL( FT_Error ) |
||||
tt_face_load_svg( TT_Face face, |
||||
FT_Stream stream ); |
||||
|
||||
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_ */ |
||||
|
||||
|
||||
/* END */ |
Loading…
Reference in new issue