summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--ChangeLog8
-rw-r--r--configure.in17
-rw-r--r--src/Makefile.am6
-rw-r--r--src/cairo-array.c130
-rw-r--r--src/cairo-features.h.in2
-rw-r--r--src/cairo-pdf-surface.c1314
-rw-r--r--src/cairo.h25
-rw-r--r--src/cairo_array.c130
-rw-r--r--src/cairo_gdip_surface.cpp4
-rw-r--r--src/cairo_pdf_surface.c1314
-rw-r--r--src/cairoint.h34
12 files changed, 2985 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
index 0f3c51288..43b265f79 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,6 +7,7 @@ Richard Henderson <rth@twiddle.net> "slim" macros for better shared libraries
James Henstridge <james@daa.com.au> Build fixes related to freetype
Graydon Hoare <graydon@redhat.com> Support for non-render X server, first real text support
Thomas Hunger <info@teh-web.de> Initial version of cairo_in_stroke/fill
+Kristian Høgsberg <krh@redhat.com> PDF backend
Alexander Larsson <alexl@redhat.com> Profiling and performance fixes.
Jordi Mas <jordi@ximian.com> Bug fix for cairo_show_text
Keith Packard <keithp@keithp.com> Original concept, polygon tessellation, dashing
diff --git a/ChangeLog b/ChangeLog
index d8c917391..f35d4d03b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-01-05 Kristian Høgsberg <krh@redhat.com>
+
+ * src/cairo_pdf_surface.c: New PDF backend.
+ * src/cairo.h: Add PDF surface constructors.
+ * src/cairo_array.c: New file - generic array implementation.
+ * src/cairoint.h: Add cairo_array prototypes.
+ * src/Makefile.am (libcairo_la_SOURCES): Add cairo_array.c and cairo_pdf_surface.c.
+
2004-12-23 Carl Worth <cworth@cworth.org>
* src/cairo_traps.c: Remove unused CAIRO_TRAPS_GROWTH_INC.
diff --git a/configure.in b/configure.in
index e90780405..c100f57ff 100644
--- a/configure.in
+++ b/configure.in
@@ -112,6 +112,22 @@ AC_SUBST(PS_LIBS)
dnl ===========================================================================
+AC_ARG_ENABLE(pdf,
+ [ --disable-pdf Disable cairo's PDF backend],
+ [use_pdf=$enableval], [use_pdf=yes])
+
+if test "x$use_pdf" != "xyes"; then
+ PDF_SURFACE_FEATURE=CAIRO_HAS_NO_PDF_SURFACE
+ AM_CONDITIONAL(CAIRO_HAS_PDF_SURFACE, false)
+else
+ PDF_SURFACE_FEATURE=CAIRO_HAS_PDF_SURFACE
+ AM_CONDITIONAL(CAIRO_HAS_PDF_SURFACE, true)
+fi
+
+AC_SUBST(PDF_SURFACE_FEATURE)
+
+dnl ===========================================================================
+
AC_ARG_ENABLE(png,
[ --disable-png Disable cairo's PNG backend],
[use_png=$enableval], [use_png=yes])
@@ -270,6 +286,7 @@ echo "cairo will be compiled with the following backends:"
echo " Xlib: $use_xlib"
echo " XCB: $use_xcb"
echo " PostScript: $use_ps"
+echo " PDF: $use_pdf"
echo " PNG: $use_png"
echo " glitz: $use_glitz"
echo ""
diff --git a/src/Makefile.am b/src/Makefile.am
index 8343cb1ce..b985e4d28 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -5,6 +5,10 @@ if CAIRO_HAS_PS_SURFACE
libcairo_ps_sources = cairo_ps_surface.c
endif
+if CAIRO_HAS_PDF_SURFACE
+libcairo_pdf_sources = cairo_pdf_surface.c
+endif
+
if CAIRO_HAS_PNG_SURFACE
libcairo_png_sources = cairo_png_surface.c
endif
@@ -31,6 +35,7 @@ XRENDER_LIBS=@XRENDER_LIBS@
libcairo_la_SOURCES = \
cairo.c \
cairo.h \
+ cairo_array.c \
cairo_cache.c \
cairo_color.c \
cairo_fixed.c \
@@ -54,6 +59,7 @@ libcairo_la_SOURCES = \
cairo_wideint.c \
cairo_wideint.h \
$(libcairo_ps_sources) \
+ $(libcairo_pdf_sources) \
$(libcairo_png_sources) \
$(libcairo_xlib_sources)\
$(libcairo_xcb_sources) \
diff --git a/src/cairo-array.c b/src/cairo-array.c
new file mode 100644
index 000000000..9645380b2
--- /dev/null
+++ b/src/cairo-array.c
@@ -0,0 +1,130 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 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 the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+
+void
+_cairo_array_init (cairo_array_t *array, int element_size)
+{
+ array->size = 0;
+ array->num_elements = 0;
+ array->element_size = element_size;
+ array->elements = NULL;
+}
+
+void
+_cairo_array_fini (cairo_array_t *array)
+{
+ free (array->elements);
+}
+
+cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, int additional)
+{
+ char *new_elements;
+ int old_size = array->size;
+ int required_size = array->num_elements + additional;
+ int new_size;
+
+ if (required_size <= old_size)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (old_size == 0)
+ new_size = 1;
+ else
+ new_size = old_size * 2;
+
+ while (new_size < required_size)
+ new_size = old_size * 2;
+
+ array->size = new_size;
+ new_elements = realloc (array->elements,
+ array->size * array->element_size);
+
+ if (new_elements == NULL) {
+ array->size = old_size;
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ array->elements = new_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_array_truncate (cairo_array_t *array, int num_elements)
+{
+ if (num_elements < array->num_elements)
+ array->num_elements = num_elements;
+}
+
+void *
+_cairo_array_index (cairo_array_t *array, int index)
+{
+ assert (0 <= index && index < array->num_elements);
+
+ return (void *) &array->elements[index * array->element_size];
+}
+
+void
+_cairo_array_copy_element (cairo_array_t *array, int index, void *dst)
+{
+ memcpy (dst, _cairo_array_index (array, index), array->element_size);
+}
+
+cairo_status_t
+_cairo_array_append (cairo_array_t *array, void *elements, int num_elements)
+{
+ cairo_status_t status;
+
+ status = _cairo_array_grow_by (array, num_elements);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ assert (array->num_elements + num_elements <= array->size);
+
+ memcpy (&array->elements[array->num_elements * array->element_size],
+ elements, num_elements * array->element_size);
+ array->num_elements += num_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+int
+_cairo_array_num_elements (cairo_array_t *array)
+{
+ return array->num_elements;
+}
diff --git a/src/cairo-features.h.in b/src/cairo-features.h.in
index 632ad8d72..1f6bc7b7b 100644
--- a/src/cairo-features.h.in
+++ b/src/cairo-features.h.in
@@ -39,6 +39,8 @@
#define @PS_SURFACE_FEATURE@
+#define @PDF_SURFACE_FEATURE@
+
#define @PNG_SURFACE_FEATURE@
#define @XLIB_SURFACE_FEATURE@
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
new file mode 100644
index 000000000..ed901acce
--- /dev/null
+++ b/src/cairo-pdf-surface.c
@@ -0,0 +1,1314 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 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 the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#include <time.h>
+
+/* Issues:
+ *
+ * - Why doesn't pages inherit /alpha%d GS dictionaries from the Pages
+ * object?
+ *
+ * - Why isn't the pattern passed to composite traps instead of
+ * pattern->source? If composite traps needs an image or a surface it
+ * can call create_pattern().
+ *
+ * - We embed an image in the stream each time it's composited. We
+ * could add generation counters to surfaces and remember the stream
+ * ID for a particular generation for a particular surface.
+ *
+ * - Use compression for images.
+ *
+ * - Multi stop gradients. What are the exponential interpolation
+ * functions, could they be used for gradients?
+ *
+ * - Clipping: must be able to reset clipping
+ *
+ * - Images of other formats than 8 bit RGBA.
+ *
+ * - Backend specific meta data.
+ *
+ * - Surface patterns.
+ *
+ * - Alpha channels in gradients.
+ *
+ * - Should/does cairo support drawing into a scratch surface and then
+ * using that as a fill pattern? For this backend, that would involve
+ * using a tiling pattern (4.6.2). How do you create such a scratch
+ * surface? cairo_surface_create_similar() ?
+ *
+ * - What if you create a similiar surface and does show_page and then
+ * does show_surface on another surface?
+ *
+ * - Output TM so page scales to the right size - PDF default user
+ * space has 1 unit = 1 / 72 inch.
+ *
+ * - Add test case for RGBA images.
+ *
+ * - Add test case for RGBA gradients.
+ *
+ * - Pattern extend isn't honoured by image backend.
+ *
+ * - Coordinate space for create_similar() args?
+ *
+ * - Investigate /Matrix entry in content stream dicts for pages
+ * instead of outputting the cm operator in every page.
+ */
+
+typedef struct cairo_pdf_object cairo_pdf_object_t;
+typedef struct cairo_pdf_resource cairo_pdf_resource_t;
+typedef struct cairo_pdf_stream cairo_pdf_stream_t;
+typedef struct cairo_pdf_document cairo_pdf_document_t;
+typedef struct cairo_pdf_surface cairo_pdf_surface_t;
+
+struct cairo_pdf_object {
+ long offset;
+};
+
+struct cairo_pdf_resource {
+ unsigned int id;
+};
+
+struct cairo_pdf_stream {
+ unsigned int id;
+ unsigned int length_id;
+ long start_offset;
+};
+
+struct cairo_pdf_document {
+ FILE *file;
+ unsigned long refcount;
+
+ double width_inches;
+ double height_inches;
+ double x_ppi;
+ double y_ppi;
+
+ unsigned int next_available_id;
+ unsigned int pages_id;
+
+ cairo_pdf_stream_t *current_stream;
+
+ cairo_array_t objects;
+ cairo_array_t pages;
+};
+
+struct cairo_pdf_surface {
+ cairo_surface_t base;
+
+ double width_inches;
+ double height_inches;
+
+ /* HACK: Non-null if this surface was created for a pattern. */
+ cairo_pattern_t *pattern;
+
+ cairo_pdf_document_t *document;
+ cairo_pdf_stream_t *current_stream;
+
+ cairo_array_t patterns;
+ cairo_array_t xobjects;
+ cairo_array_t streams;
+ cairo_array_t alphas;
+};
+
+
+static cairo_pdf_document_t *
+_cairo_pdf_document_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch);
+
+static void
+_cairo_pdf_document_destroy (cairo_pdf_document_t *document);
+
+static void
+_cairo_pdf_document_reference (cairo_pdf_document_t *document);
+
+static cairo_status_t
+_cairo_pdf_document_add_page (cairo_pdf_document_t *document,
+ cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface);
+
+static cairo_pdf_stream_t *
+_cairo_pdf_document_open_stream (cairo_pdf_document_t *document,
+ const char *extra_entries);
+static cairo_surface_t *
+_cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document,
+ double width_inches,
+ double height_inches);
+static void
+_cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface,
+ cairo_pdf_stream_t *stream);
+static void
+_cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface);
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend;
+
+static unsigned int
+_cairo_pdf_document_new_object (cairo_pdf_document_t *document)
+{
+ cairo_pdf_object_t object;
+
+ object.offset = ftell (document->file);
+ /* FIXME: check return value */
+ _cairo_array_append (&document->objects, &object, 1);
+
+ return document->next_available_id++;
+}
+
+static void
+_cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface,
+ cairo_pdf_stream_t *stream)
+{
+ _cairo_array_append (&surface->streams, &stream, 1);
+ surface->current_stream = stream;
+}
+
+static void
+_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, unsigned int id)
+{
+ cairo_pdf_resource_t resource;
+
+ resource.id = id;
+ _cairo_array_append (&surface->patterns, &resource, 1);
+}
+
+static void
+_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, unsigned int id)
+{
+ cairo_pdf_resource_t resource;
+ int i, num_resources;
+
+ num_resources = _cairo_array_num_elements (&surface->xobjects);
+ for (i = 0; i < num_resources; i++) {
+ _cairo_array_copy_element (&surface->xobjects, i, &resource);
+ if (resource.id == id)
+ return;
+ }
+
+ resource.id = id;
+ _cairo_array_append (&surface->xobjects, &resource, 1);
+}
+
+static unsigned int
+_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha)
+{
+ int num_alphas, i;
+ double other;
+
+ num_alphas = _cairo_array_num_elements (&surface->alphas);
+ for (i = 0; i < num_alphas; i++) {
+ _cairo_array_copy_element (&surface->alphas, i, &other);
+ if (alpha == other)
+ return i;
+ }
+
+ _cairo_array_append (&surface->alphas, &alpha, 1);
+ return _cairo_array_num_elements (&surface->alphas) - 1;
+}
+
+cairo_surface_t *
+cairo_pdf_surface_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch)
+{
+ cairo_pdf_document_t *document;
+ cairo_surface_t *surface;
+
+ document = _cairo_pdf_document_create (file,
+ width_inches,
+ height_inches,
+ x_pixels_per_inch,
+ y_pixels_per_inch);
+ if (document == NULL)
+ return NULL;
+
+ surface = _cairo_pdf_surface_create_for_document (document,
+ width_inches,
+ height_inches);
+
+ _cairo_pdf_document_destroy (document);
+
+ return surface;
+}
+
+static cairo_surface_t *
+_cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document,
+ double width_inches,
+ double height_inches)
+{
+ cairo_pdf_surface_t *surface;
+
+ surface = malloc (sizeof (cairo_pdf_surface_t));
+ if (surface == NULL)
+ return NULL;
+
+ _cairo_surface_init (&surface->base, &cairo_pdf_surface_backend);
+
+ surface->width_inches = width_inches;
+ surface->height_inches = height_inches;
+
+ surface->pattern = NULL;
+ _cairo_pdf_document_reference (document);
+ surface->document = document;
+ _cairo_array_init (&surface->streams, sizeof (cairo_pdf_stream_t *));
+ _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t));
+ _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t));
+ _cairo_array_init (&surface->alphas, sizeof (double));
+
+ return &surface->base;
+}
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
+{
+ int num_streams, i;
+ cairo_pdf_stream_t *stream;
+
+ num_streams = _cairo_array_num_elements (&surface->streams);
+ for (i = 0; i < num_streams; i++) {
+ _cairo_array_copy_element (&surface->streams, i, &stream);
+ free (stream);
+ }
+
+ _cairo_array_truncate (&surface->streams, 0);
+ _cairo_array_truncate (&surface->patterns, 0);
+ _cairo_array_truncate (&surface->xobjects, 0);
+ _cairo_array_truncate (&surface->alphas, 0);
+}
+
+static cairo_surface_t *
+_cairo_pdf_surface_create_similar (void *abstract_src,
+ cairo_format_t format,
+ int drawable,
+ int width,
+ int height)
+{
+ cairo_pdf_surface_t *template = abstract_src;
+
+ return _cairo_pdf_surface_create_for_document (template->document,
+ width, height);
+}
+
+static cairo_pdf_stream_t *
+_cairo_pdf_document_open_stream (cairo_pdf_document_t *document,
+ const char *extra_entries)
+{
+ FILE *file = document->file;
+ cairo_pdf_stream_t *stream;
+
+ stream = malloc (sizeof (cairo_pdf_stream_t));
+ if (stream == NULL) {
+ return NULL;
+ }
+
+ stream->id = _cairo_pdf_document_new_object (document);
+ stream->length_id = _cairo_pdf_document_new_object (document);
+
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Length %d 0 R\r\n"
+ "%s"
+ ">>\r\n"
+ "stream\r\n",
+ stream->id,
+ stream->length_id,
+ extra_entries);
+
+ stream->start_offset = ftell (file);
+
+ document->current_stream = stream;
+
+ return stream;
+}
+
+static void
+_cairo_pdf_document_close_stream (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ long length;
+ cairo_pdf_stream_t *stream;
+ cairo_pdf_object_t *object;
+
+ stream = document->current_stream;
+ if (stream == NULL)
+ return;
+
+ length = ftell(file) - stream->start_offset;
+ fprintf (file,
+ "\r\n"
+ "endstream\r\n"
+ "endobj\r\n");
+
+ object = _cairo_array_index (&document->objects, stream->length_id - 1);
+ object->offset = ftell(file);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ " %ld\r\n"
+ "endobj\r\n",
+ stream->length_id,
+ length);
+
+ document->current_stream = NULL;
+}
+
+static void
+_cairo_pdf_surface_destroy (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+
+ if (surface->current_stream == document->current_stream)
+ _cairo_pdf_document_close_stream (document);
+
+ _cairo_pdf_document_destroy (document);
+
+ free (surface);
+}
+
+/* XXX: We should re-work this interface to return both X/Y ppi values. */
+static double
+_cairo_pdf_surface_pixels_per_inch (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+
+ return surface->document->y_ppi;
+}
+
+static void
+_cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_document_t *document = surface->document;
+ cairo_pdf_stream_t *stream;
+ FILE *file = document->file;
+ char extra[200];
+
+ if (document->current_stream == NULL ||
+ document->current_stream != surface->current_stream) {
+ _cairo_pdf_document_close_stream (document);
+ snprintf (extra, sizeof extra,
+ " /Type /XObject\r\n"
+ " /Subtype /Form\r\n"
+ " /BBox [ 0 0 %f %f ]\r\n",
+ surface->width_inches * document->x_ppi,
+ surface->height_inches * document->y_ppi);
+ stream = _cairo_pdf_document_open_stream (document, extra);
+ _cairo_pdf_surface_add_stream (surface, stream);
+
+ /* If this is the first stream we open for this surface,
+ * output the cairo to PDF transformation matrix. */
+ if (_cairo_array_num_elements (&surface->streams) == 1)
+ fprintf (file, "1 0 0 -1 0 %f cm\r\n",
+ document->height_inches * document->y_ppi);
+ }
+}
+
+static cairo_image_surface_t *
+_cairo_pdf_surface_get_image (void *abstract_surface)
+{
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_matrix (void *abstract_surface,
+ cairo_matrix_t *matrix)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_filter (void *abstract_surface,
+ cairo_filter_t filter)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_repeat (void *abstract_surface,
+ int repeat)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned int
+emit_image_data (cairo_pdf_document_t *document,
+ cairo_image_surface_t *image)
+{
+ FILE *file = document->file;
+ cairo_pdf_stream_t *stream;
+ char entries[200];
+ int i, j;
+
+ _cairo_pdf_document_close_stream (document);
+
+ snprintf (entries, sizeof entries,
+ " /Type /XObject\r\n"
+ " /Subtype /Image\r\n"
+ " /Width %d\r\n"
+ " /Height %d\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /BitsPerComponent 8\r\n",
+ image->width, image->height);
+
+ stream = _cairo_pdf_document_open_stream (document, entries);
+
+ for (i = 0; i < image->height; i++) {
+ for (j = 0; j < image->width; j++) {
+ fputc((unsigned) image->data[i * image->stride + j * 4 + 2], file);
+ fputc((unsigned) image->data[i * image->stride + j * 4 + 1], file);
+ fputc((unsigned) image->data[i * image->stride + j * 4 + 0], file);
+ }
+ }
+
+ _cairo_pdf_document_close_stream (document);
+
+ return stream->id;
+}
+
+static void
+_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst,
+ cairo_image_surface_t *image)
+{
+ cairo_pdf_document_t *document = dst->document;
+ FILE *file = document->file;
+ unsigned id;
+ cairo_matrix_t i2u;
+
+ id = emit_image_data (dst->document, image);
+ _cairo_pdf_surface_add_xobject (dst, id);
+
+ _cairo_pdf_surface_ensure_stream (dst);
+
+ cairo_matrix_copy (&i2u, &image->base.matrix);
+ cairo_matrix_invert (&i2u);
+ cairo_matrix_translate (&i2u, 0, image->height);
+ cairo_matrix_scale (&i2u, image->width, -image->height);
+
+ fprintf (file,
+ "q %f %f %f %f %f %f cm /res%d Do Q\r\n",
+ i2u.m[0][0], i2u.m[0][1],
+ i2u.m[1][0], i2u.m[1][1],
+ i2u.m[2][0], i2u.m[2][1],
+ id);
+}
+
+/* The contents of the surface is already transformed into PDF units,
+ * but when we composite the surface we may want to use a different
+ * space. The problem I see now is that the show_surface snippet
+ * creates a surface 1x1, which in the snippet environment is the
+ * entire surface. When compositing the surface, cairo gives us the
+ * 1x1 to 256x256 matrix. This would be fine if cairo didn't actually
+ * also transform the drawing to the surface. Should the CTM be part
+ * of the current target surface?
+ */
+
+static cairo_int_status_t
+_cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst,
+ cairo_pdf_surface_t *src,
+ int width, int height)
+{
+ cairo_pdf_document_t *document = dst->document;
+ FILE *file = document->file;
+ cairo_matrix_t i2u;
+ cairo_pdf_stream_t *stream;
+ int num_streams, i;
+
+ if (src->pattern != NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_pdf_surface_ensure_stream (dst);
+
+ cairo_matrix_copy (&i2u, &src->base.matrix);
+ cairo_matrix_invert (&i2u);
+ cairo_matrix_scale (&i2u,
+ 1.0 / (src->width_inches * document->x_ppi),
+ 1.0 / (src->height_inches * document->y_ppi));
+
+ fprintf (file,
+ "q %f %f %f %f %f %f cm",
+ i2u.m[0][0], i2u.m[0][1],
+ i2u.m[1][0], i2u.m[1][1],
+ i2u.m[2][0], i2u.m[2][1]);
+
+ num_streams = _cairo_array_num_elements (&src->streams);
+ for (i = 0; i < num_streams; i++) {
+ _cairo_array_copy_element (&src->streams, i, &stream);
+ fprintf (file,
+ " /res%d Do",
+ stream->id);
+
+ _cairo_pdf_surface_add_xobject (dst, stream->id);
+
+ }
+
+ fprintf (file, " Q\r\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_composite (cairo_operator_t operator,
+ cairo_surface_t *generic_src,
+ cairo_surface_t *generic_mask,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_pdf_surface_t *dst = abstract_dst;
+ cairo_pdf_surface_t *src;
+ cairo_image_surface_t *image;
+
+ if (generic_src->backend == &cairo_pdf_surface_backend) {
+ src = (cairo_pdf_surface_t *) generic_src;
+ _cairo_pdf_surface_composite_pdf (dst, src, width, height);
+ }
+ else {
+ image = _cairo_surface_get_image (generic_src);
+ _cairo_pdf_surface_composite_image (dst, image);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t operator,
+ const cairo_color_t *color,
+ cairo_rectangle_t *rects,
+ int num_rects)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ int i;
+
+ if (surface->pattern != NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_pdf_surface_ensure_stream (surface);
+
+ fprintf (file,
+ "%f %f %f rg\r\n",
+ color->red, color->green, color->blue);
+
+ for (i = 0; i < num_rects; i++) {
+ fprintf (file,
+ "%d %d %d %d re f\r\n",
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+emit_tiling_pattern (cairo_operator_t operator,
+ cairo_pdf_surface_t *dst,
+ cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = dst->document;
+ FILE *file = document->file;
+ cairo_pdf_stream_t *stream;
+ cairo_image_surface_t *image;
+ char entries[250];
+ unsigned int id, alpha;
+ cairo_matrix_t pm;
+
+ if (pattern->u.surface.surface->backend == &cairo_pdf_surface_backend) {
+ return;
+ }
+
+ image = _cairo_surface_get_image (pattern->u.surface.surface);
+
+ _cairo_pdf_document_close_stream (document);
+
+ id = emit_image_data (dst->document, image);
+
+ /* BBox must be smaller than XStep by YStep or acroread wont
+ * display the pattern. */
+
+ cairo_matrix_set_identity (&pm);
+ cairo_matrix_scale (&pm, image->width, image->height);
+ cairo_matrix_copy (&pm, &pattern->matrix);
+ cairo_matrix_invert (&pm);
+
+ snprintf (entries, sizeof entries,
+ " /BBox [ 0 0 256 256 ]\r\n"
+ " /XStep 256\r\n"
+ " /YStep 256\r\n"
+ " /PatternType 1\r\n"
+ " /TilingType 1\r\n"
+ " /PaintType 1\r\n"
+ " /Resources << /XObject << /res%d %d 0 R >> >>\r\n"
+ " /Matrix [ %f %f %f %f %f %f ]\r\n",
+ id, id,
+ pm.m[0][0], pm.m[0][1],
+ pm.m[1][0], pm.m[1][1],
+ pm.m[2][0], pm.m[2][1]);
+
+ stream = _cairo_pdf_document_open_stream (document, entries);
+
+ _cairo_pdf_surface_add_pattern (dst, stream->id);
+
+ _cairo_pdf_surface_ensure_stream (dst);
+ alpha = _cairo_pdf_surface_add_alpha (dst, 1.0);
+ fprintf (file,
+ "/Pattern cs /res%d scn /a%d gs\r\n",
+ stream->id, alpha);
+}
+
+static unsigned int
+emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ unsigned int function_id;
+
+ function_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /FunctionType 0\r\n"
+ " /Domain [ 0.0 1.0 ]\r\n"
+ " /Size [ 2 ]\r\n"
+ " /BitsPerSample 8\r\n"
+ " /Range [ 0.0 1.0 0.0 1.0 0.0 1.0 ]\r\n"
+ " /Length 6\r\n"
+ ">>\r\n"
+ "stream\r\n",
+ function_id);
+
+ fputc (pattern->stops[0].color_char[0], file);
+ fputc (pattern->stops[0].color_char[1], file);
+ fputc (pattern->stops[0].color_char[2], file);
+ fputc (pattern->stops[1].color_char[0], file);
+ fputc (pattern->stops[1].color_char[1], file);
+ fputc (pattern->stops[1].color_char[2], file);
+
+ fprintf (file,
+ "\r\n"
+ "endstream\r\n"
+ "endobj\r\n");
+
+ return function_id;
+}
+
+static void
+emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ unsigned int function_id, pattern_id, alpha;
+ double x0, y0, x1, y1;
+ cairo_matrix_t p2u;
+
+ _cairo_pdf_document_close_stream (document);
+
+ function_id = emit_pattern_stops (surface, pattern);
+
+ cairo_matrix_copy (&p2u, &pattern->matrix);
+ cairo_matrix_invert (&p2u);
+
+ x0 = pattern->u.linear.point0.x;
+ y0 = pattern->u.linear.point0.y;
+ cairo_matrix_transform_point (&p2u, &x0, &y0);
+ x1 = pattern->u.linear.point1.x;
+ y1 = pattern->u.linear.point1.y;
+ cairo_matrix_transform_point (&p2u, &x1, &y1);
+
+ pattern_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Pattern\r\n"
+ " /PatternType 2\r\n"
+ " /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+ " /Shading\r\n"
+ " << /ShadingType 2\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /Coords [ %f %f %f %f ]\r\n"
+ " /Function %d 0 R\r\n"
+ " /Extend [ %s %s ]\r\n"
+ " >>\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ pattern_id,
+ document->height_inches * document->y_ppi,
+ x0, y0, x1, y1,
+ function_id,
+ (1 || pattern->extend) ? "true" : "false",
+ (1 || pattern->extend) ? "true" : "false");
+
+ _cairo_pdf_surface_add_pattern (surface, pattern_id);
+
+ _cairo_pdf_surface_ensure_stream (surface);
+ alpha = _cairo_pdf_surface_add_alpha (surface, 1.0);
+
+ /* Use pattern */
+ fprintf (file,
+ "/Pattern cs /res%d scn /a%d gs\r\n",
+ pattern_id, alpha);
+}
+
+static void
+emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ unsigned int function_id, pattern_id, alpha;
+ double x0, y0, x1, y1, r0, r1;
+ cairo_matrix_t p2u;
+
+ _cairo_pdf_document_close_stream (document);
+
+ function_id = emit_pattern_stops (surface, pattern);
+
+ cairo_matrix_copy (&p2u, &pattern->matrix);
+ cairo_matrix_invert (&p2u);
+
+ x0 = pattern->u.radial.center0.x;
+ y0 = pattern->u.radial.center0.y;
+ r0 = pattern->u.radial.radius0;
+ cairo_matrix_transform_point (&p2u, &x0, &y0);
+ x1 = pattern->u.radial.center1.x;
+ y1 = pattern->u.radial.center1.y;
+ r1 = pattern->u.radial.radius1;
+ cairo_matrix_transform_point (&p2u, &x1, &y1);
+
+ /* FIXME: This is surely crack, but how should you scale a radius
+ * in a non-orthogonal coordinate system? */
+ cairo_matrix_transform_distance (&p2u, &r0, &r1);
+
+ pattern_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Pattern\r\n"
+ " /PatternType 2\r\n"
+ " /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+ " /Shading\r\n"
+ " << /ShadingType 3\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /Coords [ %f %f %f %f %f %f ]\r\n"
+ " /Function %d 0 R\r\n"
+ " /Extend [ %s %s ]\r\n"
+ " >>\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ pattern_id,
+ document->height_inches * document->y_ppi,
+ x0, y0, r0, x1, y1, r1,
+ function_id,
+ (1 || pattern->extend) ? "true" : "false",
+ (1 || pattern->extend) ? "true" : "false");
+
+ _cairo_pdf_surface_add_pattern (surface, pattern_id);
+
+ _cairo_pdf_surface_ensure_stream (surface);
+ alpha = _cairo_pdf_surface_add_alpha (surface, 1.0);
+
+ /* Use pattern */
+ fprintf (file,
+ "/Pattern cs /res%d scn /a%d gs\r\n",
+ pattern_id, alpha);
+}
+
+static double
+intersect (cairo_line_t *line, cairo_fixed_t y)
+{
+ return _cairo_fixed_to_double (line->p1.x) +
+ _cairo_fixed_to_double (line->p2.x - line->p1.x) *
+ _cairo_fixed_to_double (y - line->p1.y) /
+ _cairo_fixed_to_double (line->p2.y - line->p1.y);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator,
+ cairo_surface_t *generic_src,
+ void *abstract_dst,
+ int x_src,
+ int y_src,
+ cairo_trapezoid_t *traps,
+ int num_traps)
+{
+ cairo_pdf_surface_t *surface = abstract_dst;
+ cairo_pdf_surface_t *source = (cairo_pdf_surface_t *) generic_src;
+ cairo_pdf_document_t *document = surface->document;
+ cairo_pattern_t *pattern;
+ FILE *file = document->file;
+ int i;
+ unsigned int alpha;
+
+ /* FIXME: we really just want the original pattern here, not a
+ * source surface. */
+ pattern = source->pattern;
+
+ if (source->base.backend != &cairo_pdf_surface_backend) {
+ printf ("_cairo_pdf_surface_composite_trapezoids: not a pdf source\r");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (pattern == NULL) {
+ printf ("_cairo_pdf_surface_composite_trapezoids: "
+ "non-pattern pdf source\r");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_SOLID:
+ alpha = _cairo_pdf_surface_add_alpha (surface, pattern->color.alpha);
+ _cairo_pdf_surface_ensure_stream (surface);
+ fprintf (file,
+ "%f %f %f rg /a%d gs\r\n",
+ pattern->color.red,
+ pattern->color.green,
+ pattern->color.blue,
+ alpha);
+ break;
+
+ case CAIRO_PATTERN_SURFACE:
+ emit_tiling_pattern (operator, surface, pattern);
+ break;
+
+ case CAIRO_PATTERN_LINEAR:
+ emit_linear_pattern (surface, pattern);
+ break;
+
+ case CAIRO_PATTERN_RADIAL:
+ emit_radial_pattern (surface, pattern );
+ break;
+ }
+
+ /* After the above switch the current stream should belong to this
+ * surface, so no need to _cairo_pdf_surface_ensure_stream() */
+ assert (document->current_stream != NULL &&
+ document->current_stream == surface->current_stream);
+
+ for (i = 0; i < num_traps; i++) {
+ double left_x1, left_x2, right_x1, right_x2;
+
+ left_x1 = intersect (&traps[i].left, traps[i].top);
+ left_x2 = intersect (&traps[i].left, traps[i].bottom);
+ right_x1 = intersect (&traps[i].right, traps[i].top);
+ right_x2 = intersect (&traps[i].right, traps[i].bottom);
+
+ fprintf (file,
+ "%f %f m %f %f l %f %f l %f %f l h\r\n",
+ left_x1, _cairo_fixed_to_double (traps[i].top),
+ left_x2, _cairo_fixed_to_double (traps[i].bottom),
+ right_x2, _cairo_fixed_to_double (traps[i].bottom),
+ right_x1, _cairo_fixed_to_double (traps[i].top));
+ }
+
+ fprintf (file,
+ "f\r\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_copy_page (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+
+ return _cairo_pdf_document_add_page (document, surface);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_show_page (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_document_add_page (document, surface);
+ if (status == CAIRO_STATUS_SUCCESS)
+ _cairo_pdf_surface_clear (surface);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_set_clip_region (void *abstract_surface,
+ pixman_region16_t *region)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_create_pattern (void *abstract_surface,
+ cairo_pattern_t *pattern,
+ cairo_box_t *extents)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_surface_t *source;
+
+ source = (cairo_pdf_surface_t *)
+ _cairo_pdf_surface_create_for_document (surface->document, 0, 0);
+ source->pattern = pattern;
+ pattern->source = &source->base;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend = {
+ _cairo_pdf_surface_create_similar,
+ _cairo_pdf_surface_destroy,
+ _cairo_pdf_surface_pixels_per_inch,
+ _cairo_pdf_surface_get_image,
+ _cairo_pdf_surface_set_image,
+ _cairo_pdf_surface_set_matrix,
+ _cairo_pdf_surface_set_filter,
+ _cairo_pdf_surface_set_repeat,
+ _cairo_pdf_surface_composite,
+ _cairo_pdf_surface_fill_rectangles,
+ _cairo_pdf_surface_composite_trapezoids,
+ _cairo_pdf_surface_copy_page,
+ _cairo_pdf_surface_show_page,
+ _cairo_pdf_surface_set_clip_region,
+ _cairo_pdf_surface_create_pattern,
+ NULL, /* show_glyphs */
+};
+
+static cairo_pdf_document_t *
+_cairo_pdf_document_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch)
+{
+ cairo_pdf_document_t *document;
+
+ document = malloc (sizeof (cairo_pdf_document_t));
+ if (document == NULL)
+ return NULL;
+
+ document->file = file;
+ document->refcount = 1;
+ document->width_inches = width_inches;
+ document->height_inches = height_inches;
+ document->x_ppi = x_pixels_per_inch;
+ document->y_ppi = y_pixels_per_inch;
+
+ _cairo_array_init (&document->objects, sizeof (cairo_pdf_object_t));
+ _cairo_array_init (&document->pages, sizeof (unsigned int));
+ document->next_available_id = 1;
+
+ document->current_stream = NULL;
+
+ document->pages_id = _cairo_pdf_document_new_object (document);
+
+ /* Document header */
+ fprintf (file, "%%PDF-1.4\r\n");
+
+ return document;
+}
+
+static unsigned int
+_cairo_pdf_document_write_info (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ unsigned int id;
+
+ id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Creator (cairographics.org)\r\n"
+ " /Producer (cairographics.org)\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ id);
+
+ return id;
+}
+
+static void
+_cairo_pdf_document_write_pages (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ cairo_pdf_object_t *pages_object;
+ unsigned int page_id;
+ int num_pages, i;
+
+ pages_object = _cairo_array_index (&document->objects,
+ document->pages_id - 1);
+ pages_object->offset = ftell (file);
+
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Pages\r\n"
+ " /Kids [ ",
+ document->pages_id);
+
+ num_pages = _cairo_array_num_elements (&document->pages);
+ for (i = 0; i < num_pages; i++) {
+ _cairo_array_copy_element (&document->pages, i, &page_id);
+ fprintf (file, "%d 0 R ", page_id);
+ }
+
+ fprintf (file, "]\r\n");
+ fprintf (file, " /Count %d\r\n", num_pages);
+
+ /* TODO: Figure out wich other defaults to be inherited by /Page
+ * objects. */
+ fprintf (file,
+ " /MediaBox [ 0 0 %f %f ]\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ document->width_inches * document->x_ppi,
+ document->height_inches * document->y_ppi);
+}
+
+static unsigned int
+_cairo_pdf_document_write_catalog (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ unsigned int id;
+
+ id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Catalog\r\n"
+ " /Pages %d 0 R\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ id, document->pages_id);
+
+ return id;
+}
+
+static long
+_cairo_pdf_document_write_xref (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ cairo_pdf_object_t *object;
+ int num_objects, i;
+ long offset;
+
+ num_objects = _cairo_array_num_elements (&document->objects);
+
+ offset = ftell(file);
+ fprintf (document->file,
+ "xref\r\n"
+ "%d %d\r\n",
+ 0, num_objects + 1);
+
+ fprintf (file, "0000000000 65535 f\r\n");
+ for (i = 0; i < num_objects; i++) {
+ object = _cairo_array_index (&document->objects, i);
+ fprintf (file, "%010ld 00000 n\r\n", object->offset);
+ }
+
+ return offset;
+}
+
+static void
+_cairo_pdf_document_reference (cairo_pdf_document_t *document)
+{
+ document->refcount++;
+}
+
+static void
+_cairo_pdf_document_destroy (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ long offset;
+ unsigned int info_id, catalog_id;
+
+ document->refcount--;
+ if (document->refcount > 0)
+ return;
+
+ _cairo_pdf_document_close_stream (document);
+ _cairo_pdf_document_write_pages (document);
+ info_id = _cairo_pdf_document_write_info (document);
+ catalog_id = _cairo_pdf_document_write_catalog (document);
+ offset = _cairo_pdf_document_write_xref (document);
+
+ fprintf (file,
+ "trailer\r\n"
+ "<< /Size %d\r\n"
+ " /Root %d 0 R\r\n"
+ " /Info %d 0 R\r\n"
+ ">>\r\n",
+ document->next_available_id,
+ catalog_id,
+ info_id);
+
+ fprintf (file,
+ "startxref\r\n"
+ "%ld\r\n"
+ "%%%%EOF\r\n",
+ offset);
+
+ free (document);
+}
+
+static cairo_status_t
+_cairo_pdf_document_add_page (cairo_pdf_document_t *document,
+ cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_stream_t *stream;
+ cairo_pdf_resource_t *res;
+ FILE *file = document->file;
+ unsigned int page_id;
+ double alpha;
+ int num_streams, num_alphas, num_resources, i;
+
+ _cairo_pdf_document_close_stream (document);
+
+ page_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Page\r\n"
+ " /Parent %d 0 R\r\n"
+ " /Contents [",
+ page_id,
+ document->pages_id);
+
+ num_streams = _cairo_array_num_elements (&surface->streams);
+ for (i = 0; i < num_streams; i++) {
+ _cairo_array_copy_element (&surface->streams, i, &stream);
+ fprintf (file,
+ " %d 0 R",
+ stream->id);
+ }
+
+ fprintf (file,
+ " ]\r\n"
+ " /Resources <<\r\n");
+
+
+ num_alphas = _cairo_array_num_elements (&surface->alphas);
+ if (num_alphas > 0) {
+ fprintf (file,
+ " /ExtGState <<\r\n");
+
+ for (i = 0; i < num_alphas; i++) {
+ _cairo_array_copy_element (&surface->alphas, i, &alpha);
+ fprintf (file,
+ " /a%d << /ca %f >>\r\n",
+ i, alpha);
+ }
+
+ fprintf (file,
+ " >>\r\n");
+ }
+
+ num_resources = _cairo_array_num_elements (&surface->patterns);
+ if (num_resources > 0) {
+ fprintf (file,
+ " /Pattern <<");
+ for (i = 0; i < num_resources; i++) {
+ res = _cairo_array_index (&surface->patterns, i);
+ fprintf (file,
+ " /res%d %d 0 R",
+ res->id, res->id);
+ }
+
+ fprintf (file,
+ " >>\r\n");
+ }
+
+ num_resources = _cairo_array_num_elements (&surface->xobjects);
+ if (num_resources > 0) {
+ fprintf (file,
+ " /XObject <<");
+
+ for (i = 0; i < num_resources; i++) {
+ res = _cairo_array_index (&surface->xobjects, i);
+ fprintf (file,
+ " /res%d %d 0 R",
+ res->id, res->id);
+ }
+
+ fprintf (file,
+ " >>\r\n");
+ }
+
+ fprintf (file,
+ " >>\r\n"
+ ">>\r\n"
+ "endobj\r\n");
+
+ _cairo_array_append (&document->pages, &page_id, 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+cairo_set_target_pdf (cairo_t *cr,
+ FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch)
+{
+ cairo_surface_t *surface;
+
+ surface = cairo_pdf_surface_create (file,
+ width_inches,
+ height_inches,
+ x_pixels_per_inch,
+ y_pixels_per_inch);
+
+ if (surface == NULL) {
+ cr->status = CAIRO_STATUS_NO_MEMORY;
+ return;
+ }
+
+ cairo_set_target_surface (cr, surface);
+
+ /* cairo_set_target_surface takes a reference, so we must destroy ours */
+ cairo_surface_destroy (surface);
+}
diff --git a/src/cairo.h b/src/cairo.h
index 98d92451a..4086fdd56 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -112,6 +112,20 @@ cairo_set_target_ps (cairo_t *cr,
#endif /* CAIRO_HAS_PS_SURFACE */
+#ifdef CAIRO_HAS_PDF_SURFACE
+
+#include <stdio.h>
+
+void
+cairo_set_target_pdf (cairo_t *cr,
+ FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch);
+
+#endif /* CAIRO_HAS_PDF_SURFACE */
+
#ifdef CAIRO_HAS_PNG_SURFACE
#include <stdio.h>
@@ -748,6 +762,17 @@ cairo_ps_surface_create (FILE *file,
#endif /* CAIRO_HAS_PS_SURFACE */
+#ifdef CAIRO_HAS_PDF_SURFACE
+
+cairo_surface_t *
+cairo_pdf_surface_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch);
+
+#endif /* CAIRO_HAS_PDF_SURFACE */
+
#ifdef CAIRO_HAS_PNG_SURFACE
/* PNG-surface functions */
diff --git a/src/cairo_array.c b/src/cairo_array.c
new file mode 100644
index 000000000..9645380b2
--- /dev/null
+++ b/src/cairo_array.c
@@ -0,0 +1,130 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 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 the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+
+void
+_cairo_array_init (cairo_array_t *array, int element_size)
+{
+ array->size = 0;
+ array->num_elements = 0;
+ array->element_size = element_size;
+ array->elements = NULL;
+}
+
+void
+_cairo_array_fini (cairo_array_t *array)
+{
+ free (array->elements);
+}
+
+cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, int additional)
+{
+ char *new_elements;
+ int old_size = array->size;
+ int required_size = array->num_elements + additional;
+ int new_size;
+
+ if (required_size <= old_size)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (old_size == 0)
+ new_size = 1;
+ else
+ new_size = old_size * 2;
+
+ while (new_size < required_size)
+ new_size = old_size * 2;
+
+ array->size = new_size;
+ new_elements = realloc (array->elements,
+ array->size * array->element_size);
+
+ if (new_elements == NULL) {
+ array->size = old_size;
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ array->elements = new_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_array_truncate (cairo_array_t *array, int num_elements)
+{
+ if (num_elements < array->num_elements)
+ array->num_elements = num_elements;
+}
+
+void *
+_cairo_array_index (cairo_array_t *array, int index)
+{
+ assert (0 <= index && index < array->num_elements);
+
+ return (void *) &array->elements[index * array->element_size];
+}
+
+void
+_cairo_array_copy_element (cairo_array_t *array, int index, void *dst)
+{
+ memcpy (dst, _cairo_array_index (array, index), array->element_size);
+}
+
+cairo_status_t
+_cairo_array_append (cairo_array_t *array, void *elements, int num_elements)
+{
+ cairo_status_t status;
+
+ status = _cairo_array_grow_by (array, num_elements);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ assert (array->num_elements + num_elements <= array->size);
+
+ memcpy (&array->elements[array->num_elements * array->element_size],
+ elements, num_elements * array->element_size);
+ array->num_elements += num_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+int
+_cairo_array_num_elements (cairo_array_t *array)
+{
+ return array->num_elements;
+}
diff --git a/src/cairo_gdip_surface.cpp b/src/cairo_gdip_surface.cpp
index b94e3c4ea..d7f98c5df 100644
--- a/src/cairo_gdip_surface.cpp
+++ b/src/cairo_gdip_surface.cpp
@@ -53,6 +53,10 @@ extern "C" {
#pragma comment(linker, "/EXPORT:_cairo_set_target_ps")
#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+#pragma comment(linker, "/EXPORT:_cairo_set_target_pdf")
+#endif
+
#ifdef CAIRO_HAS_PNG_SURFACE
#pragma comment(linker, "/EXPORT:_cairo_set_target_png")
#endif
diff --git a/src/cairo_pdf_surface.c b/src/cairo_pdf_surface.c
new file mode 100644
index 000000000..ed901acce
--- /dev/null
+++ b/src/cairo_pdf_surface.c
@@ -0,0 +1,1314 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 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 the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#include <time.h>
+
+/* Issues:
+ *
+ * - Why doesn't pages inherit /alpha%d GS dictionaries from the Pages
+ * object?
+ *
+ * - Why isn't the pattern passed to composite traps instead of
+ * pattern->source? If composite traps needs an image or a surface it
+ * can call create_pattern().
+ *
+ * - We embed an image in the stream each time it's composited. We
+ * could add generation counters to surfaces and remember the stream
+ * ID for a particular generation for a particular surface.
+ *
+ * - Use compression for images.
+ *
+ * - Multi stop gradients. What are the exponential interpolation
+ * functions, could they be used for gradients?
+ *
+ * - Clipping: must be able to reset clipping
+ *
+ * - Images of other formats than 8 bit RGBA.
+ *
+ * - Backend specific meta data.
+ *
+ * - Surface patterns.
+ *
+ * - Alpha channels in gradients.
+ *
+ * - Should/does cairo support drawing into a scratch surface and then
+ * using that as a fill pattern? For this backend, that would involve
+ * using a tiling pattern (4.6.2). How do you create such a scratch
+ * surface? cairo_surface_create_similar() ?
+ *
+ * - What if you create a similiar surface and does show_page and then
+ * does show_surface on another surface?
+ *
+ * - Output TM so page scales to the right size - PDF default user
+ * space has 1 unit = 1 / 72 inch.
+ *
+ * - Add test case for RGBA images.
+ *
+ * - Add test case for RGBA gradients.
+ *
+ * - Pattern extend isn't honoured by image backend.
+ *
+ * - Coordinate space for create_similar() args?
+ *
+ * - Investigate /Matrix entry in content stream dicts for pages
+ * instead of outputting the cm operator in every page.
+ */
+
+typedef struct cairo_pdf_object cairo_pdf_object_t;
+typedef struct cairo_pdf_resource cairo_pdf_resource_t;
+typedef struct cairo_pdf_stream cairo_pdf_stream_t;
+typedef struct cairo_pdf_document cairo_pdf_document_t;
+typedef struct cairo_pdf_surface cairo_pdf_surface_t;
+
+struct cairo_pdf_object {
+ long offset;
+};
+
+struct cairo_pdf_resource {
+ unsigned int id;
+};
+
+struct cairo_pdf_stream {
+ unsigned int id;
+ unsigned int length_id;
+ long start_offset;
+};
+
+struct cairo_pdf_document {
+ FILE *file;
+ unsigned long refcount;
+
+ double width_inches;
+ double height_inches;
+ double x_ppi;
+ double y_ppi;
+
+ unsigned int next_available_id;
+ unsigned int pages_id;
+
+ cairo_pdf_stream_t *current_stream;
+
+ cairo_array_t objects;
+ cairo_array_t pages;
+};
+
+struct cairo_pdf_surface {
+ cairo_surface_t base;
+
+ double width_inches;
+ double height_inches;
+
+ /* HACK: Non-null if this surface was created for a pattern. */
+ cairo_pattern_t *pattern;
+
+ cairo_pdf_document_t *document;
+ cairo_pdf_stream_t *current_stream;
+
+ cairo_array_t patterns;
+ cairo_array_t xobjects;
+ cairo_array_t streams;
+ cairo_array_t alphas;
+};
+
+
+static cairo_pdf_document_t *
+_cairo_pdf_document_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch);
+
+static void
+_cairo_pdf_document_destroy (cairo_pdf_document_t *document);
+
+static void
+_cairo_pdf_document_reference (cairo_pdf_document_t *document);
+
+static cairo_status_t
+_cairo_pdf_document_add_page (cairo_pdf_document_t *document,
+ cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface);
+
+static cairo_pdf_stream_t *
+_cairo_pdf_document_open_stream (cairo_pdf_document_t *document,
+ const char *extra_entries);
+static cairo_surface_t *
+_cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document,
+ double width_inches,
+ double height_inches);
+static void
+_cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface,
+ cairo_pdf_stream_t *stream);
+static void
+_cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface);
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend;
+
+static unsigned int
+_cairo_pdf_document_new_object (cairo_pdf_document_t *document)
+{
+ cairo_pdf_object_t object;
+
+ object.offset = ftell (document->file);
+ /* FIXME: check return value */
+ _cairo_array_append (&document->objects, &object, 1);
+
+ return document->next_available_id++;
+}
+
+static void
+_cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface,
+ cairo_pdf_stream_t *stream)
+{
+ _cairo_array_append (&surface->streams, &stream, 1);
+ surface->current_stream = stream;
+}
+
+static void
+_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, unsigned int id)
+{
+ cairo_pdf_resource_t resource;
+
+ resource.id = id;
+ _cairo_array_append (&surface->patterns, &resource, 1);
+}
+
+static void
+_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, unsigned int id)
+{
+ cairo_pdf_resource_t resource;
+ int i, num_resources;
+
+ num_resources = _cairo_array_num_elements (&surface->xobjects);
+ for (i = 0; i < num_resources; i++) {
+ _cairo_array_copy_element (&surface->xobjects, i, &resource);
+ if (resource.id == id)
+ return;
+ }
+
+ resource.id = id;
+ _cairo_array_append (&surface->xobjects, &resource, 1);
+}
+
+static unsigned int
+_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha)
+{
+ int num_alphas, i;
+ double other;
+
+ num_alphas = _cairo_array_num_elements (&surface->alphas);
+ for (i = 0; i < num_alphas; i++) {
+ _cairo_array_copy_element (&surface->alphas, i, &other);
+ if (alpha == other)
+ return i;
+ }
+
+ _cairo_array_append (&surface->alphas, &alpha, 1);
+ return _cairo_array_num_elements (&surface->alphas) - 1;
+}
+
+cairo_surface_t *
+cairo_pdf_surface_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch)
+{
+ cairo_pdf_document_t *document;
+ cairo_surface_t *surface;
+
+ document = _cairo_pdf_document_create (file,
+ width_inches,
+ height_inches,
+ x_pixels_per_inch,
+ y_pixels_per_inch);
+ if (document == NULL)
+ return NULL;
+
+ surface = _cairo_pdf_surface_create_for_document (document,
+ width_inches,
+ height_inches);
+
+ _cairo_pdf_document_destroy (document);
+
+ return surface;
+}
+
+static cairo_surface_t *
+_cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document,
+ double width_inches,
+ double height_inches)
+{
+ cairo_pdf_surface_t *surface;
+
+ surface = malloc (sizeof (cairo_pdf_surface_t));
+ if (surface == NULL)
+ return NULL;
+
+ _cairo_surface_init (&surface->base, &cairo_pdf_surface_backend);
+
+ surface->width_inches = width_inches;
+ surface->height_inches = height_inches;
+
+ surface->pattern = NULL;
+ _cairo_pdf_document_reference (document);
+ surface->document = document;
+ _cairo_array_init (&surface->streams, sizeof (cairo_pdf_stream_t *));
+ _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t));
+ _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t));
+ _cairo_array_init (&surface->alphas, sizeof (double));
+
+ return &surface->base;
+}
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
+{
+ int num_streams, i;
+ cairo_pdf_stream_t *stream;
+
+ num_streams = _cairo_array_num_elements (&surface->streams);
+ for (i = 0; i < num_streams; i++) {
+ _cairo_array_copy_element (&surface->streams, i, &stream);
+ free (stream);
+ }
+
+ _cairo_array_truncate (&surface->streams, 0);
+ _cairo_array_truncate (&surface->patterns, 0);
+ _cairo_array_truncate (&surface->xobjects, 0);
+ _cairo_array_truncate (&surface->alphas, 0);
+}
+
+static cairo_surface_t *
+_cairo_pdf_surface_create_similar (void *abstract_src,
+ cairo_format_t format,
+ int drawable,
+ int width,
+ int height)
+{
+ cairo_pdf_surface_t *template = abstract_src;
+
+ return _cairo_pdf_surface_create_for_document (template->document,
+ width, height);
+}
+
+static cairo_pdf_stream_t *
+_cairo_pdf_document_open_stream (cairo_pdf_document_t *document,
+ const char *extra_entries)
+{
+ FILE *file = document->file;
+ cairo_pdf_stream_t *stream;
+
+ stream = malloc (sizeof (cairo_pdf_stream_t));
+ if (stream == NULL) {
+ return NULL;
+ }
+
+ stream->id = _cairo_pdf_document_new_object (document);
+ stream->length_id = _cairo_pdf_document_new_object (document);
+
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Length %d 0 R\r\n"
+ "%s"
+ ">>\r\n"
+ "stream\r\n",
+ stream->id,
+ stream->length_id,
+ extra_entries);
+
+ stream->start_offset = ftell (file);
+
+ document->current_stream = stream;
+
+ return stream;
+}
+
+static void
+_cairo_pdf_document_close_stream (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ long length;
+ cairo_pdf_stream_t *stream;
+ cairo_pdf_object_t *object;
+
+ stream = document->current_stream;
+ if (stream == NULL)
+ return;
+
+ length = ftell(file) - stream->start_offset;
+ fprintf (file,
+ "\r\n"
+ "endstream\r\n"
+ "endobj\r\n");
+
+ object = _cairo_array_index (&document->objects, stream->length_id - 1);
+ object->offset = ftell(file);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ " %ld\r\n"
+ "endobj\r\n",
+ stream->length_id,
+ length);
+
+ document->current_stream = NULL;
+}
+
+static void
+_cairo_pdf_surface_destroy (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+
+ if (surface->current_stream == document->current_stream)
+ _cairo_pdf_document_close_stream (document);
+
+ _cairo_pdf_document_destroy (document);
+
+ free (surface);
+}
+
+/* XXX: We should re-work this interface to return both X/Y ppi values. */
+static double
+_cairo_pdf_surface_pixels_per_inch (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+
+ return surface->document->y_ppi;
+}
+
+static void
+_cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_document_t *document = surface->document;
+ cairo_pdf_stream_t *stream;
+ FILE *file = document->file;
+ char extra[200];
+
+ if (document->current_stream == NULL ||
+ document->current_stream != surface->current_stream) {
+ _cairo_pdf_document_close_stream (document);
+ snprintf (extra, sizeof extra,
+ " /Type /XObject\r\n"
+ " /Subtype /Form\r\n"
+ " /BBox [ 0 0 %f %f ]\r\n",
+ surface->width_inches * document->x_ppi,
+ surface->height_inches * document->y_ppi);
+ stream = _cairo_pdf_document_open_stream (document, extra);
+ _cairo_pdf_surface_add_stream (surface, stream);
+
+ /* If this is the first stream we open for this surface,
+ * output the cairo to PDF transformation matrix. */
+ if (_cairo_array_num_elements (&surface->streams) == 1)
+ fprintf (file, "1 0 0 -1 0 %f cm\r\n",
+ document->height_inches * document->y_ppi);
+ }
+}
+
+static cairo_image_surface_t *
+_cairo_pdf_surface_get_image (void *abstract_surface)
+{
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_matrix (void *abstract_surface,
+ cairo_matrix_t *matrix)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_filter (void *abstract_surface,
+ cairo_filter_t filter)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_set_repeat (void *abstract_surface,
+ int repeat)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned int
+emit_image_data (cairo_pdf_document_t *document,
+ cairo_image_surface_t *image)
+{
+ FILE *file = document->file;
+ cairo_pdf_stream_t *stream;
+ char entries[200];
+ int i, j;
+
+ _cairo_pdf_document_close_stream (document);
+
+ snprintf (entries, sizeof entries,
+ " /Type /XObject\r\n"
+ " /Subtype /Image\r\n"
+ " /Width %d\r\n"
+ " /Height %d\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /BitsPerComponent 8\r\n",
+ image->width, image->height);
+
+ stream = _cairo_pdf_document_open_stream (document, entries);
+
+ for (i = 0; i < image->height; i++) {
+ for (j = 0; j < image->width; j++) {
+ fputc((unsigned) image->data[i * image->stride + j * 4 + 2], file);
+ fputc((unsigned) image->data[i * image->stride + j * 4 + 1], file);
+ fputc((unsigned) image->data[i * image->stride + j * 4 + 0], file);
+ }
+ }
+
+ _cairo_pdf_document_close_stream (document);
+
+ return stream->id;
+}
+
+static void
+_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst,
+ cairo_image_surface_t *image)
+{
+ cairo_pdf_document_t *document = dst->document;
+ FILE *file = document->file;
+ unsigned id;
+ cairo_matrix_t i2u;
+
+ id = emit_image_data (dst->document, image);
+ _cairo_pdf_surface_add_xobject (dst, id);
+
+ _cairo_pdf_surface_ensure_stream (dst);
+
+ cairo_matrix_copy (&i2u, &image->base.matrix);
+ cairo_matrix_invert (&i2u);
+ cairo_matrix_translate (&i2u, 0, image->height);
+ cairo_matrix_scale (&i2u, image->width, -image->height);
+
+ fprintf (file,
+ "q %f %f %f %f %f %f cm /res%d Do Q\r\n",
+ i2u.m[0][0], i2u.m[0][1],
+ i2u.m[1][0], i2u.m[1][1],
+ i2u.m[2][0], i2u.m[2][1],
+ id);
+}
+
+/* The contents of the surface is already transformed into PDF units,
+ * but when we composite the surface we may want to use a different
+ * space. The problem I see now is that the show_surface snippet
+ * creates a surface 1x1, which in the snippet environment is the
+ * entire surface. When compositing the surface, cairo gives us the
+ * 1x1 to 256x256 matrix. This would be fine if cairo didn't actually
+ * also transform the drawing to the surface. Should the CTM be part
+ * of the current target surface?
+ */
+
+static cairo_int_status_t
+_cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst,
+ cairo_pdf_surface_t *src,
+ int width, int height)
+{
+ cairo_pdf_document_t *document = dst->document;
+ FILE *file = document->file;
+ cairo_matrix_t i2u;
+ cairo_pdf_stream_t *stream;
+ int num_streams, i;
+
+ if (src->pattern != NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_pdf_surface_ensure_stream (dst);
+
+ cairo_matrix_copy (&i2u, &src->base.matrix);
+ cairo_matrix_invert (&i2u);
+ cairo_matrix_scale (&i2u,
+ 1.0 / (src->width_inches * document->x_ppi),
+ 1.0 / (src->height_inches * document->y_ppi));
+
+ fprintf (file,
+ "q %f %f %f %f %f %f cm",
+ i2u.m[0][0], i2u.m[0][1],
+ i2u.m[1][0], i2u.m[1][1],
+ i2u.m[2][0], i2u.m[2][1]);
+
+ num_streams = _cairo_array_num_elements (&src->streams);
+ for (i = 0; i < num_streams; i++) {
+ _cairo_array_copy_element (&src->streams, i, &stream);
+ fprintf (file,
+ " /res%d Do",
+ stream->id);
+
+ _cairo_pdf_surface_add_xobject (dst, stream->id);
+
+ }
+
+ fprintf (file, " Q\r\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_composite (cairo_operator_t operator,
+ cairo_surface_t *generic_src,
+ cairo_surface_t *generic_mask,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_pdf_surface_t *dst = abstract_dst;
+ cairo_pdf_surface_t *src;
+ cairo_image_surface_t *image;
+
+ if (generic_src->backend == &cairo_pdf_surface_backend) {
+ src = (cairo_pdf_surface_t *) generic_src;
+ _cairo_pdf_surface_composite_pdf (dst, src, width, height);
+ }
+ else {
+ image = _cairo_surface_get_image (generic_src);
+ _cairo_pdf_surface_composite_image (dst, image);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t operator,
+ const cairo_color_t *color,
+ cairo_rectangle_t *rects,
+ int num_rects)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ int i;
+
+ if (surface->pattern != NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_pdf_surface_ensure_stream (surface);
+
+ fprintf (file,
+ "%f %f %f rg\r\n",
+ color->red, color->green, color->blue);
+
+ for (i = 0; i < num_rects; i++) {
+ fprintf (file,
+ "%d %d %d %d re f\r\n",
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+emit_tiling_pattern (cairo_operator_t operator,
+ cairo_pdf_surface_t *dst,
+ cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = dst->document;
+ FILE *file = document->file;
+ cairo_pdf_stream_t *stream;
+ cairo_image_surface_t *image;
+ char entries[250];
+ unsigned int id, alpha;
+ cairo_matrix_t pm;
+
+ if (pattern->u.surface.surface->backend == &cairo_pdf_surface_backend) {
+ return;
+ }
+
+ image = _cairo_surface_get_image (pattern->u.surface.surface);
+
+ _cairo_pdf_document_close_stream (document);
+
+ id = emit_image_data (dst->document, image);
+
+ /* BBox must be smaller than XStep by YStep or acroread wont
+ * display the pattern. */
+
+ cairo_matrix_set_identity (&pm);
+ cairo_matrix_scale (&pm, image->width, image->height);
+ cairo_matrix_copy (&pm, &pattern->matrix);
+ cairo_matrix_invert (&pm);
+
+ snprintf (entries, sizeof entries,
+ " /BBox [ 0 0 256 256 ]\r\n"
+ " /XStep 256\r\n"
+ " /YStep 256\r\n"
+ " /PatternType 1\r\n"
+ " /TilingType 1\r\n"
+ " /PaintType 1\r\n"
+ " /Resources << /XObject << /res%d %d 0 R >> >>\r\n"
+ " /Matrix [ %f %f %f %f %f %f ]\r\n",
+ id, id,
+ pm.m[0][0], pm.m[0][1],
+ pm.m[1][0], pm.m[1][1],
+ pm.m[2][0], pm.m[2][1]);
+
+ stream = _cairo_pdf_document_open_stream (document, entries);
+
+ _cairo_pdf_surface_add_pattern (dst, stream->id);
+
+ _cairo_pdf_surface_ensure_stream (dst);
+ alpha = _cairo_pdf_surface_add_alpha (dst, 1.0);
+ fprintf (file,
+ "/Pattern cs /res%d scn /a%d gs\r\n",
+ stream->id, alpha);
+}
+
+static unsigned int
+emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ unsigned int function_id;
+
+ function_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /FunctionType 0\r\n"
+ " /Domain [ 0.0 1.0 ]\r\n"
+ " /Size [ 2 ]\r\n"
+ " /BitsPerSample 8\r\n"
+ " /Range [ 0.0 1.0 0.0 1.0 0.0 1.0 ]\r\n"
+ " /Length 6\r\n"
+ ">>\r\n"
+ "stream\r\n",
+ function_id);
+
+ fputc (pattern->stops[0].color_char[0], file);
+ fputc (pattern->stops[0].color_char[1], file);
+ fputc (pattern->stops[0].color_char[2], file);
+ fputc (pattern->stops[1].color_char[0], file);
+ fputc (pattern->stops[1].color_char[1], file);
+ fputc (pattern->stops[1].color_char[2], file);
+
+ fprintf (file,
+ "\r\n"
+ "endstream\r\n"
+ "endobj\r\n");
+
+ return function_id;
+}
+
+static void
+emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ unsigned int function_id, pattern_id, alpha;
+ double x0, y0, x1, y1;
+ cairo_matrix_t p2u;
+
+ _cairo_pdf_document_close_stream (document);
+
+ function_id = emit_pattern_stops (surface, pattern);
+
+ cairo_matrix_copy (&p2u, &pattern->matrix);
+ cairo_matrix_invert (&p2u);
+
+ x0 = pattern->u.linear.point0.x;
+ y0 = pattern->u.linear.point0.y;
+ cairo_matrix_transform_point (&p2u, &x0, &y0);
+ x1 = pattern->u.linear.point1.x;
+ y1 = pattern->u.linear.point1.y;
+ cairo_matrix_transform_point (&p2u, &x1, &y1);
+
+ pattern_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Pattern\r\n"
+ " /PatternType 2\r\n"
+ " /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+ " /Shading\r\n"
+ " << /ShadingType 2\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /Coords [ %f %f %f %f ]\r\n"
+ " /Function %d 0 R\r\n"
+ " /Extend [ %s %s ]\r\n"
+ " >>\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ pattern_id,
+ document->height_inches * document->y_ppi,
+ x0, y0, x1, y1,
+ function_id,
+ (1 || pattern->extend) ? "true" : "false",
+ (1 || pattern->extend) ? "true" : "false");
+
+ _cairo_pdf_surface_add_pattern (surface, pattern_id);
+
+ _cairo_pdf_surface_ensure_stream (surface);
+ alpha = _cairo_pdf_surface_add_alpha (surface, 1.0);
+
+ /* Use pattern */
+ fprintf (file,
+ "/Pattern cs /res%d scn /a%d gs\r\n",
+ pattern_id, alpha);
+}
+
+static void
+emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern)
+{
+ cairo_pdf_document_t *document = surface->document;
+ FILE *file = document->file;
+ unsigned int function_id, pattern_id, alpha;
+ double x0, y0, x1, y1, r0, r1;
+ cairo_matrix_t p2u;
+
+ _cairo_pdf_document_close_stream (document);
+
+ function_id = emit_pattern_stops (surface, pattern);
+
+ cairo_matrix_copy (&p2u, &pattern->matrix);
+ cairo_matrix_invert (&p2u);
+
+ x0 = pattern->u.radial.center0.x;
+ y0 = pattern->u.radial.center0.y;
+ r0 = pattern->u.radial.radius0;
+ cairo_matrix_transform_point (&p2u, &x0, &y0);
+ x1 = pattern->u.radial.center1.x;
+ y1 = pattern->u.radial.center1.y;
+ r1 = pattern->u.radial.radius1;
+ cairo_matrix_transform_point (&p2u, &x1, &y1);
+
+ /* FIXME: This is surely crack, but how should you scale a radius
+ * in a non-orthogonal coordinate system? */
+ cairo_matrix_transform_distance (&p2u, &r0, &r1);
+
+ pattern_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Pattern\r\n"
+ " /PatternType 2\r\n"
+ " /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+ " /Shading\r\n"
+ " << /ShadingType 3\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /Coords [ %f %f %f %f %f %f ]\r\n"
+ " /Function %d 0 R\r\n"
+ " /Extend [ %s %s ]\r\n"
+ " >>\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ pattern_id,
+ document->height_inches * document->y_ppi,
+ x0, y0, r0, x1, y1, r1,
+ function_id,
+ (1 || pattern->extend) ? "true" : "false",
+ (1 || pattern->extend) ? "true" : "false");
+
+ _cairo_pdf_surface_add_pattern (surface, pattern_id);
+
+ _cairo_pdf_surface_ensure_stream (surface);
+ alpha = _cairo_pdf_surface_add_alpha (surface, 1.0);
+
+ /* Use pattern */
+ fprintf (file,
+ "/Pattern cs /res%d scn /a%d gs\r\n",
+ pattern_id, alpha);
+}
+
+static double
+intersect (cairo_line_t *line, cairo_fixed_t y)
+{
+ return _cairo_fixed_to_double (line->p1.x) +
+ _cairo_fixed_to_double (line->p2.x - line->p1.x) *
+ _cairo_fixed_to_double (y - line->p1.y) /
+ _cairo_fixed_to_double (line->p2.y - line->p1.y);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator,
+ cairo_surface_t *generic_src,
+ void *abstract_dst,
+ int x_src,
+ int y_src,
+ cairo_trapezoid_t *traps,
+ int num_traps)
+{
+ cairo_pdf_surface_t *surface = abstract_dst;
+ cairo_pdf_surface_t *source = (cairo_pdf_surface_t *) generic_src;
+ cairo_pdf_document_t *document = surface->document;
+ cairo_pattern_t *pattern;
+ FILE *file = document->file;
+ int i;
+ unsigned int alpha;
+
+ /* FIXME: we really just want the original pattern here, not a
+ * source surface. */
+ pattern = source->pattern;
+
+ if (source->base.backend != &cairo_pdf_surface_backend) {
+ printf ("_cairo_pdf_surface_composite_trapezoids: not a pdf source\r");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (pattern == NULL) {
+ printf ("_cairo_pdf_surface_composite_trapezoids: "
+ "non-pattern pdf source\r");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_SOLID:
+ alpha = _cairo_pdf_surface_add_alpha (surface, pattern->color.alpha);
+ _cairo_pdf_surface_ensure_stream (surface);
+ fprintf (file,
+ "%f %f %f rg /a%d gs\r\n",
+ pattern->color.red,
+ pattern->color.green,
+ pattern->color.blue,
+ alpha);
+ break;
+
+ case CAIRO_PATTERN_SURFACE:
+ emit_tiling_pattern (operator, surface, pattern);
+ break;
+
+ case CAIRO_PATTERN_LINEAR:
+ emit_linear_pattern (surface, pattern);
+ break;
+
+ case CAIRO_PATTERN_RADIAL:
+ emit_radial_pattern (surface, pattern );
+ break;
+ }
+
+ /* After the above switch the current stream should belong to this
+ * surface, so no need to _cairo_pdf_surface_ensure_stream() */
+ assert (document->current_stream != NULL &&
+ document->current_stream == surface->current_stream);
+
+ for (i = 0; i < num_traps; i++) {
+ double left_x1, left_x2, right_x1, right_x2;
+
+ left_x1 = intersect (&traps[i].left, traps[i].top);
+ left_x2 = intersect (&traps[i].left, traps[i].bottom);
+ right_x1 = intersect (&traps[i].right, traps[i].top);
+ right_x2 = intersect (&traps[i].right, traps[i].bottom);
+
+ fprintf (file,
+ "%f %f m %f %f l %f %f l %f %f l h\r\n",
+ left_x1, _cairo_fixed_to_double (traps[i].top),
+ left_x2, _cairo_fixed_to_double (traps[i].bottom),
+ right_x2, _cairo_fixed_to_double (traps[i].bottom),
+ right_x1, _cairo_fixed_to_double (traps[i].top));
+ }
+
+ fprintf (file,
+ "f\r\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_copy_page (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+
+ return _cairo_pdf_document_add_page (document, surface);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_show_page (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_document_t *document = surface->document;
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_document_add_page (document, surface);
+ if (status == CAIRO_STATUS_SUCCESS)
+ _cairo_pdf_surface_clear (surface);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_set_clip_region (void *abstract_surface,
+ pixman_region16_t *region)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_create_pattern (void *abstract_surface,
+ cairo_pattern_t *pattern,
+ cairo_box_t *extents)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_surface_t *source;
+
+ source = (cairo_pdf_surface_t *)
+ _cairo_pdf_surface_create_for_document (surface->document, 0, 0);
+ source->pattern = pattern;
+ pattern->source = &source->base;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend = {
+ _cairo_pdf_surface_create_similar,
+ _cairo_pdf_surface_destroy,
+ _cairo_pdf_surface_pixels_per_inch,
+ _cairo_pdf_surface_get_image,
+ _cairo_pdf_surface_set_image,
+ _cairo_pdf_surface_set_matrix,
+ _cairo_pdf_surface_set_filter,
+ _cairo_pdf_surface_set_repeat,
+ _cairo_pdf_surface_composite,
+ _cairo_pdf_surface_fill_rectangles,
+ _cairo_pdf_surface_composite_trapezoids,
+ _cairo_pdf_surface_copy_page,
+ _cairo_pdf_surface_show_page,
+ _cairo_pdf_surface_set_clip_region,
+ _cairo_pdf_surface_create_pattern,
+ NULL, /* show_glyphs */
+};
+
+static cairo_pdf_document_t *
+_cairo_pdf_document_create (FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch)
+{
+ cairo_pdf_document_t *document;
+
+ document = malloc (sizeof (cairo_pdf_document_t));
+ if (document == NULL)
+ return NULL;
+
+ document->file = file;
+ document->refcount = 1;
+ document->width_inches = width_inches;
+ document->height_inches = height_inches;
+ document->x_ppi = x_pixels_per_inch;
+ document->y_ppi = y_pixels_per_inch;
+
+ _cairo_array_init (&document->objects, sizeof (cairo_pdf_object_t));
+ _cairo_array_init (&document->pages, sizeof (unsigned int));
+ document->next_available_id = 1;
+
+ document->current_stream = NULL;
+
+ document->pages_id = _cairo_pdf_document_new_object (document);
+
+ /* Document header */
+ fprintf (file, "%%PDF-1.4\r\n");
+
+ return document;
+}
+
+static unsigned int
+_cairo_pdf_document_write_info (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ unsigned int id;
+
+ id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Creator (cairographics.org)\r\n"
+ " /Producer (cairographics.org)\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ id);
+
+ return id;
+}
+
+static void
+_cairo_pdf_document_write_pages (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ cairo_pdf_object_t *pages_object;
+ unsigned int page_id;
+ int num_pages, i;
+
+ pages_object = _cairo_array_index (&document->objects,
+ document->pages_id - 1);
+ pages_object->offset = ftell (file);
+
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Pages\r\n"
+ " /Kids [ ",
+ document->pages_id);
+
+ num_pages = _cairo_array_num_elements (&document->pages);
+ for (i = 0; i < num_pages; i++) {
+ _cairo_array_copy_element (&document->pages, i, &page_id);
+ fprintf (file, "%d 0 R ", page_id);
+ }
+
+ fprintf (file, "]\r\n");
+ fprintf (file, " /Count %d\r\n", num_pages);
+
+ /* TODO: Figure out wich other defaults to be inherited by /Page
+ * objects. */
+ fprintf (file,
+ " /MediaBox [ 0 0 %f %f ]\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ document->width_inches * document->x_ppi,
+ document->height_inches * document->y_ppi);
+}
+
+static unsigned int
+_cairo_pdf_document_write_catalog (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ unsigned int id;
+
+ id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Catalog\r\n"
+ " /Pages %d 0 R\r\n"
+ ">>\r\n"
+ "endobj\r\n",
+ id, document->pages_id);
+
+ return id;
+}
+
+static long
+_cairo_pdf_document_write_xref (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ cairo_pdf_object_t *object;
+ int num_objects, i;
+ long offset;
+
+ num_objects = _cairo_array_num_elements (&document->objects);
+
+ offset = ftell(file);
+ fprintf (document->file,
+ "xref\r\n"
+ "%d %d\r\n",
+ 0, num_objects + 1);
+
+ fprintf (file, "0000000000 65535 f\r\n");
+ for (i = 0; i < num_objects; i++) {
+ object = _cairo_array_index (&document->objects, i);
+ fprintf (file, "%010ld 00000 n\r\n", object->offset);
+ }
+
+ return offset;
+}
+
+static void
+_cairo_pdf_document_reference (cairo_pdf_document_t *document)
+{
+ document->refcount++;
+}
+
+static void
+_cairo_pdf_document_destroy (cairo_pdf_document_t *document)
+{
+ FILE *file = document->file;
+ long offset;
+ unsigned int info_id, catalog_id;
+
+ document->refcount--;
+ if (document->refcount > 0)
+ return;
+
+ _cairo_pdf_document_close_stream (document);
+ _cairo_pdf_document_write_pages (document);
+ info_id = _cairo_pdf_document_write_info (document);
+ catalog_id = _cairo_pdf_document_write_catalog (document);
+ offset = _cairo_pdf_document_write_xref (document);
+
+ fprintf (file,
+ "trailer\r\n"
+ "<< /Size %d\r\n"
+ " /Root %d 0 R\r\n"
+ " /Info %d 0 R\r\n"
+ ">>\r\n",
+ document->next_available_id,
+ catalog_id,
+ info_id);
+
+ fprintf (file,
+ "startxref\r\n"
+ "%ld\r\n"
+ "%%%%EOF\r\n",
+ offset);
+
+ free (document);
+}
+
+static cairo_status_t
+_cairo_pdf_document_add_page (cairo_pdf_document_t *document,
+ cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_stream_t *stream;
+ cairo_pdf_resource_t *res;
+ FILE *file = document->file;
+ unsigned int page_id;
+ double alpha;
+ int num_streams, num_alphas, num_resources, i;
+
+ _cairo_pdf_document_close_stream (document);
+
+ page_id = _cairo_pdf_document_new_object (document);
+ fprintf (file,
+ "%d 0 obj\r\n"
+ "<< /Type /Page\r\n"
+ " /Parent %d 0 R\r\n"
+ " /Contents [",
+ page_id,
+ document->pages_id);
+
+ num_streams = _cairo_array_num_elements (&surface->streams);
+ for (i = 0; i < num_streams; i++) {
+ _cairo_array_copy_element (&surface->streams, i, &stream);
+ fprintf (file,
+ " %d 0 R",
+ stream->id);
+ }
+
+ fprintf (file,
+ " ]\r\n"
+ " /Resources <<\r\n");
+
+
+ num_alphas = _cairo_array_num_elements (&surface->alphas);
+ if (num_alphas > 0) {
+ fprintf (file,
+ " /ExtGState <<\r\n");
+
+ for (i = 0; i < num_alphas; i++) {
+ _cairo_array_copy_element (&surface->alphas, i, &alpha);
+ fprintf (file,
+ " /a%d << /ca %f >>\r\n",
+ i, alpha);
+ }
+
+ fprintf (file,
+ " >>\r\n");
+ }
+
+ num_resources = _cairo_array_num_elements (&surface->patterns);
+ if (num_resources > 0) {
+ fprintf (file,
+ " /Pattern <<");
+ for (i = 0; i < num_resources; i++) {
+ res = _cairo_array_index (&surface->patterns, i);
+ fprintf (file,
+ " /res%d %d 0 R",
+ res->id, res->id);
+ }
+
+ fprintf (file,
+ " >>\r\n");
+ }
+
+ num_resources = _cairo_array_num_elements (&surface->xobjects);
+ if (num_resources > 0) {
+ fprintf (file,
+ " /XObject <<");
+
+ for (i = 0; i < num_resources; i++) {
+ res = _cairo_array_index (&surface->xobjects, i);
+ fprintf (file,
+ " /res%d %d 0 R",
+ res->id, res->id);
+ }
+
+ fprintf (file,
+ " >>\r\n");
+ }
+
+ fprintf (file,
+ " >>\r\n"
+ ">>\r\n"
+ "endobj\r\n");
+
+ _cairo_array_append (&document->pages, &page_id, 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+cairo_set_target_pdf (cairo_t *cr,
+ FILE *file,
+ double width_inches,
+ double height_inches,
+ double x_pixels_per_inch,
+ double y_pixels_per_inch)
+{
+ cairo_surface_t *surface;
+
+ surface = cairo_pdf_surface_create (file,
+ width_inches,
+ height_inches,
+ x_pixels_per_inch,
+ y_pixels_per_inch);
+
+ if (surface == NULL) {
+ cr->status = CAIRO_STATUS_NO_MEMORY;
+ return;
+ }
+
+ cairo_set_target_surface (cr, surface);
+
+ /* cairo_set_target_surface takes a reference, so we must destroy ours */
+ cairo_surface_destroy (surface);
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index f780b7717..7908a97b8 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -254,6 +254,40 @@ typedef struct cairo_pen {
typedef struct cairo_color cairo_color_t;
typedef struct cairo_image_surface cairo_image_surface_t;
+/* cairo_array.c structures and functions */
+
+typedef struct cairo_array cairo_array_t;
+struct cairo_array {
+ int size;
+ int num_elements;
+ int element_size;
+ char *elements;
+};
+
+cairo_private void
+_cairo_array_init (cairo_array_t *array, int element_size);
+
+cairo_private void
+_cairo_array_fini (cairo_array_t *array);
+
+cairo_private cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, int additional);
+
+cairo_private void
+_cairo_array_truncate (cairo_array_t *array, int length);
+
+cairo_private cairo_status_t
+_cairo_array_append (cairo_array_t *array, void *elements, int num_elements);
+
+cairo_private void *
+_cairo_array_index (cairo_array_t *array, int index);
+
+cairo_private void
+_cairo_array_copy_element (cairo_array_t *array, int index, void *dst);
+
+cairo_private int
+_cairo_array_num_elements (cairo_array_t *array);
+
/* cairo_cache.c structures and functions */
typedef struct cairo_cache_backend {