1 | /**************************************************************************** |
2 | * |
3 | * ftsvg.c |
4 | * |
5 | * The FreeType SVG renderer interface (body). |
6 | * |
7 | * Copyright (C) 2022-2023 by |
8 | * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. |
9 | * |
10 | * This file is part of the FreeType project, and may only be used, |
11 | * modified, and distributed under the terms of the FreeType project |
12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
13 | * this file you indicate that you have read the license and |
14 | * understand and accept it fully. |
15 | * |
16 | */ |
17 | |
18 | #include <freetype/internal/ftdebug.h> |
19 | #include <freetype/internal/ftserv.h> |
20 | #include <freetype/internal/services/svprop.h> |
21 | #include <freetype/otsvg.h> |
22 | #include <freetype/internal/svginterface.h> |
23 | #include <freetype/ftbbox.h> |
24 | |
25 | #include "ftsvg.h" |
26 | #include "svgtypes.h" |
27 | |
28 | |
29 | /************************************************************************** |
30 | * |
31 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
32 | * parameter of the FT_TRACE() and FT_ERROR() macros, usued to print/log |
33 | * messages during execution. |
34 | */ |
35 | #undef FT_COMPONENT |
36 | #define FT_COMPONENT otsvg |
37 | |
38 | |
39 | #ifdef FT_CONFIG_OPTION_SVG |
40 | |
41 | /* ft_svg_init */ |
42 | static FT_Error |
43 | ft_svg_init( FT_Module module ) |
44 | { |
45 | SVG_Renderer render = (SVG_Renderer)module; |
46 | |
47 | FT_Error error = FT_Err_Ok; |
48 | |
49 | |
50 | render->loaded = FALSE; |
51 | render->hooks_set = FALSE; |
52 | |
53 | return error; |
54 | } |
55 | |
56 | |
57 | static void |
58 | ft_svg_done( FT_Module module ) |
59 | { |
60 | SVG_Renderer render = (SVG_Renderer)module; |
61 | |
62 | |
63 | if ( render->loaded == TRUE && |
64 | render->hooks_set == TRUE ) |
65 | render->hooks.free_svg( &render->state ); |
66 | |
67 | render->loaded = FALSE; |
68 | } |
69 | |
70 | |
71 | static FT_Error |
72 | ft_svg_preset_slot( FT_Module module, |
73 | FT_GlyphSlot slot, |
74 | FT_Bool cache ) |
75 | { |
76 | SVG_Renderer svg_renderer = (SVG_Renderer)module; |
77 | SVG_RendererHooks hooks = svg_renderer->hooks; |
78 | |
79 | |
80 | if ( svg_renderer->hooks_set == FALSE ) |
81 | { |
82 | FT_TRACE1(( "Hooks are NOT set. Can't render OT-SVG glyphs\n" )); |
83 | return FT_THROW( Missing_SVG_Hooks ); |
84 | } |
85 | |
86 | if ( svg_renderer->loaded == FALSE ) |
87 | { |
88 | FT_TRACE3(( "ft_svg_preset_slot: first presetting call," |
89 | " calling init hook\n" )); |
90 | hooks.init_svg( &svg_renderer->state ); |
91 | |
92 | svg_renderer->loaded = TRUE; |
93 | } |
94 | |
95 | return hooks.preset_slot( slot, cache, &svg_renderer->state ); |
96 | } |
97 | |
98 | |
99 | static FT_Error |
100 | ft_svg_render( FT_Renderer renderer, |
101 | FT_GlyphSlot slot, |
102 | FT_Render_Mode mode, |
103 | const FT_Vector* origin ) |
104 | { |
105 | SVG_Renderer svg_renderer = (SVG_Renderer)renderer; |
106 | |
107 | FT_Library library = renderer->root.library; |
108 | FT_Memory memory = library->memory; |
109 | FT_Error error; |
110 | |
111 | FT_ULong size_image_buffer; |
112 | |
113 | SVG_RendererHooks hooks = svg_renderer->hooks; |
114 | |
115 | |
116 | FT_UNUSED( mode ); |
117 | FT_UNUSED( origin ); |
118 | |
119 | if ( mode != FT_RENDER_MODE_NORMAL ) |
120 | return FT_THROW( Bad_Argument ); |
121 | |
122 | if ( svg_renderer->hooks_set == FALSE ) |
123 | { |
124 | FT_TRACE1(( "Hooks are NOT set. Can't render OT-SVG glyphs\n" )); |
125 | return FT_THROW( Missing_SVG_Hooks ); |
126 | } |
127 | |
128 | if ( svg_renderer->loaded == FALSE ) |
129 | { |
130 | FT_TRACE3(( "ft_svg_render: first rendering, calling init hook\n" )); |
131 | error = hooks.init_svg( &svg_renderer->state ); |
132 | |
133 | svg_renderer->loaded = TRUE; |
134 | } |
135 | |
136 | ft_svg_preset_slot( (FT_Module)renderer, slot, TRUE ); |
137 | |
138 | size_image_buffer = (FT_ULong)slot->bitmap.pitch * slot->bitmap.rows; |
139 | /* No `FT_QALLOC` here since we need a clean, empty canvas */ |
140 | /* to start with. */ |
141 | if ( FT_ALLOC( slot->bitmap.buffer, size_image_buffer ) ) |
142 | return error; |
143 | |
144 | error = hooks.render_svg( slot, &svg_renderer->state ); |
145 | if ( error ) |
146 | FT_FREE( slot->bitmap.buffer ); |
147 | else |
148 | slot->internal->flags |= FT_GLYPH_OWN_BITMAP; |
149 | |
150 | return error; |
151 | } |
152 | |
153 | |
154 | static const SVG_Interface svg_interface = |
155 | { |
156 | ft_svg_preset_slot /* Preset_Bitmap_Func preset_slot */ |
157 | }; |
158 | |
159 | |
160 | static FT_Error |
161 | ft_svg_property_set( FT_Module module, |
162 | const char* property_name, |
163 | const void* value, |
164 | FT_Bool value_is_string ) |
165 | { |
166 | FT_Error error = FT_Err_Ok; |
167 | SVG_Renderer renderer = (SVG_Renderer)module; |
168 | |
169 | |
170 | if ( !ft_strcmp( property_name, "svg-hooks" ) ) |
171 | { |
172 | SVG_RendererHooks* hooks; |
173 | |
174 | |
175 | if ( value_is_string == TRUE ) |
176 | { |
177 | error = FT_THROW( Invalid_Argument ); |
178 | goto Exit; |
179 | } |
180 | |
181 | hooks = (SVG_RendererHooks*)value; |
182 | |
183 | if ( !hooks->init_svg || |
184 | !hooks->free_svg || |
185 | !hooks->render_svg || |
186 | !hooks->preset_slot ) |
187 | { |
188 | FT_TRACE0(( "ft_svg_property_set:" |
189 | " SVG rendering hooks not set because\n" )); |
190 | FT_TRACE0(( " " |
191 | " at least one function pointer is NULL\n" )); |
192 | |
193 | error = FT_THROW( Invalid_Argument ); |
194 | goto Exit; |
195 | } |
196 | |
197 | renderer->hooks = *hooks; |
198 | renderer->hooks_set = TRUE; |
199 | } |
200 | else |
201 | error = FT_THROW( Missing_Property ); |
202 | |
203 | Exit: |
204 | return error; |
205 | } |
206 | |
207 | |
208 | static FT_Error |
209 | ft_svg_property_get( FT_Module module, |
210 | const char* property_name, |
211 | void* value ) |
212 | { |
213 | FT_Error error = FT_Err_Ok; |
214 | SVG_Renderer renderer = (SVG_Renderer)module; |
215 | |
216 | |
217 | if ( !ft_strcmp( property_name, "svg-hooks" ) ) |
218 | { |
219 | SVG_RendererHooks* hooks = (SVG_RendererHooks*)value; |
220 | |
221 | |
222 | *hooks = renderer->hooks; |
223 | } |
224 | else |
225 | error = FT_THROW( Missing_Property ); |
226 | |
227 | return error; |
228 | } |
229 | |
230 | |
231 | FT_DEFINE_SERVICE_PROPERTIESREC( |
232 | ft_svg_service_properties, |
233 | |
234 | ft_svg_property_set, /* FT_Properties_SetFunc set_property */ |
235 | ft_svg_property_get /* FT_Properties_GetFunc get_property */ |
236 | ) |
237 | |
238 | |
239 | FT_DEFINE_SERVICEDESCREC1( |
240 | ft_svg_services, |
241 | FT_SERVICE_ID_PROPERTIES, &ft_svg_service_properties ) |
242 | |
243 | |
244 | FT_CALLBACK_DEF( FT_Module_Interface ) |
245 | ft_svg_get_interface( FT_Module module, |
246 | const char* ft_svg_interface ) |
247 | { |
248 | FT_Module_Interface result; |
249 | |
250 | |
251 | FT_UNUSED( module ); |
252 | |
253 | result = ft_service_list_lookup( ft_svg_services, ft_svg_interface ); |
254 | if ( result ) |
255 | return result; |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | |
261 | static FT_Error |
262 | ft_svg_transform( FT_Renderer renderer, |
263 | FT_GlyphSlot slot, |
264 | const FT_Matrix* _matrix, |
265 | const FT_Vector* _delta ) |
266 | { |
267 | FT_SVG_Document doc = (FT_SVG_Document)slot->other; |
268 | FT_Matrix* matrix = (FT_Matrix*)_matrix; |
269 | FT_Vector* delta = (FT_Vector*)_delta; |
270 | |
271 | FT_Matrix tmp_matrix; |
272 | FT_Vector tmp_delta; |
273 | |
274 | FT_Matrix a, b; |
275 | FT_Pos x, y; |
276 | |
277 | |
278 | FT_UNUSED( renderer ); |
279 | |
280 | if ( !matrix ) |
281 | { |
282 | tmp_matrix.xx = 0x10000; |
283 | tmp_matrix.xy = 0; |
284 | tmp_matrix.yx = 0; |
285 | tmp_matrix.yy = 0x10000; |
286 | |
287 | matrix = &tmp_matrix; |
288 | } |
289 | |
290 | if ( !delta ) |
291 | { |
292 | tmp_delta.x = 0; |
293 | tmp_delta.y = 0; |
294 | |
295 | delta = &tmp_delta; |
296 | } |
297 | |
298 | a = doc->transform; |
299 | b = *matrix; |
300 | FT_Matrix_Multiply( &b, &a ); |
301 | |
302 | |
303 | x = ADD_LONG( ADD_LONG( FT_MulFix( matrix->xx, doc->delta.x ), |
304 | FT_MulFix( matrix->xy, doc->delta.y ) ), |
305 | delta->x ); |
306 | y = ADD_LONG( ADD_LONG( FT_MulFix( matrix->yx, doc->delta.x ), |
307 | FT_MulFix( matrix->yy, doc->delta.y ) ), |
308 | delta->y ); |
309 | |
310 | doc->delta.x = x; |
311 | doc->delta.y = y; |
312 | doc->transform = a; |
313 | |
314 | return FT_Err_Ok; |
315 | } |
316 | |
317 | #endif /* FT_CONFIG_OPTION_SVG */ |
318 | |
319 | |
320 | #ifdef FT_CONFIG_OPTION_SVG |
321 | #define PUT_SVG_MODULE( a ) a |
322 | #define SVG_GLYPH_FORMAT FT_GLYPH_FORMAT_SVG |
323 | #else |
324 | #define PUT_SVG_MODULE( a ) NULL |
325 | #define SVG_GLYPH_FORMAT FT_GLYPH_FORMAT_NONE |
326 | #endif |
327 | |
328 | |
329 | FT_DEFINE_RENDERER( |
330 | ft_svg_renderer_class, |
331 | |
332 | FT_MODULE_RENDERER, |
333 | sizeof ( SVG_RendererRec ), |
334 | |
335 | "ot-svg" , |
336 | 0x10000L, |
337 | 0x20000L, |
338 | |
339 | (const void*)PUT_SVG_MODULE( &svg_interface ), /* module specific interface */ |
340 | |
341 | PUT_SVG_MODULE( ft_svg_init ), /* FT_Module_Constructor module_init */ |
342 | PUT_SVG_MODULE( ft_svg_done ), /* FT_Module_Destructor module_done */ |
343 | PUT_SVG_MODULE( ft_svg_get_interface ), /* FT_Module_Requester get_interface */ |
344 | |
345 | SVG_GLYPH_FORMAT, |
346 | |
347 | PUT_SVG_MODULE( ft_svg_render ), /* FT_Renderer_RenderFunc render_glyph */ |
348 | PUT_SVG_MODULE( ft_svg_transform ), /* FT_Renderer_TransformFunc transform_glyph */ |
349 | NULL, /* FT_Renderer_GetCBoxFunc get_glyph_cbox */ |
350 | NULL, /* FT_Renderer_SetModeFunc set_mode */ |
351 | NULL /* FT_Raster_Funcs* raster_class */ |
352 | ) |
353 | |
354 | |
355 | /* END */ |
356 | |