Tizen 2.0 Release
[framework/graphics/cairo.git] / src / cairo-xml-surface.c
1 /* -*- 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 © 2009 Chris Wilson
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it either under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * (the "LGPL") or, at your option, under the terms of the Mozilla
10  * Public License Version 1.1 (the "MPL"). If you do not alter this
11  * notice, a recipient may use your version of this file under either
12  * the MPL or the LGPL.
13  *
14  * You should have received a copy of the LGPL along with this library
15  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
20  * The contents of this file are subject to the Mozilla Public License
21  * Version 1.1 (the "License"); you may not use this file except in
22  * compliance with the License. You may obtain a copy of the License at
23  * http://www.mozilla.org/MPL/
24  *
25  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27  * the specific language governing rights and limitations.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is Chris Wilson.
32  *
33  * Contributor(s):
34  *      Chris Wilson <chris@chris-wilson.co.uk>
35  */
36
37 /* This surface is intended to produce a verbose, hierarchical, DAG XML file
38  * representing a single surface. It is intended to be used by debuggers,
39  * such as cairo-sphinx, or by application test-suites that what a log of
40  * operations.
41  */
42
43 #include "cairoint.h"
44
45 #include "cairo-xml.h"
46
47 #include "cairo-clip-private.h"
48 #include "cairo-device-private.h"
49 #include "cairo-default-context-private.h"
50 #include "cairo-image-surface-private.h"
51 #include "cairo-error-private.h"
52 #include "cairo-output-stream-private.h"
53 #include "cairo-recording-surface-inline.h"
54
55 #define static cairo_warn static
56
57 typedef struct _cairo_xml_surface cairo_xml_surface_t;
58
59 typedef struct _cairo_xml {
60     cairo_device_t base;
61
62     cairo_output_stream_t *stream;
63     int indent;
64 } cairo_xml_t;
65
66 struct _cairo_xml_surface {
67     cairo_surface_t base;
68
69     double width, height;
70 };
71
72 slim_hidden_proto (cairo_xml_for_recording_surface);
73
74 static const cairo_surface_backend_t _cairo_xml_surface_backend;
75
76 static const char *
77 _operator_to_string (cairo_operator_t op)
78 {
79     static const char *names[] = {
80         "CLEAR",        /* CAIRO_OPERATOR_CLEAR */
81
82         "SOURCE",       /* CAIRO_OPERATOR_SOURCE */
83         "OVER",         /* CAIRO_OPERATOR_OVER */
84         "IN",           /* CAIRO_OPERATOR_IN */
85         "OUT",          /* CAIRO_OPERATOR_OUT */
86         "ATOP",         /* CAIRO_OPERATOR_ATOP */
87
88         "DEST",         /* CAIRO_OPERATOR_DEST */
89         "DEST_OVER",    /* CAIRO_OPERATOR_DEST_OVER */
90         "DEST_IN",      /* CAIRO_OPERATOR_DEST_IN */
91         "DEST_OUT",     /* CAIRO_OPERATOR_DEST_OUT */
92         "DEST_ATOP",    /* CAIRO_OPERATOR_DEST_ATOP */
93
94         "XOR",          /* CAIRO_OPERATOR_XOR */
95         "ADD",          /* CAIRO_OPERATOR_ADD */
96         "SATURATE",     /* CAIRO_OPERATOR_SATURATE */
97
98         "MULTIPLY",     /* CAIRO_OPERATOR_MULTIPLY */
99         "SCREEN",       /* CAIRO_OPERATOR_SCREEN */
100         "OVERLAY",      /* CAIRO_OPERATOR_OVERLAY */
101         "DARKEN",       /* CAIRO_OPERATOR_DARKEN */
102         "LIGHTEN",      /* CAIRO_OPERATOR_LIGHTEN */
103         "DODGE",        /* CAIRO_OPERATOR_COLOR_DODGE */
104         "BURN",         /* CAIRO_OPERATOR_COLOR_BURN */
105         "HARD_LIGHT",   /* CAIRO_OPERATOR_HARD_LIGHT */
106         "SOFT_LIGHT",   /* CAIRO_OPERATOR_SOFT_LIGHT */
107         "DIFFERENCE",   /* CAIRO_OPERATOR_DIFFERENCE */
108         "EXCLUSION",    /* CAIRO_OPERATOR_EXCLUSION */
109         "HSL_HUE",      /* CAIRO_OPERATOR_HSL_HUE */
110         "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
111         "HSL_COLOR",    /* CAIRO_OPERATOR_HSL_COLOR */
112         "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
113     };
114     assert (op < ARRAY_LENGTH (names));
115     return names[op];
116 }
117
118 static const char *
119 _extend_to_string (cairo_extend_t extend)
120 {
121     static const char *names[] = {
122         "EXTEND_NONE",          /* CAIRO_EXTEND_NONE */
123         "EXTEND_REPEAT",        /* CAIRO_EXTEND_REPEAT */
124         "EXTEND_REFLECT",       /* CAIRO_EXTEND_REFLECT */
125         "EXTEND_PAD"            /* CAIRO_EXTEND_PAD */
126     };
127     assert (extend < ARRAY_LENGTH (names));
128     return names[extend];
129 }
130
131 static const char *
132 _filter_to_string (cairo_filter_t filter)
133 {
134     static const char *names[] = {
135         "FILTER_FAST",          /* CAIRO_FILTER_FAST */
136         "FILTER_GOOD",          /* CAIRO_FILTER_GOOD */
137         "FILTER_BEST",          /* CAIRO_FILTER_BEST */
138         "FILTER_NEAREST",       /* CAIRO_FILTER_NEAREST */
139         "FILTER_BILINEAR",      /* CAIRO_FILTER_BILINEAR */
140         "FILTER_GAUSSIAN",      /* CAIRO_FILTER_GAUSSIAN */
141     };
142     assert (filter < ARRAY_LENGTH (names));
143     return names[filter];
144 }
145
146 static const char *
147 _fill_rule_to_string (cairo_fill_rule_t rule)
148 {
149     static const char *names[] = {
150         "WINDING",      /* CAIRO_FILL_RULE_WINDING */
151         "EVEN_ODD"      /* CAIRO_FILL_RILE_EVEN_ODD */
152     };
153     assert (rule < ARRAY_LENGTH (names));
154     return names[rule];
155 }
156
157 static const char *
158 _antialias_to_string (cairo_antialias_t antialias)
159 {
160     static const char *names[] = {
161         "DEFAULT",      /* CAIRO_ANTIALIAS_DEFAULT */
162         "NONE",         /* CAIRO_ANTIALIAS_NONE */
163         "GRAY",         /* CAIRO_ANTIALIAS_GRAY */
164         "SUBPIXEL",     /* CAIRO_ANTIALIAS_SUBPIXEL */
165         "FAST",         /* CAIRO_ANTIALIAS_FAST */
166         "GOOD",         /* CAIRO_ANTIALIAS_GOOD */
167         "BEST",         /* CAIRO_ANTIALIAS_BEST */
168     };
169     assert (antialias < ARRAY_LENGTH (names));
170     return names[antialias];
171 }
172
173 static const char *
174 _line_cap_to_string (cairo_line_cap_t line_cap)
175 {
176     static const char *names[] = {
177         "LINE_CAP_BUTT",        /* CAIRO_LINE_CAP_BUTT */
178         "LINE_CAP_ROUND",       /* CAIRO_LINE_CAP_ROUND */
179         "LINE_CAP_SQUARE"       /* CAIRO_LINE_CAP_SQUARE */
180     };
181     assert (line_cap < ARRAY_LENGTH (names));
182     return names[line_cap];
183 }
184
185 static const char *
186 _line_join_to_string (cairo_line_join_t line_join)
187 {
188     static const char *names[] = {
189         "LINE_JOIN_MITER",      /* CAIRO_LINE_JOIN_MITER */
190         "LINE_JOIN_ROUND",      /* CAIRO_LINE_JOIN_ROUND */
191         "LINE_JOIN_BEVEL",      /* CAIRO_LINE_JOIN_BEVEL */
192     };
193     assert (line_join < ARRAY_LENGTH (names));
194     return names[line_join];
195 }
196
197 static const char *
198 _content_to_string (cairo_content_t content)
199 {
200     switch (content) {
201     case CAIRO_CONTENT_ALPHA: return "ALPHA";
202     case CAIRO_CONTENT_COLOR: return "COLOR";
203     default:
204     case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
205     }
206 }
207
208 static const char *
209 _format_to_string (cairo_format_t format)
210 {
211     switch (format) {
212     case CAIRO_FORMAT_ARGB32:  return "ARGB32";
213     case CAIRO_FORMAT_RGB30:   return "RGB30";
214     case CAIRO_FORMAT_RGB24:   return "RGB24";
215     case CAIRO_FORMAT_RGB16_565:   return "RGB16_565";
216     case CAIRO_FORMAT_A8:      return "A8";
217     case CAIRO_FORMAT_A1:      return "A1";
218     case CAIRO_FORMAT_INVALID: return "INVALID";
219     }
220     ASSERT_NOT_REACHED;
221     return "INVALID";
222 }
223
224 static cairo_status_t
225 _device_flush (void *abstract_device)
226 {
227     cairo_xml_t *xml = abstract_device;
228     cairo_status_t status;
229
230     status = _cairo_output_stream_flush (xml->stream);
231
232     return status;
233 }
234
235 static void
236 _device_destroy (void *abstract_device)
237 {
238     cairo_xml_t *xml = abstract_device;
239     cairo_status_t status;
240
241     status = _cairo_output_stream_destroy (xml->stream);
242
243     free (xml);
244 }
245
246 static const cairo_device_backend_t _cairo_xml_device_backend = {
247     CAIRO_DEVICE_TYPE_XML,
248
249     NULL, NULL, /* lock, unlock */
250
251     _device_flush,
252     NULL,  /* finish */
253     _device_destroy
254 };
255
256 static cairo_device_t *
257 _cairo_xml_create_internal (cairo_output_stream_t *stream)
258 {
259     cairo_xml_t *xml;
260
261     xml = malloc (sizeof (cairo_xml_t));
262     if (unlikely (xml == NULL))
263         return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
264
265     memset (xml, 0, sizeof (cairo_xml_t));
266
267     _cairo_device_init (&xml->base, &_cairo_xml_device_backend);
268
269     xml->indent = 0;
270     xml->stream = stream;
271
272     return &xml->base;
273 }
274
275 static void
276 _cairo_xml_indent (cairo_xml_t *xml, int indent)
277 {
278     xml->indent += indent;
279     assert (xml->indent >= 0);
280 }
281
282 static void CAIRO_PRINTF_FORMAT (2, 3)
283 _cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...)
284 {
285     va_list ap;
286     char indent[80];
287     int len;
288
289     len = MIN (xml->indent, ARRAY_LENGTH (indent));
290     memset (indent, ' ', len);
291     _cairo_output_stream_write (xml->stream, indent, len);
292
293     va_start (ap, fmt);
294     _cairo_output_stream_vprintf (xml->stream, fmt, ap);
295     va_end (ap);
296
297     _cairo_output_stream_write (xml->stream, "\n", 1);
298 }
299
300 static void CAIRO_PRINTF_FORMAT (2, 3)
301 _cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...)
302 {
303     char indent[80];
304     int len;
305
306     len = MIN (xml->indent, ARRAY_LENGTH (indent));
307     memset (indent, ' ', len);
308     _cairo_output_stream_write (xml->stream, indent, len);
309
310     if (fmt != NULL) {
311         va_list ap;
312
313         va_start (ap, fmt);
314         _cairo_output_stream_vprintf (xml->stream, fmt, ap);
315         va_end (ap);
316     }
317 }
318
319 static void CAIRO_PRINTF_FORMAT (2, 3)
320 _cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...)
321 {
322     va_list ap;
323
324     va_start (ap, fmt);
325     _cairo_output_stream_vprintf (xml->stream, fmt, ap);
326     va_end (ap);
327 }
328
329 static void CAIRO_PRINTF_FORMAT (2, 3)
330 _cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...)
331 {
332     if (fmt != NULL) {
333         va_list ap;
334
335         va_start (ap, fmt);
336         _cairo_output_stream_vprintf (xml->stream, fmt, ap);
337         va_end (ap);
338     }
339
340     _cairo_output_stream_write (xml->stream, "\n", 1);
341 }
342
343 static cairo_surface_t *
344 _cairo_xml_surface_create_similar (void                 *abstract_surface,
345                                    cairo_content_t       content,
346                                    int                   width,
347                                    int                   height)
348 {
349     cairo_rectangle_t extents;
350
351     extents.x = extents.y = 0;
352     extents.width  = width;
353     extents.height = height;
354
355     return cairo_recording_surface_create (content, &extents);
356 }
357
358 static cairo_bool_t
359 _cairo_xml_surface_get_extents (void *abstract_surface,
360                                 cairo_rectangle_int_t *rectangle)
361 {
362     cairo_xml_surface_t *surface = abstract_surface;
363
364     if (surface->width < 0 || surface->height < 0)
365         return FALSE;
366
367     rectangle->x = 0;
368     rectangle->y = 0;
369     rectangle->width  = surface->width;
370     rectangle->height = surface->height;
371
372     return TRUE;
373 }
374
375 static cairo_status_t
376 _cairo_xml_move_to (void *closure,
377                     const cairo_point_t *p1)
378 {
379     _cairo_xml_printf_continue (closure, " %f %f m",
380                                 _cairo_fixed_to_double (p1->x),
381                                 _cairo_fixed_to_double (p1->y));
382
383     return CAIRO_STATUS_SUCCESS;
384 }
385
386 static cairo_status_t
387 _cairo_xml_line_to (void *closure,
388                     const cairo_point_t *p1)
389 {
390     _cairo_xml_printf_continue (closure, " %f %f l",
391                                 _cairo_fixed_to_double (p1->x),
392                                 _cairo_fixed_to_double (p1->y));
393
394     return CAIRO_STATUS_SUCCESS;
395 }
396
397 static cairo_status_t
398 _cairo_xml_curve_to (void *closure,
399                      const cairo_point_t *p1,
400                      const cairo_point_t *p2,
401                      const cairo_point_t *p3)
402 {
403     _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c",
404                                 _cairo_fixed_to_double (p1->x),
405                                 _cairo_fixed_to_double (p1->y),
406                                 _cairo_fixed_to_double (p2->x),
407                                 _cairo_fixed_to_double (p2->y),
408                                 _cairo_fixed_to_double (p3->x),
409                                 _cairo_fixed_to_double (p3->y));
410
411     return CAIRO_STATUS_SUCCESS;
412 }
413
414 static cairo_status_t
415 _cairo_xml_close_path (void *closure)
416 {
417     _cairo_xml_printf_continue (closure, " h");
418
419     return CAIRO_STATUS_SUCCESS;
420 }
421
422 static void
423 _cairo_xml_emit_path (cairo_xml_t *xml,
424                       const cairo_path_fixed_t *path)
425 {
426     cairo_status_t status;
427
428     _cairo_xml_printf_start (xml, "<path>");
429     status = _cairo_path_fixed_interpret (path,
430                                         _cairo_xml_move_to,
431                                         _cairo_xml_line_to,
432                                         _cairo_xml_curve_to,
433                                         _cairo_xml_close_path,
434                                         xml);
435     assert (status == CAIRO_STATUS_SUCCESS);
436     _cairo_xml_printf_end (xml, "</path>");
437 }
438
439 static void
440 _cairo_xml_emit_string (cairo_xml_t *xml,
441                         const char *node,
442                         const char *data)
443 {
444     _cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node);
445 }
446
447 static void
448 _cairo_xml_emit_double (cairo_xml_t *xml,
449                         const char *node,
450                         double data)
451 {
452     _cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node);
453 }
454
455 static cairo_xml_t *
456 to_xml (cairo_xml_surface_t *surface)
457 {
458     return (cairo_xml_t *) surface->base.device;
459 }
460
461 static cairo_status_t
462 _cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface,
463                                    cairo_clip_path_t *clip_path)
464 {
465     cairo_box_t box;
466     cairo_status_t status;
467     cairo_xml_t *xml;
468
469     if (clip_path->prev != NULL) {
470         status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev);
471         if (unlikely (status))
472             return status;
473     }
474
475
476     /* skip the trivial clip covering the surface extents */
477     if (surface->width >= 0 && surface->height >= 0 &&
478         _cairo_path_fixed_is_box (&clip_path->path, &box))
479     {
480         if (box.p1.x <= 0 && box.p1.y <= 0 &&
481             box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
482             box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
483         {
484             return CAIRO_STATUS_SUCCESS;
485         }
486     }
487
488     xml = to_xml (surface);
489
490     _cairo_xml_printf_start (xml, "<clip>");
491     _cairo_xml_indent (xml, 2);
492
493     _cairo_xml_emit_path (xml, &clip_path->path);
494     _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance);
495     _cairo_xml_emit_string (xml, "antialias",
496                             _antialias_to_string (clip_path->antialias));
497     _cairo_xml_emit_string (xml, "fill-rule",
498                             _fill_rule_to_string (clip_path->fill_rule));
499
500     _cairo_xml_indent (xml, -2);
501     _cairo_xml_printf_end (xml, "</clip>");
502
503     return CAIRO_STATUS_SUCCESS;
504 }
505
506 static cairo_status_t
507 _cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
508                               const cairo_clip_t *clip)
509 {
510     if (clip == NULL)
511         return CAIRO_STATUS_SUCCESS;
512
513     return _cairo_xml_surface_emit_clip_path (surface, clip->path);
514 }
515
516 static cairo_status_t
517 _cairo_xml_emit_solid (cairo_xml_t *xml,
518                        const cairo_solid_pattern_t *solid)
519 {
520     _cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>",
521                        solid->color.red,
522                        solid->color.green,
523                        solid->color.blue,
524                        solid->color.alpha);
525     return CAIRO_STATUS_SUCCESS;
526 }
527
528 static void
529 _cairo_xml_emit_matrix (cairo_xml_t *xml,
530                         const cairo_matrix_t *matrix)
531 {
532     if (! _cairo_matrix_is_identity (matrix)) {
533         _cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>",
534                            matrix->xx, matrix->yx,
535                            matrix->xy, matrix->yy,
536                            matrix->x0, matrix->y0);
537     }
538 }
539
540 static void
541 _cairo_xml_emit_gradient (cairo_xml_t *xml,
542                           const cairo_gradient_pattern_t *gradient)
543 {
544     unsigned int i;
545
546     for (i = 0; i < gradient->n_stops; i++) {
547         _cairo_xml_printf (xml,
548                            "<color-stop>%f %f %f %f %f</color-stop>",
549                            gradient->stops[i].offset,
550                            gradient->stops[i].color.red,
551                            gradient->stops[i].color.green,
552                            gradient->stops[i].color.blue,
553                            gradient->stops[i].color.alpha);
554     }
555 }
556
557 static cairo_status_t
558 _cairo_xml_emit_linear (cairo_xml_t *xml,
559                         const cairo_linear_pattern_t *linear)
560 {
561     _cairo_xml_printf (xml,
562                        "<linear x1='%f' y1='%f' x2='%f' y2='%f'>",
563                        linear->pd1.x, linear->pd1.y,
564                        linear->pd2.x, linear->pd2.y);
565     _cairo_xml_indent (xml, 2);
566     _cairo_xml_emit_gradient (xml, &linear->base);
567     _cairo_xml_indent (xml, -2);
568     _cairo_xml_printf (xml, "</linear>");
569     return CAIRO_STATUS_SUCCESS;
570 }
571
572 static cairo_status_t
573 _cairo_xml_emit_radial (cairo_xml_t *xml,
574                         const cairo_radial_pattern_t *radial)
575 {
576     _cairo_xml_printf (xml,
577                        "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
578                        radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius,
579                        radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius);
580     _cairo_xml_indent (xml, 2);
581     _cairo_xml_emit_gradient (xml, &radial->base);
582     _cairo_xml_indent (xml, -2);
583     _cairo_xml_printf (xml, "</radial>");
584     return CAIRO_STATUS_SUCCESS;
585 }
586
587 static cairo_status_t
588 _write_func (void *closure, const unsigned char *data, unsigned len)
589 {
590     _cairo_output_stream_write (closure, data, len);
591     return CAIRO_STATUS_SUCCESS;
592 }
593
594 static cairo_status_t
595 _cairo_xml_emit_image (cairo_xml_t *xml,
596                        cairo_image_surface_t *image)
597 {
598     cairo_output_stream_t *stream;
599     cairo_status_t status;
600
601     _cairo_xml_printf_start (xml,
602                              "<image width='%d' height='%d' format='%s'>",
603                              image->width, image->height,
604                              _format_to_string (image->format));
605
606     stream = _cairo_base64_stream_create (xml->stream);
607     status = cairo_surface_write_to_png_stream (&image->base,
608                                                 _write_func, stream);
609     assert (status == CAIRO_STATUS_SUCCESS);
610     status = _cairo_output_stream_destroy (stream);
611     if (unlikely (status))
612         return status;
613
614     _cairo_xml_printf_end (xml, "</image>");
615
616     return CAIRO_STATUS_SUCCESS;
617 }
618
619 static cairo_status_t
620 _cairo_xml_emit_surface (cairo_xml_t *xml,
621                          const cairo_surface_pattern_t *pattern)
622 {
623     cairo_surface_t *source = pattern->surface;
624     cairo_status_t status;
625
626     if (_cairo_surface_is_recording (source)) {
627         status = cairo_xml_for_recording_surface (&xml->base, source);
628     } else {
629         cairo_image_surface_t *image;
630         void *image_extra;
631
632         status = _cairo_surface_acquire_source_image (source,
633                                                       &image, &image_extra);
634         if (unlikely (status))
635             return status;
636
637         status = _cairo_xml_emit_image (xml, image);
638
639         _cairo_surface_release_source_image (source, image, image_extra);
640     }
641
642     return status;
643 }
644
645 static cairo_status_t
646 _cairo_xml_emit_pattern (cairo_xml_t *xml,
647                          const char *source_or_mask,
648                          const cairo_pattern_t *pattern)
649 {
650     cairo_status_t status;
651
652     _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
653     _cairo_xml_indent (xml, 2);
654
655     switch (pattern->type) {
656     case CAIRO_PATTERN_TYPE_SOLID:
657         status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
658         break;
659     case CAIRO_PATTERN_TYPE_LINEAR:
660         status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
661         break;
662     case CAIRO_PATTERN_TYPE_RADIAL:
663         status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
664         break;
665     case CAIRO_PATTERN_TYPE_SURFACE:
666         status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
667         break;
668     default:
669         ASSERT_NOT_REACHED;
670         status = CAIRO_INT_STATUS_UNSUPPORTED;
671         break;
672     }
673
674     if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
675         _cairo_xml_emit_matrix (xml, &pattern->matrix);
676         _cairo_xml_printf (xml,
677                            "<extend>%s</extend>",
678                            _extend_to_string (pattern->extend));
679         _cairo_xml_printf (xml,
680                            "<filter>%s</filter>",
681                            _filter_to_string (pattern->filter));
682     }
683
684     _cairo_xml_indent (xml, -2);
685     _cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
686
687     return status;
688 }
689
690 static cairo_int_status_t
691 _cairo_xml_surface_paint (void                  *abstract_surface,
692                           cairo_operator_t       op,
693                           const cairo_pattern_t *source,
694                           const cairo_clip_t    *clip)
695 {
696     cairo_xml_surface_t *surface = abstract_surface;
697     cairo_xml_t *xml = to_xml (surface);
698     cairo_status_t status;
699
700     _cairo_xml_printf (xml, "<paint>");
701     _cairo_xml_indent (xml, 2);
702
703     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
704
705     status = _cairo_xml_surface_emit_clip (surface, clip);
706     if (unlikely (status))
707         return status;
708
709     status = _cairo_xml_emit_pattern (xml, "source", source);
710     if (unlikely (status))
711         return status;
712
713     _cairo_xml_indent (xml, -2);
714     _cairo_xml_printf (xml, "</paint>");
715
716     return CAIRO_STATUS_SUCCESS;
717 }
718
719 static cairo_int_status_t
720 _cairo_xml_surface_mask (void                   *abstract_surface,
721                          cairo_operator_t        op,
722                          const cairo_pattern_t  *source,
723                          const cairo_pattern_t  *mask,
724                          const cairo_clip_t     *clip)
725 {
726     cairo_xml_surface_t *surface = abstract_surface;
727     cairo_xml_t *xml = to_xml (surface);
728     cairo_status_t status;
729
730     _cairo_xml_printf (xml, "<mask>");
731     _cairo_xml_indent (xml, 2);
732
733     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
734
735     status = _cairo_xml_surface_emit_clip (surface, clip);
736     if (unlikely (status))
737         return status;
738
739     status = _cairo_xml_emit_pattern (xml, "source", source);
740     if (unlikely (status))
741         return status;
742
743     status = _cairo_xml_emit_pattern (xml, "mask", mask);
744     if (unlikely (status))
745         return status;
746
747     _cairo_xml_indent (xml, -2);
748     _cairo_xml_printf (xml, "</mask>");
749
750     return CAIRO_STATUS_SUCCESS;
751 }
752
753 static cairo_int_status_t
754 _cairo_xml_surface_stroke (void                         *abstract_surface,
755                            cairo_operator_t              op,
756                            const cairo_pattern_t        *source,
757                            const cairo_path_fixed_t             *path,
758                            const cairo_stroke_style_t           *style,
759                            const cairo_matrix_t         *ctm,
760                            const cairo_matrix_t         *ctm_inverse,
761                            double                        tolerance,
762                            cairo_antialias_t             antialias,
763                            const cairo_clip_t           *clip)
764 {
765     cairo_xml_surface_t *surface = abstract_surface;
766     cairo_xml_t *xml = to_xml (surface);
767     cairo_status_t status;
768
769     _cairo_xml_printf (xml, "<stroke>");
770     _cairo_xml_indent (xml, 2);
771
772     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
773     _cairo_xml_emit_double (xml, "line-width", style->line_width);
774     _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
775     _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
776     _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
777
778     status = _cairo_xml_surface_emit_clip (surface, clip);
779     if (unlikely (status))
780         return status;
781
782     status = _cairo_xml_emit_pattern (xml, "source", source);
783     if (unlikely (status))
784         return status;
785
786     if (style->num_dashes) {
787         unsigned int i;
788
789         _cairo_xml_printf_start (xml, "<dash offset='%f'>",
790                                  style->dash_offset);
791         for (i = 0; i < style->num_dashes; i++)
792             _cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
793
794         _cairo_xml_printf_end (xml, "</dash>");
795     }
796
797     _cairo_xml_emit_path (xml, path);
798     _cairo_xml_emit_double (xml, "tolerance", tolerance);
799     _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
800
801     _cairo_xml_emit_matrix (xml, ctm);
802
803     _cairo_xml_indent (xml, -2);
804     _cairo_xml_printf (xml, "</stroke>");
805
806     return CAIRO_STATUS_SUCCESS;
807 }
808
809 static cairo_int_status_t
810 _cairo_xml_surface_fill (void                   *abstract_surface,
811                          cairo_operator_t        op,
812                          const cairo_pattern_t  *source,
813                          const cairo_path_fixed_t*path,
814                          cairo_fill_rule_t       fill_rule,
815                          double                  tolerance,
816                          cairo_antialias_t       antialias,
817                          const cairo_clip_t     *clip)
818 {
819     cairo_xml_surface_t *surface = abstract_surface;
820     cairo_xml_t *xml = to_xml (surface);
821     cairo_status_t status;
822
823     _cairo_xml_printf (xml, "<fill>");
824     _cairo_xml_indent (xml, 2);
825
826     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
827
828     status = _cairo_xml_surface_emit_clip (surface, clip);
829     if (unlikely (status))
830         return status;
831
832     status = _cairo_xml_emit_pattern (xml, "source", source);
833     if (unlikely (status))
834         return status;
835
836     _cairo_xml_emit_path (xml, path);
837     _cairo_xml_emit_double (xml, "tolerance", tolerance);
838     _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
839     _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
840
841     _cairo_xml_indent (xml, -2);
842     _cairo_xml_printf (xml, "</fill>");
843
844     return CAIRO_STATUS_SUCCESS;
845 }
846
847 #if CAIRO_HAS_FT_FONT
848 #include "cairo-ft-private.h"
849 static cairo_status_t
850 _cairo_xml_emit_type42_font (cairo_xml_t *xml,
851                              cairo_scaled_font_t *scaled_font)
852 {
853     const cairo_scaled_font_backend_t *backend;
854     cairo_output_stream_t *base64_stream;
855     cairo_output_stream_t *zlib_stream;
856     cairo_status_t status, status2;
857     unsigned long size;
858     uint32_t len;
859     uint8_t *buf;
860
861     backend = scaled_font->backend;
862     if (backend->load_truetype_table == NULL)
863         return CAIRO_INT_STATUS_UNSUPPORTED;
864
865     size = 0;
866     status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
867     if (unlikely (status))
868         return status;
869
870     buf = malloc (size);
871     if (unlikely (buf == NULL))
872         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
873
874     status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
875     if (unlikely (status)) {
876         free (buf);
877         return status;
878     }
879
880     _cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
881                        _cairo_ft_scaled_font_get_load_flags (scaled_font));
882
883
884     base64_stream = _cairo_base64_stream_create (xml->stream);
885     len = size;
886     _cairo_output_stream_write (base64_stream, &len, sizeof (len));
887
888     zlib_stream = _cairo_deflate_stream_create (base64_stream);
889
890     _cairo_output_stream_write (zlib_stream, buf, size);
891     free (buf);
892
893     status2 = _cairo_output_stream_destroy (zlib_stream);
894     if (status == CAIRO_STATUS_SUCCESS)
895         status = status2;
896
897     status2 = _cairo_output_stream_destroy (base64_stream);
898     if (status == CAIRO_STATUS_SUCCESS)
899         status = status2;
900
901     _cairo_xml_printf_end (xml, "</font>");
902
903     return status;
904 }
905 #else
906 static cairo_status_t
907 _cairo_xml_emit_type42_font (cairo_xml_t *xml,
908                              cairo_scaled_font_t *scaled_font)
909 {
910     return CAIRO_INT_STATUS_UNSUPPORTED;
911 }
912 #endif
913
914 static cairo_status_t
915 _cairo_xml_emit_type3_font (cairo_xml_t *xml,
916                             cairo_scaled_font_t *scaled_font,
917                             cairo_glyph_t *glyphs,
918                             int num_glyphs)
919 {
920     _cairo_xml_printf_start (xml, "<font type='3'>");
921     _cairo_xml_printf_end (xml, "</font>");
922
923     return CAIRO_STATUS_SUCCESS;
924 }
925
926 static cairo_status_t
927 _cairo_xml_emit_scaled_font (cairo_xml_t *xml,
928                              cairo_scaled_font_t *scaled_font,
929                              cairo_glyph_t *glyphs,
930                              int num_glyphs)
931 {
932     cairo_int_status_t status;
933
934     _cairo_xml_printf (xml, "<scaled-font>");
935     _cairo_xml_indent (xml, 2);
936
937     status = _cairo_xml_emit_type42_font (xml, scaled_font);
938     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
939         status = _cairo_xml_emit_type3_font (xml, scaled_font,
940                                              glyphs, num_glyphs);
941     }
942
943     _cairo_xml_indent (xml, -2);
944     _cairo_xml_printf (xml, "<scaled-font>");
945
946     return status;
947 }
948
949 static cairo_int_status_t
950 _cairo_xml_surface_glyphs (void                     *abstract_surface,
951                            cairo_operator_t          op,
952                            const cairo_pattern_t    *source,
953                            cairo_glyph_t            *glyphs,
954                            int                       num_glyphs,
955                            cairo_scaled_font_t      *scaled_font,
956                            const cairo_clip_t       *clip)
957 {
958     cairo_xml_surface_t *surface = abstract_surface;
959     cairo_xml_t *xml = to_xml (surface);
960     cairo_status_t status;
961     int i;
962
963     _cairo_xml_printf (xml, "<glyphs>");
964     _cairo_xml_indent (xml, 2);
965
966     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
967
968     status = _cairo_xml_surface_emit_clip (surface, clip);
969     if (unlikely (status))
970         return status;
971
972     status = _cairo_xml_emit_pattern (xml, "source", source);
973     if (unlikely (status))
974         return status;
975
976     status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
977     if (unlikely (status))
978         return status;
979
980     for (i = 0; i < num_glyphs; i++) {
981         _cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
982                            glyphs[i].index,
983                            glyphs[i].x,
984                            glyphs[i].y);
985     }
986
987     _cairo_xml_indent (xml, -2);
988     _cairo_xml_printf (xml, "</glyphs>");
989
990     return CAIRO_STATUS_SUCCESS;
991 }
992
993 static const cairo_surface_backend_t
994 _cairo_xml_surface_backend = {
995     CAIRO_SURFACE_TYPE_XML,
996     NULL,
997
998     _cairo_default_context_create,
999
1000     _cairo_xml_surface_create_similar,
1001     NULL, /* create_similar_image */
1002     NULL, /* map_to_image */
1003     NULL, /* unmap_image */
1004
1005     _cairo_surface_default_source,
1006     NULL, /* acquire source image */
1007     NULL, /* release source image */
1008     NULL, /* snapshot */
1009
1010     NULL, /* copy page */
1011     NULL, /* show page */
1012
1013     _cairo_xml_surface_get_extents,
1014     NULL, /* get_font_options */
1015
1016     NULL, /* flush */
1017     NULL, /* mark_dirty_rectangle */
1018
1019     _cairo_xml_surface_paint,
1020     _cairo_xml_surface_mask,
1021     _cairo_xml_surface_stroke,
1022     _cairo_xml_surface_fill,
1023     NULL, /* fill_stroke */
1024     _cairo_xml_surface_glyphs,
1025 };
1026
1027 static cairo_surface_t *
1028 _cairo_xml_surface_create_internal (cairo_device_t *device,
1029                                     cairo_content_t content,
1030                                     double width,
1031                                     double height)
1032 {
1033     cairo_xml_surface_t *surface;
1034
1035     surface = malloc (sizeof (cairo_xml_surface_t));
1036     if (unlikely (surface == NULL))
1037         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1038
1039     _cairo_surface_init (&surface->base,
1040                          &_cairo_xml_surface_backend,
1041                          device,
1042                          content);
1043
1044     surface->width = width;
1045     surface->height = height;
1046
1047     return &surface->base;
1048 }
1049
1050 cairo_device_t *
1051 cairo_xml_create (const char *filename)
1052 {
1053     cairo_output_stream_t *stream;
1054     cairo_status_t status;
1055
1056     stream = _cairo_output_stream_create_for_filename (filename);
1057     if ((status = _cairo_output_stream_get_status (stream)))
1058         return _cairo_device_create_in_error (status);
1059
1060     return _cairo_xml_create_internal (stream);
1061 }
1062
1063 cairo_device_t *
1064 cairo_xml_create_for_stream (cairo_write_func_t  write_func,
1065                              void               *closure)
1066 {
1067     cairo_output_stream_t *stream;
1068     cairo_status_t status;
1069
1070     stream = _cairo_output_stream_create (write_func, NULL, closure);
1071     if ((status = _cairo_output_stream_get_status (stream)))
1072         return _cairo_device_create_in_error (status);
1073
1074     return _cairo_xml_create_internal (stream);
1075 }
1076
1077 cairo_surface_t *
1078 cairo_xml_surface_create (cairo_device_t *device,
1079                           cairo_content_t content,
1080                           double width, double height)
1081 {
1082     if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
1083         return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
1084
1085     if (unlikely (device->status))
1086         return _cairo_surface_create_in_error (device->status);
1087
1088     return _cairo_xml_surface_create_internal (device, content, width, height);
1089 }
1090
1091 cairo_status_t
1092 cairo_xml_for_recording_surface (cairo_device_t  *device,
1093                                  cairo_surface_t *recording_surface)
1094 {
1095     cairo_box_t bbox;
1096     cairo_rectangle_int_t extents;
1097     cairo_surface_t *surface;
1098     cairo_xml_t *xml;
1099     cairo_status_t status;
1100
1101     if (unlikely (device->status))
1102         return device->status;
1103
1104     if (unlikely (recording_surface->status))
1105         return recording_surface->status;
1106
1107     if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
1108         return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
1109
1110     if (unlikely (! _cairo_surface_is_recording (recording_surface)))
1111         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1112
1113     status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
1114                                                 &bbox, NULL);
1115     if (unlikely (status))
1116         return status;
1117
1118     _cairo_box_round_to_rectangle (&bbox, &extents);
1119     surface = _cairo_xml_surface_create_internal (device,
1120                                                   recording_surface->content,
1121                                                   extents.width,
1122                                                   extents.height);
1123     if (unlikely (surface->status))
1124         return surface->status;
1125
1126     xml = (cairo_xml_t *) device;
1127
1128     _cairo_xml_printf (xml,
1129                        "<surface content='%s' width='%d' height='%d'>",
1130                        _content_to_string (recording_surface->content),
1131                        extents.width, extents.height);
1132     _cairo_xml_indent (xml, 2);
1133
1134     cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
1135     status = _cairo_recording_surface_replay (recording_surface, surface);
1136     cairo_surface_destroy (surface);
1137
1138     _cairo_xml_indent (xml, -2);
1139     _cairo_xml_printf (xml, "</surface>");
1140
1141     return status;
1142 }
1143 slim_hidden_def (cairo_xml_for_recording_surface);