/**************************************************************************** * * ftsvg.c * * The FreeType SVG renderer interface (body). * * 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. * */ #include #include #include #include #include #include #include "ftsvg.h" #include "svgtypes.h" /************************************************************************** * * 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 otsvg #ifdef FT_CONFIG_OPTION_SVG /* ft_svg_init */ static FT_Error ft_svg_init( SVG_Renderer svg_module ) { FT_Error error = FT_Err_Ok; svg_module->loaded = FALSE; svg_module->hooks_set = FALSE; return error; } static void ft_svg_done( SVG_Renderer svg_module ) { if ( svg_module->loaded == TRUE && svg_module->hooks_set == TRUE ) svg_module->hooks.free_svg( &svg_module->state ); svg_module->loaded = FALSE; } static FT_Error ft_svg_preset_slot( FT_Module module, FT_GlyphSlot slot, FT_Bool cache ) { SVG_Renderer svg_renderer = (SVG_Renderer)module; SVG_RendererHooks hooks = svg_renderer->hooks; if ( svg_renderer->hooks_set == FALSE ) { FT_TRACE1(( "Hooks are NOT set. Can't render OT-SVG glyphs\n" )); return FT_THROW( Missing_SVG_Hooks ); } if ( svg_renderer->loaded == FALSE ) { FT_TRACE3(( "ft_svg_preset_slot: first presetting call," " calling init hook\n" )); hooks.init_svg( &svg_renderer->state ); svg_renderer->loaded = TRUE; } return hooks.preset_slot( slot, cache, &svg_renderer->state ); } static FT_Error ft_svg_render( FT_Renderer renderer, FT_GlyphSlot slot, FT_Render_Mode mode, const FT_Vector* origin ) { SVG_Renderer svg_renderer = (SVG_Renderer)renderer; FT_Library library = renderer->root.library; FT_Memory memory = library->memory; FT_Error error; FT_ULong size_image_buffer; SVG_RendererHooks hooks = svg_renderer->hooks; FT_UNUSED( mode ); FT_UNUSED( origin ); if ( mode != FT_RENDER_MODE_NORMAL ) return FT_THROW( Bad_Argument ); if ( svg_renderer->hooks_set == FALSE ) { FT_TRACE1(( "Hooks are NOT set. Can't render OT-SVG glyphs\n" )); return FT_THROW( Missing_SVG_Hooks ); } if ( svg_renderer->loaded == FALSE ) { FT_TRACE3(( "ft_svg_render: first rendering, calling init hook\n" )); error = hooks.init_svg( &svg_renderer->state ); svg_renderer->loaded = TRUE; } ft_svg_preset_slot( (FT_Module)renderer, slot, TRUE ); size_image_buffer = (FT_ULong)slot->bitmap.pitch * slot->bitmap.rows; /* No `FT_QALLOC` here since we need a clean, empty canvas */ /* to start with. */ if ( FT_ALLOC( slot->bitmap.buffer, size_image_buffer ) ) return error; error = hooks.render_svg( slot, &svg_renderer->state ); if ( error ) FT_FREE( slot->bitmap.buffer ); else slot->internal->flags |= FT_GLYPH_OWN_BITMAP; return error; } static const SVG_Interface svg_interface = { (Preset_Bitmap_Func)ft_svg_preset_slot }; static FT_Error ft_svg_property_set( FT_Module module, const char* property_name, const void* value, FT_Bool value_is_string ) { FT_Error error = FT_Err_Ok; SVG_Renderer renderer = (SVG_Renderer)module; if ( !ft_strcmp( property_name, "svg_hooks" ) ) { SVG_RendererHooks* hooks; if ( value_is_string == TRUE ) return FT_THROW( Invalid_Argument ); hooks = (SVG_RendererHooks*)value; renderer->hooks = *hooks; renderer->hooks_set = TRUE; } else error = FT_THROW( Missing_Property ); return error; } static FT_Error ft_svg_property_get( FT_Module module, const char* property_name, const void* value ) { FT_Error error = FT_Err_Ok; SVG_Renderer renderer = (SVG_Renderer)module; if ( !ft_strcmp( property_name, "svg_hooks" ) ) { SVG_RendererHooks* hooks = (SVG_RendererHooks*)value; *hooks = renderer->hooks; } else error = FT_THROW( Missing_Property ); return error; } FT_DEFINE_SERVICE_PROPERTIESREC( ft_svg_service_properties, (FT_Properties_SetFunc)ft_svg_property_set, /* set_property */ (FT_Properties_GetFunc)ft_svg_property_get /* get_property */ ) FT_DEFINE_SERVICEDESCREC1( ft_svg_services, FT_SERVICE_ID_PROPERTIES, &ft_svg_service_properties ) FT_CALLBACK_DEF( FT_Module_Interface ) ft_svg_get_interface( FT_Module module, const char* ft_svg_interface ) { FT_Module_Interface result; FT_UNUSED( module ); result = ft_service_list_lookup( ft_svg_services, ft_svg_interface ); if ( result ) return result; return 0; } static FT_Error ft_svg_transform( FT_Renderer renderer, FT_GlyphSlot slot, const FT_Matrix* _matrix, const FT_Vector* _delta ) { FT_SVG_Document doc = (FT_SVG_Document)slot->other; FT_Matrix* matrix = (FT_Matrix*)_matrix; FT_Vector* delta = (FT_Vector*)_delta; FT_Matrix tmp_matrix; FT_Vector tmp_delta; FT_Matrix a, b; FT_Pos x, y; FT_UNUSED( renderer ); if ( !matrix ) { tmp_matrix.xx = 0x10000; tmp_matrix.xy = 0; tmp_matrix.yx = 0; tmp_matrix.yy = 0x10000; matrix = &tmp_matrix; } if ( !delta ) { tmp_delta.x = 0; tmp_delta.y = 0; delta = &tmp_delta; } a = doc->transform; b = *matrix; FT_Matrix_Multiply( &b, &a ); x = ADD_LONG( ADD_LONG( FT_MulFix( matrix->xx, doc->delta.x ), FT_MulFix( matrix->xy, doc->delta.y ) ), delta->x ); y = ADD_LONG( ADD_LONG( FT_MulFix( matrix->yx, doc->delta.x ), FT_MulFix( matrix->yy, doc->delta.y ) ), delta->y ); doc->delta.x = x; doc->delta.y = y; doc->transform = a; return FT_Err_Ok; } #endif /* FT_CONFIG_OPTION_SVG */ #ifdef FT_CONFIG_OPTION_SVG #define PUT_SVG_MODULE( a ) a #define SVG_GLYPH_FORMAT FT_GLYPH_FORMAT_SVG #else #define PUT_SVG_MODULE( a ) NULL #define SVG_GLYPH_FORMAT FT_GLYPH_FORMAT_NONE #endif FT_DEFINE_RENDERER( ft_svg_renderer_class, FT_MODULE_RENDERER, sizeof ( SVG_RendererRec ), "ot-svg", 0x10000L, 0x20000L, (const void*)PUT_SVG_MODULE( &svg_interface ), /* module specific interface */ (FT_Module_Constructor)PUT_SVG_MODULE( ft_svg_init ), /* module_init */ (FT_Module_Destructor)PUT_SVG_MODULE( ft_svg_done ), /* module_done */ PUT_SVG_MODULE( ft_svg_get_interface ), /* get_interface */ SVG_GLYPH_FORMAT, (FT_Renderer_RenderFunc) PUT_SVG_MODULE( ft_svg_render ), /* render_glyph */ (FT_Renderer_TransformFunc)PUT_SVG_MODULE( ft_svg_transform ), /* transform_glyph */ NULL, /* get_glyph_cbox */ NULL, /* set_mode */ NULL /* raster_class */ ) /* END */