Rev 1897 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1897 | serge | 1 | /* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
2 | /* cairo - a vector graphics library with display and print output |
||
3 | * |
||
4 | * Copyright © 2004 Red Hat, Inc |
||
5 | * Copyright © 2005-2007 Emmanuel Pacaud |
||
6 | * Copyright © 2006 Red Hat, Inc |
||
7 | * |
||
8 | * This library is free software; you can redistribute it and/or |
||
9 | * modify it either under the terms of the GNU Lesser General Public |
||
10 | * License version 2.1 as published by the Free Software Foundation |
||
11 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
||
12 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
||
13 | * notice, a recipient may use your version of this file under either |
||
14 | * the MPL or the LGPL. |
||
15 | * |
||
16 | * You should have received a copy of the LGPL along with this library |
||
17 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
||
18 | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
||
19 | * You should have received a copy of the MPL along with this library |
||
20 | * in the file COPYING-MPL-1.1 |
||
21 | * |
||
22 | * The contents of this file are subject to the Mozilla Public License |
||
23 | * Version 1.1 (the "License"); you may not use this file except in |
||
24 | * compliance with the License. You may obtain a copy of the License at |
||
25 | * http://www.mozilla.org/MPL/ |
||
26 | * |
||
27 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
||
28 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
||
29 | * the specific language governing rights and limitations. |
||
30 | * |
||
31 | * The Original Code is the cairo graphics library. |
||
32 | * |
||
33 | * The Initial Developer of the Original Code is University of Southern |
||
34 | * California. |
||
35 | * |
||
36 | * Contributor(s): |
||
37 | * Kristian Høgsberg |
||
38 | * Emmanuel Pacaud |
||
39 | * Carl Worth |
||
40 | */ |
||
41 | |||
42 | #define _BSD_SOURCE /* for snprintf() */ |
||
43 | #include "cairoint.h" |
||
3959 | Serge | 44 | |
1897 | serge | 45 | #include "cairo-svg.h" |
3959 | Serge | 46 | |
47 | #include "cairo-array-private.h" |
||
1897 | serge | 48 | #include "cairo-analysis-surface-private.h" |
3959 | Serge | 49 | #include "cairo-default-context-private.h" |
1897 | serge | 50 | #include "cairo-error-private.h" |
51 | #include "cairo-image-info-private.h" |
||
3959 | Serge | 52 | #include "cairo-image-surface-private.h" |
53 | #include "cairo-recording-surface-inline.h" |
||
1897 | serge | 54 | #include "cairo-output-stream-private.h" |
55 | #include "cairo-path-fixed-private.h" |
||
56 | #include "cairo-paginated-private.h" |
||
57 | #include "cairo-scaled-font-subsets-private.h" |
||
58 | #include "cairo-surface-clipper-private.h" |
||
3959 | Serge | 59 | #include "cairo-surface-snapshot-inline.h" |
1897 | serge | 60 | #include "cairo-svg-surface-private.h" |
61 | |||
62 | /** |
||
63 | * SECTION:cairo-svg |
||
64 | * @Title: SVG Surfaces |
||
65 | * @Short_Description: Rendering SVG documents |
||
66 | * @See_Also: #cairo_surface_t |
||
67 | * |
||
68 | * The SVG surface is used to render cairo graphics to |
||
69 | * SVG files and is a multi-page vector surface backend. |
||
3959 | Serge | 70 | **/ |
1897 | serge | 71 | |
72 | /** |
||
73 | * CAIRO_HAS_SVG_SURFACE: |
||
74 | * |
||
75 | * Defined if the SVG surface backend is available. |
||
76 | * This macro can be used to conditionally compile backend-specific code. |
||
3959 | Serge | 77 | * |
78 | * Since: 1.2 |
||
79 | **/ |
||
1897 | serge | 80 | |
81 | typedef struct cairo_svg_page cairo_svg_page_t; |
||
82 | |||
83 | static const int invalid_pattern_id = -1; |
||
84 | |||
85 | static const cairo_svg_version_t _cairo_svg_versions[] = |
||
86 | { |
||
87 | CAIRO_SVG_VERSION_1_1, |
||
88 | CAIRO_SVG_VERSION_1_2 |
||
89 | }; |
||
90 | |||
91 | #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions) |
||
92 | |||
3959 | Serge | 93 | static const char *_cairo_svg_supported_mime_types[] = |
94 | { |
||
95 | CAIRO_MIME_TYPE_JPEG, |
||
96 | CAIRO_MIME_TYPE_PNG, |
||
97 | CAIRO_MIME_TYPE_URI, |
||
98 | NULL |
||
99 | }; |
||
100 | |||
1897 | serge | 101 | static void |
102 | _cairo_svg_surface_emit_path (cairo_output_stream_t *output, |
||
3959 | Serge | 103 | const cairo_path_fixed_t *path, |
1897 | serge | 104 | const cairo_matrix_t *ctm_inverse); |
105 | |||
106 | static cairo_bool_t |
||
107 | _cairo_svg_version_has_page_set_support (cairo_svg_version_t version) |
||
108 | { |
||
109 | return version > CAIRO_SVG_VERSION_1_1; |
||
110 | } |
||
111 | |||
112 | static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] = |
||
113 | { |
||
114 | "SVG 1.1", |
||
115 | "SVG 1.2" |
||
116 | }; |
||
117 | |||
118 | static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] = |
||
119 | { |
||
120 | "1.1", |
||
121 | "1.2" |
||
122 | }; |
||
123 | |||
124 | struct cairo_svg_page { |
||
125 | unsigned int surface_id; |
||
126 | unsigned int clip_level; |
||
127 | cairo_output_stream_t *xml_node; |
||
128 | }; |
||
129 | |||
130 | struct cairo_svg_document { |
||
131 | cairo_output_stream_t *output_stream; |
||
132 | unsigned long refcount; |
||
133 | cairo_surface_t *owner; |
||
134 | cairo_bool_t finished; |
||
135 | |||
136 | double width; |
||
137 | double height; |
||
138 | |||
139 | cairo_output_stream_t *xml_node_defs; |
||
140 | cairo_output_stream_t *xml_node_glyphs; |
||
141 | |||
142 | unsigned int linear_pattern_id; |
||
143 | unsigned int radial_pattern_id; |
||
144 | unsigned int pattern_id; |
||
145 | unsigned int filter_id; |
||
146 | unsigned int clip_id; |
||
147 | unsigned int mask_id; |
||
148 | |||
149 | cairo_bool_t alpha_filter; |
||
150 | |||
151 | cairo_svg_version_t svg_version; |
||
152 | |||
153 | cairo_scaled_font_subsets_t *font_subsets; |
||
154 | }; |
||
155 | |||
156 | static cairo_status_t |
||
157 | _cairo_svg_document_create (cairo_output_stream_t *stream, |
||
158 | double width, |
||
159 | double height, |
||
160 | cairo_svg_version_t version, |
||
161 | cairo_svg_document_t **document_out); |
||
162 | |||
163 | static cairo_status_t |
||
164 | _cairo_svg_document_destroy (cairo_svg_document_t *document); |
||
165 | |||
166 | static cairo_status_t |
||
167 | _cairo_svg_document_finish (cairo_svg_document_t *document); |
||
168 | |||
169 | static cairo_svg_document_t * |
||
170 | _cairo_svg_document_reference (cairo_svg_document_t *document); |
||
171 | |||
172 | static unsigned int |
||
173 | _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document); |
||
174 | |||
175 | static cairo_surface_t * |
||
176 | _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, |
||
177 | cairo_content_t content, |
||
178 | double width, |
||
179 | double height); |
||
180 | static cairo_surface_t * |
||
181 | _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, |
||
182 | double width, |
||
183 | double height, |
||
184 | cairo_svg_version_t version); |
||
185 | |||
186 | static const cairo_surface_backend_t cairo_svg_surface_backend; |
||
187 | static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend; |
||
188 | |||
189 | /** |
||
190 | * cairo_svg_surface_create_for_stream: |
||
191 | * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL |
||
192 | * to indicate a no-op @write_func. With a no-op @write_func, |
||
193 | * the surface may be queried or used as a source without |
||
194 | * generating any temporary files. |
||
195 | * @closure: the closure argument for @write_func |
||
196 | * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) |
||
197 | * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) |
||
198 | * |
||
199 | * Creates a SVG surface of the specified size in points to be written |
||
200 | * incrementally to the stream represented by @write_func and @closure. |
||
201 | * |
||
202 | * Return value: a pointer to the newly created surface. The caller |
||
203 | * owns the surface and should call cairo_surface_destroy() when done |
||
204 | * with it. |
||
205 | * |
||
206 | * This function always returns a valid pointer, but it will return a |
||
207 | * pointer to a "nil" surface if an error such as out of memory |
||
208 | * occurs. You can use cairo_surface_status() to check for this. |
||
209 | * |
||
210 | * Since: 1.2 |
||
3959 | Serge | 211 | **/ |
1897 | serge | 212 | cairo_surface_t * |
213 | cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, |
||
214 | void *closure, |
||
215 | double width, |
||
216 | double height) |
||
217 | { |
||
218 | cairo_output_stream_t *stream; |
||
219 | |||
220 | stream = _cairo_output_stream_create (write_func, NULL, closure); |
||
221 | if (_cairo_output_stream_get_status (stream)) |
||
222 | return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); |
||
223 | |||
224 | return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * cairo_svg_surface_create: |
||
229 | * @filename: a filename for the SVG output (must be writable), %NULL may be |
||
230 | * used to specify no output. This will generate a SVG surface that |
||
231 | * may be queried and used as a source, without generating a |
||
232 | * temporary file. |
||
233 | * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) |
||
234 | * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) |
||
235 | * |
||
236 | * Creates a SVG surface of the specified size in points to be written |
||
237 | * to @filename. |
||
238 | * |
||
239 | * The SVG surface backend recognizes the following MIME types for the |
||
240 | * data attached to a surface (see cairo_surface_set_mime_data()) when |
||
241 | * it is used as a source pattern for drawing on this surface: |
||
242 | * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG, |
||
243 | * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend |
||
244 | * emits a href with the content of MIME data instead of a surface |
||
245 | * snapshot (PNG, Base64-encoded) in the corresponding image tag. |
||
246 | * |
||
247 | * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined |
||
248 | * first. If present, the URI is emitted as is: assuring the |
||
249 | * correctness of URI is left to the client code. |
||
250 | * |
||
251 | * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG |
||
252 | * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is |
||
253 | * Base64-encoded and emitted. |
||
254 | * |
||
255 | * Return value: a pointer to the newly created surface. The caller |
||
256 | * owns the surface and should call cairo_surface_destroy() when done |
||
257 | * with it. |
||
258 | * |
||
259 | * This function always returns a valid pointer, but it will return a |
||
260 | * pointer to a "nil" surface if an error such as out of memory |
||
261 | * occurs. You can use cairo_surface_status() to check for this. |
||
262 | * |
||
263 | * Since: 1.2 |
||
264 | **/ |
||
265 | cairo_surface_t * |
||
266 | cairo_svg_surface_create (const char *filename, |
||
267 | double width, |
||
268 | double height) |
||
269 | { |
||
270 | cairo_output_stream_t *stream; |
||
271 | |||
272 | stream = _cairo_output_stream_create_for_filename (filename); |
||
273 | if (_cairo_output_stream_get_status (stream)) |
||
274 | return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); |
||
275 | |||
276 | return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); |
||
277 | } |
||
278 | |||
279 | static cairo_bool_t |
||
280 | _cairo_surface_is_svg (cairo_surface_t *surface) |
||
281 | { |
||
282 | return surface->backend == &cairo_svg_surface_backend; |
||
283 | } |
||
284 | |||
285 | /* If the abstract_surface is a paginated surface, and that paginated |
||
286 | * surface's target is a svg_surface, then set svg_surface to that |
||
287 | * target. Otherwise return FALSE. |
||
288 | */ |
||
289 | static cairo_bool_t |
||
290 | _extract_svg_surface (cairo_surface_t *surface, |
||
291 | cairo_svg_surface_t **svg_surface) |
||
292 | { |
||
293 | cairo_surface_t *target; |
||
294 | cairo_status_t status_ignored; |
||
295 | |||
296 | if (surface->status) |
||
297 | return FALSE; |
||
298 | if (surface->finished) { |
||
299 | status_ignored = _cairo_surface_set_error (surface, |
||
300 | _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); |
||
301 | return FALSE; |
||
302 | } |
||
303 | |||
304 | if (! _cairo_surface_is_paginated (surface)) { |
||
305 | status_ignored = _cairo_surface_set_error (surface, |
||
306 | _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); |
||
307 | return FALSE; |
||
308 | } |
||
309 | |||
310 | target = _cairo_paginated_surface_get_target (surface); |
||
311 | if (target->status) { |
||
312 | status_ignored = _cairo_surface_set_error (surface, |
||
313 | target->status); |
||
314 | return FALSE; |
||
315 | } |
||
316 | if (target->finished) { |
||
317 | status_ignored = _cairo_surface_set_error (surface, |
||
318 | _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); |
||
319 | return FALSE; |
||
320 | } |
||
321 | |||
322 | if (! _cairo_surface_is_svg (target)) { |
||
323 | status_ignored = _cairo_surface_set_error (surface, |
||
324 | _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); |
||
325 | return FALSE; |
||
326 | } |
||
327 | |||
328 | *svg_surface = (cairo_svg_surface_t *) target; |
||
329 | return TRUE; |
||
330 | } |
||
331 | |||
332 | /** |
||
333 | * cairo_svg_surface_restrict_to_version: |
||
334 | * @surface: a SVG #cairo_surface_t |
||
335 | * @version: SVG version |
||
336 | * |
||
337 | * Restricts the generated SVG file to @version. See cairo_svg_get_versions() |
||
338 | * for a list of available version values that can be used here. |
||
339 | * |
||
340 | * This function should only be called before any drawing operations |
||
341 | * have been performed on the given surface. The simplest way to do |
||
342 | * this is to call this function immediately after creating the |
||
343 | * surface. |
||
344 | * |
||
345 | * Since: 1.2 |
||
346 | **/ |
||
347 | void |
||
348 | cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, |
||
349 | cairo_svg_version_t version) |
||
350 | { |
||
351 | cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ |
||
352 | |||
353 | if (! _extract_svg_surface (abstract_surface, &surface)) |
||
354 | return; |
||
355 | |||
356 | if (version < CAIRO_SVG_VERSION_LAST) |
||
357 | surface->document->svg_version = version; |
||
358 | } |
||
359 | |||
360 | /** |
||
361 | * cairo_svg_get_versions: |
||
362 | * @versions: supported version list |
||
363 | * @num_versions: list length |
||
364 | * |
||
365 | * Used to retrieve the list of supported versions. See |
||
366 | * cairo_svg_surface_restrict_to_version(). |
||
367 | * |
||
368 | * Since: 1.2 |
||
369 | **/ |
||
370 | void |
||
371 | cairo_svg_get_versions (cairo_svg_version_t const **versions, |
||
372 | int *num_versions) |
||
373 | { |
||
374 | if (versions != NULL) |
||
375 | *versions = _cairo_svg_versions; |
||
376 | |||
377 | if (num_versions != NULL) |
||
378 | *num_versions = CAIRO_SVG_VERSION_LAST; |
||
379 | } |
||
380 | |||
381 | /** |
||
382 | * cairo_svg_version_to_string: |
||
383 | * @version: a version id |
||
384 | * |
||
385 | * Get the string representation of the given @version id. This function |
||
386 | * will return %NULL if @version isn't valid. See cairo_svg_get_versions() |
||
387 | * for a way to get the list of valid version ids. |
||
388 | * |
||
389 | * Return value: the string associated to given version. |
||
390 | * |
||
391 | * Since: 1.2 |
||
392 | **/ |
||
393 | const char * |
||
394 | cairo_svg_version_to_string (cairo_svg_version_t version) |
||
395 | { |
||
396 | if (version >= CAIRO_SVG_VERSION_LAST) |
||
397 | return NULL; |
||
398 | |||
399 | return _cairo_svg_version_strings[version]; |
||
400 | } |
||
401 | |||
402 | static cairo_bool_t |
||
403 | _cliprect_covers_surface (cairo_svg_surface_t *surface, |
||
404 | cairo_path_fixed_t *path) |
||
405 | { |
||
406 | cairo_box_t box; |
||
407 | |||
408 | if (_cairo_path_fixed_is_box (path, &box)) { |
||
409 | if (box.p1.x <= 0 && |
||
410 | box.p1.y <= 0 && |
||
411 | _cairo_fixed_to_double (box.p2.x) >= surface->width && |
||
412 | _cairo_fixed_to_double (box.p2.y) >= surface->height) |
||
413 | { |
||
414 | return TRUE; |
||
415 | } |
||
416 | } |
||
417 | |||
418 | return FALSE; |
||
419 | } |
||
420 | |||
421 | static cairo_status_t |
||
422 | _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, |
||
423 | cairo_path_fixed_t *path, |
||
424 | cairo_fill_rule_t fill_rule, |
||
425 | double tolerance, |
||
426 | cairo_antialias_t antialias) |
||
427 | { |
||
428 | cairo_svg_surface_t *surface = cairo_container_of (clipper, |
||
429 | cairo_svg_surface_t, |
||
430 | clipper); |
||
431 | cairo_svg_document_t *document = surface->document; |
||
432 | unsigned int i; |
||
433 | |||
434 | if (path == NULL) { |
||
435 | for (i = 0; i < surface->clip_level; i++) |
||
436 | _cairo_output_stream_printf (surface->xml_node, "\n"); |
||
437 | |||
438 | surface->clip_level = 0; |
||
439 | return CAIRO_STATUS_SUCCESS; |
||
440 | } |
||
441 | |||
442 | /* skip trivial whole-page clips */ |
||
443 | if (_cliprect_covers_surface (surface, path)) |
||
444 | return CAIRO_STATUS_SUCCESS; |
||
445 | |||
446 | _cairo_output_stream_printf (document->xml_node_defs, |
||
447 | " |
||
448 | " |
||
449 | document->clip_id); |
||
450 | _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); |
||
451 | |||
452 | _cairo_output_stream_printf (document->xml_node_defs, |
||
453 | "/>\n" |
||
454 | "\n"); |
||
455 | |||
456 | _cairo_output_stream_printf (surface->xml_node, |
||
457 | " |
||
458 | "clip-rule=\"%s\">\n", |
||
459 | document->clip_id, |
||
460 | fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? |
||
461 | "evenodd" : "nonzero"); |
||
462 | |||
463 | document->clip_id++; |
||
464 | surface->clip_level++; |
||
465 | |||
466 | return CAIRO_STATUS_SUCCESS; |
||
467 | } |
||
468 | |||
469 | static cairo_surface_t * |
||
470 | _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, |
||
471 | cairo_content_t content, |
||
472 | double width, |
||
473 | double height) |
||
474 | { |
||
475 | cairo_svg_surface_t *surface; |
||
476 | cairo_surface_t *paginated; |
||
477 | cairo_status_t status, status_ignored; |
||
478 | |||
479 | surface = malloc (sizeof (cairo_svg_surface_t)); |
||
480 | if (unlikely (surface == NULL)) |
||
481 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
482 | |||
483 | _cairo_surface_init (&surface->base, |
||
484 | &cairo_svg_surface_backend, |
||
485 | NULL, /* device */ |
||
486 | content); |
||
487 | |||
488 | surface->width = width; |
||
489 | surface->height = height; |
||
490 | |||
491 | surface->document = _cairo_svg_document_reference (document); |
||
492 | |||
493 | surface->clip_level = 0; |
||
494 | _cairo_surface_clipper_init (&surface->clipper, |
||
495 | _cairo_svg_surface_clipper_intersect_clip_path); |
||
496 | |||
497 | surface->base_clip = document->clip_id++; |
||
498 | surface->is_base_clip_emitted = FALSE; |
||
499 | |||
500 | surface->xml_node = _cairo_memory_stream_create (); |
||
501 | status = _cairo_output_stream_get_status (surface->xml_node); |
||
502 | if (unlikely (status)) |
||
503 | goto CLEANUP; |
||
504 | |||
505 | _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); |
||
506 | |||
507 | if (content == CAIRO_CONTENT_COLOR) { |
||
508 | _cairo_output_stream_printf (surface->xml_node, |
||
509 | " |
||
510 | "style=\"opacity:1;stroke:none;" |
||
511 | "fill:rgb(0,0,0);\"/>\n", |
||
512 | width, height); |
||
513 | status = _cairo_output_stream_get_status (surface->xml_node); |
||
514 | if (unlikely (status)) |
||
515 | goto CLEANUP; |
||
516 | } |
||
517 | |||
518 | surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; |
||
519 | surface->force_fallbacks = FALSE; |
||
520 | surface->content = content; |
||
521 | |||
522 | paginated = _cairo_paginated_surface_create (&surface->base, |
||
523 | surface->content, |
||
524 | &cairo_svg_surface_paginated_backend); |
||
525 | status = paginated->status; |
||
526 | if (status == CAIRO_STATUS_SUCCESS) { |
||
527 | /* paginated keeps the only reference to surface now, drop ours */ |
||
528 | cairo_surface_destroy (&surface->base); |
||
529 | return paginated; |
||
530 | } |
||
531 | |||
532 | /* ignore status as we are on the error path */ |
||
533 | CLEANUP: |
||
534 | status_ignored = _cairo_output_stream_destroy (surface->xml_node); |
||
535 | status_ignored = _cairo_svg_document_destroy (document); |
||
536 | |||
537 | free (surface); |
||
538 | |||
539 | return _cairo_surface_create_in_error (status); |
||
540 | } |
||
541 | |||
542 | static cairo_surface_t * |
||
543 | _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, |
||
544 | double width, |
||
545 | double height, |
||
546 | cairo_svg_version_t version) |
||
547 | { |
||
548 | cairo_svg_document_t *document = NULL; /* silence compiler */ |
||
549 | cairo_surface_t *surface; |
||
550 | cairo_status_t status; |
||
551 | |||
552 | status = _cairo_svg_document_create (stream, |
||
553 | width, height, version, |
||
554 | &document); |
||
555 | if (unlikely (status)) { |
||
556 | surface = _cairo_surface_create_in_error (status); |
||
557 | /* consume the output stream on behalf of caller */ |
||
558 | status = _cairo_output_stream_destroy (stream); |
||
559 | return surface; |
||
560 | } |
||
561 | |||
562 | surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, |
||
563 | width, height); |
||
564 | if (surface->status) { |
||
565 | status = _cairo_svg_document_destroy (document); |
||
566 | return surface; |
||
567 | } |
||
568 | |||
569 | document->owner = surface; |
||
570 | status = _cairo_svg_document_destroy (document); |
||
571 | /* the ref count should be 2 at this point */ |
||
572 | assert (status == CAIRO_STATUS_SUCCESS); |
||
573 | |||
574 | return surface; |
||
575 | } |
||
576 | |||
577 | static cairo_svg_page_t * |
||
578 | _cairo_svg_surface_store_page (cairo_svg_surface_t *surface) |
||
579 | { |
||
580 | cairo_svg_page_t page; |
||
581 | cairo_output_stream_t *stream; |
||
3959 | Serge | 582 | cairo_int_status_t status; |
583 | unsigned int i; |
||
1897 | serge | 584 | |
585 | stream = _cairo_memory_stream_create (); |
||
586 | if (_cairo_output_stream_get_status (stream)) { |
||
587 | status = _cairo_output_stream_destroy (stream); |
||
588 | return NULL; |
||
589 | } |
||
590 | |||
591 | page.surface_id = surface->base.unique_id; |
||
592 | page.clip_level = surface->clip_level; |
||
593 | page.xml_node = surface->xml_node; |
||
594 | |||
595 | if (_cairo_array_append (&surface->page_set, &page)) { |
||
596 | status = _cairo_output_stream_destroy (stream); |
||
597 | return NULL; |
||
598 | } |
||
599 | |||
600 | surface->xml_node = stream; |
||
601 | surface->clip_level = 0; |
||
602 | for (i = 0; i < page.clip_level; i++) |
||
603 | _cairo_output_stream_printf (page.xml_node, "\n"); |
||
604 | |||
605 | _cairo_surface_clipper_reset (&surface->clipper); |
||
606 | |||
607 | return _cairo_array_index (&surface->page_set, |
||
608 | surface->page_set.num_elements - 1); |
||
609 | } |
||
610 | |||
611 | static cairo_int_status_t |
||
612 | _cairo_svg_surface_copy_page (void *abstract_surface) |
||
613 | { |
||
614 | cairo_svg_surface_t *surface = abstract_surface; |
||
615 | cairo_svg_page_t *page; |
||
616 | |||
617 | page = _cairo_svg_surface_store_page (surface); |
||
618 | if (unlikely (page == NULL)) |
||
619 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
620 | |||
621 | _cairo_memory_stream_copy (page->xml_node, surface->xml_node); |
||
622 | |||
623 | return CAIRO_STATUS_SUCCESS; |
||
624 | } |
||
625 | |||
626 | static cairo_int_status_t |
||
627 | _cairo_svg_surface_show_page (void *abstract_surface) |
||
628 | { |
||
629 | cairo_svg_surface_t *surface = abstract_surface; |
||
630 | |||
631 | if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) |
||
632 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
633 | |||
634 | return CAIRO_STATUS_SUCCESS; |
||
635 | } |
||
636 | |||
637 | static void |
||
638 | _cairo_svg_surface_emit_transform (cairo_output_stream_t *output, |
||
639 | char const *attribute_str, |
||
640 | const cairo_matrix_t *object_matrix, |
||
641 | const cairo_matrix_t *parent_matrix) |
||
642 | { |
||
643 | cairo_matrix_t matrix = *object_matrix; |
||
644 | |||
645 | if (parent_matrix != NULL) |
||
646 | cairo_matrix_multiply (&matrix, &matrix, parent_matrix); |
||
647 | |||
648 | if (!_cairo_matrix_is_identity (&matrix)) |
||
649 | _cairo_output_stream_printf (output, |
||
650 | "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"", |
||
651 | attribute_str, |
||
652 | matrix.xx, matrix.yx, |
||
653 | matrix.xy, matrix.yy, |
||
654 | matrix.x0, matrix.y0); |
||
655 | } |
||
656 | |||
657 | typedef struct { |
||
658 | cairo_output_stream_t *output; |
||
659 | const cairo_matrix_t *ctm_inverse; |
||
660 | } svg_path_info_t; |
||
661 | |||
662 | static cairo_status_t |
||
663 | _cairo_svg_path_move_to (void *closure, |
||
664 | const cairo_point_t *point) |
||
665 | { |
||
666 | svg_path_info_t *info = closure; |
||
667 | double x = _cairo_fixed_to_double (point->x); |
||
668 | double y = _cairo_fixed_to_double (point->y); |
||
669 | |||
670 | if (info->ctm_inverse) |
||
671 | cairo_matrix_transform_point (info->ctm_inverse, &x, &y); |
||
672 | |||
673 | _cairo_output_stream_printf (info->output, "M %f %f ", x, y); |
||
674 | |||
675 | return CAIRO_STATUS_SUCCESS; |
||
676 | } |
||
677 | |||
678 | static cairo_status_t |
||
679 | _cairo_svg_path_line_to (void *closure, |
||
680 | const cairo_point_t *point) |
||
681 | { |
||
682 | svg_path_info_t *info = closure; |
||
683 | double x = _cairo_fixed_to_double (point->x); |
||
684 | double y = _cairo_fixed_to_double (point->y); |
||
685 | |||
686 | if (info->ctm_inverse) |
||
687 | cairo_matrix_transform_point (info->ctm_inverse, &x, &y); |
||
688 | |||
689 | _cairo_output_stream_printf (info->output, "L %f %f ", x, y); |
||
690 | |||
691 | return CAIRO_STATUS_SUCCESS; |
||
692 | } |
||
693 | |||
694 | static cairo_status_t |
||
695 | _cairo_svg_path_curve_to (void *closure, |
||
696 | const cairo_point_t *b, |
||
697 | const cairo_point_t *c, |
||
698 | const cairo_point_t *d) |
||
699 | { |
||
700 | svg_path_info_t *info = closure; |
||
701 | double bx = _cairo_fixed_to_double (b->x); |
||
702 | double by = _cairo_fixed_to_double (b->y); |
||
703 | double cx = _cairo_fixed_to_double (c->x); |
||
704 | double cy = _cairo_fixed_to_double (c->y); |
||
705 | double dx = _cairo_fixed_to_double (d->x); |
||
706 | double dy = _cairo_fixed_to_double (d->y); |
||
707 | |||
708 | if (info->ctm_inverse) { |
||
709 | cairo_matrix_transform_point (info->ctm_inverse, &bx, &by); |
||
710 | cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy); |
||
711 | cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy); |
||
712 | } |
||
713 | |||
714 | _cairo_output_stream_printf (info->output, |
||
715 | "C %f %f %f %f %f %f ", |
||
716 | bx, by, cx, cy, dx, dy); |
||
717 | |||
718 | return CAIRO_STATUS_SUCCESS; |
||
719 | } |
||
720 | |||
721 | static cairo_status_t |
||
722 | _cairo_svg_path_close_path (void *closure) |
||
723 | { |
||
724 | svg_path_info_t *info = closure; |
||
725 | |||
726 | _cairo_output_stream_printf (info->output, "Z "); |
||
727 | |||
728 | return CAIRO_STATUS_SUCCESS; |
||
729 | } |
||
730 | |||
731 | static void |
||
732 | _cairo_svg_surface_emit_path (cairo_output_stream_t *output, |
||
3959 | Serge | 733 | const cairo_path_fixed_t *path, |
1897 | serge | 734 | const cairo_matrix_t *ctm_inverse) |
735 | { |
||
736 | cairo_status_t status; |
||
737 | svg_path_info_t info; |
||
738 | |||
739 | _cairo_output_stream_printf (output, "d=\""); |
||
740 | |||
741 | info.output = output; |
||
742 | info.ctm_inverse = ctm_inverse; |
||
743 | status = _cairo_path_fixed_interpret (path, |
||
744 | _cairo_svg_path_move_to, |
||
745 | _cairo_svg_path_line_to, |
||
746 | _cairo_svg_path_curve_to, |
||
747 | _cairo_svg_path_close_path, |
||
748 | &info); |
||
749 | assert (status == CAIRO_STATUS_SUCCESS); |
||
750 | |||
751 | _cairo_output_stream_printf (output, "\""); |
||
752 | } |
||
753 | |||
754 | static cairo_int_status_t |
||
755 | _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, |
||
756 | cairo_scaled_font_t *scaled_font, |
||
757 | unsigned long glyph_index) |
||
758 | { |
||
759 | cairo_scaled_glyph_t *scaled_glyph; |
||
760 | cairo_int_status_t status; |
||
761 | |||
762 | status = _cairo_scaled_glyph_lookup (scaled_font, |
||
763 | glyph_index, |
||
764 | CAIRO_SCALED_GLYPH_INFO_METRICS| |
||
765 | CAIRO_SCALED_GLYPH_INFO_PATH, |
||
766 | &scaled_glyph); |
||
767 | if (unlikely (status)) |
||
768 | return status; |
||
769 | |||
770 | _cairo_output_stream_printf (document->xml_node_glyphs, |
||
771 | " |
||
772 | |||
773 | _cairo_svg_surface_emit_path (document->xml_node_glyphs, |
||
774 | scaled_glyph->path, NULL); |
||
775 | |||
776 | _cairo_output_stream_printf (document->xml_node_glyphs, |
||
777 | "/>\n"); |
||
778 | |||
779 | return status; |
||
780 | } |
||
781 | |||
782 | static cairo_int_status_t |
||
783 | _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, |
||
784 | cairo_scaled_font_t *scaled_font, |
||
785 | unsigned long glyph_index) |
||
786 | { |
||
787 | cairo_scaled_glyph_t *scaled_glyph; |
||
788 | cairo_image_surface_t *image; |
||
789 | cairo_status_t status; |
||
790 | uint8_t *row, *byte; |
||
791 | int rows, cols; |
||
792 | int x, y, bit; |
||
793 | |||
794 | status = _cairo_scaled_glyph_lookup (scaled_font, |
||
795 | glyph_index, |
||
796 | CAIRO_SCALED_GLYPH_INFO_METRICS | |
||
797 | CAIRO_SCALED_GLYPH_INFO_SURFACE, |
||
798 | &scaled_glyph); |
||
799 | if (unlikely (status)) |
||
800 | return status; |
||
801 | |||
802 | image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface, |
||
803 | CAIRO_FORMAT_A1); |
||
804 | status = image->base.status; |
||
805 | if (unlikely (status)) |
||
806 | return status; |
||
807 | |||
808 | _cairo_output_stream_printf (document->xml_node_glyphs, " |
||
809 | _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform", |
||
810 | &image->base.device_transform_inverse, NULL); |
||
811 | _cairo_output_stream_printf (document->xml_node_glyphs, ">/n"); |
||
812 | |||
813 | for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) { |
||
814 | for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { |
||
815 | uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); |
||
816 | for (bit = 7; bit >= 0 && x < image->width; bit--, x++) { |
||
817 | if (output_byte & (1 << bit)) { |
||
818 | _cairo_output_stream_printf (document->xml_node_glyphs, |
||
819 | " |
||
820 | x, y); |
||
821 | } |
||
822 | } |
||
823 | } |
||
824 | } |
||
825 | _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); |
||
826 | |||
827 | cairo_surface_destroy (&image->base); |
||
828 | |||
829 | return CAIRO_STATUS_SUCCESS; |
||
830 | } |
||
831 | |||
3959 | Serge | 832 | static cairo_int_status_t |
1897 | serge | 833 | _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, |
834 | cairo_scaled_font_t *scaled_font, |
||
835 | unsigned long scaled_font_glyph_index, |
||
836 | unsigned int font_id, |
||
837 | unsigned int subset_glyph_index) |
||
838 | { |
||
3959 | Serge | 839 | cairo_int_status_t status; |
1897 | serge | 840 | |
841 | _cairo_output_stream_printf (document->xml_node_glyphs, |
||
842 | " |
||
843 | font_id, |
||
844 | subset_glyph_index); |
||
845 | |||
846 | status = _cairo_svg_document_emit_outline_glyph_data (document, |
||
847 | scaled_font, |
||
848 | scaled_font_glyph_index); |
||
849 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) |
||
850 | status = _cairo_svg_document_emit_bitmap_glyph_data (document, |
||
851 | scaled_font, |
||
852 | scaled_font_glyph_index); |
||
853 | if (unlikely (status)) |
||
854 | return status; |
||
855 | |||
856 | _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); |
||
857 | |||
3959 | Serge | 858 | return CAIRO_INT_STATUS_SUCCESS; |
1897 | serge | 859 | } |
860 | |||
3959 | Serge | 861 | static cairo_int_status_t |
1897 | serge | 862 | _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset, |
863 | void *closure) |
||
864 | { |
||
865 | cairo_svg_document_t *document = closure; |
||
3959 | Serge | 866 | cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; |
1897 | serge | 867 | unsigned int i; |
868 | |||
869 | _cairo_scaled_font_freeze_cache (font_subset->scaled_font); |
||
870 | for (i = 0; i < font_subset->num_glyphs; i++) { |
||
871 | status = _cairo_svg_document_emit_glyph (document, |
||
872 | font_subset->scaled_font, |
||
873 | font_subset->glyphs[i], |
||
874 | font_subset->font_id, i); |
||
875 | if (unlikely (status)) |
||
876 | break; |
||
877 | } |
||
878 | _cairo_scaled_font_thaw_cache (font_subset->scaled_font); |
||
879 | |||
880 | return status; |
||
881 | } |
||
882 | |||
883 | static cairo_status_t |
||
884 | _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) |
||
885 | { |
||
886 | cairo_status_t status; |
||
887 | |||
888 | status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, |
||
889 | _cairo_svg_document_emit_font_subset, |
||
890 | document); |
||
891 | if (unlikely (status)) |
||
892 | goto FAIL; |
||
893 | |||
894 | status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, |
||
895 | _cairo_svg_document_emit_font_subset, |
||
896 | document); |
||
897 | |||
898 | FAIL: |
||
899 | _cairo_scaled_font_subsets_destroy (document->font_subsets); |
||
900 | document->font_subsets = NULL; |
||
901 | |||
902 | return status; |
||
903 | } |
||
904 | |||
905 | static char const * |
||
906 | _cairo_svg_surface_operators[] = { |
||
907 | "clear", |
||
908 | |||
909 | "src", "src-over", "src-in", |
||
910 | "src-out", "src-atop", |
||
911 | |||
912 | "dst", "dst-over", "dst-in", |
||
913 | "dst-out", "dst-atop", |
||
914 | |||
915 | "xor", "plus", |
||
916 | "color-dodge", /* FIXME: saturate ? */ |
||
917 | |||
918 | "multiply", "screen", "overlay", |
||
919 | "darken", "lighten", |
||
920 | "color-dodge", "color-burn", |
||
921 | "hard-light", "soft-light", |
||
922 | "difference", "exclusion" |
||
923 | }; |
||
924 | |||
925 | static cairo_bool_t |
||
926 | _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface, |
||
927 | cairo_operator_t op) |
||
928 | { |
||
929 | /* guard against newly added operators */ |
||
930 | if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators)) |
||
931 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
932 | |||
933 | /* allow operators being NULL if they are unsupported */ |
||
934 | if (_cairo_svg_surface_operators[op] == NULL) |
||
935 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
936 | |||
937 | return CAIRO_STATUS_SUCCESS; |
||
938 | } |
||
939 | |||
940 | static cairo_int_status_t |
||
941 | _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface, |
||
942 | cairo_operator_t op, |
||
943 | const cairo_pattern_t *pattern) |
||
944 | { |
||
945 | cairo_svg_document_t *document = surface->document; |
||
946 | |||
947 | if (surface->force_fallbacks && |
||
948 | surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) |
||
949 | { |
||
950 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
951 | } |
||
952 | |||
3959 | Serge | 953 | if (pattern->type == CAIRO_PATTERN_TYPE_MESH) |
954 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
955 | |||
1897 | serge | 956 | /* SVG doesn't support extend reflect for image pattern */ |
957 | if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && |
||
958 | pattern->extend == CAIRO_EXTEND_REFLECT) |
||
959 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
960 | |||
961 | if (document->svg_version >= CAIRO_SVG_VERSION_1_2) |
||
962 | return _cairo_svg_surface_analyze_operator (surface, op); |
||
963 | |||
964 | if (op == CAIRO_OPERATOR_OVER) |
||
965 | return CAIRO_STATUS_SUCCESS; |
||
966 | |||
967 | /* The SOURCE operator is only supported if there is nothing |
||
968 | * painted underneath. */ |
||
969 | if (op == CAIRO_OPERATOR_SOURCE) |
||
970 | return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; |
||
971 | |||
972 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
973 | } |
||
974 | |||
975 | static cairo_int_status_t |
||
976 | _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface, |
||
977 | cairo_operator_t op, |
||
978 | const cairo_pattern_t *pattern) |
||
979 | { |
||
980 | return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED; |
||
981 | } |
||
982 | |||
983 | static cairo_status_t |
||
984 | _cairo_svg_surface_finish (void *abstract_surface) |
||
985 | { |
||
986 | cairo_status_t status, status2; |
||
987 | cairo_svg_surface_t *surface = abstract_surface; |
||
988 | cairo_svg_document_t *document = surface->document; |
||
989 | cairo_svg_page_t *page; |
||
990 | unsigned int i; |
||
991 | |||
992 | if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) |
||
993 | status = _cairo_svg_document_finish (document); |
||
994 | else |
||
995 | status = CAIRO_STATUS_SUCCESS; |
||
996 | |||
997 | if (surface->xml_node != NULL) { |
||
998 | status2 = _cairo_output_stream_destroy (surface->xml_node); |
||
999 | if (status == CAIRO_STATUS_SUCCESS) |
||
1000 | status = status2; |
||
1001 | } |
||
1002 | |||
1003 | for (i = 0; i < surface->page_set.num_elements; i++) { |
||
1004 | page = _cairo_array_index (&surface->page_set, i); |
||
1005 | status2 = _cairo_output_stream_destroy (page->xml_node); |
||
1006 | if (status == CAIRO_STATUS_SUCCESS) |
||
1007 | status = status2; |
||
1008 | } |
||
1009 | _cairo_array_fini (&surface->page_set); |
||
1010 | |||
1011 | _cairo_surface_clipper_reset (&surface->clipper); |
||
1012 | |||
1013 | status2 = _cairo_svg_document_destroy (document); |
||
1014 | if (status == CAIRO_STATUS_SUCCESS) |
||
1015 | status = status2; |
||
1016 | |||
1017 | return status; |
||
1018 | } |
||
1019 | |||
1020 | |||
1021 | static void |
||
1022 | _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) |
||
1023 | { |
||
1024 | if (document->alpha_filter) |
||
1025 | return; |
||
1026 | |||
1027 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1028 | " |
||
1029 | "filterUnits=\"objectBoundingBox\" " |
||
1030 | "x=\"0%%\" y=\"0%%\" " |
||
1031 | "width=\"100%%\" height=\"100%%\">\n" |
||
1032 | " |
||
1033 | "in=\"SourceGraphic\" " |
||
1034 | "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n" |
||
1035 | "\n"); |
||
1036 | |||
1037 | document->alpha_filter = TRUE; |
||
1038 | } |
||
1039 | |||
1040 | typedef struct { |
||
1041 | cairo_output_stream_t *output; |
||
1042 | unsigned int in_mem; |
||
1043 | unsigned int trailing; |
||
1044 | unsigned char src[3]; |
||
1045 | } base64_write_closure_t; |
||
1046 | |||
1047 | static char const base64_table[64] = |
||
1048 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
||
1049 | |||
1050 | static cairo_status_t |
||
1051 | base64_write_func (void *closure, |
||
1052 | const unsigned char *data, |
||
1053 | unsigned int length) |
||
1054 | { |
||
1055 | base64_write_closure_t *info = (base64_write_closure_t *) closure; |
||
1056 | unsigned int i; |
||
1057 | unsigned char *src; |
||
1058 | |||
1059 | src = info->src; |
||
1060 | |||
1061 | if (info->in_mem + length < 3) { |
||
1062 | for (i = 0; i < length; i++) { |
||
1063 | src[i + info->in_mem] = *data++; |
||
1064 | } |
||
1065 | info->in_mem += length; |
||
1066 | return CAIRO_STATUS_SUCCESS; |
||
1067 | } |
||
1068 | |||
1069 | do { |
||
1070 | unsigned char dst[4]; |
||
1071 | |||
1072 | for (i = info->in_mem; i < 3; i++) { |
||
1073 | src[i] = *data++; |
||
1074 | length--; |
||
1075 | } |
||
1076 | info->in_mem = 0; |
||
1077 | |||
1078 | dst[0] = base64_table[src[0] >> 2]; |
||
1079 | dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; |
||
1080 | dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; |
||
1081 | dst[3] = base64_table[src[2] & 0xfc >> 2]; |
||
1082 | /* Special case for the last missing bits */ |
||
1083 | switch (info->trailing) { |
||
1084 | case 2: |
||
1085 | dst[2] = '='; |
||
1086 | case 1: |
||
1087 | dst[3] = '='; |
||
1088 | default: |
||
1089 | break; |
||
1090 | } |
||
1091 | _cairo_output_stream_write (info->output, dst, 4); |
||
1092 | } while (length >= 3); |
||
1093 | |||
1094 | for (i = 0; i < length; i++) { |
||
1095 | src[i] = *data++; |
||
1096 | } |
||
1097 | info->in_mem = length; |
||
1098 | |||
1099 | return _cairo_output_stream_get_status (info->output); |
||
1100 | } |
||
1101 | |||
1102 | static cairo_int_status_t |
||
1103 | _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, |
||
1104 | cairo_output_stream_t *output) |
||
1105 | { |
||
1106 | const unsigned char *mime_data; |
||
1107 | unsigned long mime_data_length; |
||
1108 | cairo_image_info_t image_info; |
||
1109 | base64_write_closure_t info; |
||
1110 | cairo_status_t status; |
||
1111 | |||
1112 | cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG, |
||
1113 | &mime_data, &mime_data_length); |
||
1114 | if (mime_data == NULL) |
||
1115 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1116 | |||
1117 | status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length); |
||
1118 | if (unlikely (status)) |
||
1119 | return status; |
||
1120 | |||
1121 | _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); |
||
1122 | |||
1123 | info.output = output; |
||
1124 | info.in_mem = 0; |
||
1125 | info.trailing = 0; |
||
1126 | |||
1127 | status = base64_write_func (&info, mime_data, mime_data_length); |
||
1128 | if (unlikely (status)) |
||
1129 | return status; |
||
1130 | |||
1131 | if (info.in_mem > 0) { |
||
1132 | memset (info.src + info.in_mem, 0, 3 - info.in_mem); |
||
1133 | info.trailing = 3 - info.in_mem; |
||
1134 | info.in_mem = 3; |
||
1135 | status = base64_write_func (&info, NULL, 0); |
||
1136 | } |
||
1137 | |||
1138 | return status; |
||
1139 | } |
||
1140 | |||
1141 | static cairo_int_status_t |
||
1142 | _cairo_surface_base64_encode_png (cairo_surface_t *surface, |
||
1143 | cairo_output_stream_t *output) |
||
1144 | { |
||
1145 | const unsigned char *mime_data; |
||
1146 | unsigned long mime_data_length; |
||
1147 | base64_write_closure_t info; |
||
1148 | cairo_status_t status; |
||
1149 | |||
1150 | cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, |
||
1151 | &mime_data, &mime_data_length); |
||
1152 | if (unlikely (surface->status)) |
||
1153 | return surface->status; |
||
1154 | if (mime_data == NULL) |
||
1155 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1156 | |||
1157 | _cairo_output_stream_printf (output, "data:image/png;base64,"); |
||
1158 | |||
1159 | info.output = output; |
||
1160 | info.in_mem = 0; |
||
1161 | info.trailing = 0; |
||
1162 | |||
1163 | status = base64_write_func (&info, mime_data, mime_data_length); |
||
1164 | if (unlikely (status)) |
||
1165 | return status; |
||
1166 | |||
1167 | if (info.in_mem > 0) { |
||
1168 | memset (info.src + info.in_mem, 0, 3 - info.in_mem); |
||
1169 | info.trailing = 3 - info.in_mem; |
||
1170 | info.in_mem = 3; |
||
1171 | status = base64_write_func (&info, NULL, 0); |
||
1172 | } |
||
1173 | |||
1174 | return status; |
||
1175 | } |
||
1176 | |||
1177 | static cairo_int_status_t |
||
1178 | _cairo_surface_base64_encode (cairo_surface_t *surface, |
||
1179 | cairo_output_stream_t *output) |
||
1180 | { |
||
3959 | Serge | 1181 | cairo_int_status_t status; |
1897 | serge | 1182 | base64_write_closure_t info; |
1183 | |||
1184 | status = _cairo_surface_base64_encode_jpeg (surface, output); |
||
1185 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
||
1186 | return status; |
||
1187 | |||
1188 | status = _cairo_surface_base64_encode_png (surface, output); |
||
1189 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
||
1190 | return status; |
||
1191 | |||
1192 | info.output = output; |
||
1193 | info.in_mem = 0; |
||
1194 | info.trailing = 0; |
||
1195 | |||
1196 | _cairo_output_stream_printf (info.output, "data:image/png;base64,"); |
||
1197 | |||
1198 | status = cairo_surface_write_to_png_stream (surface, base64_write_func, |
||
1199 | (void *) &info); |
||
1200 | |||
1201 | if (unlikely (status)) |
||
1202 | return status; |
||
1203 | |||
1204 | if (info.in_mem > 0) { |
||
1205 | memset (info.src + info.in_mem, 0, 3 - info.in_mem); |
||
1206 | info.trailing = 3 - info.in_mem; |
||
1207 | info.in_mem = 3; |
||
1208 | status = base64_write_func (&info, NULL, 0); |
||
1209 | } |
||
1210 | |||
1211 | return status; |
||
1212 | } |
||
1213 | |||
1214 | static void |
||
1215 | _cairo_svg_surface_emit_operator (cairo_output_stream_t *output, |
||
1216 | cairo_svg_surface_t *surface, |
||
1217 | cairo_operator_t op) |
||
1218 | { |
||
1219 | if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && |
||
1220 | op != CAIRO_OPERATOR_OVER) { |
||
1221 | _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]); |
||
1222 | if (!_cairo_operator_bounded_by_source (op)) |
||
1223 | _cairo_output_stream_printf (output, " clip-to-self=\"true\""); |
||
1224 | } |
||
1225 | } |
||
1226 | |||
1227 | static void |
||
1228 | _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, |
||
1229 | cairo_svg_surface_t *surface, |
||
1230 | cairo_operator_t op) |
||
1231 | { |
||
1232 | if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && |
||
1233 | op != CAIRO_OPERATOR_OVER) { |
||
1234 | _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]); |
||
1235 | if (!_cairo_operator_bounded_by_source (op)) |
||
1236 | _cairo_output_stream_printf (output, "clip-to-self:true;"); |
||
1237 | } |
||
1238 | } |
||
1239 | |||
1240 | /** |
||
1241 | * _cairo_svg_surface_emit_attr_value: |
||
1242 | * |
||
1243 | * Write the value to output the stream as a sequence of characters, |
||
1244 | * while escaping those which have special meaning in the XML |
||
1245 | * attribute's value context: & and ". |
||
1246 | **/ |
||
1247 | static void |
||
1248 | _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream, |
||
1249 | const unsigned char *value, |
||
1250 | unsigned int length) |
||
1251 | { |
||
1252 | const unsigned char *p; |
||
1253 | const unsigned char *q; |
||
1254 | unsigned int i; |
||
1255 | |||
1256 | /* we'll accumulate non-special chars in [q, p) range */ |
||
1257 | p = value; |
||
1258 | q = p; |
||
1259 | for (i = 0; i < length; i++, p++) { |
||
1260 | if (*p == '&' || *p == '"') { |
||
1261 | /* flush what's left before special char */ |
||
1262 | if (p != q) { |
||
1263 | _cairo_output_stream_write (stream, q, p - q); |
||
1264 | q = p + 1; |
||
1265 | } |
||
1266 | |||
1267 | if (*p == '&') |
||
1268 | _cairo_output_stream_printf (stream, "&"); |
||
1269 | else // p == '"' |
||
1270 | _cairo_output_stream_printf (stream, """); |
||
1271 | } |
||
1272 | } |
||
1273 | |||
1274 | /* flush the trailing chars if any */ |
||
1275 | if (p != q) |
||
1276 | _cairo_output_stream_write (stream, q, p - q); |
||
1277 | } |
||
1278 | |||
1279 | static cairo_status_t |
||
1280 | _cairo_svg_surface_emit_surface (cairo_svg_document_t *document, |
||
1281 | cairo_surface_t *surface) |
||
1282 | { |
||
1283 | cairo_rectangle_int_t extents; |
||
1284 | cairo_bool_t is_bounded; |
||
1285 | cairo_status_t status; |
||
1286 | const unsigned char *uri; |
||
1287 | unsigned long uri_len; |
||
1288 | |||
1289 | if (_cairo_user_data_array_get_data (&surface->user_data, |
||
1290 | (cairo_user_data_key_t *) document)) |
||
1291 | { |
||
1292 | return CAIRO_STATUS_SUCCESS; |
||
1293 | } |
||
1294 | |||
1295 | is_bounded = _cairo_surface_get_extents (surface, &extents); |
||
1296 | assert (is_bounded); |
||
1297 | |||
1298 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1299 | " |
||
1300 | surface->unique_id, |
||
1301 | extents.width, extents.height); |
||
1302 | |||
1303 | _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\""); |
||
1304 | |||
1305 | cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI, |
||
1306 | &uri, &uri_len); |
||
1307 | if (uri != NULL) { |
||
1308 | _cairo_svg_surface_emit_attr_value (document->xml_node_defs, |
||
1309 | uri, uri_len); |
||
1310 | } else { |
||
1311 | status = _cairo_surface_base64_encode (surface, |
||
1312 | document->xml_node_defs); |
||
1313 | if (unlikely (status)) |
||
1314 | return status; |
||
1315 | } |
||
1316 | |||
1317 | _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n"); |
||
1318 | |||
1319 | /* and tag it */ |
||
1320 | return _cairo_user_data_array_set_data (&surface->user_data, |
||
1321 | (cairo_user_data_key_t *) document, |
||
1322 | document, NULL); |
||
1323 | } |
||
1324 | |||
1325 | static cairo_status_t |
||
1326 | _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output, |
||
1327 | cairo_svg_surface_t *svg_surface, |
||
1328 | cairo_operator_t op, |
||
1329 | cairo_surface_pattern_t *pattern, |
||
1330 | int pattern_id, |
||
1331 | const cairo_matrix_t *parent_matrix, |
||
1332 | const char *extra_attributes) |
||
1333 | { |
||
1334 | cairo_status_t status; |
||
1335 | cairo_matrix_t p2u; |
||
1336 | |||
1337 | p2u = pattern->base.matrix; |
||
1338 | status = cairo_matrix_invert (&p2u); |
||
1339 | /* cairo_pattern_set_matrix ensures the matrix is invertible */ |
||
1340 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1341 | |||
1342 | status = _cairo_svg_surface_emit_surface (svg_surface->document, |
||
1343 | pattern->surface); |
||
1344 | if (unlikely (status)) |
||
1345 | return status; |
||
1346 | |||
1347 | if (pattern_id != invalid_pattern_id) { |
||
1348 | cairo_rectangle_int_t extents; |
||
1349 | cairo_bool_t is_bounded; |
||
1350 | |||
1351 | is_bounded = _cairo_surface_get_extents (pattern->surface, &extents); |
||
1352 | assert (is_bounded); |
||
1353 | |||
1354 | _cairo_output_stream_printf (output, |
||
1355 | " |
||
1356 | "patternUnits=\"userSpaceOnUse\" " |
||
1357 | "width=\"%d\" height=\"%d\" ", |
||
1358 | pattern_id, |
||
1359 | extents.width, extents.height); |
||
1360 | _cairo_svg_surface_emit_transform (output, |
||
1361 | " patternTransform", |
||
1362 | &p2u, parent_matrix); |
||
1363 | _cairo_output_stream_printf (output, ">\n "); |
||
1364 | } |
||
1365 | |||
1366 | _cairo_output_stream_printf (output, |
||
1367 | " |
||
1368 | pattern->surface->unique_id); |
||
1369 | if (extra_attributes) |
||
1370 | _cairo_output_stream_printf (output, " %s", extra_attributes); |
||
1371 | |||
1372 | if (pattern_id == invalid_pattern_id) { |
||
1373 | _cairo_svg_surface_emit_operator (output, svg_surface, op); |
||
1374 | _cairo_svg_surface_emit_transform (output, |
||
1375 | " transform", |
||
1376 | &p2u, parent_matrix); |
||
1377 | } |
||
1378 | _cairo_output_stream_printf (output, "/>\n"); |
||
1379 | |||
1380 | |||
1381 | if (pattern_id != invalid_pattern_id) |
||
1382 | _cairo_output_stream_printf (output, "\n"); |
||
1383 | |||
1384 | return CAIRO_STATUS_SUCCESS; |
||
1385 | } |
||
1386 | |||
1387 | static cairo_status_t |
||
1388 | _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, |
||
1389 | cairo_recording_surface_t *source) |
||
1390 | { |
||
1391 | cairo_status_t status; |
||
1392 | cairo_surface_t *paginated_surface; |
||
1393 | cairo_svg_surface_t *svg_surface; |
||
1394 | cairo_array_t *page_set; |
||
1395 | |||
1396 | cairo_output_stream_t *contents; |
||
1397 | |||
1398 | if (_cairo_user_data_array_get_data (&source->base.user_data, |
||
1399 | (cairo_user_data_key_t *) document)) |
||
1400 | { |
||
1401 | return CAIRO_STATUS_SUCCESS; |
||
1402 | } |
||
1403 | |||
1404 | paginated_surface = _cairo_svg_surface_create_for_document (document, |
||
3959 | Serge | 1405 | source->base.content, |
1897 | serge | 1406 | source->extents_pixels.width, |
1407 | source->extents_pixels.height); |
||
1408 | if (unlikely (paginated_surface->status)) |
||
1409 | return paginated_surface->status; |
||
1410 | |||
1411 | svg_surface = (cairo_svg_surface_t *) |
||
1412 | _cairo_paginated_surface_get_target (paginated_surface); |
||
1413 | cairo_surface_set_fallback_resolution (paginated_surface, |
||
1414 | document->owner->x_fallback_resolution, |
||
1415 | document->owner->y_fallback_resolution); |
||
1416 | cairo_surface_set_device_offset (&svg_surface->base, |
||
1417 | -source->extents_pixels.x, |
||
1418 | -source->extents_pixels.y); |
||
1419 | |||
1420 | status = _cairo_recording_surface_replay (&source->base, paginated_surface); |
||
1421 | if (unlikely (status)) { |
||
1422 | cairo_surface_destroy (paginated_surface); |
||
1423 | return status; |
||
1424 | } |
||
1425 | |||
1426 | cairo_surface_show_page (paginated_surface); |
||
1427 | status = cairo_surface_status (paginated_surface); |
||
1428 | if (unlikely (status)) { |
||
1429 | cairo_surface_destroy (paginated_surface); |
||
1430 | return status; |
||
1431 | } |
||
1432 | |||
1433 | if (! svg_surface->is_base_clip_emitted) { |
||
1434 | svg_surface->is_base_clip_emitted = TRUE; |
||
1435 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1436 | " |
||
1437 | " |
||
1438 | "\n", |
||
1439 | svg_surface->base_clip, |
||
1440 | svg_surface->width, |
||
1441 | svg_surface->height); |
||
1442 | } |
||
1443 | |||
3959 | Serge | 1444 | if (source->base.content == CAIRO_CONTENT_ALPHA) { |
1897 | serge | 1445 | _cairo_svg_surface_emit_alpha_filter (document); |
1446 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1447 | " |
||
1448 | "clip-path=\"url(#clip%d)\" " |
||
1449 | "filter=\"url(#alpha)\">\n", |
||
1450 | source->base.unique_id, |
||
1451 | svg_surface->base_clip); |
||
1452 | } else { |
||
1453 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1454 | " |
||
1455 | "clip-path=\"url(#clip%d)\">\n", |
||
1456 | source->base.unique_id, |
||
1457 | svg_surface->base_clip); |
||
1458 | } |
||
1459 | |||
1460 | contents = svg_surface->xml_node; |
||
1461 | page_set = &svg_surface->page_set; |
||
1462 | |||
1463 | if (_cairo_memory_stream_length (contents) > 0) { |
||
1464 | if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) { |
||
1465 | cairo_surface_destroy (paginated_surface); |
||
1466 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1467 | } |
||
1468 | } |
||
1469 | |||
1470 | if (page_set->num_elements > 0) { |
||
1471 | cairo_svg_page_t *page; |
||
1472 | |||
1473 | page = _cairo_array_index (page_set, page_set->num_elements - 1); |
||
1474 | _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs); |
||
1475 | } |
||
1476 | |||
1477 | _cairo_output_stream_printf (document->xml_node_defs, "\n"); |
||
1478 | |||
1479 | status = cairo_surface_status (paginated_surface); |
||
1480 | cairo_surface_destroy (paginated_surface); |
||
1481 | |||
1482 | if (unlikely (status)) |
||
1483 | return status; |
||
1484 | |||
1485 | /* and tag it */ |
||
1486 | return _cairo_user_data_array_set_data (&source->base.user_data, |
||
1487 | (cairo_user_data_key_t *) document, |
||
1488 | document, NULL); |
||
1489 | } |
||
1490 | |||
3959 | Serge | 1491 | static cairo_recording_surface_t * |
1492 | to_recording_surface (const cairo_surface_pattern_t *pattern) |
||
1493 | { |
||
1494 | cairo_surface_t *surface = pattern->surface; |
||
1495 | if (_cairo_surface_is_paginated (surface)) |
||
1496 | surface = _cairo_paginated_surface_get_recording (surface); |
||
1497 | if (_cairo_surface_is_snapshot (surface)) |
||
1498 | surface = _cairo_surface_snapshot_get_target (surface); |
||
1499 | return (cairo_recording_surface_t *) surface; |
||
1500 | } |
||
1501 | |||
1897 | serge | 1502 | static cairo_status_t |
1503 | _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output, |
||
1504 | cairo_svg_surface_t *surface, |
||
1505 | cairo_operator_t op, |
||
1506 | cairo_surface_pattern_t *pattern, |
||
1507 | int pattern_id, |
||
1508 | const cairo_matrix_t *parent_matrix, |
||
1509 | const char *extra_attributes) |
||
1510 | { |
||
1511 | cairo_svg_document_t *document = surface->document; |
||
1512 | cairo_recording_surface_t *recording_surface; |
||
1513 | cairo_matrix_t p2u; |
||
1514 | cairo_status_t status; |
||
1515 | |||
1516 | p2u = pattern->base.matrix; |
||
1517 | status = cairo_matrix_invert (&p2u); |
||
1518 | /* cairo_pattern_set_matrix ensures the matrix is invertible */ |
||
1519 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1520 | |||
3959 | Serge | 1521 | recording_surface = to_recording_surface (pattern); |
1897 | serge | 1522 | status = _cairo_svg_surface_emit_recording_surface (document, recording_surface); |
1523 | if (unlikely (status)) |
||
1524 | return status; |
||
1525 | |||
1526 | if (pattern_id != invalid_pattern_id) { |
||
1527 | _cairo_output_stream_printf (output, |
||
1528 | " |
||
1529 | "patternUnits=\"userSpaceOnUse\" " |
||
1530 | "width=\"%d\" height=\"%d\"", |
||
1531 | pattern_id, |
||
1532 | recording_surface->extents.width, |
||
1533 | recording_surface->extents.height); |
||
1534 | _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix); |
||
1535 | _cairo_output_stream_printf (output, ">\n"); |
||
1536 | } |
||
1537 | |||
1538 | _cairo_output_stream_printf (output, |
||
1539 | " |
||
1540 | recording_surface->base.unique_id); |
||
1541 | |||
1542 | if (pattern_id == invalid_pattern_id) { |
||
1543 | _cairo_svg_surface_emit_operator (output, surface, op); |
||
1544 | _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix); |
||
1545 | } |
||
1546 | |||
1547 | if (extra_attributes) |
||
1548 | _cairo_output_stream_printf (output, " %s", extra_attributes); |
||
1549 | |||
1550 | _cairo_output_stream_printf (output, "/>\n"); |
||
1551 | |||
1552 | if (pattern_id != invalid_pattern_id) |
||
1553 | _cairo_output_stream_printf (output, "\n"); |
||
1554 | |||
1555 | return CAIRO_STATUS_SUCCESS; |
||
1556 | } |
||
1557 | |||
1558 | static cairo_status_t |
||
1559 | _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output, |
||
1560 | cairo_svg_surface_t *surface, |
||
1561 | cairo_operator_t op, |
||
1562 | cairo_surface_pattern_t *pattern, |
||
1563 | int pattern_id, |
||
1564 | const cairo_matrix_t *parent_matrix, |
||
1565 | const char *extra_attributes) |
||
1566 | { |
||
1567 | |||
3959 | Serge | 1568 | if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { |
1897 | serge | 1569 | return _cairo_svg_surface_emit_composite_recording_pattern (output, surface, |
1570 | op, pattern, |
||
1571 | pattern_id, |
||
1572 | parent_matrix, |
||
1573 | extra_attributes); |
||
1574 | } |
||
1575 | |||
1576 | return _cairo_svg_surface_emit_composite_surface_pattern (output, surface, |
||
1577 | op, pattern, |
||
1578 | pattern_id, |
||
1579 | parent_matrix, |
||
1580 | extra_attributes); |
||
1581 | } |
||
1582 | |||
1583 | static cairo_status_t |
||
1584 | _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface, |
||
1585 | cairo_solid_pattern_t *pattern, |
||
1586 | cairo_output_stream_t *style, |
||
1587 | cairo_bool_t is_stroke) |
||
1588 | { |
||
1589 | _cairo_output_stream_printf (style, is_stroke ? |
||
1590 | "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;": |
||
1591 | "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;", |
||
1592 | pattern->color.red * 100.0, |
||
1593 | pattern->color.green * 100.0, |
||
1594 | pattern->color.blue * 100.0, |
||
1595 | pattern->color.alpha); |
||
1596 | |||
1597 | return CAIRO_STATUS_SUCCESS; |
||
1598 | } |
||
1599 | |||
1600 | static cairo_status_t |
||
1601 | _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface, |
||
1602 | cairo_surface_pattern_t *pattern, |
||
1603 | cairo_output_stream_t *style, |
||
1604 | cairo_bool_t is_stroke, |
||
1605 | const cairo_matrix_t *parent_matrix) |
||
1606 | { |
||
1607 | cairo_svg_document_t *document = surface->document; |
||
1608 | cairo_status_t status; |
||
1609 | int pattern_id; |
||
1610 | |||
1611 | pattern_id = document->pattern_id++; |
||
1612 | status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs, |
||
1613 | surface, CAIRO_OPERATOR_SOURCE, pattern, |
||
1614 | pattern_id, parent_matrix, NULL); |
||
1615 | if (unlikely (status)) |
||
1616 | return status; |
||
1617 | |||
1618 | _cairo_output_stream_printf (style, |
||
1619 | "%s:url(#pattern%d);", |
||
1620 | is_stroke ? "stroke" : "fill", |
||
1621 | pattern_id); |
||
1622 | |||
1623 | return CAIRO_STATUS_SUCCESS; |
||
1624 | } |
||
1625 | |||
1626 | static cairo_status_t |
||
1627 | _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, |
||
1628 | cairo_gradient_pattern_t const *pattern, |
||
1629 | double start_offset, |
||
1630 | cairo_bool_t reverse_stops, |
||
1631 | cairo_bool_t emulate_reflect) |
||
1632 | { |
||
1633 | cairo_gradient_stop_t *stops; |
||
1634 | double offset; |
||
1635 | unsigned int n_stops; |
||
1636 | unsigned int i; |
||
1637 | |||
1638 | if (pattern->n_stops < 1) |
||
1639 | return CAIRO_STATUS_SUCCESS; |
||
1640 | |||
1641 | if (pattern->n_stops == 1) { |
||
1642 | _cairo_output_stream_printf (output, |
||
1643 | " |
||
1644 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1645 | "stop-opacity:%f;\"/>\n", |
||
1646 | pattern->stops[0].offset, |
||
1647 | pattern->stops[0].color.red * 100.0, |
||
1648 | pattern->stops[0].color.green * 100.0, |
||
1649 | pattern->stops[0].color.blue * 100.0, |
||
1650 | pattern->stops[0].color.alpha); |
||
1651 | return CAIRO_STATUS_SUCCESS; |
||
1652 | } |
||
1653 | |||
1654 | if (emulate_reflect || reverse_stops) { |
||
1655 | n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops; |
||
1656 | stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t)); |
||
1657 | if (unlikely (stops == NULL)) |
||
1658 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1659 | |||
1660 | for (i = 0; i < pattern->n_stops; i++) { |
||
1661 | if (reverse_stops) { |
||
1662 | stops[i] = pattern->stops[pattern->n_stops - i - 1]; |
||
1663 | stops[i].offset = 1.0 - stops[i].offset; |
||
1664 | } else |
||
1665 | stops[i] = pattern->stops[i]; |
||
1666 | if (emulate_reflect) { |
||
1667 | stops[i].offset /= 2; |
||
1668 | if (i > 0 && i < (pattern->n_stops - 1)) { |
||
1669 | if (reverse_stops) { |
||
1670 | stops[i + pattern->n_stops - 1] = pattern->stops[i]; |
||
1671 | stops[i + pattern->n_stops - 1].offset = |
||
1672 | 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset; |
||
1673 | } else { |
||
1674 | stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1]; |
||
1675 | stops[i + pattern->n_stops - 1].offset = |
||
1676 | 1 - 0.5 * stops[i + pattern->n_stops - 1].offset; |
||
1677 | } |
||
1678 | } |
||
1679 | } |
||
1680 | } |
||
1681 | } else { |
||
1682 | n_stops = pattern->n_stops; |
||
1683 | stops = pattern->stops; |
||
1684 | } |
||
1685 | |||
1686 | if (start_offset >= 0.0) |
||
1687 | for (i = 0; i < n_stops; i++) { |
||
1688 | offset = start_offset + (1 - start_offset ) * stops[i].offset; |
||
1689 | _cairo_output_stream_printf (output, |
||
1690 | " |
||
1691 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1692 | "stop-opacity:%f;\"/>\n", |
||
1693 | offset, |
||
1694 | stops[i].color.red * 100.0, |
||
1695 | stops[i].color.green * 100.0, |
||
1696 | stops[i].color.blue * 100.0, |
||
1697 | stops[i].color.alpha); |
||
1698 | } |
||
1699 | else { |
||
1700 | cairo_bool_t found = FALSE; |
||
1701 | unsigned int offset_index; |
||
1702 | cairo_color_stop_t offset_color_start, offset_color_stop; |
||
1703 | |||
1704 | for (i = 0; i < n_stops; i++) { |
||
1705 | if (stops[i].offset >= -start_offset) { |
||
1706 | if (i > 0) { |
||
1707 | if (stops[i].offset != stops[i-1].offset) { |
||
1708 | double x0, x1; |
||
1709 | cairo_color_stop_t *color0, *color1; |
||
1710 | |||
1711 | x0 = stops[i-1].offset; |
||
1712 | x1 = stops[i].offset; |
||
1713 | color0 = &stops[i-1].color; |
||
1714 | color1 = &stops[i].color; |
||
1715 | offset_color_start.red = color0->red + (color1->red - color0->red) |
||
1716 | * (-start_offset - x0) / (x1 - x0); |
||
1717 | offset_color_start.green = color0->green + (color1->green - color0->green) |
||
1718 | * (-start_offset - x0) / (x1 - x0); |
||
1719 | offset_color_start.blue = color0->blue + (color1->blue - color0->blue) |
||
1720 | * (-start_offset - x0) / (x1 - x0); |
||
1721 | offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha) |
||
1722 | * (-start_offset - x0) / (x1 - x0); |
||
1723 | offset_color_stop = offset_color_start; |
||
1724 | } else { |
||
1725 | offset_color_stop = stops[i-1].color; |
||
1726 | offset_color_start = stops[i].color; |
||
1727 | } |
||
1728 | } else |
||
1729 | offset_color_stop = offset_color_start = stops[i].color; |
||
1730 | offset_index = i; |
||
1731 | found = TRUE; |
||
1732 | break; |
||
1733 | } |
||
1734 | } |
||
1735 | |||
1736 | if (!found) { |
||
1737 | offset_index = n_stops - 1; |
||
1738 | offset_color_stop = offset_color_start = stops[offset_index].color; |
||
1739 | } |
||
1740 | |||
1741 | _cairo_output_stream_printf (output, |
||
1742 | " |
||
1743 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1744 | "stop-opacity:%f;\"/>\n", |
||
1745 | offset_color_start.red * 100.0, |
||
1746 | offset_color_start.green * 100.0, |
||
1747 | offset_color_start.blue * 100.0, |
||
1748 | offset_color_start.alpha); |
||
1749 | for (i = offset_index; i < n_stops; i++) { |
||
1750 | _cairo_output_stream_printf (output, |
||
1751 | " |
||
1752 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1753 | "stop-opacity:%f;\"/>\n", |
||
1754 | stops[i].offset + start_offset, |
||
1755 | stops[i].color.red * 100.0, |
||
1756 | stops[i].color.green * 100.0, |
||
1757 | stops[i].color.blue * 100.0, |
||
1758 | stops[i].color.alpha); |
||
1759 | } |
||
1760 | for (i = 0; i < offset_index; i++) { |
||
1761 | _cairo_output_stream_printf (output, |
||
1762 | " |
||
1763 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1764 | "stop-opacity:%f;\"/>\n", |
||
1765 | 1.0 + stops[i].offset + start_offset, |
||
1766 | stops[i].color.red * 100.0, |
||
1767 | stops[i].color.green * 100.0, |
||
1768 | stops[i].color.blue * 100.0, |
||
1769 | stops[i].color.alpha); |
||
1770 | } |
||
1771 | |||
1772 | _cairo_output_stream_printf (output, |
||
1773 | " |
||
1774 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1775 | "stop-opacity:%f;\"/>\n", |
||
1776 | offset_color_stop.red * 100.0, |
||
1777 | offset_color_stop.green * 100.0, |
||
1778 | offset_color_stop.blue * 100.0, |
||
1779 | offset_color_stop.alpha); |
||
1780 | |||
1781 | } |
||
1782 | |||
1783 | if (reverse_stops || emulate_reflect) |
||
1784 | free (stops); |
||
1785 | |||
1786 | return CAIRO_STATUS_SUCCESS; |
||
1787 | } |
||
1788 | |||
1789 | static void |
||
1790 | _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output, |
||
1791 | cairo_pattern_t *pattern) |
||
1792 | { |
||
1793 | switch (pattern->extend) { |
||
1794 | case CAIRO_EXTEND_REPEAT: |
||
1795 | _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" "); |
||
1796 | break; |
||
1797 | case CAIRO_EXTEND_REFLECT: |
||
1798 | _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" "); |
||
1799 | break; |
||
1800 | case CAIRO_EXTEND_NONE: |
||
1801 | case CAIRO_EXTEND_PAD: |
||
1802 | break; |
||
1803 | } |
||
1804 | } |
||
1805 | |||
1806 | static cairo_status_t |
||
1807 | _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, |
||
1808 | cairo_linear_pattern_t *pattern, |
||
1809 | cairo_output_stream_t *style, |
||
1810 | cairo_bool_t is_stroke, |
||
1811 | const cairo_matrix_t *parent_matrix) |
||
1812 | { |
||
1813 | cairo_svg_document_t *document = surface->document; |
||
1814 | cairo_matrix_t p2u; |
||
1815 | cairo_status_t status; |
||
1816 | |||
1817 | p2u = pattern->base.base.matrix; |
||
1818 | status = cairo_matrix_invert (&p2u); |
||
1819 | /* cairo_pattern_set_matrix ensures the matrix is invertible */ |
||
1820 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1821 | |||
1822 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1823 | " |
||
1824 | "gradientUnits=\"userSpaceOnUse\" " |
||
1825 | "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ", |
||
1826 | document->linear_pattern_id, |
||
3959 | Serge | 1827 | pattern->pd1.x, pattern->pd1.y, |
1828 | pattern->pd2.x, pattern->pd2.y); |
||
1897 | serge | 1829 | |
1830 | _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base), |
||
1831 | _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); |
||
1832 | _cairo_output_stream_printf (document->xml_node_defs, ">\n"); |
||
1833 | |||
1834 | status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, |
||
1835 | &pattern->base, 0.0, |
||
1836 | FALSE, FALSE); |
||
1837 | if (unlikely (status)) |
||
1838 | return status; |
||
1839 | |||
1840 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1841 | "\n"); |
||
1842 | |||
1843 | _cairo_output_stream_printf (style, |
||
1844 | "%s:url(#linear%d);", |
||
1845 | is_stroke ? "stroke" : "fill", |
||
1846 | document->linear_pattern_id); |
||
1847 | |||
1848 | document->linear_pattern_id++; |
||
1849 | |||
1850 | return CAIRO_STATUS_SUCCESS; |
||
1851 | } |
||
1852 | |||
1853 | static cairo_status_t |
||
1854 | _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, |
||
1855 | cairo_radial_pattern_t *pattern, |
||
1856 | cairo_output_stream_t *style, |
||
1857 | cairo_bool_t is_stroke, |
||
1858 | const cairo_matrix_t *parent_matrix) |
||
1859 | { |
||
1860 | cairo_svg_document_t *document = surface->document; |
||
1861 | cairo_matrix_t p2u; |
||
1862 | cairo_extend_t extend; |
||
1863 | double x0, y0, x1, y1, r0, r1; |
||
1864 | double fx, fy; |
||
1865 | cairo_bool_t reverse_stops; |
||
1866 | cairo_status_t status; |
||
3959 | Serge | 1867 | cairo_circle_double_t *c0, *c1; |
1897 | serge | 1868 | |
1869 | extend = pattern->base.base.extend; |
||
1870 | |||
3959 | Serge | 1871 | if (pattern->cd1.radius < pattern->cd2.radius) { |
1872 | c0 = &pattern->cd1; |
||
1873 | c1 = &pattern->cd2; |
||
1897 | serge | 1874 | reverse_stops = FALSE; |
1875 | } else { |
||
3959 | Serge | 1876 | c0 = &pattern->cd2; |
1877 | c1 = &pattern->cd1; |
||
1897 | serge | 1878 | reverse_stops = TRUE; |
1879 | } |
||
1880 | |||
3959 | Serge | 1881 | x0 = c0->center.x; |
1882 | y0 = c0->center.y; |
||
1883 | r0 = c0->radius; |
||
1884 | x1 = c1->center.x; |
||
1885 | y1 = c1->center.y; |
||
1886 | r1 = c1->radius; |
||
1897 | serge | 1887 | |
1888 | p2u = pattern->base.base.matrix; |
||
1889 | status = cairo_matrix_invert (&p2u); |
||
1890 | /* cairo_pattern_set_matrix ensures the matrix is invertible */ |
||
1891 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1892 | |||
3959 | Serge | 1893 | if (r0 == r1) { |
1897 | serge | 1894 | unsigned int n_stops = pattern->base.n_stops; |
1895 | |||
1896 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1897 | " |
||
1898 | "gradientUnits=\"userSpaceOnUse\" " |
||
1899 | "cx=\"%f\" cy=\"%f\" " |
||
1900 | "fx=\"%f\" fy=\"%f\" r=\"%f\" ", |
||
1901 | document->radial_pattern_id, |
||
1902 | x1, y1, |
||
1903 | x1, y1, r1); |
||
1904 | _cairo_svg_surface_emit_transform (document->xml_node_defs, |
||
1905 | "gradientTransform", |
||
1906 | &p2u, parent_matrix); |
||
1907 | _cairo_output_stream_printf (document->xml_node_defs, ">\n"); |
||
1908 | |||
1909 | if (extend == CAIRO_EXTEND_NONE || n_stops < 1) |
||
1910 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1911 | " |
||
1912 | "stop-color:rgb(0%%,0%%,0%%);" |
||
1913 | "stop-opacity:0;\"/>\n"); |
||
1914 | else { |
||
1915 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1916 | " |
||
1917 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1918 | "stop-opacity %f;\"/>\n", |
||
1919 | pattern->base.stops[0].color.red * 100.0, |
||
1920 | pattern->base.stops[0].color.green * 100.0, |
||
1921 | pattern->base.stops[0].color.blue * 100.0, |
||
1922 | pattern->base.stops[0].color.alpha); |
||
1923 | if (n_stops > 1) |
||
1924 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1925 | " |
||
1926 | "stop-color:rgb(%f%%,%f%%,%f%%);" |
||
1927 | "stop-opacity:%f;\"/>\n", |
||
1928 | pattern->base.stops[n_stops - 1].color.red * 100.0, |
||
1929 | pattern->base.stops[n_stops - 1].color.green * 100.0, |
||
1930 | pattern->base.stops[n_stops - 1].color.blue * 100.0, |
||
1931 | pattern->base.stops[n_stops - 1].color.alpha); |
||
1932 | } |
||
1933 | |||
1934 | } else { |
||
1935 | double offset, r, x, y; |
||
1936 | cairo_bool_t emulate_reflect = FALSE; |
||
1937 | |||
1938 | fx = (r1 * x0 - r0 * x1) / (r1 - r0); |
||
1939 | fy = (r1 * y0 - r0 * y1) / (r1 - r0); |
||
1940 | |||
1941 | /* SVG doesn't support the inner circle and use instead a gradient focal. |
||
1942 | * That means we need to emulate the cairo behaviour by processing the |
||
1943 | * cairo gradient stops. |
||
1944 | * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, |
||
1945 | * it's just a matter of stop position translation and calculation of |
||
1946 | * the corresponding SVG radial gradient focal. |
||
1947 | * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new |
||
1948 | * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT |
||
1949 | * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop |
||
1950 | * list that maps to the original cairo stop list. |
||
1951 | */ |
||
1952 | if ((extend == CAIRO_EXTEND_REFLECT |
||
1953 | || extend == CAIRO_EXTEND_REPEAT) |
||
1954 | && r0 > 0.0) { |
||
1955 | double r_org = r1; |
||
1956 | |||
1957 | if (extend == CAIRO_EXTEND_REFLECT) { |
||
1958 | r1 = 2 * r1 - r0; |
||
1959 | emulate_reflect = TRUE; |
||
1960 | } |
||
1961 | |||
1962 | offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; |
||
1963 | r = r1 - r0; |
||
1964 | |||
1965 | /* New position of outer circle. */ |
||
1966 | x = r * (x1 - fx) / r_org + fx; |
||
1967 | y = r * (y1 - fy) / r_org + fy; |
||
1968 | |||
1969 | x1 = x; |
||
1970 | y1 = y; |
||
1971 | r1 = r; |
||
1972 | r0 = 0.0; |
||
1973 | } else { |
||
1974 | offset = r0 / r1; |
||
1975 | } |
||
1976 | |||
1977 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1978 | " |
||
1979 | "gradientUnits=\"userSpaceOnUse\" " |
||
1980 | "cx=\"%f\" cy=\"%f\" " |
||
1981 | "fx=\"%f\" fy=\"%f\" r=\"%f\" ", |
||
1982 | document->radial_pattern_id, |
||
1983 | x1, y1, |
||
1984 | fx, fy, r1); |
||
1985 | |||
1986 | if (emulate_reflect) |
||
1987 | _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" "); |
||
1988 | else |
||
1989 | _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base); |
||
1990 | _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); |
||
1991 | _cairo_output_stream_printf (document->xml_node_defs, ">\n"); |
||
1992 | |||
1993 | /* To support cairo's EXTEND_NONE, (for which SVG has no similar |
||
1994 | * notion), we add transparent color stops on either end of the |
||
1995 | * user-provided stops. */ |
||
1996 | if (extend == CAIRO_EXTEND_NONE) { |
||
1997 | _cairo_output_stream_printf (document->xml_node_defs, |
||
1998 | " |
||
1999 | "stop-color:rgb(0%%,0%%,0%%);" |
||
2000 | "stop-opacity:0;\"/>\n"); |
||
2001 | if (r0 != 0.0) |
||
2002 | _cairo_output_stream_printf (document->xml_node_defs, |
||
2003 | " |
||
2004 | "stop-color:rgb(0%%,0%%,0%%);" |
||
2005 | "stop-opacity:0;\"/>\n", |
||
2006 | r0 / r1); |
||
2007 | } |
||
2008 | status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, |
||
2009 | &pattern->base, offset, |
||
2010 | reverse_stops, |
||
2011 | emulate_reflect); |
||
2012 | if (unlikely (status)) |
||
2013 | return status; |
||
2014 | |||
2015 | if (pattern->base.base.extend == CAIRO_EXTEND_NONE) |
||
2016 | _cairo_output_stream_printf (document->xml_node_defs, |
||
2017 | " |
||
2018 | "stop-color:rgb(0%%,0%%,0%%);" |
||
2019 | "stop-opacity:0;\"/>\n"); |
||
2020 | } |
||
2021 | |||
2022 | _cairo_output_stream_printf (document->xml_node_defs, |
||
2023 | "\n"); |
||
2024 | |||
2025 | _cairo_output_stream_printf (style, |
||
2026 | "%s:url(#radial%d);", |
||
2027 | is_stroke ? "stroke" : "fill", |
||
2028 | document->radial_pattern_id); |
||
2029 | |||
2030 | document->radial_pattern_id++; |
||
2031 | |||
2032 | return CAIRO_STATUS_SUCCESS; |
||
2033 | } |
||
2034 | |||
2035 | static cairo_status_t |
||
2036 | _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, |
||
2037 | const cairo_pattern_t *pattern, |
||
2038 | cairo_output_stream_t *output, |
||
2039 | cairo_bool_t is_stroke, |
||
2040 | const cairo_matrix_t *parent_matrix) |
||
2041 | { |
||
2042 | switch (pattern->type) { |
||
2043 | case CAIRO_PATTERN_TYPE_SOLID: |
||
2044 | return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, |
||
2045 | output, is_stroke); |
||
2046 | |||
2047 | case CAIRO_PATTERN_TYPE_SURFACE: |
||
2048 | return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, |
||
2049 | output, is_stroke, parent_matrix); |
||
2050 | |||
2051 | case CAIRO_PATTERN_TYPE_LINEAR: |
||
2052 | return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, |
||
2053 | output, is_stroke, parent_matrix); |
||
2054 | |||
2055 | case CAIRO_PATTERN_TYPE_RADIAL: |
||
2056 | return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, |
||
2057 | output, is_stroke, parent_matrix); |
||
3959 | Serge | 2058 | |
2059 | case CAIRO_PATTERN_TYPE_MESH: |
||
2060 | case CAIRO_PATTERN_TYPE_RASTER_SOURCE: |
||
2061 | ASSERT_NOT_REACHED; |
||
1897 | serge | 2062 | } |
2063 | return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
||
2064 | } |
||
2065 | |||
2066 | static cairo_status_t |
||
2067 | _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, |
||
2068 | cairo_svg_surface_t *surface, |
||
2069 | cairo_operator_t op, |
||
2070 | const cairo_pattern_t *source, |
||
2071 | cairo_fill_rule_t fill_rule, |
||
2072 | const cairo_matrix_t *parent_matrix) |
||
2073 | { |
||
2074 | _cairo_output_stream_printf (output, |
||
2075 | "fill-rule:%s;", |
||
2076 | fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? |
||
2077 | "evenodd" : "nonzero"); |
||
2078 | _cairo_svg_surface_emit_operator_for_style (output, surface, op); |
||
2079 | return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix); |
||
2080 | } |
||
2081 | |||
2082 | static cairo_status_t |
||
2083 | _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, |
||
2084 | cairo_svg_surface_t *surface, |
||
2085 | cairo_operator_t op, |
||
2086 | const cairo_pattern_t *source, |
||
2087 | const cairo_stroke_style_t *stroke_style, |
||
2088 | const cairo_matrix_t *parent_matrix) |
||
2089 | { |
||
2090 | cairo_status_t status; |
||
2091 | const char *line_cap, *line_join; |
||
2092 | unsigned int i; |
||
2093 | |||
2094 | switch (stroke_style->line_cap) { |
||
2095 | case CAIRO_LINE_CAP_BUTT: |
||
2096 | line_cap = "butt"; |
||
2097 | break; |
||
2098 | case CAIRO_LINE_CAP_ROUND: |
||
2099 | line_cap = "round"; |
||
2100 | break; |
||
2101 | case CAIRO_LINE_CAP_SQUARE: |
||
2102 | line_cap = "square"; |
||
2103 | break; |
||
2104 | default: |
||
2105 | ASSERT_NOT_REACHED; |
||
2106 | } |
||
2107 | |||
2108 | switch (stroke_style->line_join) { |
||
2109 | case CAIRO_LINE_JOIN_MITER: |
||
2110 | line_join = "miter"; |
||
2111 | break; |
||
2112 | case CAIRO_LINE_JOIN_ROUND: |
||
2113 | line_join = "round"; |
||
2114 | break; |
||
2115 | case CAIRO_LINE_JOIN_BEVEL: |
||
2116 | line_join = "bevel"; |
||
2117 | break; |
||
2118 | default: |
||
2119 | ASSERT_NOT_REACHED; |
||
2120 | } |
||
2121 | |||
2122 | _cairo_output_stream_printf (output, |
||
2123 | "stroke-width:%f;" |
||
2124 | "stroke-linecap:%s;" |
||
2125 | "stroke-linejoin:%s;", |
||
2126 | stroke_style->line_width, |
||
2127 | line_cap, |
||
2128 | line_join); |
||
2129 | |||
2130 | status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); |
||
2131 | if (unlikely (status)) |
||
2132 | return status; |
||
2133 | |||
2134 | _cairo_svg_surface_emit_operator_for_style (output, surface, op); |
||
2135 | |||
2136 | if (stroke_style->num_dashes > 0) { |
||
2137 | _cairo_output_stream_printf (output, "stroke-dasharray:"); |
||
2138 | for (i = 0; i < stroke_style->num_dashes; i++) { |
||
2139 | _cairo_output_stream_printf (output, "%f", |
||
2140 | stroke_style->dash[i]); |
||
2141 | if (i + 1 < stroke_style->num_dashes) |
||
2142 | _cairo_output_stream_printf (output, ","); |
||
2143 | else |
||
2144 | _cairo_output_stream_printf (output, ";"); |
||
2145 | } |
||
2146 | if (stroke_style->dash_offset != 0.0) { |
||
2147 | _cairo_output_stream_printf (output, |
||
2148 | "stroke-dashoffset:%f;", |
||
2149 | stroke_style->dash_offset); |
||
2150 | } |
||
2151 | } |
||
2152 | |||
2153 | _cairo_output_stream_printf (output, |
||
2154 | "stroke-miterlimit:%f;", |
||
2155 | stroke_style->miter_limit); |
||
2156 | |||
2157 | return CAIRO_STATUS_SUCCESS; |
||
2158 | } |
||
2159 | |||
2160 | static cairo_int_status_t |
||
2161 | _cairo_svg_surface_fill_stroke (void *abstract_surface, |
||
2162 | cairo_operator_t fill_op, |
||
2163 | const cairo_pattern_t *fill_source, |
||
2164 | cairo_fill_rule_t fill_rule, |
||
2165 | double fill_tolerance, |
||
2166 | cairo_antialias_t fill_antialias, |
||
3959 | Serge | 2167 | const cairo_path_fixed_t*path, |
1897 | serge | 2168 | cairo_operator_t stroke_op, |
2169 | const cairo_pattern_t *stroke_source, |
||
2170 | const cairo_stroke_style_t *stroke_style, |
||
2171 | const cairo_matrix_t *stroke_ctm, |
||
2172 | const cairo_matrix_t *stroke_ctm_inverse, |
||
2173 | double stroke_tolerance, |
||
2174 | cairo_antialias_t stroke_antialias, |
||
3959 | Serge | 2175 | const cairo_clip_t *clip) |
1897 | serge | 2176 | { |
2177 | cairo_svg_surface_t *surface = abstract_surface; |
||
2178 | cairo_status_t status; |
||
2179 | |||
2180 | status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
||
2181 | if (unlikely (status)) |
||
2182 | return status; |
||
2183 | |||
2184 | _cairo_output_stream_printf (surface->xml_node, " |
||
2185 | status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op, |
||
2186 | fill_source, fill_rule, stroke_ctm_inverse); |
||
2187 | if (unlikely (status)) |
||
2188 | return status; |
||
2189 | |||
2190 | status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op, |
||
2191 | stroke_source, stroke_style, stroke_ctm_inverse); |
||
2192 | if (unlikely (status)) |
||
2193 | return status; |
||
2194 | |||
2195 | _cairo_output_stream_printf (surface->xml_node, "\" "); |
||
2196 | |||
2197 | _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); |
||
2198 | |||
2199 | _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); |
||
2200 | _cairo_output_stream_printf (surface->xml_node, "/>\n"); |
||
2201 | |||
2202 | return CAIRO_STATUS_SUCCESS; |
||
2203 | } |
||
2204 | |||
2205 | static cairo_int_status_t |
||
2206 | _cairo_svg_surface_fill (void *abstract_surface, |
||
2207 | cairo_operator_t op, |
||
2208 | const cairo_pattern_t *source, |
||
3959 | Serge | 2209 | const cairo_path_fixed_t*path, |
1897 | serge | 2210 | cairo_fill_rule_t fill_rule, |
2211 | double tolerance, |
||
2212 | cairo_antialias_t antialias, |
||
3959 | Serge | 2213 | const cairo_clip_t *clip) |
1897 | serge | 2214 | { |
2215 | cairo_svg_surface_t *surface = abstract_surface; |
||
2216 | cairo_status_t status; |
||
2217 | |||
2218 | if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) |
||
2219 | return _cairo_svg_surface_analyze_operation (surface, op, source); |
||
2220 | |||
2221 | assert (_cairo_svg_surface_operation_supported (surface, op, source)); |
||
2222 | |||
2223 | status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
||
2224 | if (unlikely (status)) |
||
2225 | return status; |
||
2226 | |||
2227 | _cairo_output_stream_printf (surface->xml_node, " |
||
2228 | status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL); |
||
2229 | if (unlikely (status)) |
||
2230 | return status; |
||
2231 | |||
2232 | _cairo_output_stream_printf (surface->xml_node, "\" "); |
||
2233 | |||
2234 | _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); |
||
2235 | |||
2236 | _cairo_output_stream_printf (surface->xml_node, "/>\n"); |
||
2237 | |||
2238 | return CAIRO_STATUS_SUCCESS; |
||
2239 | } |
||
2240 | |||
2241 | static cairo_bool_t |
||
2242 | _cairo_svg_surface_get_extents (void *abstract_surface, |
||
2243 | cairo_rectangle_int_t *rectangle) |
||
2244 | { |
||
2245 | cairo_svg_surface_t *surface = abstract_surface; |
||
2246 | |||
2247 | rectangle->x = 0; |
||
2248 | rectangle->y = 0; |
||
2249 | |||
2250 | /* XXX: The conversion to integers here is pretty bogus, (not to |
||
2251 | * mention the arbitrary limitation of width to a short(!). We |
||
2252 | * may need to come up with a better interface for get_size. |
||
2253 | */ |
||
2254 | rectangle->width = ceil (surface->width); |
||
2255 | rectangle->height = ceil (surface->height); |
||
2256 | |||
2257 | return TRUE; |
||
2258 | } |
||
2259 | |||
2260 | static cairo_status_t |
||
2261 | _cairo_svg_surface_emit_paint (cairo_output_stream_t *output, |
||
2262 | cairo_svg_surface_t *surface, |
||
2263 | cairo_operator_t op, |
||
2264 | const cairo_pattern_t *source, |
||
2265 | const cairo_pattern_t *mask_source, |
||
2266 | const char *extra_attributes) |
||
2267 | { |
||
2268 | cairo_status_t status; |
||
2269 | |||
2270 | if (source->type == CAIRO_PATTERN_TYPE_SURFACE && |
||
2271 | source->extend == CAIRO_EXTEND_NONE) |
||
2272 | return _cairo_svg_surface_emit_composite_pattern (output, |
||
2273 | surface, |
||
2274 | op, |
||
2275 | (cairo_surface_pattern_t *) source, |
||
2276 | invalid_pattern_id, |
||
2277 | mask_source ? &mask_source->matrix :NULL, |
||
2278 | extra_attributes); |
||
2279 | |||
2280 | _cairo_output_stream_printf (output, |
||
2281 | " |
||
2282 | "width=\"%f\" height=\"%f\" " |
||
2283 | "style=\"", |
||
2284 | surface->width, surface->height); |
||
2285 | _cairo_svg_surface_emit_operator_for_style (output, surface, op); |
||
2286 | status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); |
||
2287 | if (unlikely (status)) |
||
2288 | return status; |
||
2289 | |||
2290 | _cairo_output_stream_printf (output, "stroke:none;\""); |
||
2291 | |||
2292 | if (extra_attributes) |
||
2293 | _cairo_output_stream_printf (output, " %s", extra_attributes); |
||
2294 | |||
2295 | _cairo_output_stream_printf (output, "/>\n"); |
||
2296 | |||
2297 | return CAIRO_STATUS_SUCCESS; |
||
2298 | } |
||
2299 | |||
2300 | static cairo_int_status_t |
||
2301 | _cairo_svg_surface_paint (void *abstract_surface, |
||
2302 | cairo_operator_t op, |
||
2303 | const cairo_pattern_t *source, |
||
3959 | Serge | 2304 | const cairo_clip_t *clip) |
1897 | serge | 2305 | { |
2306 | cairo_status_t status; |
||
2307 | cairo_svg_surface_t *surface = abstract_surface; |
||
2308 | |||
2309 | /* Emulation of clear and source operators, when no clipping region |
||
2310 | * is defined. We just delete existing content of surface root node, |
||
2311 | * and exit early if operator is clear. |
||
2312 | */ |
||
2313 | if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) && |
||
2314 | clip == NULL) |
||
2315 | { |
||
2316 | switch (surface->paginated_mode) { |
||
2317 | case CAIRO_PAGINATED_MODE_FALLBACK: |
||
2318 | ASSERT_NOT_REACHED; |
||
2319 | case CAIRO_PAGINATED_MODE_ANALYZE: |
||
2320 | return CAIRO_STATUS_SUCCESS; |
||
2321 | |||
2322 | case CAIRO_PAGINATED_MODE_RENDER: |
||
2323 | status = _cairo_output_stream_destroy (surface->xml_node); |
||
2324 | if (unlikely (status)) { |
||
2325 | surface->xml_node = NULL; |
||
2326 | return status; |
||
2327 | } |
||
2328 | |||
2329 | surface->xml_node = _cairo_memory_stream_create (); |
||
2330 | if (_cairo_output_stream_get_status (surface->xml_node)) { |
||
2331 | status = _cairo_output_stream_destroy (surface->xml_node); |
||
2332 | surface->xml_node = NULL; |
||
2333 | return status; |
||
2334 | } |
||
2335 | |||
2336 | if (op == CAIRO_OPERATOR_CLEAR) { |
||
2337 | if (surface->content == CAIRO_CONTENT_COLOR) { |
||
2338 | _cairo_output_stream_printf (surface->xml_node, |
||
2339 | " |
||
2340 | "width=\"%f\" height=\"%f\" " |
||
2341 | "style=\"opacity:1;" |
||
2342 | "stroke:none;" |
||
2343 | "fill:rgb(0,0,0);\"/>\n", |
||
2344 | surface->width, surface->height); |
||
2345 | } |
||
2346 | return CAIRO_STATUS_SUCCESS; |
||
2347 | } |
||
2348 | break; |
||
2349 | } |
||
2350 | } else { |
||
2351 | if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) |
||
2352 | return _cairo_svg_surface_analyze_operation (surface, op, source); |
||
2353 | |||
2354 | assert (_cairo_svg_surface_operation_supported (surface, op, source)); |
||
2355 | } |
||
2356 | |||
2357 | status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
||
2358 | if (unlikely (status)) |
||
2359 | return status; |
||
2360 | |||
2361 | return _cairo_svg_surface_emit_paint (surface->xml_node, |
||
2362 | surface, op, source, 0, NULL); |
||
2363 | } |
||
2364 | |||
2365 | static cairo_int_status_t |
||
2366 | _cairo_svg_surface_mask (void *abstract_surface, |
||
2367 | cairo_operator_t op, |
||
2368 | const cairo_pattern_t *source, |
||
2369 | const cairo_pattern_t *mask, |
||
3959 | Serge | 2370 | const cairo_clip_t *clip) |
1897 | serge | 2371 | { |
2372 | cairo_status_t status; |
||
2373 | cairo_svg_surface_t *surface = abstract_surface; |
||
2374 | cairo_svg_document_t *document = surface->document; |
||
2375 | cairo_output_stream_t *mask_stream; |
||
2376 | char buffer[64]; |
||
2377 | cairo_bool_t discard_filter = FALSE; |
||
2378 | unsigned int mask_id; |
||
2379 | |||
2380 | if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { |
||
2381 | cairo_status_t source_status, mask_status; |
||
2382 | |||
2383 | source_status = _cairo_svg_surface_analyze_operation (surface, op, source); |
||
2384 | if (_cairo_status_is_error (source_status)) |
||
2385 | return source_status; |
||
2386 | |||
2387 | if (mask->has_component_alpha) { |
||
2388 | mask_status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
2389 | } else { |
||
2390 | mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); |
||
2391 | if (_cairo_status_is_error (mask_status)) |
||
2392 | return mask_status; |
||
2393 | } |
||
2394 | |||
2395 | return _cairo_analysis_surface_merge_status (source_status, |
||
2396 | mask_status); |
||
2397 | } |
||
2398 | |||
2399 | assert (_cairo_svg_surface_operation_supported (surface, op, source)); |
||
2400 | assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); |
||
2401 | |||
2402 | status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
||
2403 | if (unlikely (status)) |
||
2404 | return status; |
||
2405 | |||
2406 | if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { |
||
2407 | const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; |
||
3959 | Serge | 2408 | cairo_content_t content = surface_pattern->surface->content; |
1897 | serge | 2409 | if (content == CAIRO_CONTENT_ALPHA) |
2410 | discard_filter = TRUE; |
||
2411 | } |
||
2412 | |||
2413 | if (!discard_filter) |
||
2414 | _cairo_svg_surface_emit_alpha_filter (document); |
||
2415 | |||
2416 | /* _cairo_svg_surface_emit_paint() will output a pattern definition to |
||
2417 | * document->xml_node_defs so we need to write the mask element to |
||
2418 | * a temporary stream and then copy that to xml_node_defs. */ |
||
2419 | mask_stream = _cairo_memory_stream_create (); |
||
2420 | if (_cairo_output_stream_get_status (mask_stream)) |
||
2421 | return _cairo_output_stream_destroy (mask_stream); |
||
2422 | |||
2423 | mask_id = _cairo_svg_document_allocate_mask_id (document); |
||
2424 | |||
2425 | _cairo_output_stream_printf (mask_stream, |
||
2426 | " |
||
2427 | "%s", |
||
2428 | mask_id, |
||
2429 | discard_filter ? "" : " |
||
2430 | status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); |
||
2431 | if (unlikely (status)) { |
||
2432 | cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); |
||
2433 | return status; |
||
2434 | (void) ignore; |
||
2435 | } |
||
2436 | |||
2437 | _cairo_output_stream_printf (mask_stream, |
||
2438 | "%s" |
||
2439 | "\n", |
||
2440 | discard_filter ? "" : " \n"); |
||
2441 | _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); |
||
2442 | |||
2443 | status = _cairo_output_stream_destroy (mask_stream); |
||
2444 | if (unlikely (status)) |
||
2445 | return status; |
||
2446 | |||
2447 | snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", |
||
2448 | mask_id); |
||
2449 | status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); |
||
2450 | if (unlikely (status)) |
||
2451 | return status; |
||
2452 | |||
2453 | return CAIRO_STATUS_SUCCESS; |
||
2454 | } |
||
2455 | |||
2456 | static cairo_int_status_t |
||
2457 | _cairo_svg_surface_stroke (void *abstract_dst, |
||
2458 | cairo_operator_t op, |
||
2459 | const cairo_pattern_t *source, |
||
3959 | Serge | 2460 | const cairo_path_fixed_t*path, |
1897 | serge | 2461 | const cairo_stroke_style_t *stroke_style, |
2462 | const cairo_matrix_t *ctm, |
||
2463 | const cairo_matrix_t *ctm_inverse, |
||
2464 | double tolerance, |
||
2465 | cairo_antialias_t antialias, |
||
3959 | Serge | 2466 | const cairo_clip_t *clip) |
1897 | serge | 2467 | { |
2468 | cairo_svg_surface_t *surface = abstract_dst; |
||
2469 | cairo_status_t status; |
||
2470 | |||
2471 | if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) |
||
2472 | return _cairo_svg_surface_analyze_operation (surface, op, source); |
||
2473 | |||
2474 | assert (_cairo_svg_surface_operation_supported (surface, op, source)); |
||
2475 | |||
2476 | status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
||
2477 | if (unlikely (status)) |
||
2478 | return status; |
||
2479 | |||
2480 | _cairo_output_stream_printf (surface->xml_node, " |
||
2481 | status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op, |
||
2482 | source, stroke_style, ctm_inverse); |
||
2483 | if (unlikely (status)) |
||
2484 | return status; |
||
2485 | |||
2486 | _cairo_output_stream_printf (surface->xml_node, "\" "); |
||
2487 | |||
2488 | _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); |
||
2489 | |||
2490 | _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); |
||
2491 | _cairo_output_stream_printf (surface->xml_node, "/>\n"); |
||
2492 | |||
2493 | return CAIRO_STATUS_SUCCESS; |
||
2494 | } |
||
2495 | |||
2496 | static cairo_int_status_t |
||
2497 | _cairo_svg_surface_show_glyphs (void *abstract_surface, |
||
2498 | cairo_operator_t op, |
||
2499 | const cairo_pattern_t *pattern, |
||
2500 | cairo_glyph_t *glyphs, |
||
2501 | int num_glyphs, |
||
2502 | cairo_scaled_font_t *scaled_font, |
||
3959 | Serge | 2503 | const cairo_clip_t *clip) |
1897 | serge | 2504 | { |
2505 | cairo_svg_surface_t *surface = abstract_surface; |
||
2506 | cairo_svg_document_t *document = surface->document; |
||
2507 | cairo_path_fixed_t path; |
||
3959 | Serge | 2508 | cairo_int_status_t status; |
1897 | serge | 2509 | cairo_scaled_font_subsets_glyph_t subset_glyph; |
2510 | int i; |
||
2511 | |||
2512 | if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) |
||
2513 | return _cairo_svg_surface_analyze_operation (surface, op, pattern); |
||
2514 | |||
2515 | assert (_cairo_svg_surface_operation_supported (surface, op, pattern)); |
||
2516 | |||
2517 | if (num_glyphs <= 0) |
||
2518 | return CAIRO_STATUS_SUCCESS; |
||
2519 | |||
2520 | status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
||
2521 | if (unlikely (status)) |
||
2522 | return status; |
||
2523 | |||
2524 | /* FIXME it's probably possible to apply a pattern of a gradient to |
||
2525 | * a group of symbols, but I don't know how yet. Gradients or patterns |
||
2526 | * are translated by x and y properties of use element. */ |
||
2527 | if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) |
||
2528 | goto FALLBACK; |
||
2529 | |||
2530 | _cairo_output_stream_printf (surface->xml_node, " |
||
2531 | status = _cairo_svg_surface_emit_pattern (surface, pattern, |
||
2532 | surface->xml_node, FALSE, NULL); |
||
2533 | if (unlikely (status)) |
||
2534 | return status; |
||
2535 | |||
2536 | _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); |
||
2537 | |||
2538 | _cairo_output_stream_printf (surface->xml_node, "\">\n"); |
||
2539 | |||
2540 | for (i = 0; i < num_glyphs; i++) { |
||
2541 | status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets, |
||
2542 | scaled_font, glyphs[i].index, |
||
2543 | NULL, 0, |
||
2544 | &subset_glyph); |
||
2545 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
||
2546 | _cairo_output_stream_printf (surface->xml_node, "\n"); |
||
2547 | |||
2548 | glyphs += i; |
||
2549 | num_glyphs -= i; |
||
2550 | goto FALLBACK; |
||
2551 | } |
||
2552 | |||
2553 | if (unlikely (status)) |
||
2554 | return status; |
||
2555 | |||
2556 | _cairo_output_stream_printf (surface->xml_node, |
||
2557 | " |
||
2558 | "x=\"%f\" y=\"%f\"/>\n", |
||
2559 | subset_glyph.font_id, |
||
2560 | subset_glyph.subset_glyph_index, |
||
2561 | glyphs[i].x, glyphs[i].y); |
||
2562 | } |
||
2563 | |||
2564 | _cairo_output_stream_printf (surface->xml_node, "\n"); |
||
2565 | |||
2566 | return CAIRO_STATUS_SUCCESS; |
||
2567 | |||
2568 | FALLBACK: |
||
2569 | _cairo_path_fixed_init (&path); |
||
2570 | |||
2571 | status = _cairo_scaled_font_glyph_path (scaled_font, |
||
2572 | (cairo_glyph_t *) glyphs, |
||
2573 | num_glyphs, &path); |
||
2574 | |||
2575 | if (unlikely (status)) { |
||
2576 | _cairo_path_fixed_fini (&path); |
||
2577 | return status; |
||
2578 | } |
||
2579 | |||
2580 | status = _cairo_svg_surface_fill (abstract_surface, op, pattern, |
||
2581 | &path, CAIRO_FILL_RULE_WINDING, |
||
2582 | 0.0, CAIRO_ANTIALIAS_SUBPIXEL, |
||
2583 | clip); |
||
2584 | |||
2585 | _cairo_path_fixed_fini (&path); |
||
2586 | |||
2587 | return status; |
||
2588 | } |
||
2589 | |||
2590 | static void |
||
2591 | _cairo_svg_surface_get_font_options (void *abstract_surface, |
||
2592 | cairo_font_options_t *options) |
||
2593 | { |
||
2594 | _cairo_font_options_init_default (options); |
||
2595 | |||
2596 | cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); |
||
2597 | cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); |
||
2598 | cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); |
||
3959 | Serge | 2599 | _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); |
1897 | serge | 2600 | } |
2601 | |||
3959 | Serge | 2602 | |
2603 | static const char ** |
||
2604 | _cairo_svg_surface_get_supported_mime_types (void *abstract_surface) |
||
2605 | { |
||
2606 | return _cairo_svg_supported_mime_types; |
||
2607 | } |
||
2608 | |||
1897 | serge | 2609 | static const cairo_surface_backend_t cairo_svg_surface_backend = { |
2610 | CAIRO_SURFACE_TYPE_SVG, |
||
3959 | Serge | 2611 | _cairo_svg_surface_finish, |
2612 | |||
2613 | _cairo_default_context_create, |
||
2614 | |||
1897 | serge | 2615 | NULL, /* create_similar: handled by wrapper */ |
3959 | Serge | 2616 | NULL, /* create_similar_image */ |
2617 | NULL, /* map to image */ |
||
2618 | NULL, /* unmap image */ |
||
2619 | |||
2620 | _cairo_surface_default_source, |
||
1897 | serge | 2621 | NULL, /* acquire_source_image */ |
2622 | NULL, /* release_source_image */ |
||
3959 | Serge | 2623 | NULL, /* snapshot */ |
2624 | |||
1897 | serge | 2625 | _cairo_svg_surface_copy_page, |
2626 | _cairo_svg_surface_show_page, |
||
3959 | Serge | 2627 | |
1897 | serge | 2628 | _cairo_svg_surface_get_extents, |
2629 | _cairo_svg_surface_get_font_options, |
||
3959 | Serge | 2630 | |
1897 | serge | 2631 | NULL, /* flush */ |
2632 | NULL, /* mark dirty rectangle */ |
||
3959 | Serge | 2633 | |
1897 | serge | 2634 | _cairo_svg_surface_paint, |
2635 | _cairo_svg_surface_mask, |
||
2636 | _cairo_svg_surface_stroke, |
||
2637 | _cairo_svg_surface_fill, |
||
3959 | Serge | 2638 | _cairo_svg_surface_fill_stroke, |
1897 | serge | 2639 | _cairo_svg_surface_show_glyphs, |
3959 | Serge | 2640 | NULL, /* has_show_text_glyphs */ |
2641 | NULL, /* show_text_glyphs */ |
||
2642 | _cairo_svg_surface_get_supported_mime_types, |
||
1897 | serge | 2643 | }; |
2644 | |||
2645 | static cairo_status_t |
||
2646 | _cairo_svg_document_create (cairo_output_stream_t *output_stream, |
||
2647 | double width, |
||
2648 | double height, |
||
2649 | cairo_svg_version_t version, |
||
2650 | cairo_svg_document_t **document_out) |
||
2651 | { |
||
2652 | cairo_svg_document_t *document; |
||
2653 | cairo_status_t status, status_ignored; |
||
2654 | |||
2655 | if (output_stream->status) |
||
2656 | return output_stream->status; |
||
2657 | |||
2658 | document = malloc (sizeof (cairo_svg_document_t)); |
||
2659 | if (unlikely (document == NULL)) |
||
2660 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
2661 | |||
2662 | /* The use of defs for font glyphs imposes no per-subset limit. */ |
||
2663 | document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); |
||
2664 | if (unlikely (document->font_subsets == NULL)) { |
||
2665 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
2666 | goto CLEANUP_DOCUMENT; |
||
2667 | } |
||
2668 | |||
2669 | document->output_stream = output_stream; |
||
2670 | document->refcount = 1; |
||
2671 | document->owner = NULL; |
||
2672 | document->finished = FALSE; |
||
2673 | document->width = width; |
||
2674 | document->height = height; |
||
2675 | |||
2676 | document->linear_pattern_id = 0; |
||
2677 | document->radial_pattern_id = 0; |
||
2678 | document->pattern_id = 0; |
||
2679 | document->filter_id = 0; |
||
2680 | document->clip_id = 0; |
||
2681 | document->mask_id = 0; |
||
2682 | |||
2683 | document->xml_node_defs = _cairo_memory_stream_create (); |
||
2684 | status = _cairo_output_stream_get_status (document->xml_node_defs); |
||
2685 | if (unlikely (status)) |
||
2686 | goto CLEANUP_NODE_DEFS; |
||
2687 | |||
2688 | document->xml_node_glyphs = _cairo_memory_stream_create (); |
||
2689 | status = _cairo_output_stream_get_status (document->xml_node_glyphs); |
||
2690 | if (unlikely (status)) |
||
2691 | goto CLEANUP_NODE_GLYPHS; |
||
2692 | |||
2693 | document->alpha_filter = FALSE; |
||
2694 | |||
2695 | document->svg_version = version; |
||
2696 | |||
2697 | *document_out = document; |
||
2698 | return CAIRO_STATUS_SUCCESS; |
||
2699 | |||
2700 | CLEANUP_NODE_GLYPHS: |
||
2701 | status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs); |
||
2702 | CLEANUP_NODE_DEFS: |
||
2703 | status_ignored = _cairo_output_stream_destroy (document->xml_node_defs); |
||
2704 | _cairo_scaled_font_subsets_destroy (document->font_subsets); |
||
2705 | CLEANUP_DOCUMENT: |
||
2706 | free (document); |
||
2707 | return status; |
||
2708 | } |
||
2709 | |||
2710 | static cairo_svg_document_t * |
||
2711 | _cairo_svg_document_reference (cairo_svg_document_t *document) |
||
2712 | { |
||
2713 | document->refcount++; |
||
2714 | |||
2715 | return document; |
||
2716 | } |
||
2717 | |||
2718 | static unsigned int |
||
2719 | _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document) |
||
2720 | { |
||
2721 | return document->mask_id++; |
||
2722 | } |
||
2723 | |||
2724 | static cairo_status_t |
||
2725 | _cairo_svg_document_destroy (cairo_svg_document_t *document) |
||
2726 | { |
||
2727 | cairo_status_t status; |
||
2728 | |||
2729 | document->refcount--; |
||
2730 | if (document->refcount > 0) |
||
2731 | return CAIRO_STATUS_SUCCESS; |
||
2732 | |||
2733 | status = _cairo_svg_document_finish (document); |
||
2734 | |||
2735 | free (document); |
||
2736 | |||
2737 | return status; |
||
2738 | } |
||
2739 | |||
2740 | static cairo_status_t |
||
2741 | _cairo_svg_document_finish (cairo_svg_document_t *document) |
||
2742 | { |
||
2743 | cairo_status_t status, status2; |
||
2744 | cairo_output_stream_t *output = document->output_stream; |
||
2745 | cairo_svg_page_t *page; |
||
2746 | unsigned int i; |
||
2747 | |||
2748 | if (document->finished) |
||
2749 | return CAIRO_STATUS_SUCCESS; |
||
2750 | |||
2751 | /* |
||
2752 | * Should we add DOCTYPE? |
||
2753 | * |
||
2754 | * Google says no. |
||
2755 | * |
||
2756 | * http://tech.groups.yahoo.com/group/svg-developers/message/48562: |
||
2757 | * There's a bunch of issues, but just to pick a few: |
||
2758 | * - they'll give false positives. |
||
2759 | * - they'll give false negatives. |
||
2760 | * - they're namespace-unaware. |
||
2761 | * - they don't wildcard. |
||
2762 | * So when they say OK they really haven't checked anything, when |
||
2763 | * they say NOT OK they might be on crack, and like all |
||
2764 | * namespace-unaware things they're a dead branch of the XML tree. |
||
2765 | * |
||
2766 | * http://jwatt.org/svg/authoring/: |
||
2767 | * Unfortunately the SVG DTDs are a source of so many issues that the |
||
2768 | * SVG WG has decided not to write one for the upcoming SVG 1.2 |
||
2769 | * standard. In fact SVG WG members are even telling people not to use |
||
2770 | * a DOCTYPE declaration in SVG 1.0 and 1.1 documents. |
||
2771 | */ |
||
2772 | |||
2773 | _cairo_output_stream_printf (output, |
||
2774 | "\n" |
||
2775 | " |
||
2776 | "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " |
||
2777 | "width=\"%fpt\" height=\"%fpt\" " |
||
2778 | "viewBox=\"0 0 %f %f\" version=\"%s\">\n", |
||
2779 | document->width, document->height, |
||
2780 | document->width, document->height, |
||
2781 | _cairo_svg_internal_version_strings [document->svg_version]); |
||
2782 | |||
2783 | status = _cairo_svg_document_emit_font_subsets (document); |
||
2784 | |||
2785 | if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 || |
||
2786 | _cairo_memory_stream_length (document->xml_node_defs) > 0) { |
||
2787 | _cairo_output_stream_printf (output, " |
||
2788 | if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) { |
||
2789 | _cairo_output_stream_printf (output, " |
||
2790 | _cairo_memory_stream_copy (document->xml_node_glyphs, output); |
||
2791 | _cairo_output_stream_printf (output, "\n"); |
||
2792 | } |
||
2793 | _cairo_memory_stream_copy (document->xml_node_defs, output); |
||
2794 | _cairo_output_stream_printf (output, "\n"); |
||
2795 | } |
||
2796 | |||
2797 | if (document->owner != NULL) { |
||
2798 | cairo_svg_surface_t *surface; |
||
2799 | |||
2800 | surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); |
||
2801 | if (surface->xml_node != NULL && |
||
2802 | _cairo_memory_stream_length (surface->xml_node) > 0) { |
||
2803 | if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) { |
||
2804 | if (status == CAIRO_STATUS_SUCCESS) |
||
2805 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
2806 | } |
||
2807 | } |
||
2808 | |||
2809 | if (surface->page_set.num_elements > 1 && |
||
2810 | _cairo_svg_version_has_page_set_support (document->svg_version)) { |
||
2811 | _cairo_output_stream_printf (output, " |
||
2812 | for (i = 0; i < surface->page_set.num_elements; i++) { |
||
2813 | page = _cairo_array_index (&surface->page_set, i); |
||
2814 | _cairo_output_stream_printf (output, " |
||
2815 | _cairo_output_stream_printf (output, |
||
2816 | " |
||
2817 | page->surface_id); |
||
2818 | _cairo_memory_stream_copy (page->xml_node, output); |
||
2819 | _cairo_output_stream_printf (output, "\n\n"); |
||
2820 | } |
||
2821 | _cairo_output_stream_printf (output, "\n"); |
||
2822 | } else if (surface->page_set.num_elements > 0) { |
||
2823 | page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); |
||
2824 | _cairo_output_stream_printf (output, |
||
2825 | " |
||
2826 | page->surface_id); |
||
2827 | _cairo_memory_stream_copy (page->xml_node, output); |
||
2828 | _cairo_output_stream_printf (output, "\n"); |
||
2829 | } |
||
2830 | } |
||
2831 | |||
2832 | _cairo_output_stream_printf (output, "\n"); |
||
2833 | |||
2834 | status2 = _cairo_output_stream_destroy (document->xml_node_glyphs); |
||
2835 | if (status == CAIRO_STATUS_SUCCESS) |
||
2836 | status = status2; |
||
2837 | |||
2838 | status2 = _cairo_output_stream_destroy (document->xml_node_defs); |
||
2839 | if (status == CAIRO_STATUS_SUCCESS) |
||
2840 | status = status2; |
||
2841 | |||
2842 | status2 = _cairo_output_stream_destroy (output); |
||
2843 | if (status == CAIRO_STATUS_SUCCESS) |
||
2844 | status = status2; |
||
2845 | |||
2846 | document->finished = TRUE; |
||
2847 | |||
2848 | return status; |
||
2849 | } |
||
2850 | |||
2851 | static void |
||
2852 | _cairo_svg_surface_set_paginated_mode (void *abstract_surface, |
||
2853 | cairo_paginated_mode_t paginated_mode) |
||
2854 | { |
||
2855 | cairo_svg_surface_t *surface = abstract_surface; |
||
2856 | |||
2857 | surface->paginated_mode = paginated_mode; |
||
2858 | } |
||
2859 | |||
2860 | static cairo_bool_t |
||
2861 | _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface) |
||
2862 | { |
||
2863 | cairo_svg_surface_t *surface = abstract_surface; |
||
2864 | cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
2865 | |||
2866 | if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) { |
||
2867 | status = _cairo_svg_surface_analyze_operator (surface, |
||
2868 | CAIRO_OPERATOR_SOURCE); |
||
2869 | } |
||
2870 | |||
3959 | Serge | 2871 | return status == CAIRO_INT_STATUS_SUCCESS; |
1897 | serge | 2872 | } |
2873 | |||
2874 | static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = { |
||
2875 | NULL /*_cairo_svg_surface_start_page*/, |
||
2876 | _cairo_svg_surface_set_paginated_mode, |
||
2877 | NULL, /* _cairo_svg_surface_set_bounding_box */ |
||
2878 | NULL, /* _cairo_svg_surface_set_fallback_images_required */ |
||
2879 | _cairo_svg_surface_supports_fine_grained_fallbacks, |
||
2880 | |||
2881 | };>?xml>>=>>>>>>>>>>>>>>><>><>>>>>>><>> |