From 3575c942f81d54c88fd48aee2352ecd29406e711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 12 Jun 2006 03:07:19 -0400 Subject: [PATCH 1/8] Make cairo_output_stream_t an embeddable type. Most internal cairo types are transparent within cairo and have init and fini functions to intialize and finialize them in place. This way they can be easily be embedded in other structs or derived from. Initially, the cairo_output_stream_t type was proposed as a publically visible type and thus kept opaque. However, now it's only used internally and derived from in a number of places so let's make it an embeddable type for consistency and ease of use. The patch keeps _cairo_output_stream_create() and _cairo_output_stream_close() around for (internal) backwards compatibility by deriving a cairo_output_stream_with_closure_t stream type. The patch also moves all cairo_output_stream_t functions out of cairoint.h and into new file cairo-output-stream-private.h, thus chipping away at the monolithic cairoint.h. --- src/cairo-base85-stream.c | 1 + src/cairo-output-stream.c | 76 +++++++++++++++++++++++++---------- src/cairo-pdf-surface.c | 1 + src/cairo-ps-surface.c | 1 + src/cairo-svg-surface.c | 1 + src/cairoint.h | 84 --------------------------------------- 6 files changed, 60 insertions(+), 104 deletions(-) diff --git a/src/cairo-base85-stream.c b/src/cairo-base85-stream.c index d42700de9..2861a4233 100644 --- a/src/cairo-base85-stream.c +++ b/src/cairo-base85-stream.c @@ -35,6 +35,7 @@ */ #include "cairoint.h" +#include "cairo-output-stream-private.h" typedef struct _cairo_base85_stream { cairo_output_stream_t *output; diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index 5ef36e5fd..c71b89fa0 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -1,4 +1,4 @@ -/* cairo_output_stream.c: Output stream abstraction +/* cairo-output-stream.c: Output stream abstraction * * Copyright © 2005 Red Hat, Inc * @@ -38,24 +38,35 @@ #include #include #include "cairoint.h" +#include "cairo-output-stream-private.h" #ifdef _MSC_VER #define snprintf _snprintf #endif /* _MSC_VER */ -struct _cairo_output_stream { - cairo_write_func_t write_func; - cairo_close_func_t close_func; - void *closure; - unsigned long position; - cairo_status_t status; - cairo_bool_t closed; -}; + +cairo_private void +_cairo_output_stream_init (cairo_output_stream_t *stream, + cairo_output_stream_write_func_t write_func, + cairo_output_stream_close_func_t close_func) +{ + stream->write_func = write_func; + stream->close_func = close_func; + stream->position = 0; + stream->status = CAIRO_STATUS_SUCCESS; + stream->closed = FALSE; +} + +cairo_private void +_cairo_output_stream_fini (cairo_output_stream_t *stream) +{ + _cairo_output_stream_close (stream); +} + const cairo_output_stream_t cairo_output_stream_nil = { NULL, /* write_func */ NULL, /* close_func */ - NULL, /* closure */ 0, /* position */ CAIRO_STATUS_NO_MEMORY, FALSE /* closed */ @@ -64,31 +75,56 @@ const cairo_output_stream_t cairo_output_stream_nil = { static const cairo_output_stream_t cairo_output_stream_nil_write_error = { NULL, /* write_func */ NULL, /* close_func */ - NULL, /* closure */ 0, /* position */ CAIRO_STATUS_WRITE_ERROR, FALSE /* closed */ }; +typedef struct _cairo_output_stream_with_closure { + cairo_output_stream_t base; + cairo_write_func_t write_func; + cairo_close_func_t close_func; + void *closure; +} cairo_output_stream_with_closure_t; + + +static cairo_status_t +closure_write (cairo_output_stream_t *stream, + const unsigned char *data, unsigned int length) +{ + cairo_output_stream_with_closure_t *stream_with_closure = + (cairo_output_stream_with_closure_t *) stream; + + return stream_with_closure->write_func (stream_with_closure->closure, + data, length); +} + +static cairo_status_t +closure_close (cairo_output_stream_t *stream) +{ + cairo_output_stream_with_closure_t *stream_with_closure = + (cairo_output_stream_with_closure_t *) stream; + + return stream_with_closure->close_func (stream_with_closure->closure); +} + cairo_output_stream_t * _cairo_output_stream_create (cairo_write_func_t write_func, cairo_close_func_t close_func, void *closure) { - cairo_output_stream_t *stream; + cairo_output_stream_with_closure_t *stream; - stream = malloc (sizeof (cairo_output_stream_t)); + stream = malloc (sizeof (cairo_output_stream_with_closure_t)); if (stream == NULL) return (cairo_output_stream_t *) &cairo_output_stream_nil; + _cairo_output_stream_init (&stream->base, closure_write, closure_close); stream->write_func = write_func; stream->close_func = close_func; stream->closure = closure; - stream->position = 0; - stream->status = CAIRO_STATUS_SUCCESS; - stream->closed = FALSE; - return stream; + return &stream->base; } void @@ -106,7 +142,7 @@ _cairo_output_stream_close (cairo_output_stream_t *stream) } if (stream->close_func) { - status = stream->close_func (stream->closure); + status = stream->close_func (stream); if (status) stream->status = status; } @@ -120,7 +156,7 @@ _cairo_output_stream_destroy (cairo_output_stream_t *stream) if (stream == NULL) return; - _cairo_output_stream_close (stream); + _cairo_output_stream_fini (stream); free (stream); } @@ -134,7 +170,7 @@ _cairo_output_stream_write (cairo_output_stream_t *stream, if (stream->status) return; - stream->status = stream->write_func (stream->closure, data, length); + stream->status = stream->write_func (stream, data, length); stream->position += length; } diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index c0586b9e0..cca02a606 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -43,6 +43,7 @@ #include "cairo-ft-private.h" #include "cairo-paginated-surface-private.h" #include "cairo-path-fixed-private.h" +#include "cairo-output-stream-private.h" #include #include diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index a621670e4..53d646825 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -44,6 +44,7 @@ #include "cairo-paginated-surface-private.h" #include "cairo-meta-surface-private.h" #include "cairo-ft-private.h" +#include "cairo-output-stream-private.h" #include #include diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 6d43119fe..893ef8938 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -46,6 +46,7 @@ #include "cairo-meta-surface-private.h" #include "cairo-paginated-surface-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-output-stream-private.h" #include diff --git a/src/cairoint.h b/src/cairoint.h index 78062a798..91e450287 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -2191,90 +2191,6 @@ _cairo_utf8_to_utf16 (const unsigned char *str, uint16_t **result, int *items_written); -/* cairo_output_stream.c */ - -typedef struct _cairo_output_stream cairo_output_stream_t; - -extern const cairo_private cairo_output_stream_t cairo_output_stream_nil; - -/* We already have the following declared in cairo.h: - -typedef cairo_status_t (*cairo_write_func_t) (void *closure, - const unsigned char *data, - unsigned int length); -*/ -typedef cairo_status_t (*cairo_close_func_t) (void *closure); - -/* This function never returns NULL. If an error occurs (NO_MEMORY) - * while trying to create the output stream this function returns a - * valid pointer to a nil output stream. - * - * Note that even with a nil surface, the close_func callback will be - * called by a call to _cairo_output_stream_close or - * _cairo_output_stream_destroy. - */ -cairo_private cairo_output_stream_t * -_cairo_output_stream_create (cairo_write_func_t write_func, - cairo_close_func_t close_func, - void *closure); - -cairo_private void -_cairo_output_stream_close (cairo_output_stream_t *stream); - -cairo_private void -_cairo_output_stream_destroy (cairo_output_stream_t *stream); - -cairo_private void -_cairo_output_stream_write (cairo_output_stream_t *stream, - const void *data, size_t length); - -cairo_private void -_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, - const char *data, - size_t length); - -cairo_private unsigned char * -_cairo_lzw_compress (unsigned char *data, unsigned long *data_size_in_out); - -cairo_private void -_cairo_output_stream_vprintf (cairo_output_stream_t *stream, - const char *fmt, va_list ap); - -cairo_private void -_cairo_output_stream_printf (cairo_output_stream_t *stream, - const char *fmt, ...) CAIRO_PRINTF_FORMAT(2, 3); - -cairo_private long -_cairo_output_stream_get_position (cairo_output_stream_t *status); - -cairo_private cairo_status_t -_cairo_output_stream_get_status (cairo_output_stream_t *stream); - -/* This function never returns NULL. If an error occurs (NO_MEMORY or - * WRITE_ERROR) while trying to create the output stream this function - * returns a valid pointer to a nil output stream. - * - * NOTE: Even if a nil surface is returned, the caller should still - * call _cairo_output_stream_destroy (or _cairo_output_stream_close at - * least) in order to ensure that everything is properly cleaned up. - */ -cairo_private cairo_output_stream_t * -_cairo_output_stream_create_for_filename (const char *filename); - -/* This function never returns NULL. If an error occurs (NO_MEMORY or - * WRITE_ERROR) while trying to create the output stream this function - * returns a valid pointer to a nil output stream. - * - * The caller still "owns" file and is responsible for calling fclose - * on it when finished. The stream will not do this itself. - */ -cairo_private cairo_output_stream_t * -_cairo_output_stream_create_for_file (FILE *file); - -/* cairo_base85_stream.c */ -cairo_output_stream_t * -_cairo_base85_stream_create (cairo_output_stream_t *output); - cairo_private void _cairo_error (cairo_status_t status); From 45cbe055d9cf4ac2cf2fbcd21b020c8f869a0380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 12 Jun 2006 03:08:51 -0400 Subject: [PATCH 2/8] Convert the stdio output stream to use the new output stream interface. --- src/cairo-output-stream.c | 54 ++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index c71b89fa0..2ef7add80 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -365,39 +365,46 @@ _cairo_output_stream_get_status (cairo_output_stream_t *stream) /* Maybe this should be a configure time option, so embedded targets * don't have to pull in stdio. */ -static cairo_status_t -stdio_write (void *closure, const unsigned char *data, unsigned int length) -{ - FILE *file = closure; - if (fwrite (data, 1, length, file) != length) +typedef struct _stdio_stream { + cairo_output_stream_t base; + FILE *file; +} stdio_stream_t; + +static cairo_status_t +stdio_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + stdio_stream_t *stream = (stdio_stream_t *) base; + + if (fwrite (data, 1, length, stream->file) != length) return CAIRO_STATUS_WRITE_ERROR; return CAIRO_STATUS_SUCCESS; } static cairo_status_t -stdio_flush (void *closure) +stdio_flush (cairo_output_stream_t *base) { - FILE *file = closure; + stdio_stream_t *stream = (stdio_stream_t *) base; - fflush (file); + fflush (stream->file); - if (ferror (file)) + if (ferror (stream->file)) return CAIRO_STATUS_WRITE_ERROR; else return CAIRO_STATUS_SUCCESS; } static cairo_status_t -stdio_close (void *closure) +stdio_close (cairo_output_stream_t *base) { cairo_status_t status; - FILE *file = closure; + stdio_stream_t *stream = (stdio_stream_t *) base; - status = stdio_flush (closure); + status = stdio_flush (base); - fclose (file); + fclose (stream->file); return status; } @@ -405,20 +412,37 @@ stdio_close (void *closure) cairo_output_stream_t * _cairo_output_stream_create_for_file (FILE *file) { + stdio_stream_t *stream; + if (file == NULL) return (cairo_output_stream_t *) &cairo_output_stream_nil_write_error; - return _cairo_output_stream_create (stdio_write, stdio_flush, file); + stream = malloc (sizeof *stream); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; + + _cairo_output_stream_init (&stream->base, stdio_write, stdio_flush); + stream->file = file; + + return &stream->base; } cairo_output_stream_t * _cairo_output_stream_create_for_filename (const char *filename) { + stdio_stream_t *stream; FILE *file; file = fopen (filename, "wb"); if (file == NULL) return (cairo_output_stream_t *) &cairo_output_stream_nil_write_error; - return _cairo_output_stream_create (stdio_write, stdio_close, file); + stream = malloc (sizeof *stream); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; + + _cairo_output_stream_init (&stream->base, stdio_write, stdio_close); + stream->file = file; + + return &stream->base; } From d43321066081eacca2e0c86258c68bae49036087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 12 Jun 2006 03:10:40 -0400 Subject: [PATCH 3/8] Convert the word wrap stream, the base85 stream and the ps string stream. --- src/cairo-base85-stream.c | 27 +++++++++++------------- src/cairo-ps-surface.c | 44 +++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/cairo-base85-stream.c b/src/cairo-base85-stream.c index 2861a4233..7163d0092 100644 --- a/src/cairo-base85-stream.c +++ b/src/cairo-base85-stream.c @@ -38,6 +38,7 @@ #include "cairo-output-stream-private.h" typedef struct _cairo_base85_stream { + cairo_output_stream_t base; cairo_output_stream_t *output; unsigned char four_tuple[4]; int pending; @@ -64,11 +65,11 @@ _expand_four_tuple_to_five (unsigned char four_tuple[4], } static cairo_status_t -_cairo_base85_stream_write (void *closure, - const unsigned char *data, - unsigned int length) +_cairo_base85_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) { - cairo_base85_stream_t *stream = closure; + cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base; const unsigned char *ptr = data; unsigned char five_tuple[5]; cairo_bool_t is_zero; @@ -90,10 +91,9 @@ _cairo_base85_stream_write (void *closure, } static cairo_status_t -_cairo_base85_stream_close (void *closure) +_cairo_base85_stream_close (cairo_output_stream_t *base) { - cairo_status_t status; - cairo_base85_stream_t *stream = closure; + cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base; unsigned char five_tuple[5]; if (stream->pending) { @@ -105,11 +105,7 @@ _cairo_base85_stream_close (void *closure) /* Mark end of base85 data */ _cairo_output_stream_printf (stream->output, "~>"); - status = _cairo_output_stream_get_status (stream->output); - - free (stream); - - return status; + return _cairo_output_stream_get_status (stream->output); } cairo_output_stream_t * @@ -121,10 +117,11 @@ _cairo_base85_stream_create (cairo_output_stream_t *output) if (stream == NULL) return (cairo_output_stream_t *) &cairo_output_stream_nil; + _cairo_output_stream_init (&stream->base, + _cairo_base85_stream_write, + _cairo_base85_stream_close); stream->output = output; stream->pending = 0; - return _cairo_output_stream_create (_cairo_base85_stream_write, - _cairo_base85_stream_close, - stream); + return &stream->base; } diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 53d646825..bed1f9f13 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -757,6 +757,7 @@ cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) * max_column it will not be broken up. */ typedef struct _word_wrap_stream { + cairo_output_stream_t base; cairo_output_stream_t *output; int max_column; int column; @@ -779,11 +780,11 @@ _count_word_up_to (const unsigned char *s, int length) } static cairo_status_t -_word_wrap_stream_write (void *closure, +_word_wrap_stream_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { - word_wrap_stream_t *stream = closure; + word_wrap_stream_t *stream = (word_wrap_stream_t *) base; cairo_bool_t newline; int word; @@ -824,16 +825,11 @@ _word_wrap_stream_write (void *closure, } static cairo_status_t -_word_wrap_stream_close (void *closure) +_word_wrap_stream_close (cairo_output_stream_t *base) { - cairo_status_t status; - word_wrap_stream_t *stream = closure; + word_wrap_stream_t *stream = (word_wrap_stream_t *) base; - status = _cairo_output_stream_get_status (stream->output); - - free (stream); - - return status; + return _cairo_output_stream_get_status (stream->output); } static cairo_output_stream_t * @@ -845,13 +841,15 @@ _word_wrap_stream_create (cairo_output_stream_t *output, int max_column) if (stream == NULL) return (cairo_output_stream_t *) &cairo_output_stream_nil; + _cairo_output_stream_init (&stream->base, + _word_wrap_stream_write, + _word_wrap_stream_close); stream->output = output; stream->max_column = max_column; stream->column = 0; stream->last_write_was_space = FALSE; - return _cairo_output_stream_create (_word_wrap_stream_write, - _word_wrap_stream_close, stream); + return &stream->base; } static cairo_surface_t * @@ -1099,17 +1097,18 @@ _analyze_operation (cairo_ps_surface_t *surface, #define STRING_ARRAY_MAX_COLUMN 72 typedef struct _string_array_stream { + cairo_output_stream_t base; cairo_output_stream_t *output; int column; int string_size; } string_array_stream_t; static cairo_status_t -_string_array_stream_write (void *closure, - const unsigned char *data, - unsigned int length) +_string_array_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) { - string_array_stream_t *stream = closure; + string_array_stream_t *stream = (string_array_stream_t *) base; unsigned char c; const unsigned char backslash = '\\'; @@ -1152,17 +1151,15 @@ _string_array_stream_write (void *closure, } static cairo_status_t -_string_array_stream_close (void *closure) +_string_array_stream_close (cairo_output_stream_t *base) { cairo_status_t status; - string_array_stream_t *stream = closure; + string_array_stream_t *stream = (string_array_stream_t *) base; _cairo_output_stream_printf (stream->output, ")\n"); status = _cairo_output_stream_get_status (stream->output); - free (stream); - return status; } @@ -1189,13 +1186,14 @@ _string_array_stream_create (cairo_output_stream_t *output) if (stream == NULL) return (cairo_output_stream_t *) &cairo_output_stream_nil; + _cairo_output_stream_init (&stream->base, + _string_array_stream_write, + _string_array_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; - return _cairo_output_stream_create (_string_array_stream_write, - _string_array_stream_close, - stream); + return &stream->base; } /* PS Output - this section handles output of the parts of the meta From 8581447e7b26f34c7be43fef7dfa5458f2278556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 12 Jun 2006 03:15:33 -0400 Subject: [PATCH 4/8] Add an in-memory output-stream implementation. --- src/cairo-output-stream.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index 2ef7add80..9ff939371 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -446,3 +446,62 @@ _cairo_output_stream_create_for_filename (const char *filename) return &stream->base; } + + +typedef struct _memory_stream { + cairo_output_stream_t base; + cairo_array_t array; +} memory_stream_t; + +static cairo_status_t +memory_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + return _cairo_array_append_multiple (&stream->array, data, length); +} + +static cairo_status_t +memory_close (cairo_output_stream_t *base) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + _cairo_array_fini (&stream->array); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_memory_stream_create (void) +{ + memory_stream_t *stream; + + stream = malloc (sizeof *stream); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; + + _cairo_output_stream_init (&stream->base, memory_write, memory_close); + _cairo_array_init (&stream->array, 1); + + return &stream->base; +} + +void +_cairo_memory_stream_copy (cairo_output_stream_t *base, + cairo_output_stream_t *dest) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + _cairo_output_stream_write (dest, + _cairo_array_index (&stream->array, 0), + _cairo_array_num_elements (&stream->array)); +} + +int +_cairo_memory_stream_length (cairo_output_stream_t *base) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + return _cairo_array_num_elements (&stream->array); +} From 17d41d1f71a5d5af489bc395e3bb3cf414aeda57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Tue, 13 Jun 2006 17:24:36 -0400 Subject: [PATCH 5/8] Only run fallback-resolution test case when all of PDF, PS and SVG are enabled. --- test/Makefile.am | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/Makefile.am b/test/Makefile.am index f6a86e757..26fce8717 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -21,7 +21,6 @@ dash-offset-negative \ dash-zero-length \ device-offset \ extend-reflect \ -fallback-resolution \ fill-and-stroke \ fill-and-stroke-alpha \ fill-and-stroke-alpha-add \ @@ -108,6 +107,14 @@ if CAIRO_HAS_MULTI_PAGE_SURFACES TESTS += multi-page endif +if CAIRO_HAS_SVG_SURFACE +if CAIRO_HAS_PDF_SURFACE +if CAIRO_HAS_PS_SURFACE +TESTS += fallback-resolution +endif +endif +endif + # XXX: Here are some existing tests that are currently disabled for # one reason or another. # From ed60128bd0822e56c8002f3503044d5b0efaec79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Tue, 13 Jun 2006 17:25:24 -0400 Subject: [PATCH 6/8] Rewrite SVG backend to just use cairo output stream instead of libxml2. --- src/cairo-svg-surface.c | 1151 ++++++++++++++++----------------------- 1 file changed, 466 insertions(+), 685 deletions(-) diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 893ef8938..9a3bda44f 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -48,16 +48,11 @@ #include "cairo-scaled-font-subsets-private.h" #include "cairo-output-stream-private.h" -#include - -#define CC2XML(s) ((const xmlChar *)(s)) -#define C2XML(s) ((xmlChar *)(s)) - -#define CAIRO_SVG_DTOSTR_BUFFER_LEN 30 - typedef struct cairo_svg_document cairo_svg_document_t; typedef struct cairo_svg_surface cairo_svg_surface_t; +static const int invalid_pattern_id = -1; + static const cairo_svg_version_t _cairo_svg_versions[CAIRO_SVG_VERSION_LAST] = { CAIRO_SVG_VERSION_1_1, @@ -85,10 +80,8 @@ struct cairo_svg_document { double width; double height; - xmlDocPtr xml_doc; - xmlNodePtr xml_node_defs; - xmlNodePtr xml_node_main; - xmlNodePtr xml_node_glyphs; + cairo_output_stream_t *xml_node_defs; + cairo_output_stream_t *xml_node_glyphs; unsigned int surface_id; unsigned int linear_pattern_id; @@ -119,10 +112,10 @@ struct cairo_svg_surface { cairo_svg_document_t *document; - xmlNodePtr xml_node; - xmlNodePtr xml_root_node; + cairo_output_stream_t *xml_node; unsigned int clip_level; + unsigned int base_clip; cairo_paginated_mode_t paginated_mode; }; @@ -340,9 +333,6 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, double height) { cairo_svg_surface_t *surface; - xmlNodePtr clip, rect; - int clip_id; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; surface = malloc (sizeof (cairo_svg_surface_t)); if (surface == NULL) { @@ -361,36 +351,23 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, surface->clip_level = 0; surface->id = document->surface_id++; - clip_id = document->clip_id++; + surface->base_clip = document->clip_id++; - clip = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("clipPath"), NULL); - snprintf (buffer, sizeof buffer, "clip%d", clip_id); - xmlSetProp (clip, CC2XML ("id"), C2XML (buffer)); - rect = xmlNewChild (clip, NULL, CC2XML ("rect"), NULL); - _cairo_dtostr (buffer, sizeof buffer, width); - xmlSetProp (rect, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, height); - xmlSetProp (rect, CC2XML ("height"), C2XML (buffer)); + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " \n" + "\n", + surface->base_clip, + width, height); - /* Use of xlink namespace requires node to be linked to tree. - * So by default we link surface main node to document svg node. - * For surfaces that don't own document, their main node will be - * unlinked and freed in surface finish. */ - surface->xml_node = xmlNewChild (document->xml_node_main, NULL, CC2XML ("g"), NULL); - surface->xml_root_node = surface->xml_node; - - snprintf (buffer, sizeof buffer, "surface%d", surface->id); - xmlSetProp (surface->xml_node, CC2XML ("id"), C2XML (buffer)); - snprintf (buffer, sizeof buffer, "url(#clip%d)", clip_id); - xmlSetProp (surface->xml_node, CC2XML ("clip-path"), C2XML (buffer)); + surface->xml_node = _cairo_memory_stream_create (); if (content == CAIRO_CONTENT_COLOR) { - rect = xmlNewChild (surface->xml_node, NULL, CC2XML ("rect"), NULL); - _cairo_dtostr (buffer, sizeof buffer, width); - xmlSetProp (rect, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, height); - xmlSetProp (rect, CC2XML ("height"), C2XML (buffer)); - xmlSetProp (rect, CC2XML ("style"), CC2XML ("opacity:1; stroke:none; fill:rgb(0,0,0);")); + _cairo_output_stream_printf (surface->xml_node, + "\n", + width, height); } surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; @@ -428,8 +405,7 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, typedef struct { - cairo_svg_document_t *document; - xmlBufferPtr path; + cairo_output_stream_t *output; cairo_matrix_t *ctm_inverse; } svg_path_info_t; @@ -437,21 +413,13 @@ static cairo_status_t _cairo_svg_path_move_to (void *closure, cairo_point_t *point) { svg_path_info_t *info = closure; - xmlBufferPtr path = info->path; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (info->ctm_inverse) cairo_matrix_transform_point (info->ctm_inverse, &x, &y); - xmlBufferCat (path, CC2XML ("M ")); - _cairo_dtostr (buffer, sizeof buffer, x); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, y); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); + _cairo_output_stream_printf (info->output, "M %f %f ", x, y); return CAIRO_STATUS_SUCCESS; } @@ -460,22 +428,13 @@ static cairo_status_t _cairo_svg_path_line_to (void *closure, cairo_point_t *point) { svg_path_info_t *info = closure; - xmlBufferPtr path = info->path; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (info->ctm_inverse) cairo_matrix_transform_point (info->ctm_inverse, &x, &y); - xmlBufferCat (path, CC2XML ("L ")); - - _cairo_dtostr (buffer, sizeof buffer, x); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, y); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); + _cairo_output_stream_printf (info->output, "L %f %f ", x, y); return CAIRO_STATUS_SUCCESS; } @@ -487,8 +446,6 @@ _cairo_svg_path_curve_to (void *closure, cairo_point_t *d) { svg_path_info_t *info = closure; - xmlBufferPtr path = info->path; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; double bx = _cairo_fixed_to_double (b->x); double by = _cairo_fixed_to_double (b->y); double cx = _cairo_fixed_to_double (c->x); @@ -502,25 +459,9 @@ _cairo_svg_path_curve_to (void *closure, cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy); } - xmlBufferCat (path, CC2XML ("C ")); - _cairo_dtostr (buffer, sizeof buffer, bx); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, by); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, cx); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, cy); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, dx); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, dy); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); + _cairo_output_stream_printf (info->output, + "C %f %f %f %f %f %f ", + bx, by, cx, cy, dx, dy); return CAIRO_STATUS_SUCCESS; } @@ -530,11 +471,36 @@ _cairo_svg_path_close_path (void *closure) { svg_path_info_t *info = closure; - xmlBufferCat (info->path, CC2XML ("Z ")); + _cairo_output_stream_printf (info->output, "Z "); return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +emit_path (cairo_output_stream_t *output, + cairo_path_fixed_t *path, + cairo_matrix_t *ctm_inverse) +{ + cairo_status_t status; + svg_path_info_t info; + + _cairo_output_stream_printf (output, "d=\""); + + info.output = output; + info.ctm_inverse = ctm_inverse; + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_svg_path_move_to, + _cairo_svg_path_line_to, + _cairo_svg_path_curve_to, + _cairo_svg_path_close_path, + &info); + + _cairo_output_stream_printf (output, "\""); + + return status; +} + static void _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, @@ -544,9 +510,6 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, { cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status; - svg_path_info_t info; - xmlNodePtr symbol, child; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; status = _cairo_scaled_glyph_lookup (scaled_font, scaled_font_glyph_index, @@ -567,29 +530,15 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, return; } - info.document = document; - info.path = xmlBufferCreate (); - info.ctm_inverse = NULL; + _cairo_output_stream_printf (document->xml_node_glyphs, + "\n" + "path, - CAIRO_DIRECTION_FORWARD, - _cairo_svg_path_move_to, - _cairo_svg_path_line_to, - _cairo_svg_path_curve_to, - _cairo_svg_path_close_path, - &info); + status = emit_path (document->xml_node_glyphs, scaled_glyph->path, NULL); - symbol = xmlNewChild (document->xml_node_glyphs, NULL, - CC2XML ("symbol"), NULL); - snprintf (buffer, sizeof buffer, "glyph%d-%d", - font_id, - subset_glyph_index); - xmlSetProp (symbol, CC2XML ("id"), C2XML (buffer)); - child = xmlNewChild (symbol, NULL, CC2XML ("path"), NULL); - xmlSetProp (child, CC2XML ("d"), xmlBufferContent (info.path)); - xmlSetProp (child, CC2XML ("style"), CC2XML ("stroke: none;")); - - xmlBufferFree (info.path); + _cairo_output_stream_printf (document->xml_node_glyphs, "/>\n\n"); } static void @@ -681,14 +630,12 @@ _cairo_svg_surface_finish (void *abstract_surface) cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; - if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) { + if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) status = _cairo_svg_document_finish (document); - } else { - /* See _cairo_svg_surface_create_for_document */ - xmlUnlinkNode (surface->xml_root_node); - xmlFreeNode (surface->xml_root_node); + else status = CAIRO_STATUS_SUCCESS; - } + + _cairo_output_stream_destroy (surface->xml_node); _cairo_svg_document_destroy (document); @@ -698,60 +645,39 @@ _cairo_svg_surface_finish (void *abstract_surface) static void emit_alpha_filter (cairo_svg_document_t *document) { - if (!document->alpha_filter) { - xmlNodePtr node; - xmlNodePtr child; + if (document->alpha_filter) + return; - node = xmlNewChild (document->xml_node_defs, NULL, - CC2XML ("filter"), NULL); - xmlSetProp (node, CC2XML ("id"), CC2XML ("alpha")); - xmlSetProp (node, CC2XML ("filterUnits"), CC2XML ("objectBoundingBox")); - xmlSetProp (node, CC2XML ("x"), CC2XML ("0%")); - xmlSetProp (node, CC2XML ("y"), CC2XML ("0%")); - xmlSetProp (node, CC2XML ("width"), CC2XML ("100%")); - xmlSetProp (node, CC2XML ("height"), CC2XML ("100%")); - child = xmlNewChild (node, NULL, CC2XML ("feColorMatrix"), NULL); - xmlSetProp (child, CC2XML("type"), CC2XML ("matrix")); - xmlSetProp (child, CC2XML("in"), CC2XML ("SourceGraphic")); - xmlSetProp (child, CC2XML("values"), CC2XML ("0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0")); - document->alpha_filter = TRUE; - } + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " \n" + "\n"); + + document->alpha_filter = TRUE; } static void -emit_transform (xmlNodePtr node, +emit_transform (cairo_output_stream_t *output, char const *attribute_str, + char const *trailer, cairo_matrix_t *matrix) { - xmlBufferPtr matrix_buffer; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - - matrix_buffer = xmlBufferCreate (); - xmlBufferCat (matrix_buffer, CC2XML ("matrix(")); - _cairo_dtostr (buffer, sizeof buffer, matrix->xx); - xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, CC2XML (",")); - _cairo_dtostr (buffer, sizeof buffer, matrix->yx); - xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, CC2XML (",")); - _cairo_dtostr (buffer, sizeof buffer, matrix->xy); - xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, CC2XML (",")); - _cairo_dtostr (buffer, sizeof buffer, matrix->yy); - xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, CC2XML (",")); - _cairo_dtostr (buffer, sizeof buffer, matrix->x0); - xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, CC2XML(",")); - _cairo_dtostr (buffer, sizeof buffer, matrix->y0); - xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, CC2XML (")")); - xmlSetProp (node, CC2XML (attribute_str), C2XML (xmlBufferContent (matrix_buffer))); - xmlBufferFree (matrix_buffer); + _cairo_output_stream_printf (output, + "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"%s", + attribute_str, + matrix->xx, matrix->yx, + matrix->xy, matrix->yy, + matrix->x0, matrix->y0, + trailer); } typedef struct { - xmlBufferPtr buffer; + cairo_output_stream_t *output; unsigned int in_mem; unsigned char src[3]; unsigned char dst[5]; @@ -801,7 +727,7 @@ base64_write_func (void *closure, default: break; } - xmlBufferCat (info->buffer, dst); + _cairo_output_stream_write (info->output, dst, 4); info->in_mem = 0; } @@ -815,32 +741,25 @@ base64_write_func (void *closure, } static cairo_int_status_t -_cairo_surface_base64_encode (cairo_surface_t *surface, - xmlBufferPtr *buffer) +_cairo_surface_base64_encode (cairo_surface_t *surface, + cairo_output_stream_t *output) { cairo_status_t status; base64_write_closure_t info; unsigned int i; - if (buffer == NULL) - return CAIRO_STATUS_NULL_POINTER; - - info.buffer = xmlBufferCreate(); + info.output = output; info.in_mem = 0; info.trailing = 0; memset (info.dst, '\x0', 5); - *buffer = info.buffer; - xmlBufferCat (info.buffer, CC2XML ("data:image/png;base64,")); + _cairo_output_stream_printf (info.output, "data:image/png;base64,"); status = cairo_surface_write_to_png_stream (surface, base64_write_func, (void *) &info); - if (status) { - xmlBufferFree (*buffer); - *buffer = NULL; + if (status) return status; - } if (info.in_mem > 0) { for (i = info.in_mem; i < 3; i++) @@ -853,66 +772,65 @@ _cairo_surface_base64_encode (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static xmlNodePtr -emit_composite_image_pattern (xmlNodePtr node, +static cairo_status_t +emit_composite_image_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *svg_surface, cairo_surface_pattern_t *pattern, - double *width, - double *height, - cairo_bool_t is_pattern) + int pattern_id, + const char *extra_attributes) { - cairo_surface_t *surface = pattern->surface; cairo_image_surface_t *image; cairo_status_t status; cairo_matrix_t p2u; - xmlNodePtr child = NULL; - xmlBufferPtr image_buffer; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; void *image_extra; - status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); - if (status) { - if (width != NULL) - *width = 0; - if (height != NULL) - *height = 0; - return NULL; - } - - status = _cairo_surface_base64_encode (surface, &image_buffer); + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, &image_extra); if (status) - goto BAIL; + return status; - child = xmlNewChild (node, NULL, CC2XML ("image"), NULL); - _cairo_dtostr (buffer, sizeof buffer, image->width); - xmlSetProp (child, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, image->height); - xmlSetProp (child, CC2XML ("height"), C2XML (buffer)); - xmlSetProp (child, CC2XML ("xlink:href"), C2XML (xmlBufferContent (image_buffer))); + p2u = pattern->base.matrix; + cairo_matrix_invert (&p2u); - xmlBufferFree (image_buffer); - - if (!is_pattern) { - p2u = pattern->base.matrix; - cairo_matrix_invert (&p2u); - emit_transform (child, "transform", &p2u); + if (pattern_id != invalid_pattern_id) { + _cairo_output_stream_printf (output, + "width, image->height); + emit_transform (output, " patternTransform", ">\n", &p2u); } - if (width != NULL) - *width = image->width; - if (height != NULL) - *height = image->height; + _cairo_output_stream_printf (output, + " width, image->height); + + if (pattern_id == invalid_pattern_id) + emit_transform (output, " transform", "", &p2u); + + if (extra_attributes) + _cairo_output_stream_printf (output, " %s", extra_attributes); + + _cairo_output_stream_printf (output, " xlink:href=\""); + + status = _cairo_surface_base64_encode (pattern->surface, output); + + _cairo_output_stream_printf (output, "\"/>\n"); + + if (pattern_id != invalid_pattern_id) + _cairo_output_stream_printf (output, "\n"); -BAIL: _cairo_surface_release_source_image (pattern->surface, image, image_extra); - return child; + return status; } static int emit_meta_surface (cairo_svg_document_t *document, cairo_meta_surface_t *surface) { + cairo_output_stream_t *contents; cairo_meta_surface_t *meta; cairo_meta_snapshot_t *snapshot; int num_elements; @@ -933,7 +851,6 @@ emit_meta_surface (cairo_svg_document_t *document, cairo_surface_t *paginated_surface; cairo_surface_t *svg_surface; cairo_meta_snapshot_t new_snapshot; - xmlNodePtr child; meta = (cairo_meta_surface_t *) _cairo_surface_snapshot ((cairo_surface_t *)surface); paginated_surface = _cairo_svg_surface_create_for_document (document, @@ -951,78 +868,114 @@ emit_meta_surface (cairo_svg_document_t *document, new_snapshot.id = ((cairo_svg_surface_t *) svg_surface)->id; _cairo_array_append (&document->meta_snapshots, &new_snapshot); - if (meta->content == CAIRO_CONTENT_ALPHA) + if (meta->content == CAIRO_CONTENT_ALPHA) { emit_alpha_filter (document); - child = xmlAddChild (document->xml_node_defs, - xmlCopyNode (((cairo_svg_surface_t *) svg_surface)->xml_root_node, 1)); - if (meta->content == CAIRO_CONTENT_ALPHA) - xmlSetProp (child, CC2XML ("filter"), CC2XML("url(#alpha)")); + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + ((cairo_svg_surface_t *) svg_surface)->id, + ((cairo_svg_surface_t *) svg_surface)->base_clip); + } else { + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + ((cairo_svg_surface_t *) svg_surface)->id, + ((cairo_svg_surface_t *) svg_surface)->base_clip); + } + + contents = ((cairo_svg_surface_t *) svg_surface)->xml_node; + _cairo_memory_stream_copy (contents, document->xml_node_defs); + + for (i = 0; i < ((cairo_svg_surface_t *) svg_surface)->clip_level; i++) + _cairo_output_stream_printf (document->xml_node_defs, "\n"); + + _cairo_output_stream_printf (document->xml_node_defs, "\n"); id = new_snapshot.id; cairo_surface_destroy (paginated_surface); + + /* FIXME: cairo_paginated_surface doesn't take a ref to the + * passed in target surface so we can't call destroy here. + * cairo_paginated_surface should be fixed, but for now just + * work around it. */ + + /* cairo_surface_destroy (svg_surface); */ } return id; } -static xmlNodePtr -emit_composite_meta_pattern (xmlNodePtr node, +static cairo_status_t +emit_composite_meta_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_surface_pattern_t *pattern, - double *width, - double *height, - cairo_bool_t is_pattern) + int pattern_id, + const char *extra_attributes) { cairo_svg_document_t *document = surface->document; cairo_meta_surface_t *meta_surface; cairo_matrix_t p2u; - xmlNodePtr child; int id; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; meta_surface = (cairo_meta_surface_t *) pattern->surface; id = emit_meta_surface (document, meta_surface); - child = xmlNewChild (node, NULL, CC2XML("use"), NULL); - snprintf (buffer, sizeof buffer, "#surface%d", id); - xmlSetProp (child, CC2XML ("xlink:href"), C2XML (buffer)); + p2u = pattern->base.matrix; + cairo_matrix_invert (&p2u); - if (!is_pattern) { - p2u = pattern->base.matrix; - cairo_matrix_invert (&p2u); - emit_transform (child, "transform", &p2u); + if (pattern_id != invalid_pattern_id) { + _cairo_output_stream_printf (output, + "width_pixels, + meta_surface->height_pixels); + emit_transform (output, " patternTransform", ">\n", &p2u); } - if (width != NULL) - *width = meta_surface->width_pixels; - if (height != NULL) - *height = meta_surface->height_pixels; + _cairo_output_stream_printf (output, + "\n"); + + if (pattern_id != invalid_pattern_id) + _cairo_output_stream_printf (output, "\n"); + + return CAIRO_STATUS_SUCCESS; } -static xmlNodePtr -emit_composite_pattern (xmlNodePtr node, +static cairo_status_t +emit_composite_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_surface_pattern_t *pattern, - double *width, - double *height, - int is_pattern) + int pattern_id, + const char *extra_attributes) { if (_cairo_surface_is_meta (pattern->surface)) { - return emit_composite_meta_pattern (node, surface, pattern, width, height, is_pattern); + return emit_composite_meta_pattern (output, surface, pattern, + pattern_id, extra_attributes); } - return emit_composite_image_pattern (node, surface, pattern, width, height, is_pattern); + return emit_composite_image_pattern (output, surface, pattern, + pattern_id, extra_attributes); } static void -emit_operator (xmlNodePtr node, - cairo_svg_surface_t *surface, - cairo_operator_t op) +emit_operator (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) { char const *op_str[] = { "clear", @@ -1038,129 +991,77 @@ emit_operator (xmlNodePtr node, }; if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) - xmlSetProp (node, CC2XML ("comp-op"), C2XML (op_str[op])); -} - -static void -emit_color (cairo_color_t const *color, - xmlBufferPtr style, - char const *color_str, - char const *opacity_str) -{ - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - - xmlBufferCat (style, CC2XML (color_str)); - xmlBufferCat (style, CC2XML (": rgb(")); - _cairo_dtostr (buffer, sizeof buffer, color->red * 100.0); - xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, CC2XML ("%,")); - _cairo_dtostr (buffer, sizeof buffer, color->green * 100.0); - xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, CC2XML ("%,")); - _cairo_dtostr (buffer, sizeof buffer, color->blue * 100.0); - xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, CC2XML ("%); ")); - xmlBufferCat (style, CC2XML (opacity_str)); - xmlBufferCat (style, CC2XML (": ")); - _cairo_dtostr (buffer, sizeof buffer, color->alpha); - xmlBufferCat (style, CC2XML (buffer)); - xmlBufferCat (style, CC2XML (";")); + _cairo_output_stream_printf (output, "comp-op: %s; ", op_str[op]); } static void emit_solid_pattern (cairo_svg_surface_t *surface, cairo_solid_pattern_t *pattern, - xmlBufferPtr style, - int is_stroke) + cairo_output_stream_t *style, + cairo_bool_t is_stroke) { - emit_color (&pattern->color, - style, is_stroke ? "stroke" : "fill", - "opacity"); + _cairo_output_stream_printf (style, + "%s: rgb(%f%%,%f%%,%f%%); " + "opacity: %f;", + is_stroke ? "stroke" : "fill", + pattern->color.red * 100.0, + pattern->color.green * 100.0, + pattern->color.blue * 100.0, + pattern->color.alpha); } static void emit_surface_pattern (cairo_svg_surface_t *surface, cairo_surface_pattern_t *pattern, - xmlBufferPtr style, - int is_stroke) + cairo_output_stream_t *style, + cairo_bool_t is_stroke) { cairo_svg_document_t *document = surface->document; - xmlNodePtr child; - xmlBufferPtr id; - cairo_matrix_t p2u; - double width, height; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + int pattern_id; - child = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("pattern"), NULL); + pattern_id = document->pattern_id++; + emit_composite_pattern (document->xml_node_defs, surface, pattern, + pattern_id, NULL); - id = xmlBufferCreate (); - xmlBufferCat (id, CC2XML ("pattern")); - snprintf (buffer, sizeof buffer, "%d", document->pattern_id); - xmlBufferCat (id, C2XML (buffer)); - xmlSetProp (child, CC2XML ("id"), C2XML (xmlBufferContent (id))); - - xmlBufferCat (style, CC2XML (is_stroke ? "color: url(#" : "fill: url(#")); - xmlBufferCat (style, xmlBufferContent (id)); - xmlBufferCat (style, CC2XML (");")); - xmlBufferFree (id); - - document->pattern_id++; - - emit_composite_pattern (child, surface, pattern, &width, &height, TRUE); - - _cairo_dtostr (buffer, sizeof buffer, width); - xmlSetProp (child, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, height); - xmlSetProp (child, CC2XML ("height"), C2XML (buffer)); - - p2u = pattern->base.matrix; - cairo_matrix_invert (&p2u); - - emit_transform (child, "patternTransform", &p2u); - - xmlSetProp (child, CC2XML ("patternUnits"), CC2XML ("userSpaceOnUse")); + _cairo_output_stream_printf (style, + "%s: url(#pattern%d);", + is_stroke ? "color" : "fill", + pattern_id); } static void -emit_pattern_stops (xmlNodePtr parent, +emit_pattern_stops (cairo_output_stream_t *output, cairo_gradient_pattern_t const *pattern, double start_offset) { - xmlNodePtr child; - xmlBufferPtr style; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - cairo_color_t color; + double offset; int i; for (i = 0; i < pattern->n_stops; i++) { - child = xmlNewChild (parent, NULL, CC2XML ("stop"), NULL); - _cairo_dtostr (buffer, sizeof buffer, - start_offset + (1 - start_offset ) * - _cairo_fixed_to_double (pattern->stops[i].x)); - xmlSetProp (child, CC2XML ("offset"), C2XML (buffer)); - style = xmlBufferCreate (); - _cairo_color_init_rgba (&color, - pattern->stops[i].color.red / 65535.0, - pattern->stops[i].color.green / 65535.0, - pattern->stops[i].color.blue / 65535.0, - pattern->stops[i].color.alpha / 65535.0); - emit_color (&color, style, "stop-color", "stop-opacity"); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - xmlBufferFree (style); + offset = start_offset + (1 - start_offset ) * + _cairo_fixed_to_double (pattern->stops[i].x); + _cairo_output_stream_printf (output, + "\n", + offset, + pattern->stops[i].color.red / 655.35, + pattern->stops[i].color.green / 655.35, + pattern->stops[i].color.blue / 655.35, + pattern->stops[i].color.alpha / 65535.0); } } static void -emit_pattern_extend (xmlNodePtr node, cairo_extend_t extend) +emit_pattern_extend (cairo_output_stream_t *output, + cairo_pattern_t *pattern) { - char const *value = NULL; - - switch (extend) { + switch (pattern->extend) { case CAIRO_EXTEND_REPEAT: - value = "repeat"; + _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" "); break; case CAIRO_EXTEND_REFLECT: - value = "reflect"; + _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" "); break; case CAIRO_EXTEND_NONE: break; @@ -1168,94 +1069,58 @@ emit_pattern_extend (xmlNodePtr node, cairo_extend_t extend) /* FIXME not implemented */ break; } - - if (value != NULL) - xmlSetProp (node, CC2XML ("spreadMethod"), CC2XML (value)); } static void emit_linear_pattern (cairo_svg_surface_t *surface, cairo_linear_pattern_t *pattern, - xmlBufferPtr style, - int is_stroke) + cairo_output_stream_t *style, + cairo_bool_t is_stroke) { cairo_svg_document_t *document = surface->document; - xmlNodePtr child; - xmlBufferPtr id; double x0, y0, x1, y1; cairo_matrix_t p2u; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - - child = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("linearGradient"), NULL); - - id = xmlBufferCreate (); - xmlBufferCat (id, CC2XML ("linear")); - snprintf (buffer, sizeof buffer, "%d", document->linear_pattern_id); - xmlBufferCat (id, C2XML (buffer)); - xmlSetProp (child, CC2XML ("id"), C2XML (xmlBufferContent (id))); - xmlSetProp (child, CC2XML ("gradientUnits"), CC2XML ("userSpaceOnUse")); - emit_pattern_extend (child, pattern->base.base.extend); - - xmlBufferCat (style, CC2XML (is_stroke ? "color: url(#" : "fill: url(#")); - xmlBufferCat (style, xmlBufferContent (id)); - xmlBufferCat (style, CC2XML (");")); - - xmlBufferFree (id); - - document->linear_pattern_id++; - - emit_pattern_stops (child ,&pattern->base, 0.0); x0 = _cairo_fixed_to_double (pattern->gradient.p1.x); y0 = _cairo_fixed_to_double (pattern->gradient.p1.y); x1 = _cairo_fixed_to_double (pattern->gradient.p2.x); y1 = _cairo_fixed_to_double (pattern->gradient.p2.y); - _cairo_dtostr (buffer, sizeof buffer, x0); - xmlSetProp (child, CC2XML ("x1"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, y0); - xmlSetProp (child, CC2XML ("y1"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, x1); - xmlSetProp (child, CC2XML ("x2"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, y1); - xmlSetProp (child, CC2XML ("y2"), C2XML (buffer)); + _cairo_output_stream_printf (document->xml_node_defs, + "linear_pattern_id, + x0, y0, x1, y1); + emit_pattern_extend (document->xml_node_defs, &pattern->base.base), p2u = pattern->base.base.matrix; cairo_matrix_invert (&p2u); + emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u); - emit_transform (child, "gradientTransform", &p2u); + emit_pattern_stops (document->xml_node_defs ,&pattern->base, 0.0); + + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + + _cairo_output_stream_printf (style, + "%s: url(#linear%d);", + is_stroke ? "color" : "fill", + document->linear_pattern_id); + + document->linear_pattern_id++; } static void -emit_radial_pattern (cairo_svg_surface_t *surface, +emit_radial_pattern (cairo_svg_surface_t *surface, cairo_radial_pattern_t *pattern, - xmlBufferPtr style, int is_stroke) + cairo_output_stream_t *style, + cairo_bool_t is_stroke) { cairo_svg_document_t *document = surface->document; cairo_matrix_t p2u; - xmlNodePtr child; - xmlBufferPtr id; double x0, y0, x1, y1, r0, r1; double fx, fy; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - - child = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("radialGradient"), NULL); - - id = xmlBufferCreate (); - xmlBufferCat (id, CC2XML ("radial")); - snprintf (buffer, sizeof buffer, "%d", document->radial_pattern_id); - xmlBufferCat (id, C2XML (buffer)); - xmlSetProp (child, CC2XML ("id"), C2XML (xmlBufferContent (id))); - xmlSetProp (child, CC2XML ("gradientUnits"), CC2XML ("userSpaceOnUse")); - emit_pattern_extend (child, pattern->base.base.extend); - - xmlBufferCat (style, CC2XML (is_stroke ? "color: url(#" : "fill: url(#")); - xmlBufferCat (style, xmlBufferContent (id)); - xmlBufferCat (style, CC2XML (");")); - - xmlBufferFree (id); - - document->radial_pattern_id++; x0 = _cairo_fixed_to_double (pattern->gradient.inner.x); y0 = _cairo_fixed_to_double (pattern->gradient.inner.y); @@ -1272,44 +1137,52 @@ emit_radial_pattern (cairo_svg_surface_t *surface, fx = (r1 * x0 - r0 * x1) / (r1 - r0); fy = (r1 * y0 - r0 * y1) / (r1 - r0); - emit_pattern_stops (child, &pattern->base, r0 / r1); - - _cairo_dtostr (buffer, sizeof buffer, x1); - xmlSetProp (child, CC2XML ("cx"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, y1); - xmlSetProp (child, CC2XML ("cy"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, fx); - xmlSetProp (child, CC2XML ("fx"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, fy); - xmlSetProp (child, CC2XML ("fy"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, r1); - xmlSetProp (child, CC2XML ("r"), C2XML (buffer)); + _cairo_output_stream_printf (document->xml_node_defs, + "radial_pattern_id, + x1, y1, + fx, fy, r1); + emit_pattern_extend (document->xml_node_defs, &pattern->base.base), p2u = pattern->base.base.matrix; cairo_matrix_invert (&p2u); + emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u); - emit_transform (child, "gradientTransform", &p2u); + emit_pattern_stops (document->xml_node_defs, &pattern->base, r0 / r1); + + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + + _cairo_output_stream_printf (style, + "%s: url(#radial%d);", + is_stroke ? "color" : "fill", + document->radial_pattern_id); + + document->radial_pattern_id++; } static void emit_pattern (cairo_svg_surface_t *surface, cairo_pattern_t *pattern, - xmlBufferPtr style, int is_stroke) + cairo_output_stream_t *output, cairo_bool_t is_stroke) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: - emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, style, is_stroke); + emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, output, is_stroke); break; case CAIRO_PATTERN_TYPE_SURFACE: - emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, style, is_stroke); + emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, output, is_stroke); break; case CAIRO_PATTERN_TYPE_LINEAR: - emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, style, is_stroke); + emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, output, is_stroke); break; case CAIRO_PATTERN_TYPE_RADIAL: - emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, style, is_stroke); + emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, output, is_stroke); break; } } @@ -1324,42 +1197,25 @@ _cairo_svg_surface_fill (void *abstract_surface, cairo_antialias_t antialias) { cairo_svg_surface_t *surface = abstract_surface; - cairo_svg_document_t *document = surface->document; cairo_status_t status; - svg_path_info_t info; - xmlNodePtr child; - xmlBufferPtr style; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _analyze_operation (surface, op, source); assert (_operation_supported (surface, op, source)); - info.document = document; - info.path = xmlBufferCreate (); - info.ctm_inverse = NULL; + _cairo_output_stream_printf (surface->xml_node, + "xml_node, surface, op); + emit_pattern (surface, source, surface->xml_node, FALSE); + _cairo_output_stream_printf (surface->xml_node, "\" "); - style = xmlBufferCreate (); - emit_pattern (surface, source, style, 0); - xmlBufferCat (style, CC2XML (" stroke: none;")); - xmlBufferCat (style, CC2XML (" fill-rule: ")); - xmlBufferCat (style, fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? CC2XML("evenodd;") : CC2XML ("nonzero;")); + status = emit_path (surface->xml_node, path, NULL); - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_svg_path_move_to, - _cairo_svg_path_line_to, - _cairo_svg_path_curve_to, - _cairo_svg_path_close_path, - &info); - - child = xmlNewChild (surface->xml_node, NULL, CC2XML ("path"), NULL); - xmlSetProp (child, CC2XML ("d"), xmlBufferContent (info.path)); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - emit_operator (child, surface, op); - - xmlBufferFree (info.path); - xmlBufferFree (style); + _cairo_output_stream_printf (surface->xml_node, "/>\n"); return status; } @@ -1383,40 +1239,37 @@ _cairo_svg_surface_get_extents (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static xmlNodePtr -emit_paint (xmlNodePtr node, - cairo_svg_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source) +static cairo_status_t +emit_paint (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + cairo_pattern_t *source, + const char *extra_attributes) { - xmlNodePtr child; - xmlBufferPtr style; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && source->extend == CAIRO_EXTEND_NONE) - return emit_composite_pattern (node, + return emit_composite_pattern (output, surface, (cairo_surface_pattern_t *) source, - NULL, NULL, FALSE); + invalid_pattern_id, + extra_attributes); - style = xmlBufferCreate (); - emit_pattern (surface, source, style, 0); - xmlBufferCat (style, CC2XML (" stroke: none;")); + _cairo_output_stream_printf (output, + "width, surface->height); + emit_operator (output, surface, op); + emit_pattern (surface, source, output, FALSE); + _cairo_output_stream_printf (output, " stroke: none;\""); - child = xmlNewChild (node, NULL, CC2XML ("rect"), NULL); - xmlSetProp (child, CC2XML ("x"), CC2XML ("0")); - xmlSetProp (child, CC2XML ("y"), CC2XML ("0")); - _cairo_dtostr (buffer, sizeof buffer, surface->width); - xmlSetProp (child, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, surface->height); - xmlSetProp (child, CC2XML ("height"), C2XML (buffer)); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - emit_operator (child, surface, op); + if (extra_attributes) + _cairo_output_stream_printf (output, " %s", extra_attributes); - xmlBufferFree (style); + _cairo_output_stream_printf (output, "/>\n"); - return child; + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -1450,31 +1303,24 @@ _cairo_svg_surface_paint (void *abstract_surface, if (surface->clip_level == 0 && (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)) { - xmlNodePtr child = surface->xml_root_node->children; - - while (child != NULL) { - xmlUnlinkNode (child); - xmlFreeNode (child); - child = surface->xml_root_node->children; - } + _cairo_output_stream_destroy (surface->xml_node); + surface->xml_node = _cairo_memory_stream_create (); if (op == CAIRO_OPERATOR_CLEAR) { if (surface->content == CAIRO_CONTENT_COLOR) { - xmlNodePtr rect; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - - rect = xmlNewChild (surface->xml_node, NULL, CC2XML ("rect"), NULL); - _cairo_dtostr (buffer, sizeof buffer, surface->width); - xmlSetProp (rect, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, surface->height); - xmlSetProp (rect, CC2XML ("height"), C2XML (buffer)); - xmlSetProp (rect, CC2XML ("style"), CC2XML ("opacity:1; stroke:none; fill:rgb(0,0,0);")); + _cairo_output_stream_printf (surface->xml_node, + "\n", + surface->width, surface->height); } return CAIRO_STATUS_SUCCESS; } } - emit_paint (surface->xml_node, surface, op, source); + emit_paint (surface->xml_node, surface, op, source, NULL); return CAIRO_STATUS_SUCCESS; } @@ -1487,8 +1333,8 @@ _cairo_svg_surface_mask (void *abstract_surface, { cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; - xmlNodePtr child, mask_node; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + cairo_output_stream_t *mask_stream; + char buffer[64]; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _analyze_operation (surface, op, source); @@ -1497,25 +1343,23 @@ _cairo_svg_surface_mask (void *abstract_surface, emit_alpha_filter (document); - mask_node = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("mask"), NULL); - snprintf (buffer, sizeof buffer, "mask%d", document->mask_id); - xmlSetProp (mask_node, CC2XML ("id"), C2XML (buffer)); - child = xmlNewChild (mask_node, NULL, CC2XML ("g"), NULL); - xmlSetProp (child, CC2XML ("filter"), CC2XML ("url(#alpha)")); - emit_paint (child, surface, op, mask); + /* emit_paint() will output a pattern definition to + * document->xml_node_defs so we need to write the mask element to + * a temporary stream and then copy that to xml_node_defs. */ + mask_stream = _cairo_memory_stream_create (); + _cairo_output_stream_printf (mask_stream, + "\n" + " \n", + document->mask_id); + emit_paint (mask_stream, surface, op, mask, NULL); + _cairo_output_stream_printf (mask_stream, + " \n" + "\n"); + _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); - /* mask node need to be located after surface it references, - * but also needs to be linked to xml tree for xlink namespace. - * So we unlink readd it here. */ - xmlUnlinkNode (mask_node); - xmlAddChild (document->xml_node_defs, mask_node); - - child = emit_paint (surface->xml_node, surface, op, source); - - if (child) { - snprintf (buffer, sizeof buffer, "url(#mask%d)", document->mask_id); - xmlSetProp (child, CC2XML ("mask"), C2XML (buffer)); - } + snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d);\"", + document->mask_id); + emit_paint (surface->xml_node, surface, op, source, buffer); document->mask_id++; @@ -1534,92 +1378,79 @@ _cairo_svg_surface_stroke (void *abstract_dst, cairo_antialias_t antialias) { cairo_svg_surface_t *surface = abstract_dst; - cairo_svg_document_t *document = surface->document; cairo_status_t status; - xmlBufferPtr style; - xmlNodePtr child; - svg_path_info_t info; + const char *line_cap, *line_join; unsigned int i; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _analyze_operation (surface, op, source); assert (_operation_supported (surface, op, source)); - info.document = document; - info.path = xmlBufferCreate (); - info.ctm_inverse = ctm_inverse; - - style = xmlBufferCreate (); - emit_pattern (surface, source, style, 1); - xmlBufferCat (style, CC2XML ("fill: none; stroke-width: ")); - _cairo_dtostr (buffer, sizeof buffer, stroke_style->line_width); - xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, CC2XML (";")); - switch (stroke_style->line_cap) { - case CAIRO_LINE_CAP_BUTT: - xmlBufferCat (style, CC2XML ("stroke-linecap: butt;")); - break; - case CAIRO_LINE_CAP_ROUND: - xmlBufferCat (style, CC2XML ("stroke-linecap: round;")); - break; - case CAIRO_LINE_CAP_SQUARE: - xmlBufferCat (style, CC2XML ("stroke-linecap: square;")); - break; + case CAIRO_LINE_CAP_BUTT: + line_cap = "butt"; + break; + case CAIRO_LINE_CAP_ROUND: + line_cap = "round"; + break; + case CAIRO_LINE_CAP_SQUARE: + line_cap = "square"; + break; + default: + ASSERT_NOT_REACHED; } switch (stroke_style->line_join) { - case CAIRO_LINE_JOIN_MITER: - xmlBufferCat (style, CC2XML ("stroke-linejoin: miter;")); - break; - case CAIRO_LINE_JOIN_ROUND: - xmlBufferCat (style, CC2XML ("stroke-linejoin: round;")); - break; - case CAIRO_LINE_JOIN_BEVEL: - xmlBufferCat (style, CC2XML ("stroke-linejoin: bevel;")); - break; + case CAIRO_LINE_JOIN_MITER: + line_join = "miter"; + break; + case CAIRO_LINE_JOIN_ROUND: + line_join = "round"; + break; + case CAIRO_LINE_JOIN_BEVEL: + line_join = "bevel"; + break; + default: + ASSERT_NOT_REACHED; } + _cairo_output_stream_printf (surface->xml_node, + "line_width, + line_cap, + line_join); + + emit_pattern (surface, source, surface->xml_node, TRUE); + emit_operator (surface->xml_node, surface, op); + if (stroke_style->num_dashes > 0) { - xmlBufferCat (style, CC2XML (" stroke-dasharray: ")); + _cairo_output_stream_printf (surface->xml_node, "stroke-dasharray: "); for (i = 0; i < stroke_style->num_dashes; i++) { - if (i != 0) - xmlBufferCat (style, CC2XML (",")); - _cairo_dtostr (buffer, sizeof buffer, stroke_style->dash[i]); - xmlBufferCat (style, C2XML (buffer)); + _cairo_output_stream_printf (surface->xml_node, "%f", + stroke_style->dash[i]); + if (i + 1 < stroke_style->num_dashes) + _cairo_output_stream_printf (surface->xml_node, ","); + else + _cairo_output_stream_printf (surface->xml_node, "; "); } - xmlBufferCat (style, CC2XML (";")); if (stroke_style->dash_offset != 0.0) { - xmlBufferCat (style, CC2XML (" stroke-dashoffset: ")); - _cairo_dtostr (buffer, sizeof buffer, stroke_style->dash_offset); - xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, CC2XML (";")); + _cairo_output_stream_printf (surface->xml_node, + "stroke-dashoffset: %f; ", + stroke_style->dash_offset); } } - xmlBufferCat (style, CC2XML (" stroke-miterlimit: ")); - _cairo_dtostr (buffer, sizeof buffer, stroke_style->miter_limit); - xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, CC2XML (";")); + _cairo_output_stream_printf (surface->xml_node, + "stroke-miterlimit: %f;\" ", + stroke_style->miter_limit); - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_svg_path_move_to, - _cairo_svg_path_line_to, - _cairo_svg_path_curve_to, - _cairo_svg_path_close_path, - &info); + status = emit_path (surface->xml_node, path, ctm_inverse); - child = xmlNewChild (surface->xml_node, NULL, CC2XML ("path"), NULL); - emit_transform (child, "transform", ctm); - xmlSetProp (child, CC2XML ("d"), xmlBufferContent (info.path)); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - emit_operator (child, surface, op); - - xmlBufferFree (info.path); - xmlBufferFree (style); + emit_transform (surface->xml_node, " transform", "/>\n", ctm); return status; } @@ -1636,10 +1467,6 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, cairo_svg_document_t *document = surface->document; cairo_path_fixed_t path; cairo_status_t status; - xmlNodePtr glyph_node; - xmlNodePtr child; - xmlBufferPtr style; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; unsigned int font_id, subset_id, subset_glyph_index; int i; @@ -1657,11 +1484,9 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) goto FALLBACK; - child = xmlNewChild (surface->xml_node, NULL, CC2XML ("g"), NULL); - style = xmlBufferCreate (); - emit_pattern (surface, pattern, style, 0); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - xmlBufferFree (style); + _cairo_output_stream_printf (surface->xml_node, "xml_node, FALSE); + _cairo_output_stream_printf (surface->xml_node, "\">\n"); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets, @@ -1673,16 +1498,15 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, goto FALLBACK; } - glyph_node = xmlNewChild (child, NULL, CC2XML ("use"), NULL); - snprintf (buffer, sizeof buffer, "#glyph%d-%d", - font_id, subset_glyph_index); - xmlSetProp (glyph_node, CC2XML ("xlink:href"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, glyphs[i].x); - xmlSetProp (glyph_node, CC2XML ("x"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, glyphs[i].y); - xmlSetProp (glyph_node, CC2XML ("y"), C2XML (buffer)); + _cairo_output_stream_printf (surface->xml_node, + " \n", + font_id, subset_glyph_index, + glyphs[i].x, glyphs[i].y); } + _cairo_output_stream_printf (surface->xml_node, "\n"); + return CAIRO_STATUS_SUCCESS; FALLBACK: @@ -1712,47 +1536,34 @@ _cairo_svg_surface_intersect_clip_path (void *dst, cairo_svg_surface_t *surface = dst; cairo_svg_document_t *document = surface->document; cairo_status_t status; - xmlNodePtr group, clip, clip_path; - svg_path_info_t info; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + int i; if (path == NULL) { - surface->xml_node = surface->xml_root_node; + for (i = 0; i < surface->clip_level; i++) + _cairo_output_stream_printf (surface->xml_node, "\n"); + surface->clip_level = 0; return CAIRO_STATUS_SUCCESS; } - if (path != NULL) { - info.document = document; - info.path = xmlBufferCreate (); - info.ctm_inverse = NULL; + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " clip_id); + status = emit_path (document->xml_node_defs, path, NULL); + _cairo_output_stream_printf (document->xml_node_defs, + "/>\n" + "\n"); - group = xmlNewChild (surface->xml_node, NULL, CC2XML ("g"), NULL); - clip = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("clipPath"), NULL); - snprintf (buffer, sizeof buffer, "clip%d", document->clip_id); - xmlSetProp (clip, CC2XML ("id"), C2XML (buffer)); + _cairo_output_stream_printf (surface->xml_node, + "\n", + document->clip_id, + fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? + "evenodd" : "nonzero"); - clip_path = xmlNewChild (clip, NULL, CC2XML ("path"), NULL); - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_svg_path_move_to, - _cairo_svg_path_line_to, - _cairo_svg_path_curve_to, - _cairo_svg_path_close_path, - &info); - xmlSetProp (clip_path, CC2XML ("d"), xmlBufferContent (info.path)); - xmlBufferFree (info.path); - - snprintf (buffer, sizeof buffer, "url(#clip%d)", document->clip_id); - xmlSetProp (group, CC2XML ("clip-path"), C2XML (buffer)); - xmlSetProp (group, CC2XML ("clip-rule"), - fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? - CC2XML ("evenodd") : CC2XML ("nonzero")); - - document->clip_id++; - surface->xml_node = group; - surface->clip_level++; - } + document->clip_id++; + surface->clip_level++; return status; } @@ -1804,10 +1615,6 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, cairo_svg_version_t version) { cairo_svg_document_t *document; - xmlDocPtr doc; - xmlNodePtr node; - xmlBufferPtr xml_buffer; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; document = malloc (sizeof (cairo_svg_document_t)); if (document == NULL) { @@ -1838,48 +1645,13 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, document->clip_id = 0; document->mask_id = 0; - doc = xmlNewDoc (CC2XML ("1.0")); - node = xmlNewNode (NULL, CC2XML ("svg")); - - xmlDocSetRootElement (doc, node); - - document->xml_doc = doc; - document->xml_node_main = node; - document->xml_node_defs = xmlNewChild (node, NULL, CC2XML ("defs"), NULL); - - xml_buffer = xmlBufferCreate (); - - _cairo_dtostr (buffer, sizeof buffer, width); - xmlBufferCat (xml_buffer, C2XML (buffer)); - xmlBufferCat (xml_buffer, CC2XML ("pt")); - xmlSetProp (node, CC2XML ("width"), C2XML (xmlBufferContent (xml_buffer))); - xmlBufferEmpty (xml_buffer); - - _cairo_dtostr (buffer, sizeof buffer, height); - xmlBufferCat (xml_buffer, C2XML (buffer)); - xmlBufferCat (xml_buffer, CC2XML ("pt")); - xmlSetProp (node, CC2XML ("height"), C2XML (xmlBufferContent (xml_buffer))); - xmlBufferEmpty (xml_buffer); - - xmlBufferCat (xml_buffer, CC2XML ("0 0 ")); - _cairo_dtostr (buffer, sizeof buffer, width); - xmlBufferCat (xml_buffer, C2XML (buffer)); - xmlBufferCat (xml_buffer, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, height); - xmlBufferCat (xml_buffer, C2XML (buffer)); - xmlSetProp (node, CC2XML ("viewBox"), C2XML (xmlBufferContent (xml_buffer))); - - xmlBufferFree (xml_buffer); - - xmlNewNs (node, CC2XML ("http://www.w3.org/2000/svg"), NULL); - xmlNewNs (node, CC2XML ("http://www.w3.org/1999/xlink"), CC2XML ("xlink")); - - document->xml_node_glyphs = xmlNewChild (document->xml_node_defs, NULL, - CC2XML ("g"), NULL); + document->xml_node_defs = _cairo_memory_stream_create (); + document->xml_node_glyphs = _cairo_memory_stream_create (); document->alpha_filter = FALSE; - _cairo_array_init (&document->meta_snapshots, sizeof (cairo_meta_snapshot_t)); + _cairo_array_init (&document->meta_snapshots, + sizeof (cairo_meta_snapshot_t)); document->svg_version = version; @@ -1906,47 +1678,56 @@ _cairo_svg_document_destroy (cairo_svg_document_t *document) free (document); } -static int -_cairo_svg_document_write (cairo_output_stream_t *output_stream, - const char * buffer, - int len) -{ - cairo_status_t status; - - _cairo_output_stream_write (output_stream, buffer, len); - status = _cairo_output_stream_get_status (output_stream); - if (status) { - _cairo_error (status); - return -1; - } - - return len; -} - static cairo_status_t _cairo_svg_document_finish (cairo_svg_document_t *document) { cairo_status_t status; cairo_output_stream_t *output = document->output_stream; cairo_meta_snapshot_t *snapshot; - xmlOutputBufferPtr xml_output_buffer; + cairo_svg_surface_t *surface; unsigned int i; if (document->finished) return CAIRO_STATUS_SUCCESS; + _cairo_output_stream_printf (output, + "\n" + "\n", + document->width, document->height, + document->width, document->height, + _cairo_svg_internal_version_strings [document->svg_version]); + _cairo_svg_document_emit_font_subsets (document); + if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 || + _cairo_memory_stream_length (document->xml_node_defs) > 0) { + _cairo_output_stream_printf (output, "\n"); + if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) { + _cairo_output_stream_printf (output, "\n"); + _cairo_memory_stream_copy (document->xml_node_glyphs, output); + _cairo_output_stream_printf (output, "\n"); + } + _cairo_memory_stream_copy (document->xml_node_defs, output); + _cairo_output_stream_printf (output, "\n"); + } - xmlSetProp (document->xml_node_main, CC2XML ("version"), - CC2XML (_cairo_svg_internal_version_strings [document->svg_version])); + surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); + _cairo_output_stream_printf (output, + "\n", + surface->id, + surface->base_clip); + _cairo_memory_stream_copy (surface->xml_node, output); - xml_output_buffer = xmlOutputBufferCreateIO ((xmlOutputWriteCallback) _cairo_svg_document_write, - (xmlOutputCloseCallback) NULL, - (void *) document->output_stream, - NULL); - xmlSaveFormatFileTo (xml_output_buffer, document->xml_doc, "UTF-8", 1); + for (i = 0; i < surface->clip_level; i++) + _cairo_output_stream_printf (output, "\n"); - xmlFreeDoc (document->xml_doc); + _cairo_output_stream_printf (output, "\n\n"); + + _cairo_output_stream_destroy (document->xml_node_glyphs); + _cairo_output_stream_destroy (document->xml_node_defs); status = _cairo_output_stream_get_status (output); _cairo_output_stream_destroy (output); From cdf07a9be87515b75156a6f02f050bc5b1e6b5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Tue, 13 Jun 2006 17:45:55 -0400 Subject: [PATCH 7/8] Remove libxml2 checks from configure.in. --- configure.in | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/configure.in b/configure.in index 1335e4ce9..a462ac5da 100644 --- a/configure.in +++ b/configure.in @@ -525,17 +525,6 @@ CAIRO_BACKEND_ENABLE(svg, SVG, auto, [ fi ]) -if test "x$use_svg" = "xyes" ; then - use_svg="no (SVG backend requires libxml2)" - if $PKG_CONFIG --exists libxml-2.0 ; then - # Sets XML_CFLAGS, XML_LIBS - PKG_CHECK_MODULES(XML, libxml-2.0) - use_svg=yes - else - AC_MSG_WARN([SVG requires libxml2, which is not found in pkg-config search path, disabling]) - fi -fi - AM_CONDITIONAL(CAIRO_HAS_SVG_SURFACE, test "x$use_svg" = "xyes") if test "x$use_svg" = "xyes"; then SVG_SURFACE_FEATURE="#define CAIRO_HAS_SVG_SURFACE 1" @@ -552,9 +541,6 @@ AM_CONDITIONAL(CAIRO_CAN_TEST_SVG_SURFACE, test "x$test_svg" = "xyes") AC_SUBST(LIBRSVG_CFLAGS) AC_SUBST(LIBRSVG_LIBS) -CAIRO_CFLAGS="$CAIRO_CFLAGS $XML_CFLAGS" -CAIRO_LIBS="$CAIRO_LIBS $XML_LIBS" - dnl =========================================================================== dnl This check should default to 'yes' once we have code to actually From 4932d09a06228a08d1c8f4d2892378c004ff3313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Wed, 14 Jun 2006 14:12:55 -0400 Subject: [PATCH 8/8] Actually add src/cairo-output-stream-private.h. --- src/cairo-output-stream-private.h | 156 ++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/cairo-output-stream-private.h diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h new file mode 100644 index 000000000..d1eeeeb49 --- /dev/null +++ b/src/cairo-output-stream-private.h @@ -0,0 +1,156 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006 Red Hat, Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 cairo_output_stream.c as distributed with the + * cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Author(s): + * Kristian Høgsberg + */ + +#ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H +#define CAIRO_OUTPUT_STREAM_PRIVATE_H + +typedef struct _cairo_output_stream cairo_output_stream_t; + +typedef cairo_status_t (*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, + const unsigned char *data, + unsigned int length); + +typedef cairo_status_t (*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); + +struct _cairo_output_stream { + cairo_output_stream_write_func_t write_func; + cairo_output_stream_close_func_t close_func; + unsigned long position; + cairo_status_t status; + cairo_bool_t closed; +}; + +extern const cairo_private cairo_output_stream_t cairo_output_stream_nil; + +cairo_private void +_cairo_output_stream_init (cairo_output_stream_t *stream, + cairo_output_stream_write_func_t write_func, + cairo_output_stream_close_func_t close_func); + +cairo_private void +_cairo_output_stream_fini (cairo_output_stream_t *stream); + + +/* We already have the following declared in cairo.h: + +typedef cairo_status_t (*cairo_write_func_t) (void *closure, + const unsigned char *data, + unsigned int length); +*/ +typedef cairo_status_t (*cairo_close_func_t) (void *closure); + + +/* This function never returns NULL. If an error occurs (NO_MEMORY) + * while trying to create the output stream this function returns a + * valid pointer to a nil output stream. + * + * Note that even with a nil surface, the close_func callback will be + * called by a call to _cairo_output_stream_close or + * _cairo_output_stream_destroy. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, + void *closure); + +cairo_private void +_cairo_output_stream_close (cairo_output_stream_t *stream); + +cairo_private void +_cairo_output_stream_destroy (cairo_output_stream_t *stream); + +cairo_private void +_cairo_output_stream_write (cairo_output_stream_t *stream, + const void *data, size_t length); + +cairo_private void +_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, + const char *data, + size_t length); + +cairo_private int +_cairo_dtostr (char *buffer, size_t size, double d); + +cairo_private void +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, va_list ap); + +cairo_private void +_cairo_output_stream_printf (cairo_output_stream_t *stream, + const char *fmt, ...); + +cairo_private long +_cairo_output_stream_get_position (cairo_output_stream_t *stream); + +cairo_private cairo_status_t +_cairo_output_stream_get_status (cairo_output_stream_t *stream); + +/* This function never returns NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * NOTE: Even if a nil surface is returned, the caller should still + * call _cairo_output_stream_destroy (or _cairo_output_stream_close at + * least) in order to ensure that everything is properly cleaned up. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_for_filename (const char *filename); + +/* This function never returns NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * The caller still "owns" file and is responsible for calling fclose + * on it when finished. The stream will not do this itself. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_for_file (FILE *file); + +cairo_private cairo_output_stream_t * +_cairo_memory_stream_create (void); + +cairo_private void +_cairo_memory_stream_copy (cairo_output_stream_t *base, + cairo_output_stream_t *dest); + +cairo_private int +_cairo_memory_stream_length (cairo_output_stream_t *stream); + +/* cairo_base85_stream.c */ +cairo_private cairo_output_stream_t * +_cairo_base85_stream_create (cairo_output_stream_t *output); + +#endif /* CAIRO_OUTPUT_STREAM_PRIVATE_H */