Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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_boxes (cairo_xml_surface_t *surface,
  463.                                     cairo_clip_t *clip)
  464. {
  465.     cairo_box_t *box;
  466.     cairo_status_t status;
  467.     cairo_xml_t *xml;
  468.     int n;
  469.  
  470.     if (clip->num_boxes == 0)
  471.         return CAIRO_STATUS_SUCCESS;
  472.  
  473.     /* skip the trivial clip covering the surface extents */
  474.     if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) {
  475.         box = &clip->boxes[0];
  476.         if (box->p1.x <= 0 && box->p1.y <= 0 &&
  477.             box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) &&
  478.             box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height))
  479.         {
  480.             return CAIRO_STATUS_SUCCESS;
  481.         }
  482.     }
  483.  
  484.     xml = to_xml (surface);
  485.  
  486.     _cairo_xml_printf (xml, "<clip>");
  487.     _cairo_xml_indent (xml, 2);
  488.  
  489.     _cairo_xml_printf (xml, "<path>");
  490.     _cairo_xml_indent (xml, 2);
  491.     for (n = 0; n < clip->num_boxes; n++) {
  492.         box = &clip->boxes[n];
  493.  
  494.         _cairo_xml_printf_start (xml, "%f %f m",
  495.                                  _cairo_fixed_to_double (box->p1.x),
  496.                                  _cairo_fixed_to_double (box->p1.y));
  497.         _cairo_xml_printf_continue (xml, " %f %f l",
  498.                                     _cairo_fixed_to_double (box->p2.x),
  499.                                     _cairo_fixed_to_double (box->p1.y));
  500.         _cairo_xml_printf_continue (xml, " %f %f l",
  501.                                     _cairo_fixed_to_double (box->p2.x),
  502.                                     _cairo_fixed_to_double (box->p2.y));
  503.         _cairo_xml_printf_continue (xml, " %f %f l",
  504.                                     _cairo_fixed_to_double (box->p1.x),
  505.                                     _cairo_fixed_to_double (box->p2.y));
  506.         _cairo_xml_printf_end (xml, " h");
  507.     }
  508.     _cairo_xml_indent (xml, -2);
  509.     _cairo_xml_printf (xml, "</path>");
  510.     _cairo_xml_emit_double (xml, "tolerance", 1.0);
  511.     _cairo_xml_emit_string (xml, "antialias",
  512.                             _antialias_to_string (CAIRO_ANTIALIAS_NONE));
  513.     _cairo_xml_emit_string (xml, "fill-rule",
  514.                             _fill_rule_to_string (CAIRO_FILL_RULE_WINDING));
  515.  
  516.     _cairo_xml_indent (xml, -2);
  517.     _cairo_xml_printf (xml, "</clip>");
  518.  
  519.     return CAIRO_STATUS_SUCCESS;
  520. }
  521.  
  522. static cairo_status_t
  523. _cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface,
  524.                                    cairo_clip_path_t *clip_path)
  525. {
  526.     cairo_box_t box;
  527.     cairo_status_t status;
  528.     cairo_xml_t *xml;
  529.  
  530.     if (clip_path == NULL)
  531.         return CAIRO_STATUS_SUCCESS;
  532.  
  533.     status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev);
  534.     if (unlikely (status))
  535.         return status;
  536.  
  537.     /* skip the trivial clip covering the surface extents */
  538.     if (surface->width >= 0 && surface->height >= 0 &&
  539.         _cairo_path_fixed_is_box (&clip_path->path, &box))
  540.     {
  541.         if (box.p1.x <= 0 && box.p1.y <= 0 &&
  542.             box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
  543.             box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
  544.         {
  545.             return CAIRO_STATUS_SUCCESS;
  546.         }
  547.     }
  548.  
  549.     xml = to_xml (surface);
  550.  
  551.     _cairo_xml_printf_start (xml, "<clip>");
  552.     _cairo_xml_indent (xml, 2);
  553.  
  554.     _cairo_xml_emit_path (xml, &clip_path->path);
  555.     _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance);
  556.     _cairo_xml_emit_string (xml, "antialias",
  557.                             _antialias_to_string (clip_path->antialias));
  558.     _cairo_xml_emit_string (xml, "fill-rule",
  559.                             _fill_rule_to_string (clip_path->fill_rule));
  560.  
  561.     _cairo_xml_indent (xml, -2);
  562.     _cairo_xml_printf_end (xml, "</clip>");
  563.  
  564.     return CAIRO_STATUS_SUCCESS;
  565. }
  566.  
  567. static cairo_status_t
  568. _cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
  569.                               const cairo_clip_t *clip)
  570. {
  571.     cairo_status_t status;
  572.  
  573.     if (clip == NULL)
  574.         return CAIRO_STATUS_SUCCESS;
  575.  
  576.     status = _cairo_xml_surface_emit_clip_boxes (surface, clip);
  577.     if (unlikely (status))
  578.         return status;
  579.  
  580.     return _cairo_xml_surface_emit_clip_path (surface, clip->path);
  581. }
  582.  
  583. static cairo_status_t
  584. _cairo_xml_emit_solid (cairo_xml_t *xml,
  585.                        const cairo_solid_pattern_t *solid)
  586. {
  587.     _cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>",
  588.                        solid->color.red,
  589.                        solid->color.green,
  590.                        solid->color.blue,
  591.                        solid->color.alpha);
  592.     return CAIRO_STATUS_SUCCESS;
  593. }
  594.  
  595. static void
  596. _cairo_xml_emit_matrix (cairo_xml_t *xml,
  597.                         const cairo_matrix_t *matrix)
  598. {
  599.     if (! _cairo_matrix_is_identity (matrix)) {
  600.         _cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>",
  601.                            matrix->xx, matrix->yx,
  602.                            matrix->xy, matrix->yy,
  603.                            matrix->x0, matrix->y0);
  604.     }
  605. }
  606.  
  607. static void
  608. _cairo_xml_emit_gradient (cairo_xml_t *xml,
  609.                           const cairo_gradient_pattern_t *gradient)
  610. {
  611.     unsigned int i;
  612.  
  613.     for (i = 0; i < gradient->n_stops; i++) {
  614.         _cairo_xml_printf (xml,
  615.                            "<color-stop>%f %f %f %f %f</color-stop>",
  616.                            gradient->stops[i].offset,
  617.                            gradient->stops[i].color.red,
  618.                            gradient->stops[i].color.green,
  619.                            gradient->stops[i].color.blue,
  620.                            gradient->stops[i].color.alpha);
  621.     }
  622. }
  623.  
  624. static cairo_status_t
  625. _cairo_xml_emit_linear (cairo_xml_t *xml,
  626.                         const cairo_linear_pattern_t *linear)
  627. {
  628.     _cairo_xml_printf (xml,
  629.                        "<linear x1='%f' y1='%f' x2='%f' y2='%f'>",
  630.                        linear->pd1.x, linear->pd1.y,
  631.                        linear->pd2.x, linear->pd2.y);
  632.     _cairo_xml_indent (xml, 2);
  633.     _cairo_xml_emit_gradient (xml, &linear->base);
  634.     _cairo_xml_indent (xml, -2);
  635.     _cairo_xml_printf (xml, "</linear>");
  636.     return CAIRO_STATUS_SUCCESS;
  637. }
  638.  
  639. static cairo_status_t
  640. _cairo_xml_emit_radial (cairo_xml_t *xml,
  641.                         const cairo_radial_pattern_t *radial)
  642. {
  643.     _cairo_xml_printf (xml,
  644.                        "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
  645.                        radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius,
  646.                        radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius);
  647.     _cairo_xml_indent (xml, 2);
  648.     _cairo_xml_emit_gradient (xml, &radial->base);
  649.     _cairo_xml_indent (xml, -2);
  650.     _cairo_xml_printf (xml, "</radial>");
  651.     return CAIRO_STATUS_SUCCESS;
  652. }
  653.  
  654. static cairo_status_t
  655. _write_func (void *closure, const unsigned char *data, unsigned len)
  656. {
  657.     _cairo_output_stream_write (closure, data, len);
  658.     return CAIRO_STATUS_SUCCESS;
  659. }
  660.  
  661. static cairo_status_t
  662. _cairo_xml_emit_image (cairo_xml_t *xml,
  663.                        cairo_image_surface_t *image)
  664. {
  665.     cairo_output_stream_t *stream;
  666.     cairo_status_t status;
  667.  
  668.     _cairo_xml_printf_start (xml,
  669.                              "<image width='%d' height='%d' format='%s'>",
  670.                              image->width, image->height,
  671.                              _format_to_string (image->format));
  672.  
  673.     stream = _cairo_base64_stream_create (xml->stream);
  674.     status = cairo_surface_write_to_png_stream (&image->base,
  675.                                                 _write_func, stream);
  676.     assert (status == CAIRO_STATUS_SUCCESS);
  677.     status = _cairo_output_stream_destroy (stream);
  678.     if (unlikely (status))
  679.         return status;
  680.  
  681.     _cairo_xml_printf_end (xml, "</image>");
  682.  
  683.     return CAIRO_STATUS_SUCCESS;
  684. }
  685.  
  686. static cairo_status_t
  687. _cairo_xml_emit_surface (cairo_xml_t *xml,
  688.                          const cairo_surface_pattern_t *pattern)
  689. {
  690.     cairo_surface_t *source = pattern->surface;
  691.     cairo_status_t status;
  692.  
  693.     if (_cairo_surface_is_recording (source)) {
  694.         status = cairo_xml_for_recording_surface (&xml->base, source);
  695.     } else {
  696.         cairo_image_surface_t *image;
  697.         void *image_extra;
  698.  
  699.         status = _cairo_surface_acquire_source_image (source,
  700.                                                       &image, &image_extra);
  701.         if (unlikely (status))
  702.             return status;
  703.  
  704.         status = _cairo_xml_emit_image (xml, image);
  705.  
  706.         _cairo_surface_release_source_image (source, image, image_extra);
  707.     }
  708.  
  709.     return status;
  710. }
  711.  
  712. static cairo_status_t
  713. _cairo_xml_emit_pattern (cairo_xml_t *xml,
  714.                          const char *source_or_mask,
  715.                          const cairo_pattern_t *pattern)
  716. {
  717.     cairo_status_t status;
  718.  
  719.     _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
  720.     _cairo_xml_indent (xml, 2);
  721.  
  722.     switch (pattern->type) {
  723.     case CAIRO_PATTERN_TYPE_SOLID:
  724.         status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
  725.         break;
  726.     case CAIRO_PATTERN_TYPE_LINEAR:
  727.         status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
  728.         break;
  729.     case CAIRO_PATTERN_TYPE_RADIAL:
  730.         status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
  731.         break;
  732.     case CAIRO_PATTERN_TYPE_SURFACE:
  733.         status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
  734.         break;
  735.     default:
  736.         ASSERT_NOT_REACHED;
  737.         status = CAIRO_INT_STATUS_UNSUPPORTED;
  738.         break;
  739.     }
  740.  
  741.     if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
  742.         _cairo_xml_emit_matrix (xml, &pattern->matrix);
  743.         _cairo_xml_printf (xml,
  744.                            "<extend>%s</extend>",
  745.                            _extend_to_string (pattern->extend));
  746.         _cairo_xml_printf (xml,
  747.                            "<filter>%s</filter>",
  748.                            _filter_to_string (pattern->filter));
  749.     }
  750.  
  751.     _cairo_xml_indent (xml, -2);
  752.     _cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
  753.  
  754.     return status;
  755. }
  756.  
  757. static cairo_int_status_t
  758. _cairo_xml_surface_paint (void                  *abstract_surface,
  759.                           cairo_operator_t       op,
  760.                           const cairo_pattern_t *source,
  761.                           const cairo_clip_t    *clip)
  762. {
  763.     cairo_xml_surface_t *surface = abstract_surface;
  764.     cairo_xml_t *xml = to_xml (surface);
  765.     cairo_status_t status;
  766.  
  767.     _cairo_xml_printf (xml, "<paint>");
  768.     _cairo_xml_indent (xml, 2);
  769.  
  770.     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
  771.  
  772.     status = _cairo_xml_surface_emit_clip (surface, clip);
  773.     if (unlikely (status))
  774.         return status;
  775.  
  776.     status = _cairo_xml_emit_pattern (xml, "source", source);
  777.     if (unlikely (status))
  778.         return status;
  779.  
  780.     _cairo_xml_indent (xml, -2);
  781.     _cairo_xml_printf (xml, "</paint>");
  782.  
  783.     return CAIRO_STATUS_SUCCESS;
  784. }
  785.  
  786. static cairo_int_status_t
  787. _cairo_xml_surface_mask (void                   *abstract_surface,
  788.                          cairo_operator_t        op,
  789.                          const cairo_pattern_t  *source,
  790.                          const cairo_pattern_t  *mask,
  791.                          const cairo_clip_t     *clip)
  792. {
  793.     cairo_xml_surface_t *surface = abstract_surface;
  794.     cairo_xml_t *xml = to_xml (surface);
  795.     cairo_status_t status;
  796.  
  797.     _cairo_xml_printf (xml, "<mask>");
  798.     _cairo_xml_indent (xml, 2);
  799.  
  800.     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
  801.  
  802.     status = _cairo_xml_surface_emit_clip (surface, clip);
  803.     if (unlikely (status))
  804.         return status;
  805.  
  806.     status = _cairo_xml_emit_pattern (xml, "source", source);
  807.     if (unlikely (status))
  808.         return status;
  809.  
  810.     status = _cairo_xml_emit_pattern (xml, "mask", mask);
  811.     if (unlikely (status))
  812.         return status;
  813.  
  814.     _cairo_xml_indent (xml, -2);
  815.     _cairo_xml_printf (xml, "</mask>");
  816.  
  817.     return CAIRO_STATUS_SUCCESS;
  818. }
  819.  
  820. static cairo_int_status_t
  821. _cairo_xml_surface_stroke (void                         *abstract_surface,
  822.                            cairo_operator_t              op,
  823.                            const cairo_pattern_t        *source,
  824.                            const cairo_path_fixed_t             *path,
  825.                            const cairo_stroke_style_t           *style,
  826.                            const cairo_matrix_t         *ctm,
  827.                            const cairo_matrix_t         *ctm_inverse,
  828.                            double                        tolerance,
  829.                            cairo_antialias_t             antialias,
  830.                            const cairo_clip_t           *clip)
  831. {
  832.     cairo_xml_surface_t *surface = abstract_surface;
  833.     cairo_xml_t *xml = to_xml (surface);
  834.     cairo_status_t status;
  835.  
  836.     _cairo_xml_printf (xml, "<stroke>");
  837.     _cairo_xml_indent (xml, 2);
  838.  
  839.     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
  840.     _cairo_xml_emit_double (xml, "line-width", style->line_width);
  841.     _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
  842.     _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
  843.     _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
  844.  
  845.     status = _cairo_xml_surface_emit_clip (surface, clip);
  846.     if (unlikely (status))
  847.         return status;
  848.  
  849.     status = _cairo_xml_emit_pattern (xml, "source", source);
  850.     if (unlikely (status))
  851.         return status;
  852.  
  853.     if (style->num_dashes) {
  854.         unsigned int i;
  855.  
  856.         _cairo_xml_printf_start (xml, "<dash offset='%f'>",
  857.                                  style->dash_offset);
  858.         for (i = 0; i < style->num_dashes; i++)
  859.             _cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
  860.  
  861.         _cairo_xml_printf_end (xml, "</dash>");
  862.     }
  863.  
  864.     _cairo_xml_emit_path (xml, path);
  865.     _cairo_xml_emit_double (xml, "tolerance", tolerance);
  866.     _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
  867.  
  868.     _cairo_xml_emit_matrix (xml, ctm);
  869.  
  870.     _cairo_xml_indent (xml, -2);
  871.     _cairo_xml_printf (xml, "</stroke>");
  872.  
  873.     return CAIRO_STATUS_SUCCESS;
  874. }
  875.  
  876. static cairo_int_status_t
  877. _cairo_xml_surface_fill (void                   *abstract_surface,
  878.                          cairo_operator_t        op,
  879.                          const cairo_pattern_t  *source,
  880.                          const cairo_path_fixed_t*path,
  881.                          cairo_fill_rule_t       fill_rule,
  882.                          double                  tolerance,
  883.                          cairo_antialias_t       antialias,
  884.                          const cairo_clip_t     *clip)
  885. {
  886.     cairo_xml_surface_t *surface = abstract_surface;
  887.     cairo_xml_t *xml = to_xml (surface);
  888.     cairo_status_t status;
  889.  
  890.     _cairo_xml_printf (xml, "<fill>");
  891.     _cairo_xml_indent (xml, 2);
  892.  
  893.     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
  894.  
  895.     status = _cairo_xml_surface_emit_clip (surface, clip);
  896.     if (unlikely (status))
  897.         return status;
  898.  
  899.     status = _cairo_xml_emit_pattern (xml, "source", source);
  900.     if (unlikely (status))
  901.         return status;
  902.  
  903.     _cairo_xml_emit_path (xml, path);
  904.     _cairo_xml_emit_double (xml, "tolerance", tolerance);
  905.     _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
  906.     _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
  907.  
  908.     _cairo_xml_indent (xml, -2);
  909.     _cairo_xml_printf (xml, "</fill>");
  910.  
  911.     return CAIRO_STATUS_SUCCESS;
  912. }
  913.  
  914. #if CAIRO_HAS_FT_FONT
  915. #include "cairo-ft-private.h"
  916. static cairo_status_t
  917. _cairo_xml_emit_type42_font (cairo_xml_t *xml,
  918.                              cairo_scaled_font_t *scaled_font)
  919. {
  920.     const cairo_scaled_font_backend_t *backend;
  921.     cairo_output_stream_t *base64_stream;
  922.     cairo_output_stream_t *zlib_stream;
  923.     cairo_status_t status, status2;
  924.     unsigned long size;
  925.     uint32_t len;
  926.     uint8_t *buf;
  927.  
  928.     backend = scaled_font->backend;
  929.     if (backend->load_truetype_table == NULL)
  930.         return CAIRO_INT_STATUS_UNSUPPORTED;
  931.  
  932.     size = 0;
  933.     status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
  934.     if (unlikely (status))
  935.         return status;
  936.  
  937.     buf = malloc (size);
  938.     if (unlikely (buf == NULL))
  939.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  940.  
  941.     status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
  942.     if (unlikely (status)) {
  943.         free (buf);
  944.         return status;
  945.     }
  946.  
  947.     _cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
  948.                        _cairo_ft_scaled_font_get_load_flags (scaled_font));
  949.  
  950.  
  951.     base64_stream = _cairo_base64_stream_create (xml->stream);
  952.     len = size;
  953.     _cairo_output_stream_write (base64_stream, &len, sizeof (len));
  954.  
  955.     zlib_stream = _cairo_deflate_stream_create (base64_stream);
  956.  
  957.     _cairo_output_stream_write (zlib_stream, buf, size);
  958.     free (buf);
  959.  
  960.     status2 = _cairo_output_stream_destroy (zlib_stream);
  961.     if (status == CAIRO_STATUS_SUCCESS)
  962.         status = status2;
  963.  
  964.     status2 = _cairo_output_stream_destroy (base64_stream);
  965.     if (status == CAIRO_STATUS_SUCCESS)
  966.         status = status2;
  967.  
  968.     _cairo_xml_printf_end (xml, "</font>");
  969.  
  970.     return status;
  971. }
  972. #else
  973. static cairo_status_t
  974. _cairo_xml_emit_type42_font (cairo_xml_t *xml,
  975.                              cairo_scaled_font_t *scaled_font)
  976. {
  977.     return CAIRO_INT_STATUS_UNSUPPORTED;
  978. }
  979. #endif
  980.  
  981. static cairo_status_t
  982. _cairo_xml_emit_type3_font (cairo_xml_t *xml,
  983.                             cairo_scaled_font_t *scaled_font,
  984.                             cairo_glyph_t *glyphs,
  985.                             int num_glyphs)
  986. {
  987.     _cairo_xml_printf_start (xml, "<font type='3'>");
  988.     _cairo_xml_printf_end (xml, "</font>");
  989.  
  990.     return CAIRO_STATUS_SUCCESS;
  991. }
  992.  
  993. static cairo_status_t
  994. _cairo_xml_emit_scaled_font (cairo_xml_t *xml,
  995.                              cairo_scaled_font_t *scaled_font,
  996.                              cairo_glyph_t *glyphs,
  997.                              int num_glyphs)
  998. {
  999.     cairo_int_status_t status;
  1000.  
  1001.     _cairo_xml_printf (xml, "<scaled-font>");
  1002.     _cairo_xml_indent (xml, 2);
  1003.  
  1004.     status = _cairo_xml_emit_type42_font (xml, scaled_font);
  1005.     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
  1006.         status = _cairo_xml_emit_type3_font (xml, scaled_font,
  1007.                                              glyphs, num_glyphs);
  1008.     }
  1009.  
  1010.     _cairo_xml_indent (xml, -2);
  1011.     _cairo_xml_printf (xml, "<scaled-font>");
  1012.  
  1013.     return status;
  1014. }
  1015.  
  1016. static cairo_int_status_t
  1017. _cairo_xml_surface_glyphs (void                     *abstract_surface,
  1018.                            cairo_operator_t          op,
  1019.                            const cairo_pattern_t    *source,
  1020.                            cairo_glyph_t            *glyphs,
  1021.                            int                       num_glyphs,
  1022.                            cairo_scaled_font_t      *scaled_font,
  1023.                            const cairo_clip_t       *clip)
  1024. {
  1025.     cairo_xml_surface_t *surface = abstract_surface;
  1026.     cairo_xml_t *xml = to_xml (surface);
  1027.     cairo_status_t status;
  1028.     int i;
  1029.  
  1030.     _cairo_xml_printf (xml, "<glyphs>");
  1031.     _cairo_xml_indent (xml, 2);
  1032.  
  1033.     _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
  1034.  
  1035.     status = _cairo_xml_surface_emit_clip (surface, clip);
  1036.     if (unlikely (status))
  1037.         return status;
  1038.  
  1039.     status = _cairo_xml_emit_pattern (xml, "source", source);
  1040.     if (unlikely (status))
  1041.         return status;
  1042.  
  1043.     status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
  1044.     if (unlikely (status))
  1045.         return status;
  1046.  
  1047.     for (i = 0; i < num_glyphs; i++) {
  1048.         _cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
  1049.                            glyphs[i].index,
  1050.                            glyphs[i].x,
  1051.                            glyphs[i].y);
  1052.     }
  1053.  
  1054.     _cairo_xml_indent (xml, -2);
  1055.     _cairo_xml_printf (xml, "</glyphs>");
  1056.  
  1057.     return CAIRO_STATUS_SUCCESS;
  1058. }
  1059.  
  1060. static const cairo_surface_backend_t
  1061. _cairo_xml_surface_backend = {
  1062.     CAIRO_SURFACE_TYPE_XML,
  1063.     NULL,
  1064.  
  1065.     _cairo_default_context_create,
  1066.  
  1067.     _cairo_xml_surface_create_similar,
  1068.     NULL, /* create_similar_image */
  1069.     NULL, /* map_to_image */
  1070.     NULL, /* unmap_image */
  1071.  
  1072.     _cairo_surface_default_source,
  1073.     NULL, /* acquire source image */
  1074.     NULL, /* release source image */
  1075.     NULL, /* snapshot */
  1076.  
  1077.     NULL, /* copy page */
  1078.     NULL, /* show page */
  1079.  
  1080.     _cairo_xml_surface_get_extents,
  1081.     NULL, /* get_font_options */
  1082.  
  1083.     NULL, /* flush */
  1084.     NULL, /* mark_dirty_rectangle */
  1085.  
  1086.     _cairo_xml_surface_paint,
  1087.     _cairo_xml_surface_mask,
  1088.     _cairo_xml_surface_stroke,
  1089.     _cairo_xml_surface_fill,
  1090.     NULL, /* fill_stroke */
  1091.     _cairo_xml_surface_glyphs,
  1092. };
  1093.  
  1094. static cairo_surface_t *
  1095. _cairo_xml_surface_create_internal (cairo_device_t *device,
  1096.                                     cairo_content_t content,
  1097.                                     double width,
  1098.                                     double height)
  1099. {
  1100.     cairo_xml_surface_t *surface;
  1101.  
  1102.     surface = malloc (sizeof (cairo_xml_surface_t));
  1103.     if (unlikely (surface == NULL))
  1104.         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
  1105.  
  1106.     _cairo_surface_init (&surface->base,
  1107.                          &_cairo_xml_surface_backend,
  1108.                          device,
  1109.                          content);
  1110.  
  1111.     surface->width = width;
  1112.     surface->height = height;
  1113.  
  1114.     return &surface->base;
  1115. }
  1116.  
  1117. cairo_device_t *
  1118. cairo_xml_create (const char *filename)
  1119. {
  1120.     cairo_output_stream_t *stream;
  1121.     cairo_status_t status;
  1122.  
  1123.     stream = _cairo_output_stream_create_for_filename (filename);
  1124.     if ((status = _cairo_output_stream_get_status (stream)))
  1125.         return _cairo_device_create_in_error (status);
  1126.  
  1127.     return _cairo_xml_create_internal (stream);
  1128. }
  1129.  
  1130. cairo_device_t *
  1131. cairo_xml_create_for_stream (cairo_write_func_t  write_func,
  1132.                              void               *closure)
  1133. {
  1134.     cairo_output_stream_t *stream;
  1135.     cairo_status_t status;
  1136.  
  1137.     stream = _cairo_output_stream_create (write_func, NULL, closure);
  1138.     if ((status = _cairo_output_stream_get_status (stream)))
  1139.         return _cairo_device_create_in_error (status);
  1140.  
  1141.     return _cairo_xml_create_internal (stream);
  1142. }
  1143.  
  1144. cairo_surface_t *
  1145. cairo_xml_surface_create (cairo_device_t *device,
  1146.                           cairo_content_t content,
  1147.                           double width, double height)
  1148. {
  1149.     if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
  1150.         return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
  1151.  
  1152.     if (unlikely (device->status))
  1153.         return _cairo_surface_create_in_error (device->status);
  1154.  
  1155.     return _cairo_xml_surface_create_internal (device, content, width, height);
  1156. }
  1157.  
  1158. cairo_status_t
  1159. cairo_xml_for_recording_surface (cairo_device_t  *device,
  1160.                                  cairo_surface_t *recording_surface)
  1161. {
  1162.     cairo_box_t bbox;
  1163.     cairo_rectangle_int_t extents;
  1164.     cairo_surface_t *surface;
  1165.     cairo_xml_t *xml;
  1166.     cairo_status_t status;
  1167.  
  1168.     if (unlikely (device->status))
  1169.         return device->status;
  1170.  
  1171.     if (unlikely (recording_surface->status))
  1172.         return recording_surface->status;
  1173.  
  1174.     if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
  1175.         return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
  1176.  
  1177.     if (unlikely (! _cairo_surface_is_recording (recording_surface)))
  1178.         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  1179.  
  1180.     status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
  1181.                                                 &bbox, NULL);
  1182.     if (unlikely (status))
  1183.         return status;
  1184.  
  1185.     _cairo_box_round_to_rectangle (&bbox, &extents);
  1186.     surface = _cairo_xml_surface_create_internal (device,
  1187.                                                   recording_surface->content,
  1188.                                                   extents.width,
  1189.                                                   extents.height);
  1190.     if (unlikely (surface->status))
  1191.         return surface->status;
  1192.  
  1193.     xml = (cairo_xml_t *) device;
  1194.  
  1195.     _cairo_xml_printf (xml,
  1196.                        "<surface content='%s' width='%d' height='%d'>",
  1197.                        _content_to_string (recording_surface->content),
  1198.                        extents.width, extents.height);
  1199.     _cairo_xml_indent (xml, 2);
  1200.  
  1201.     cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
  1202.     status = _cairo_recording_surface_replay (recording_surface, surface);
  1203.     cairo_surface_destroy (surface);
  1204.  
  1205.     _cairo_xml_indent (xml, -2);
  1206.     _cairo_xml_printf (xml, "</surface>");
  1207.  
  1208.     return status;
  1209. }
  1210. slim_hidden_def (cairo_xml_for_recording_surface);
  1211.