... |
... |
@@ -2,11 +2,11 @@ |
2
|
2
|
*
|
3
|
3
|
* rsvg-port.c
|
4
|
4
|
*
|
5
|
|
- * Librsvg based hook functions for OT-SVG rendering in FreeType.
|
6
|
|
- * (implementation)
|
|
5
|
+ * Librsvg-based hook functions for OT-SVG rendering in FreeType
|
|
6
|
+ * (implementation).
|
7
|
7
|
*
|
8
|
|
- * Copyright (C) 1996-2021 by
|
9
|
|
- * David Turner, Robert Wilhelm, Werner Lemberg and Moazin Khatti.
|
|
8
|
+ * Copyright (C) 2021 by
|
|
9
|
+ * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti.
|
10
|
10
|
*
|
11
|
11
|
* This file is part of the FreeType project, and may only be used,
|
12
|
12
|
* modified, and distributed under the terms of the FreeType project
|
... |
... |
@@ -18,19 +18,21 @@ |
18
|
18
|
|
19
|
19
|
#include <cairo.h>
|
20
|
20
|
#include <librsvg/rsvg.h>
|
21
|
|
-#include <rsvg-port.h>
|
22
|
|
-#include <ft2build.h>
|
23
|
21
|
#include <stdlib.h>
|
24
|
22
|
#include <math.h>
|
|
23
|
+
|
|
24
|
+#include <ft2build.h>
|
25
|
25
|
#include <freetype/freetype.h>
|
26
|
26
|
#include <freetype/ftbbox.h>
|
27
|
27
|
#include <freetype/otsvg.h>
|
28
|
28
|
|
|
29
|
+#include "rsvg-port.h"
|
29
|
30
|
|
30
|
|
- /**
|
31
|
|
- * The init hook is called when the first OT-SVG glyph is rendered.
|
32
|
|
- * All we do is allocate an internal state structure and set the pointer
|
33
|
|
- * in `library->svg_renderer_state`. This state structure becomes very
|
|
31
|
+
|
|
32
|
+ /*
|
|
33
|
+ * The init hook is called when the first OT-SVG glyph is rendered. All
|
|
34
|
+ * we do is to allocate an internal state structure and set the pointer in
|
|
35
|
+ * `library->svg_renderer_state`. This state structure becomes very
|
34
|
36
|
* useful to cache some of the results obtained by one hook function that
|
35
|
37
|
* the other one might use.
|
36
|
38
|
*/
|
... |
... |
@@ -38,48 +40,49 @@ |
38
|
40
|
rsvg_port_init( FT_Pointer *state )
|
39
|
41
|
{
|
40
|
42
|
/* allocate the memory upon initialization */
|
41
|
|
- *state = malloc( sizeof( Rsvg_Port_StateRec ) );
|
|
43
|
+ *state = malloc( sizeof( Rsvg_Port_StateRec ) ); /* XXX error handling */
|
|
44
|
+
|
42
|
45
|
return FT_Err_Ok;
|
43
|
46
|
}
|
44
|
47
|
|
45
|
48
|
|
46
|
|
- /**
|
47
|
|
- * Freeing up the state structure.
|
|
49
|
+ /*
|
|
50
|
+ * Deallocate the state structure.
|
48
|
51
|
*/
|
49
|
52
|
void
|
50
|
|
- rsvg_port_free( FT_Pointer *state)
|
|
53
|
+ rsvg_port_free( FT_Pointer *state )
|
51
|
54
|
{
|
52
|
|
- /* free the memory of the state structure */
|
53
|
55
|
free( *state );
|
54
|
56
|
}
|
55
|
57
|
|
56
|
|
- /**
|
57
|
|
- * The render hook that's called to render. The job of this hook is to
|
58
|
|
- * simply render the glyph in the buffer that has been allocated on
|
59
|
|
- * the FreeType side. Here we simply use the recording surface by playing
|
60
|
|
- * it back against the surface.
|
|
58
|
+
|
|
59
|
+ /*
|
|
60
|
+ * The render hook. The job of this hook is to simply render the glyph in
|
|
61
|
+ * the buffer that has been allocated on the FreeType side. Here we
|
|
62
|
+ * simply use the recording surface by playing it back against the
|
|
63
|
+ * surface.
|
61
|
64
|
*/
|
62
|
65
|
FT_Error
|
63
|
|
- rsvg_port_render( FT_GlyphSlot slot,
|
64
|
|
- FT_Pointer * _state )
|
|
66
|
+ rsvg_port_render( FT_GlyphSlot slot,
|
|
67
|
+ FT_Pointer *_state )
|
65
|
68
|
{
|
66
|
|
- FT_Error error = FT_Err_Ok;
|
|
69
|
+ FT_Error error = FT_Err_Ok;
|
|
70
|
+
|
|
71
|
+ Rsvg_Port_State state;
|
|
72
|
+ cairo_status_t status;
|
|
73
|
+ cairo_t *cr;
|
|
74
|
+ cairo_surface_t *surface;
|
67
|
75
|
|
68
|
|
- Rsvg_Port_State state;
|
69
|
|
- cairo_status_t status;
|
70
|
|
- cairo_t *cr;
|
71
|
|
- cairo_surface_t *surface;
|
72
|
76
|
|
73
|
77
|
state = *(Rsvg_Port_State*)_state;
|
74
|
78
|
|
75
|
|
- /* create an image surface to store the rendered image, however,
|
76
|
|
- * don't allocate memory, instead use the space already provided
|
77
|
|
- * in `slot->bitmap.buffer'.
|
78
|
|
- */
|
|
79
|
+ /* Create an image surface to store the rendered image. However, */
|
|
80
|
+ /* don't allocate memory; instead use the space already provided in */
|
|
81
|
+ /* `slot->bitmap.buffer`. */
|
79
|
82
|
surface = cairo_image_surface_create_for_data( slot->bitmap.buffer,
|
80
|
83
|
CAIRO_FORMAT_ARGB32,
|
81
|
|
- slot->bitmap.width,
|
82
|
|
- slot->bitmap.rows,
|
|
84
|
+ (int)slot->bitmap.width,
|
|
85
|
+ (int)slot->bitmap.rows,
|
83
|
86
|
slot->bitmap.pitch );
|
84
|
87
|
status = cairo_surface_status( surface );
|
85
|
88
|
|
... |
... |
@@ -102,12 +105,13 @@ |
102
|
105
|
return FT_Err_Invalid_Outline;
|
103
|
106
|
}
|
104
|
107
|
|
105
|
|
- /* set a translate transform that translates the points in such
|
106
|
|
- * a way that we get a tight rendering with least redundant white
|
107
|
|
- * space */
|
108
|
|
- cairo_translate( cr, -1 * (state->x), -1 * (state->y) );
|
109
|
|
- /* replay from the recorded surface. Saves us from parsing the document
|
110
|
|
- * again and redoing this already done in the preset hook. */
|
|
108
|
+ /* Set a translate transform that translates the points in such a way */
|
|
109
|
+ /* that we get a tight rendering with least redundant white spac. */
|
|
110
|
+ cairo_translate( cr, -state->x, -state->y );
|
|
111
|
+
|
|
112
|
+ /* Replay from the recorded surface. This saves us from parsing the */
|
|
113
|
+ /* document again and redoing what was already done in the preset */
|
|
114
|
+ /* hook. */
|
111
|
115
|
cairo_set_source_surface( cr, state->rec_surface, 0.0, 0.0 );
|
112
|
116
|
cairo_paint( cr );
|
113
|
117
|
|
... |
... |
@@ -115,87 +119,88 @@ |
115
|
119
|
|
116
|
120
|
slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
117
|
121
|
slot->bitmap.num_grays = 256;
|
118
|
|
- slot->format = FT_GLYPH_FORMAT_BITMAP;
|
|
122
|
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
|
119
|
123
|
|
120
|
|
- /* clean everything */
|
|
124
|
+ /* Clean up everything. */
|
121
|
125
|
cairo_surface_destroy( surface );
|
122
|
126
|
cairo_destroy( cr );
|
123
|
127
|
cairo_surface_destroy( state->rec_surface );
|
|
128
|
+
|
124
|
129
|
return error;
|
125
|
130
|
}
|
126
|
131
|
|
127
|
|
- /**
|
|
132
|
+
|
|
133
|
+ /*
|
128
|
134
|
* This hook is called at two different locations. Firstly, it is called
|
129
|
|
- * when presetting the glyphslot when FT_Load_Glyph is called. Secondly,
|
130
|
|
- * it is called right before the render hook is called. When `cache` is
|
131
|
|
- * false, it's the former while when `cache` is true, it's the latter.
|
|
135
|
+ * when presetting the glyphslot when `FT_Load_Glyph` is called.
|
|
136
|
+ * Secondly, it is called right before the render hook is called. When
|
|
137
|
+ * `cache` is false, it is the former, when `cache` is true, it is the
|
|
138
|
+ * latter.
|
132
|
139
|
*
|
133
|
|
- * The job of this function is to preset the slot setting the width, height,
|
134
|
|
- * pitch, bitmap.left and bitmap.top. These are all necessary for appropriate
|
135
|
|
- * memory allocation as well as ultimately compositing the glyph later on by
|
136
|
|
- * client applications.
|
|
140
|
+ * The job of this function is to preset the slot setting the width,
|
|
141
|
+ * height, pitch, `bitmap.left`, and `bitmap.top`. These are all
|
|
142
|
+ * necessary for appropriate memory allocation, as well as ultimately
|
|
143
|
+ * compositing the glyph later on by client applications.
|
137
|
144
|
*/
|
138
|
145
|
FT_Error
|
139
|
|
- rsvg_port_preset_slot( FT_GlyphSlot slot, FT_Bool cache, FT_Pointer *_state )
|
|
146
|
+ rsvg_port_preset_slot( FT_GlyphSlot slot,
|
|
147
|
+ FT_Bool cache,
|
|
148
|
+ FT_Pointer *_state )
|
140
|
149
|
{
|
141
|
|
- /* FreeType variables */
|
142
|
|
- FT_Error error = FT_Err_Ok;
|
143
|
|
- FT_SVG_Document document = (FT_SVG_Document)slot->other;
|
144
|
|
- FT_Size_Metrics metrics = document->metrics;
|
145
|
|
- FT_UShort units_per_EM = document->units_per_EM;
|
146
|
|
- FT_UShort end_glyph_id = document->end_glyph_id;
|
147
|
|
- FT_UShort start_glyph_id = document->start_glyph_id;
|
148
|
|
-
|
149
|
|
- /* Librsvg variables */
|
150
|
|
- GError *gerror = NULL;
|
151
|
|
- gboolean ret;
|
152
|
|
- gboolean out_has_width;
|
153
|
|
- gboolean out_has_height;
|
154
|
|
- gboolean out_has_viewbox;
|
|
150
|
+ /* FreeType variables. */
|
|
151
|
+ FT_Error error = FT_Err_Ok;
|
|
152
|
+
|
|
153
|
+ FT_SVG_Document document = (FT_SVG_Document)slot->other;
|
|
154
|
+ FT_Size_Metrics metrics = document->metrics;
|
|
155
|
+
|
|
156
|
+ FT_UShort units_per_EM = document->units_per_EM;
|
|
157
|
+ FT_UShort end_glyph_id = document->end_glyph_id;
|
|
158
|
+ FT_UShort start_glyph_id = document->start_glyph_id;
|
|
159
|
+
|
|
160
|
+ /* Librsvg variables. */
|
|
161
|
+ GError *gerror = NULL;
|
|
162
|
+ gboolean ret;
|
|
163
|
+
|
|
164
|
+ gboolean out_has_width;
|
|
165
|
+ gboolean out_has_height;
|
|
166
|
+ gboolean out_has_viewbox;
|
|
167
|
+
|
155
|
168
|
RsvgHandle *handle;
|
156
|
169
|
RsvgLength out_width;
|
157
|
170
|
RsvgLength out_height;
|
158
|
171
|
RsvgRectangle out_viewbox;
|
159
|
172
|
RsvgDimensionData dimension_svg;
|
160
|
|
- cairo_t *rec_cr;
|
161
|
|
- cairo_matrix_t transform_matrix;
|
162
|
173
|
|
163
|
|
- /* Rendering port's state */
|
|
174
|
+ cairo_t *rec_cr;
|
|
175
|
+ cairo_matrix_t transform_matrix;
|
|
176
|
+
|
|
177
|
+ /* Rendering port's state. */
|
164
|
178
|
Rsvg_Port_State state;
|
165
|
179
|
Rsvg_Port_StateRec state_dummy;
|
166
|
180
|
|
167
|
|
- /* general variables */
|
168
|
|
- double x;
|
169
|
|
- double y;
|
170
|
|
- double xx;
|
171
|
|
- double xy;
|
172
|
|
- double yx;
|
173
|
|
- double yy;
|
174
|
|
- double x0;
|
175
|
|
- double y0;
|
176
|
|
- double width;
|
177
|
|
- double height;
|
178
|
|
- double x_svg_to_out;
|
179
|
|
- double y_svg_to_out;
|
180
|
|
-
|
181
|
|
- float metrics_width;
|
182
|
|
- float metrics_height;
|
183
|
|
- float horiBearingX;
|
184
|
|
- float horiBearingY;
|
185
|
|
- float vertBearingX;
|
186
|
|
- float vertBearingY;
|
187
|
|
- float vertical_advance;
|
188
|
|
-
|
189
|
|
- /* if cache is `TRUE` we store calculations in the actual port
|
190
|
|
- * state variable, otherwise we just create a dummy variable and
|
191
|
|
- * store there. This saves from too many if statements.
|
192
|
|
- */
|
|
181
|
+ /* General variables. */
|
|
182
|
+ double x, y;
|
|
183
|
+ double xx, xy, yx, yy;
|
|
184
|
+ double x0, y0;
|
|
185
|
+ double width, height;
|
|
186
|
+ double x_svg_to_out, y_svg_to_out;
|
|
187
|
+ double tmpd;
|
|
188
|
+
|
|
189
|
+ float metrics_width, metrics_height;
|
|
190
|
+ float horiBearingX, horiBearingY;
|
|
191
|
+ float vertBearingX, vertBearingY;
|
|
192
|
+ float tmpf;
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+ /* If `cache` is `TRUE` we store calculations in the actual port */
|
|
196
|
+ /* state variable, otherwise we just create a dummy variable and */
|
|
197
|
+ /* store there. This saves us from too many 'if' statements. */
|
193
|
198
|
if ( cache )
|
194
|
199
|
state = *(Rsvg_Port_State*)_state;
|
195
|
200
|
else
|
196
|
201
|
state = &state_dummy;
|
197
|
202
|
|
198
|
|
- /* form an RsvgHandle by loading the SVG document */
|
|
203
|
+ /* Form an `RsvgHandle` by loading the SVG document. */
|
199
|
204
|
handle = rsvg_handle_new_from_data( document->svg_document,
|
200
|
205
|
document->svg_document_length,
|
201
|
206
|
&gerror );
|
... |
... |
@@ -205,7 +210,7 @@ |
205
|
210
|
goto CleanLibrsvg;
|
206
|
211
|
}
|
207
|
212
|
|
208
|
|
- /* get attributes like `viewBox` and `width/height`. */
|
|
213
|
+ /* Get attributes like `viewBox` and `width`/`height`. */
|
209
|
214
|
rsvg_handle_get_intrinsic_dimensions( handle,
|
210
|
215
|
&out_has_width,
|
211
|
216
|
&out_width,
|
... |
... |
@@ -214,70 +219,71 @@ |
214
|
219
|
&out_has_viewbox,
|
215
|
220
|
&out_viewbox );
|
216
|
221
|
|
217
|
|
- /**
|
|
222
|
+ /*
|
218
|
223
|
* Figure out the units in the EM square in the SVG document. This is
|
219
|
|
- * specified by the ViewBox or the width/height attributes, if present,
|
220
|
|
- * otherwise it should be assumed that the units in the EM square are
|
221
|
|
- * the same as in the TTF/CFF outlines.
|
|
224
|
+ * specified by the `ViewBox` or the `width`/`height` attributes, if
|
|
225
|
+ * present, otherwise it should be assumed that the units in the EM
|
|
226
|
+ * square are the same as in the TTF/CFF outlines.
|
222
|
227
|
*
|
223
|
228
|
* TODO: I'm not sure what the standard says about the situation if
|
224
|
|
- * the ViewBox as well as width/height are present, however, I've never
|
225
|
|
- * seen that situation in real fonts.
|
|
229
|
+ * `ViewBox` as well as `width`/`height` are present; however, I've
|
|
230
|
+ * never seen that situation in real fonts.
|
226
|
231
|
*/
|
227
|
232
|
if ( out_has_viewbox == TRUE )
|
228
|
233
|
{
|
229
|
|
- dimension_svg.width = out_viewbox.width;
|
230
|
|
- dimension_svg.height = out_viewbox.height;
|
|
234
|
+ dimension_svg.width = (int)out_viewbox.width; /* XXX rounding? */
|
|
235
|
+ dimension_svg.height = (int)out_viewbox.height;
|
231
|
236
|
}
|
232
|
|
- else if ( ( out_has_width == TRUE ) && ( out_has_height == TRUE ) )
|
|
237
|
+ else if ( out_has_width == TRUE && out_has_height == TRUE )
|
233
|
238
|
{
|
234
|
|
- dimension_svg.width = out_width.length;
|
235
|
|
- dimension_svg.height = out_height.length;
|
|
239
|
+ dimension_svg.width = (int)out_width.length; /* XXX rounding? */
|
|
240
|
+ dimension_svg.height = (int)out_height.length;
|
236
|
241
|
}
|
237
|
242
|
else
|
238
|
243
|
{
|
239
|
|
- /* if no `viewBox` and `width/height` are present, the `units_per_EM`
|
240
|
|
- * in SVG coordinates must be the same as `units_per_EM` of the TTF/CFF
|
241
|
|
- * outlines.
|
242
|
|
- */
|
|
244
|
+ /* If neither `ViewBox` nor `width`/`height` are present, the */
|
|
245
|
+ /* `units_per_EM` in SVG coordinates must be the same as */
|
|
246
|
+ /* `units_per_EM` of the TTF/CFF outlines. */
|
243
|
247
|
dimension_svg.width = units_per_EM;
|
244
|
248
|
dimension_svg.height = units_per_EM;
|
245
|
249
|
}
|
246
|
250
|
|
247
|
|
- /* scale factors from SVG coordinates to the output size needed */
|
248
|
|
- x_svg_to_out = (float)metrics.x_ppem/(float)dimension_svg.width;
|
249
|
|
- y_svg_to_out = (float)metrics.y_ppem/(float)dimension_svg.height;
|
|
251
|
+ /* Scale factors from SVG coordinates to the needed output size. */
|
|
252
|
+ x_svg_to_out = (float)metrics.x_ppem / dimension_svg.width;
|
|
253
|
+ y_svg_to_out = (float)metrics.y_ppem / dimension_svg.height;
|
250
|
254
|
|
251
|
|
- /* create a cairo recording surface. This is done for two reasons.
|
|
255
|
+ /*
|
|
256
|
+ * Create a cairo recording surface. This is done for two reasons.
|
252
|
257
|
* Firstly, it is required to get the bounding box of the final drawing
|
253
|
|
- * so we can use appropriate translate transform to get a tight rendering.
|
254
|
|
- * Secondly, if `cache` is true, we can save this surface and later
|
255
|
|
- * replay it against an image surface for the final rendering. This save
|
256
|
|
- * us from loading and parsing the document again.
|
|
258
|
+ * so we can use an appropriate translate transform to get a tight
|
|
259
|
+ * rendering. Secondly, if `cache` is true, we can save this surface
|
|
260
|
+ * and later replay it against an image surface for the final rendering.
|
|
261
|
+ * This saves us from loading and parsing the document again.
|
257
|
262
|
*/
|
258
|
|
- state->rec_surface = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA,
|
259
|
|
- NULL );
|
|
263
|
+ state->rec_surface =
|
|
264
|
+ cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL );
|
260
|
265
|
|
261
|
266
|
rec_cr = cairo_create( state->rec_surface );
|
262
|
267
|
|
263
|
|
-
|
264
|
|
- /* we need to take into account any transformations applied. The end
|
|
268
|
+ /*
|
|
269
|
+ * We need to take into account any transformations applied. The end
|
265
|
270
|
* user who applied the transformation doesn't know the internal details
|
266
|
271
|
* of the SVG document. Thus, we expect that the end user should just
|
267
|
|
- * write the transformation as if the glyph is a traditional one. We then,
|
268
|
|
- * do some maths on this to get the equivalent transformation in SVG
|
269
|
|
- * coordinates.
|
|
272
|
+ * write the transformation as if the glyph is a traditional one. We
|
|
273
|
+ * then do some maths on this to get the equivalent transformation in
|
|
274
|
+ * SVG coordinates.
|
270
|
275
|
*/
|
271
|
|
- xx = 1 * (((double)document->transform.xx)/((double)(1 << 16 )));
|
272
|
|
- xy = -1 * (((double)document->transform.xy)/((double)(1 << 16 )));
|
273
|
|
- yx = -1 * (((double)document->transform.yx)/((double)(1 << 16 )));
|
274
|
|
- yy = 1 * (((double)document->transform.yy)/((double)(1 << 16 )));
|
275
|
|
- x0 = 1 * ((double)document->delta.x/(double)(1 << 6)) *
|
276
|
|
- ((double)dimension_svg.width/(double)metrics.x_ppem);
|
277
|
|
- y0 = -1 * ((double)document->delta.y/(double)(1 << 6)) *
|
278
|
|
- ((double)dimension_svg.height/(double)metrics.y_ppem);
|
279
|
|
-
|
280
|
|
- /* cairo stores transformation and translate both in one matrix */
|
|
276
|
+ xx = (double)document->transform.xx / ( 1 << 16 );
|
|
277
|
+ xy = -(double)document->transform.xy / ( 1 << 16 );
|
|
278
|
+ yx = -(double)document->transform.yx / ( 1 << 16 );
|
|
279
|
+ yy = (double)document->transform.yy / ( 1 << 16 );
|
|
280
|
+
|
|
281
|
+ x0 = (double)document->delta.x / 64 *
|
|
282
|
+ dimension_svg.width / metrics.x_ppem;
|
|
283
|
+ y0 = -(double)document->delta.y / 64 *
|
|
284
|
+ dimension_svg.height / metrics.y_ppem;
|
|
285
|
+
|
|
286
|
+ /* Cairo stores both transformation and translation in one matrix. */
|
281
|
287
|
transform_matrix.xx = xx;
|
282
|
288
|
transform_matrix.yx = yx;
|
283
|
289
|
transform_matrix.xy = xy;
|
... |
... |
@@ -285,18 +291,18 @@ |
285
|
291
|
transform_matrix.x0 = x0;
|
286
|
292
|
transform_matrix.y0 = y0;
|
287
|
293
|
|
288
|
|
- /* set up a scale transfromation to scale up the document to the
|
289
|
|
- * required output size */
|
|
294
|
+ /* Set up a scale transformation to scale up the document to the */
|
|
295
|
+ /* required output size. */
|
290
|
296
|
cairo_scale( rec_cr, x_svg_to_out, y_svg_to_out );
|
291
|
|
- /* set up a transformation matrix */
|
|
297
|
+ /* Set up a transformation matrix. */
|
292
|
298
|
cairo_transform( rec_cr, &transform_matrix );
|
293
|
299
|
|
294
|
|
- /* if the document contains only one glyph, `start_glyph_id' and
|
295
|
|
- * `end_glyph_id' will have the same value, else, `end_glyph_id'
|
296
|
|
- * will be higher. */
|
|
300
|
+ /* If the document contains only one glyph, `start_glyph_id` and */
|
|
301
|
+ /* `end_glyph_id` have the same value. Otherwise `end_glyph_id` */
|
|
302
|
+ /* is larger. */
|
297
|
303
|
if ( start_glyph_id == end_glyph_id )
|
298
|
304
|
{
|
299
|
|
- /* render the whole document to the recording surface */
|
|
305
|
+ /* Render the whole document to the recording surface. */
|
300
|
306
|
ret = rsvg_handle_render_cairo ( handle, rec_cr );
|
301
|
307
|
if ( ret == FALSE )
|
302
|
308
|
{
|
... |
... |
@@ -306,16 +312,12 @@ |
306
|
312
|
}
|
307
|
313
|
else if ( start_glyph_id < end_glyph_id )
|
308
|
314
|
{
|
309
|
|
- int length;
|
310
|
|
- char* str;
|
311
|
|
-
|
312
|
|
- length = snprintf( NULL, 0, "%u", slot->glyph_index );
|
313
|
|
- str = (char*)malloc( 6 + length + 1 );
|
314
|
|
- strcpy( str, "#glyph");
|
315
|
|
- snprintf( str + 6, length + 1, "%u", slot->glyph_index );
|
316
|
|
- /* render only the element with `id' equal to `glyph<ID>' */
|
|
315
|
+ char str[32] = "#glyph";
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+ /* Render only the element with its ID equal to `glyph<ID>`. */
|
|
319
|
+ sprintf( str + 6, "%u", slot->glyph_index );
|
317
|
320
|
ret = rsvg_handle_render_cairo_sub( handle, rec_cr, str );
|
318
|
|
- free(str);
|
319
|
321
|
if ( ret == FALSE )
|
320
|
322
|
{
|
321
|
323
|
error = FT_Err_Invalid_SVG_Document;
|
... |
... |
@@ -323,60 +325,76 @@ |
323
|
325
|
}
|
324
|
326
|
}
|
325
|
327
|
|
326
|
|
- /* get the bounding box of the drawing */
|
|
328
|
+ /* Get the bounding box of the drawing. */
|
327
|
329
|
cairo_recording_surface_ink_extents( state->rec_surface, &x, &y,
|
328
|
|
- &width, &height);
|
|
330
|
+ &width, &height );
|
329
|
331
|
|
330
|
|
- /* we store the bounding box's `x` and `y` so that the render hook
|
331
|
|
- * can apply a translation to get a tight rendering.
|
332
|
|
- */
|
|
332
|
+ /* We store the bounding box's `x` and `y` values so that the render */
|
|
333
|
+ /* hook can apply a translation to get a tight rendering. */
|
333
|
334
|
state->x = x;
|
334
|
335
|
state->y = y;
|
335
|
336
|
|
336
|
|
- /* preset the values */
|
337
|
|
- slot->bitmap_left = state->x ;
|
338
|
|
- slot->bitmap_top = (state->y) * -1;
|
339
|
|
- slot->bitmap.rows = ceil( height );
|
340
|
|
- slot->bitmap.width = ceil( width );
|
341
|
|
- slot->bitmap.pitch = slot->bitmap.width * 4;
|
|
337
|
+ /* Preset the values. */
|
|
338
|
+ slot->bitmap_left = (FT_Int) state->x; /* XXX rounding? */
|
|
339
|
+ slot->bitmap_top = (FT_Int)-state->y;
|
|
340
|
+
|
|
341
|
+ /* Do conversion in two steps to avoid 'bad function cast' warning. */
|
|
342
|
+ tmpd = ceil( height );
|
|
343
|
+ slot->bitmap.rows = (unsigned int)tmpd;
|
|
344
|
+ tmpd = ceil( width );
|
|
345
|
+ slot->bitmap.width = (unsigned int)tmpd;
|
|
346
|
+
|
|
347
|
+ slot->bitmap.pitch = (int)slot->bitmap.width * 4;
|
342
|
348
|
|
343
|
349
|
slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
344
|
350
|
|
345
|
|
- /* let's compute all the bearings and set them correctly.
|
346
|
|
- * The outline is scaled already, we just need to use the
|
347
|
|
- * bounding box now.
|
348
|
|
- */
|
349
|
|
- metrics_width = width;
|
350
|
|
- metrics_height = height;
|
351
|
|
- horiBearingX = state->x;
|
352
|
|
- horiBearingY = state->y * -1;
|
353
|
|
- vertBearingX = (slot->metrics.horiBearingX / (1 << 6)) - ((slot->metrics.horiAdvance / (1 << 6)) / 2.0);
|
354
|
|
- vertBearingY = ((slot->metrics.vertAdvance / (1 << 6)) - (slot->metrics.height / (1 << 6)))/2.0;
|
355
|
|
- slot->metrics.width = round(metrics_width * (1 << 6));
|
356
|
|
- slot->metrics.height = round(metrics_height * (1 << 6));
|
357
|
|
- slot->metrics.horiBearingX = horiBearingX * (1 << 6);
|
358
|
|
- slot->metrics.horiBearingY = horiBearingY * (1 << 6);
|
359
|
|
- slot->metrics.vertBearingX = vertBearingX * (1 << 6);
|
360
|
|
- slot->metrics.vertBearingY = vertBearingY * (1 << 6);
|
361
|
|
- if(slot->metrics.vertAdvance == 0)
|
362
|
|
- slot->metrics.vertAdvance = metrics_height * 1.2 * (1 << 6);
|
363
|
|
-
|
364
|
|
- /* if a render call is to follow, just destroy the context for the
|
365
|
|
- * recording surface, since no more drawing will be done on it, but,
|
366
|
|
- * keep the surface itself for use by the render hook.
|
367
|
|
- */
|
|
351
|
+ /* Compute all the bearings and set them correctly. The outline is */
|
|
352
|
+ /* scaled already, we just need to use the bounding box. */
|
|
353
|
+ metrics_width = (float)width;
|
|
354
|
+ metrics_height = (float)height;
|
|
355
|
+
|
|
356
|
+ horiBearingX = (float) state->x;
|
|
357
|
+ horiBearingY = (float)-state->y;
|
|
358
|
+
|
|
359
|
+ vertBearingX = slot->metrics.horiBearingX / 64.0f -
|
|
360
|
+ slot->metrics.horiAdvance / 64.0f / 2;
|
|
361
|
+ vertBearingY = ( slot->metrics.vertAdvance / 64.0f -
|
|
362
|
+ slot->metrics.height / 64.0f ) / 2; /* XXX parentheses correct? */
|
|
363
|
+
|
|
364
|
+ /* Do conversion in two steps to avoid 'bad function cast' warning. */
|
|
365
|
+ tmpf = roundf( metrics_width * 64 );
|
|
366
|
+ slot->metrics.width = (FT_Pos)tmpf;
|
|
367
|
+ tmpf = roundf( metrics_height * 64 );
|
|
368
|
+ slot->metrics.height = (FT_Pos)tmpf;
|
|
369
|
+
|
|
370
|
+ slot->metrics.horiBearingX = (FT_Pos)( horiBearingX * 64 ); /* XXX rounding? */
|
|
371
|
+ slot->metrics.horiBearingY = (FT_Pos)( horiBearingY * 64 );
|
|
372
|
+ slot->metrics.vertBearingX = (FT_Pos)( vertBearingX * 64 );
|
|
373
|
+ slot->metrics.vertBearingY = (FT_Pos)( vertBearingY * 64 );
|
|
374
|
+
|
|
375
|
+ if ( slot->metrics.vertAdvance == 0 )
|
|
376
|
+ slot->metrics.vertAdvance = (FT_Pos)( metrics_height * 1.2 * 64 );
|
|
377
|
+
|
|
378
|
+ /* If a render call is to follow, just destroy the context for the */
|
|
379
|
+ /* recording surface since no more drawing will be done on it. */
|
|
380
|
+ /* However, keep the surface itself for use by the render hook. */
|
368
|
381
|
if ( cache == TRUE )
|
369
|
382
|
{
|
370
|
383
|
cairo_destroy( rec_cr );
|
371
|
384
|
goto CleanLibrsvg;
|
372
|
385
|
}
|
373
|
386
|
|
374
|
|
- /* destroy the recording surface as well as the context */
|
|
387
|
+ /* Destroy the recording surface as well as the context. */
|
375
|
388
|
CleanCairo:
|
376
|
389
|
cairo_surface_destroy( state->rec_surface );
|
377
|
390
|
cairo_destroy( rec_cr );
|
|
391
|
+
|
378
|
392
|
CleanLibrsvg:
|
379
|
|
- /* destroy the handle */
|
|
393
|
+ /* Destroy the handle. */
|
380
|
394
|
g_object_unref( handle );
|
|
395
|
+
|
381
|
396
|
return error;
|
382
|
397
|
}
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+/* End */ |