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