/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson */ /* This surface is intended to produce a verbose, hierarchical, DAG XML file * representing a single surface. It is intended to be used by debuggers, * such as cairo-sphinx, or by application test-suites that want a log of * operations. */ #include "cairoint.h" #include "cairo-xml.h" #include "cairo-clip-private.h" #include "cairo-device-private.h" #include "cairo-default-context-private.h" #include "cairo-image-surface-private.h" #include "cairo-error-private.h" #include "cairo-output-stream-private.h" #include "cairo-recording-surface-inline.h" #define static cairo_warn static typedef struct _cairo_xml_surface cairo_xml_surface_t; typedef struct _cairo_xml { cairo_device_t base; cairo_output_stream_t *stream; int indent; } cairo_xml_t; struct _cairo_xml_surface { cairo_surface_t base; double width, height; }; slim_hidden_proto (cairo_xml_for_recording_surface); static const cairo_surface_backend_t _cairo_xml_surface_backend; static const char * _operator_to_string (cairo_operator_t op) { static const char *names[] = { "CLEAR", /* CAIRO_OPERATOR_CLEAR */ "SOURCE", /* CAIRO_OPERATOR_SOURCE */ "OVER", /* CAIRO_OPERATOR_OVER */ "IN", /* CAIRO_OPERATOR_IN */ "OUT", /* CAIRO_OPERATOR_OUT */ "ATOP", /* CAIRO_OPERATOR_ATOP */ "DEST", /* CAIRO_OPERATOR_DEST */ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ "XOR", /* CAIRO_OPERATOR_XOR */ "ADD", /* CAIRO_OPERATOR_ADD */ "SATURATE", /* CAIRO_OPERATOR_SATURATE */ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ "SCREEN", /* CAIRO_OPERATOR_SCREEN */ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ "DARKEN", /* CAIRO_OPERATOR_DARKEN */ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ }; assert (op < ARRAY_LENGTH (names)); return names[op]; } static const char * _extend_to_string (cairo_extend_t extend) { static const char *names[] = { "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ }; assert (extend < ARRAY_LENGTH (names)); return names[extend]; } static const char * _filter_to_string (cairo_filter_t filter) { static const char *names[] = { "FILTER_FAST", /* CAIRO_FILTER_FAST */ "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ "FILTER_BEST", /* CAIRO_FILTER_BEST */ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ }; assert (filter < ARRAY_LENGTH (names)); return names[filter]; } static const char * _fill_rule_to_string (cairo_fill_rule_t rule) { static const char *names[] = { "WINDING", /* CAIRO_FILL_RULE_WINDING */ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ }; assert (rule < ARRAY_LENGTH (names)); return names[rule]; } static const char * _antialias_to_string (cairo_antialias_t antialias) { static const char *names[] = { "DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ "NONE", /* CAIRO_ANTIALIAS_NONE */ "GRAY", /* CAIRO_ANTIALIAS_GRAY */ "SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */ "FAST", /* CAIRO_ANTIALIAS_FAST */ "GOOD", /* CAIRO_ANTIALIAS_GOOD */ "BEST", /* CAIRO_ANTIALIAS_BEST */ }; assert (antialias < ARRAY_LENGTH (names)); return names[antialias]; } static const char * _line_cap_to_string (cairo_line_cap_t line_cap) { static const char *names[] = { "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ }; assert (line_cap < ARRAY_LENGTH (names)); return names[line_cap]; } static const char * _line_join_to_string (cairo_line_join_t line_join) { static const char *names[] = { "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ }; assert (line_join < ARRAY_LENGTH (names)); return names[line_join]; } static const char * _content_to_string (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_ALPHA: return "ALPHA"; case CAIRO_CONTENT_COLOR: return "COLOR"; default: case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; } } static const char * _format_to_string (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: return "ARGB32"; case CAIRO_FORMAT_RGB30: return "RGB30"; case CAIRO_FORMAT_RGB24: return "RGB24"; case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; case CAIRO_FORMAT_A8: return "A8"; case CAIRO_FORMAT_A1: return "A1"; case CAIRO_FORMAT_INVALID: return "INVALID"; } ASSERT_NOT_REACHED; return "INVALID"; } static cairo_status_t _device_flush (void *abstract_device) { cairo_xml_t *xml = abstract_device; cairo_status_t status; status = _cairo_output_stream_flush (xml->stream); return status; } static void _device_destroy (void *abstract_device) { cairo_xml_t *xml = abstract_device; cairo_status_t status; status = _cairo_output_stream_destroy (xml->stream); free (xml); } static const cairo_device_backend_t _cairo_xml_device_backend = { CAIRO_DEVICE_TYPE_XML, NULL, NULL, /* lock, unlock */ _device_flush, NULL, /* finish */ _device_destroy }; static cairo_device_t * _cairo_xml_create_internal (cairo_output_stream_t *stream) { cairo_xml_t *xml; xml = _cairo_malloc (sizeof (cairo_xml_t)); if (unlikely (xml == NULL)) return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); memset (xml, 0, sizeof (cairo_xml_t)); _cairo_device_init (&xml->base, &_cairo_xml_device_backend); xml->indent = 0; xml->stream = stream; return &xml->base; } static void _cairo_xml_indent (cairo_xml_t *xml, int indent) { xml->indent += indent; assert (xml->indent >= 0); } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) { va_list ap; char indent[80]; int len; len = MIN (xml->indent, ARRAY_LENGTH (indent)); memset (indent, ' ', len); _cairo_output_stream_write (xml->stream, indent, len); va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); _cairo_output_stream_write (xml->stream, "\n", 1); } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) { char indent[80]; int len; len = MIN (xml->indent, ARRAY_LENGTH (indent)); memset (indent, ' ', len); _cairo_output_stream_write (xml->stream, indent, len); if (fmt != NULL) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); } } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); } _cairo_output_stream_write (xml->stream, "\n", 1); } static cairo_surface_t * _cairo_xml_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_rectangle_t extents; extents.x = extents.y = 0; extents.width = width; extents.height = height; return cairo_recording_surface_create (content, &extents); } static cairo_bool_t _cairo_xml_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_xml_surface_t *surface = abstract_surface; if (surface->width < 0 || surface->height < 0) return FALSE; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } static cairo_status_t _cairo_xml_move_to (void *closure, const cairo_point_t *p1) { _cairo_xml_printf_continue (closure, " %f %f m", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_line_to (void *closure, const cairo_point_t *p1) { _cairo_xml_printf_continue (closure, " %f %f l", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y), _cairo_fixed_to_double (p3->x), _cairo_fixed_to_double (p3->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_close_path (void *closure) { _cairo_xml_printf_continue (closure, " h"); return CAIRO_STATUS_SUCCESS; } static void _cairo_xml_emit_path (cairo_xml_t *xml, const cairo_path_fixed_t *path) { cairo_status_t status; _cairo_xml_printf_start (xml, ""); status = _cairo_path_fixed_interpret (path, _cairo_xml_move_to, _cairo_xml_line_to, _cairo_xml_curve_to, _cairo_xml_close_path, xml); assert (status == CAIRO_STATUS_SUCCESS); _cairo_xml_printf_end (xml, ""); } static void _cairo_xml_emit_string (cairo_xml_t *xml, const char *node, const char *data) { _cairo_xml_printf (xml, "<%s>%s", node, data, node); } static void _cairo_xml_emit_double (cairo_xml_t *xml, const char *node, double data) { _cairo_xml_printf (xml, "<%s>%f", node, data, node); } static cairo_xml_t * to_xml (cairo_xml_surface_t *surface) { return (cairo_xml_t *) surface->base.device; } static cairo_status_t _cairo_xml_surface_emit_clip_boxes (cairo_xml_surface_t *surface, const cairo_clip_t *clip) { cairo_box_t *box; cairo_xml_t *xml; int n; if (clip->num_boxes == 0) return CAIRO_STATUS_SUCCESS; /* skip the trivial clip covering the surface extents */ if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) { box = &clip->boxes[0]; if (box->p1.x <= 0 && box->p1.y <= 0 && box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) && box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } } xml = to_xml (surface); _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); for (n = 0; n < clip->num_boxes; n++) { box = &clip->boxes[n]; _cairo_xml_printf_start (xml, "%f %f m", _cairo_fixed_to_double (box->p1.x), _cairo_fixed_to_double (box->p1.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p2.x), _cairo_fixed_to_double (box->p1.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p2.x), _cairo_fixed_to_double (box->p2.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p1.x), _cairo_fixed_to_double (box->p2.y)); _cairo_xml_printf_end (xml, " h"); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); _cairo_xml_emit_double (xml, "tolerance", 1.0); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (CAIRO_ANTIALIAS_NONE)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (CAIRO_FILL_RULE_WINDING)); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface, const cairo_clip_path_t *clip_path) { cairo_box_t box; cairo_status_t status; cairo_xml_t *xml; if (clip_path == NULL) return CAIRO_STATUS_SUCCESS; status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev); if (unlikely (status)) return status; /* skip the trivial clip covering the surface extents */ if (surface->width >= 0 && surface->height >= 0 && _cairo_path_fixed_is_box (&clip_path->path, &box)) { if (box.p1.x <= 0 && box.p1.y <= 0 && box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } } xml = to_xml (surface); _cairo_xml_printf_start (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_path (xml, &clip_path->path); _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (clip_path->antialias)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (clip_path->fill_rule)); _cairo_xml_indent (xml, -2); _cairo_xml_printf_end (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, const cairo_clip_t *clip) { cairo_status_t status; if (clip == NULL) return CAIRO_STATUS_SUCCESS; status = _cairo_xml_surface_emit_clip_boxes (surface, clip); if (unlikely (status)) return status; return _cairo_xml_surface_emit_clip_path (surface, clip->path); } static cairo_status_t _cairo_xml_emit_solid (cairo_xml_t *xml, const cairo_solid_pattern_t *solid) { _cairo_xml_printf (xml, "%f %f %f %f", solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); return CAIRO_STATUS_SUCCESS; } static void _cairo_xml_emit_matrix (cairo_xml_t *xml, const cairo_matrix_t *matrix) { if (! _cairo_matrix_is_identity (matrix)) { _cairo_xml_printf (xml, "%f %f %f %f %f %f", matrix->xx, matrix->yx, matrix->xy, matrix->yy, matrix->x0, matrix->y0); } } static void _cairo_xml_emit_gradient (cairo_xml_t *xml, const cairo_gradient_pattern_t *gradient) { unsigned int i; for (i = 0; i < gradient->n_stops; i++) { _cairo_xml_printf (xml, "%f %f %f %f %f", gradient->stops[i].offset, gradient->stops[i].color.red, gradient->stops[i].color.green, gradient->stops[i].color.blue, gradient->stops[i].color.alpha); } } static cairo_status_t _cairo_xml_emit_linear (cairo_xml_t *xml, const cairo_linear_pattern_t *linear) { _cairo_xml_printf (xml, "", linear->pd1.x, linear->pd1.y, linear->pd2.x, linear->pd2.y); _cairo_xml_indent (xml, 2); _cairo_xml_emit_gradient (xml, &linear->base); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_radial (cairo_xml_t *xml, const cairo_radial_pattern_t *radial) { _cairo_xml_printf (xml, "", radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius, radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius); _cairo_xml_indent (xml, 2); _cairo_xml_emit_gradient (xml, &radial->base); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _write_func (void *closure, const unsigned char *data, unsigned len) { _cairo_output_stream_write (closure, data, len); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_image (cairo_xml_t *xml, cairo_image_surface_t *image) { cairo_output_stream_t *stream; cairo_status_t status; _cairo_xml_printf_start (xml, "", image->width, image->height, _format_to_string (image->format)); stream = _cairo_base64_stream_create (xml->stream); status = cairo_surface_write_to_png_stream (&image->base, _write_func, stream); assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_output_stream_destroy (stream); if (unlikely (status)) return status; _cairo_xml_printf_end (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_surface (cairo_xml_t *xml, const cairo_surface_pattern_t *pattern) { cairo_surface_t *source = pattern->surface; cairo_status_t status; if (_cairo_surface_is_recording (source)) { status = cairo_xml_for_recording_surface (&xml->base, source); } else { cairo_image_surface_t *image; void *image_extra; status = _cairo_surface_acquire_source_image (source, &image, &image_extra); if (unlikely (status)) return status; status = _cairo_xml_emit_image (xml, image); _cairo_surface_release_source_image (source, image, image_extra); } return status; } static cairo_status_t _cairo_xml_emit_pattern (cairo_xml_t *xml, const char *source_or_mask, const cairo_pattern_t *pattern) { cairo_status_t status; _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); _cairo_xml_indent (xml, 2); switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_LINEAR: status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_RADIAL: status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_SURFACE: status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); break; default: ASSERT_NOT_REACHED; status = CAIRO_INT_STATUS_UNSUPPORTED; break; } if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { _cairo_xml_emit_matrix (xml, &pattern->matrix); _cairo_xml_printf (xml, "%s", _extend_to_string (pattern->extend)); _cairo_xml_printf (xml, "%s", _filter_to_string (pattern->filter)); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, "", source_or_mask); return status; } static cairo_int_status_t _cairo_xml_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xml_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "mask", mask); if (unlikely (status)) return status; _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xml_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); _cairo_xml_emit_double (xml, "line-width", style->line_width); _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; if (style->num_dashes) { unsigned int i; _cairo_xml_printf_start (xml, "", style->dash_offset); for (i = 0; i < style->num_dashes; i++) _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); _cairo_xml_printf_end (xml, ""); } _cairo_xml_emit_path (xml, path); _cairo_xml_emit_double (xml, "tolerance", tolerance); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); _cairo_xml_emit_matrix (xml, ctm); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xml_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; _cairo_xml_emit_path (xml, path); _cairo_xml_emit_double (xml, "tolerance", tolerance); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } #if CAIRO_HAS_FT_FONT #include "cairo-ft-private.h" static cairo_status_t _cairo_xml_emit_type42_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font) { const cairo_scaled_font_backend_t *backend; cairo_output_stream_t *base64_stream; cairo_output_stream_t *zlib_stream; cairo_status_t status, status2; unsigned long size; uint32_t len; uint8_t *buf; backend = scaled_font->backend; if (backend->load_truetype_table == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; size = 0; status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); if (unlikely (status)) return status; buf = _cairo_malloc (size); if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); if (unlikely (status)) { free (buf); return status; } _cairo_xml_printf_start (xml, "", _cairo_ft_scaled_font_get_load_flags (scaled_font)); base64_stream = _cairo_base64_stream_create (xml->stream); len = size; _cairo_output_stream_write (base64_stream, &len, sizeof (len)); zlib_stream = _cairo_deflate_stream_create (base64_stream); _cairo_output_stream_write (zlib_stream, buf, size); free (buf); status2 = _cairo_output_stream_destroy (zlib_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (base64_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; _cairo_xml_printf_end (xml, ""); return status; } #else static cairo_status_t _cairo_xml_emit_type42_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font) { return CAIRO_INT_STATUS_UNSUPPORTED; } #endif static cairo_status_t _cairo_xml_emit_type3_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs) { _cairo_xml_printf_start (xml, ""); _cairo_xml_printf_end (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_scaled_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs) { cairo_int_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); status = _cairo_xml_emit_type42_font (xml, scaled_font); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_xml_emit_type3_font (xml, scaled_font, glyphs, num_glyphs); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return status; } static cairo_int_status_t _cairo_xml_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; int i; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); if (unlikely (status)) return status; for (i = 0; i < num_glyphs; i++) { _cairo_xml_printf (xml, "%f %f", glyphs[i].index, glyphs[i].x, glyphs[i].y); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t _cairo_xml_surface_backend = { CAIRO_SURFACE_TYPE_XML, NULL, _cairo_default_context_create, _cairo_xml_surface_create_similar, NULL, /* create_similar_image */ NULL, /* map_to_image */ NULL, /* unmap_image */ _cairo_surface_default_source, NULL, /* acquire source image */ NULL, /* release source image */ NULL, /* snapshot */ NULL, /* copy page */ NULL, /* show page */ _cairo_xml_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_xml_surface_paint, _cairo_xml_surface_mask, _cairo_xml_surface_stroke, _cairo_xml_surface_fill, NULL, /* fill_stroke */ _cairo_xml_surface_glyphs, }; static cairo_surface_t * _cairo_xml_surface_create_internal (cairo_device_t *device, cairo_content_t content, double width, double height) { cairo_xml_surface_t *surface; surface = _cairo_malloc (sizeof (cairo_xml_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xml_surface_backend, device, content, TRUE); /* is_vector */ surface->width = width; surface->height = height; return &surface->base; } cairo_device_t * cairo_xml_create (const char *filename) { cairo_output_stream_t *stream; cairo_status_t status; stream = _cairo_output_stream_create_for_filename (filename); if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); return _cairo_xml_create_internal (stream); } cairo_device_t * cairo_xml_create_for_stream (cairo_write_func_t write_func, void *closure) { cairo_output_stream_t *stream; cairo_status_t status; stream = _cairo_output_stream_create (write_func, NULL, closure); if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); return _cairo_xml_create_internal (stream); } cairo_surface_t * cairo_xml_surface_create (cairo_device_t *device, cairo_content_t content, double width, double height) { if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (device->status)) return _cairo_surface_create_in_error (device->status); return _cairo_xml_surface_create_internal (device, content, width, height); } cairo_status_t cairo_xml_for_recording_surface (cairo_device_t *device, cairo_surface_t *recording_surface) { cairo_box_t bbox; cairo_rectangle_int_t extents; cairo_surface_t *surface; cairo_xml_t *xml; cairo_status_t status; if (unlikely (device->status)) return device->status; if (unlikely (recording_surface->status)) return recording_surface->status; if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (! _cairo_surface_is_recording (recording_surface))) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, &bbox, NULL); if (unlikely (status)) return status; _cairo_box_round_to_rectangle (&bbox, &extents); surface = _cairo_xml_surface_create_internal (device, recording_surface->content, extents.width, extents.height); if (unlikely (surface->status)) return surface->status; xml = (cairo_xml_t *) device; _cairo_xml_printf (xml, "", _content_to_string (recording_surface->content), extents.width, extents.height); _cairo_xml_indent (xml, 2); cairo_surface_set_device_offset (surface, -extents.x, -extents.y); status = _cairo_recording_surface_replay (recording_surface, surface); cairo_surface_destroy (surface); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return status; } slim_hidden_def (cairo_xml_for_recording_surface);