summaryrefslogtreecommitdiff
path: root/src/cairo-ps-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-ps-surface.c')
-rw-r--r--src/cairo-ps-surface.c1277
1 files changed, 1109 insertions, 168 deletions
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index dd12c2ca5..0464b5a0a 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1,6 +1,7 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
+ * Copyright © 2005 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
@@ -32,14 +33,27 @@
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
+ * Kristian Høgsberg <krh@redhat.com>
*/
#include "cairoint.h"
#include "cairo-ps.h"
+#include "cairo-font-subset-private.h"
+#include "cairo-meta-surface-private.h"
+#include "cairo-ft-private.h"
#include <time.h>
#include <zlib.h>
+/* TODO:
+ *
+ * - Add document structure convention comments where appropriate.
+ *
+ * - Fix image compression.
+ *
+ * - Create a set of procs to use... specifically a trapezoid proc.
+ */
+
static const cairo_surface_backend_t cairo_ps_surface_backend;
typedef struct cairo_ps_surface {
@@ -48,29 +62,31 @@ typedef struct cairo_ps_surface {
/* PS-specific fields */
cairo_output_stream_t *stream;
- double width_in_points;
- double height_in_points;
+ double width;
+ double height;
double x_dpi;
double y_dpi;
- int pages;
-
- cairo_image_surface_t *image;
+ cairo_surface_t *current_page;
+ cairo_array_t pages;
+ cairo_array_t fonts;
} cairo_ps_surface_t;
#define PS_SURFACE_DPI_DEFAULT 300.0
-static void
-_cairo_ps_surface_erase (cairo_ps_surface_t *surface);
+static cairo_int_status_t
+_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface);
+
+static cairo_int_status_t
+_cairo_ps_surface_render_page (cairo_ps_surface_t *surface,
+ cairo_surface_t *page, int page_number);
static cairo_surface_t *
_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
- double width_in_points,
- double height_in_points)
+ double width,
+ double height)
{
cairo_ps_surface_t *surface;
- int width_in_pixels, height_in_pixels;
- time_t now = time (0);
surface = malloc (sizeof (cairo_ps_surface_t));
if (surface == NULL)
@@ -80,45 +96,20 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
surface->stream = stream;
- surface->width_in_points = width_in_points;
- surface->height_in_points = height_in_points;
+ surface->width = width;
+ surface->height = height;
surface->x_dpi = PS_SURFACE_DPI_DEFAULT;
surface->y_dpi = PS_SURFACE_DPI_DEFAULT;
- surface->pages = 0;
-
- width_in_pixels = (int) (surface->x_dpi * width_in_points / 72.0 + 1.0);
- height_in_pixels = (int) (surface->y_dpi * height_in_points / 72.0 + 1.0);
-
- surface->image = (cairo_image_surface_t *)
- cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
- width_in_pixels, height_in_pixels);
- if (surface->image == NULL) {
+ surface->current_page = _cairo_meta_surface_create (width,
+ height);
+ if (surface->current_page == NULL) {
free (surface);
return NULL;
}
- _cairo_ps_surface_erase (surface);
-
- /* Document header */
- _cairo_output_stream_printf (surface->stream,
- "%%!PS-Adobe-3.0\n"
- "%%%%Creator: Cairo (http://cairographics.org)\n");
- _cairo_output_stream_printf (surface->stream,
- "%%%%CreationDate: %s",
- ctime (&now));
- _cairo_output_stream_printf (surface->stream,
- "%%%%BoundingBox: %f %f %f %f\n",
- 0.0, 0.0,
- surface->width_in_points,
- surface->height_in_points);
- /* The "/FlateDecode filter" currently used is a feature of LanguageLevel 3 */
- _cairo_output_stream_printf (surface->stream,
- "%%%%DocumentData: Clean7Bit\n"
- "%%%%LanguageLevel: 3\n");
- _cairo_output_stream_printf (surface->stream,
- "%%%%Orientation: Portrait\n"
- "%%%%EndComments\n");
+ _cairo_array_init (&surface->pages, sizeof (cairo_surface_t *));
+ _cairo_array_init (&surface->fonts, sizeof (cairo_font_subset_t *));
return &surface->base;
}
@@ -157,10 +148,10 @@ cairo_ps_surface_create_for_stream (cairo_write_func_t write_func,
}
static cairo_surface_t *
-_cairo_ps_surface_create_similar (void *abstract_src,
- cairo_format_t format,
- int width,
- int height)
+_cairo_ps_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
{
return NULL;
}
@@ -169,210 +160,1160 @@ static cairo_status_t
_cairo_ps_surface_finish (void *abstract_surface)
{
cairo_ps_surface_t *surface = abstract_surface;
+ cairo_surface_t *page;
+ cairo_font_subset_t *subset;
+ cairo_status_t status;
+ int i;
+ time_t now;
+
+ now = time (0);
+
+ /* Document header */
+ _cairo_output_stream_printf (surface->stream,
+ "%%!PS-Adobe-3.0\n"
+ "%%%%Creator: Cairo (http://cairographics.org)\n"
+ "%%%%CreationDate: %s"
+ "%%%%Title: Some clever title\n"
+ "%%%%Pages: %d\n"
+ "%%%%BoundingBox: %f %f %f %f\n",
+ ctime (&now),
+ surface->pages.num_elements,
+ 0.0, 0.0,
+ surface->width,
+ surface->height);
+
+ /* The "/FlateDecode filter" currently used is a feature of
+ * LanguageLevel 3 */
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%DocumentData: Clean7Bit\n"
+ "%%%%LanguageLevel: 3\n"
+ "%%%%Orientation: Portrait\n"
+ "%%%%EndComments\n");
+
+ _cairo_ps_surface_write_font_subsets (surface);
+
+ status = CAIRO_STATUS_SUCCESS;
+ for (i = 0; i < surface->pages.num_elements; i++) {
+ _cairo_array_copy_element (&surface->pages, i, &page);
+
+ status = _cairo_ps_surface_render_page (surface, page, i + 1);
+ if (status)
+ break;
+ }
/* Document footer */
_cairo_output_stream_printf (surface->stream,
+ "%%%%Trailer\n"
"%%%%EOF\n");
- cairo_surface_destroy (&surface->image->base);
+ for (i = 0; i < surface->pages.num_elements; i++) {
+ _cairo_array_copy_element (&surface->pages, i, &page);
+ cairo_surface_destroy (page);
+ }
+ _cairo_array_fini (&surface->pages);
- return CAIRO_STATUS_SUCCESS;
+ for (i = 0; i < surface->fonts.num_elements; i++) {
+ _cairo_array_copy_element (&surface->fonts, i, &subset);
+ _cairo_font_subset_destroy (subset);
+ }
+ _cairo_array_fini (&surface->fonts);
+
+ _cairo_output_stream_destroy (surface->stream);
+ cairo_surface_destroy (surface->current_page);
+
+ return status;
}
-static void
-_cairo_ps_surface_erase (cairo_ps_surface_t *surface)
+static cairo_int_status_t
+_cairo_ps_surface_composite (cairo_operator_t operator,
+ cairo_pattern_t *src_pattern,
+ cairo_pattern_t *mask_pattern,
+ 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_surface_fill_rectangle (&surface->image->base,
- CAIRO_OPERATOR_SOURCE,
- CAIRO_COLOR_TRANSPARENT,
- 0, 0,
- surface->image->width,
- surface->image->height);
+ cairo_ps_surface_t *surface = abstract_dst;
+
+ return _cairo_surface_composite (operator,
+ src_pattern,
+ mask_pattern,
+ surface->current_page,
+ src_x,
+ src_y,
+ mask_x,
+ mask_y,
+ dst_x,
+ dst_y,
+ width,
+ height);
}
-static cairo_status_t
-_cairo_ps_surface_acquire_source_image (void *abstract_surface,
- cairo_image_surface_t **image_out,
- void **image_extra)
+static cairo_int_status_t
+_cairo_ps_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t operator,
+ const cairo_color_t *color,
+ cairo_rectangle_t *rects,
+ int num_rects)
{
cairo_ps_surface_t *surface = abstract_surface;
-
- *image_out = surface->image;
+
+ return _cairo_surface_fill_rectangles (surface->current_page,
+ operator,
+ color,
+ rects,
+ num_rects);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_composite_trapezoids (cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ void *abstract_dst,
+ int x_src,
+ int y_src,
+ int x_dst,
+ int y_dst,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps)
+{
+ cairo_ps_surface_t *surface = abstract_dst;
+
+ return _cairo_surface_composite_trapezoids (operator,
+ pattern,
+ surface->current_page,
+ x_src,
+ y_src,
+ x_dst,
+ y_dst,
+ width,
+ height,
+ traps,
+ num_traps);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_copy_page (void *abstract_surface)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+
+ /* FIXME: We need to copy the meta surface contents here */
+
+ return _cairo_surface_show_page (&surface->base);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_page (void *abstract_surface)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+
+ _cairo_array_append (&surface->pages, &surface->current_page, 1);
+ surface->current_page = _cairo_meta_surface_create (surface->width,
+ surface->height);
+ if (surface->current_page == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
return CAIRO_STATUS_SUCCESS;
}
-static cairo_status_t
-_cairo_ps_surface_acquire_dest_image (void *abstract_surface,
- cairo_rectangle_t *interest_rect,
- cairo_image_surface_t **image_out,
- cairo_rectangle_t *image_rect,
- void **image_extra)
+static cairo_int_status_t
+_cairo_ps_surface_intersect_clip_path (void *dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
+{
+ cairo_ps_surface_t *surface = dst;
+
+ return _cairo_surface_intersect_clip_path (surface->current_page,
+ path,
+ fill_rule,
+ tolerance);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_t *rectangle)
{
cairo_ps_surface_t *surface = abstract_surface;
-
- image_rect->x = 0;
- image_rect->y = 0;
- image_rect->width = surface->image->width;
- image_rect->height = surface->image->height;
-
- *image_out = surface->image;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+
+ /* XXX: The conversion to integers here is pretty bogus, (not to
+ * mention the aribitray limitation of width to a short(!). We
+ * may need to come up with a better interface for get_size.
+ */
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
return CAIRO_STATUS_SUCCESS;
}
+static cairo_font_subset_t *
+_cairo_ps_surface_get_font (cairo_ps_surface_t *surface,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_unscaled_font_t *unscaled_font;
+ cairo_font_subset_t *subset;
+ unsigned int num_fonts, i;
+
+ /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */
+ if (! _cairo_scaled_font_is_ft (scaled_font))
+ return NULL;
+
+ /* XXX Why is this an ft specific function? */
+ unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
+
+ num_fonts = _cairo_array_num_elements (&surface->fonts);
+ for (i = 0; i < num_fonts; i++) {
+ _cairo_array_copy_element (&surface->fonts, i, &subset);
+ if (subset->unscaled_font == unscaled_font)
+ return subset;
+ }
+
+ subset = _cairo_font_subset_create (unscaled_font);
+ if (subset == NULL)
+ return NULL;
+
+ subset->font_id = surface->fonts.num_elements;
+ if (_cairo_array_append (&surface->fonts, &subset, 1) == NULL) {
+ _cairo_font_subset_destroy (subset);
+ return NULL;
+ }
+
+ return subset;
+}
+
static cairo_int_status_t
-_cairo_ps_surface_copy_page (void *abstract_surface)
+_cairo_ps_surface_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ void *abstract_surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
{
- cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_ps_surface_t *surface = abstract_surface;
- int width = surface->image->width;
- int height = surface->image->height;
- cairo_output_stream_t *stream = surface->stream;
+ cairo_font_subset_t *subset;
+ int i;
+
+ /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */
+ if (! _cairo_scaled_font_is_ft (scaled_font))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Collect font subset info as we go. */
+ subset = _cairo_ps_surface_get_font (surface, scaled_font);
+ if (subset == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ for (i = 0; i < num_glyphs; i++)
+ _cairo_font_subset_use_glyph (subset, glyphs[i].index);
+
+ return _cairo_surface_show_glyphs (scaled_font,
+ operator,
+ pattern,
+ surface->current_page,
+ source_x,
+ source_y,
+ dest_x,
+ dest_y,
+ width,
+ height,
+ glyphs,
+ num_glyphs);
+}
- int i, x, y;
+static cairo_int_status_t
+_cairo_ps_surface_fill_path (cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
+{
+ cairo_ps_surface_t *surface = abstract_dst;
+
+ return _cairo_surface_fill_path (operator,
+ pattern,
+ surface->current_page,
+ path,
+ fill_rule,
+ tolerance);
+}
- cairo_solid_pattern_t white_pattern;
- unsigned char *rgb, *compressed;
- unsigned long rgb_size, compressed_size;
+static const cairo_surface_backend_t cairo_ps_surface_backend = {
+ _cairo_ps_surface_create_similar,
+ _cairo_ps_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ _cairo_ps_surface_composite,
+ _cairo_ps_surface_fill_rectangles,
+ _cairo_ps_surface_composite_trapezoids,
+ _cairo_ps_surface_copy_page,
+ _cairo_ps_surface_show_page,
+ NULL, /* set_clip_region */
+ _cairo_ps_surface_intersect_clip_path,
+ _cairo_ps_surface_get_extents,
+ _cairo_ps_surface_show_glyphs,
+ _cairo_ps_surface_fill_path
+};
- rgb_size = 3 * width * height;
- rgb = malloc (rgb_size);
- if (rgb == NULL) {
- status = CAIRO_STATUS_NO_MEMORY;
- goto BAIL0;
+static cairo_int_status_t
+_cairo_ps_surface_write_type42_dict (cairo_ps_surface_t *surface,
+ cairo_font_subset_t *subset)
+{
+ const char *data;
+ unsigned long data_size;
+ cairo_status_t status;
+ int i;
+
+ status = CAIRO_STATUS_SUCCESS;
+
+ /* FIXME: Figure out document structure convention for fonts */
+
+ _cairo_output_stream_printf (surface->stream,
+ "11 dict begin\n"
+ "/FontType 42 def\n"
+ "/FontName /f%d def\n"
+ "/PaintType 0 def\n"
+ "/FontMatrix [ 1 0 0 1 0 0 ] def\n"
+ "/FontBBox [ 0 0 0 0 ] def\n"
+ "/Encoding 256 array def\n"
+ "0 1 255 { Encoding exch /.notdef put } for\n",
+ subset->font_id);
+
+ /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
+
+ for (i = 1; i < subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->stream,
+ "Encoding %d /g%d put\n", i, i);
+
+ _cairo_output_stream_printf (surface->stream,
+ "/CharStrings %d dict dup begin\n"
+ "/.notdef 0 def\n",
+ subset->num_glyphs);
+
+ for (i = 1; i < subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->stream,
+ "/g%d %d def\n", i, i);
+
+ _cairo_output_stream_printf (surface->stream,
+ "end readonly def\n");
+
+ status = _cairo_font_subset_generate (subset, &data, &data_size);
+
+ /* FIXME: We need to break up fonts bigger than 64k so we don't
+ * exceed string size limitation. At glyph boundaries. Stupid
+ * postscript. */
+ _cairo_output_stream_printf (surface->stream,
+ "/sfnts [<");
+
+ _cairo_output_stream_write_hex_string (surface->stream, data, data_size);
+
+ _cairo_output_stream_printf (surface->stream,
+ ">] def\n"
+ "FontName currentdict end definefont pop\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface)
+{
+ cairo_font_subset_t *subset;
+ int i;
+
+ for (i = 0; i < surface->fonts.num_elements; i++) {
+ _cairo_array_copy_element (&surface->fonts, i, &subset);
+ _cairo_ps_surface_write_type42_dict (surface, subset);
}
- compressed_size = (int) (1.0 + 1.1 * rgb_size);
- compressed = malloc (compressed_size);
- if (compressed == NULL) {
- status = CAIRO_STATUS_NO_MEMORY;
- goto BAIL1;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cairo_ps_fallback_area cairo_ps_fallback_area_t;
+struct _cairo_ps_fallback_area {
+ int x, y;
+ unsigned int width, height;
+ cairo_ps_fallback_area_t *next;
+};
+
+typedef struct _ps_output_surface {
+ cairo_surface_t base;
+ cairo_ps_surface_t *parent;
+ cairo_ps_fallback_area_t *fallback_areas;
+} ps_output_surface_t;
+
+static cairo_int_status_t
+_ps_output_add_fallback_area (ps_output_surface_t *surface,
+ int x, int y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_ps_fallback_area_t *area;
+
+ /* FIXME: Do a better job here. Ideally, we would use a 32 bit
+ * region type, but probably just computing bounding boxes would
+ * also work fine. */
+
+ area = malloc (sizeof (cairo_ps_fallback_area_t));
+ if (area == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ area->x = x;
+ area->y = y;
+ area->width = width;
+ area->height = height;
+ area->next = surface->fallback_areas;
+
+ surface->fallback_areas = area;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_finish (void *abstract_surface)
+{
+ ps_output_surface_t *surface = abstract_surface;
+ cairo_ps_fallback_area_t *area, *next;
+
+ for (area = surface->fallback_areas; area != NULL; area = next) {
+ next = area->next;
+ free (area);
}
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+color_is_gray (cairo_color_t *color)
+{
+ const double epsilon = 0.00001;
+
+ return (fabs (color->red - color->green) < epsilon &&
+ fabs (color->red - color->blue) < epsilon);
+}
+
+static cairo_bool_t
+pattern_is_translucent (cairo_pattern_t *abstract_pattern)
+{
+ cairo_pattern_union_t *pattern;
+
+ pattern = (cairo_pattern_union_t *) abstract_pattern;
+ switch (pattern->base.type) {
+ case CAIRO_PATTERN_SOLID:
+ return pattern->solid.color.alpha < 0.9;
+ case CAIRO_PATTERN_SURFACE:
+ case CAIRO_PATTERN_LINEAR:
+ case CAIRO_PATTERN_RADIAL:
+ return FALSE;
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+/* PS Output - this section handles output of the parts of the meta
+ * surface we can render natively in PS. */
+
+static void *
+compress_dup (const void *data, unsigned long data_size,
+ unsigned long *compressed_size)
+{
+ void *compressed;
+
+ /* Bound calculation taken from zlib. */
+ *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11;
+ compressed = malloc (*compressed_size);
+ if (compressed == NULL)
+ return NULL;
+
+ compress (compressed, compressed_size, data, data_size);
+
+ return compressed;
+}
+
+static cairo_status_t
+emit_image (cairo_ps_surface_t *surface,
+ cairo_image_surface_t *image,
+ cairo_matrix_t *matrix)
+{
+ cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
+ unsigned char *rgb, *compressed;
+ unsigned long rgb_size, compressed_size;
+ cairo_surface_t *opaque;
+ cairo_pattern_union_t pattern;
+ cairo_matrix_t d2i;
+ int x, y, i;
+
/* PostScript can not represent the alpha channel, so we blend the
current image over a white RGB surface to eliminate it. */
- _cairo_pattern_init_solid (&white_pattern, CAIRO_COLOR_WHITE);
+ opaque = _cairo_surface_create_similar_solid (&image->base,
+ CAIRO_CONTENT_COLOR,
+ image->width,
+ image->height,
+ CAIRO_COLOR_WHITE);
+ if (opaque == NULL)
+ goto bail0;
+
+ _cairo_pattern_init_for_surface (&pattern.surface, &image->base);
_cairo_surface_composite (CAIRO_OPERATOR_DEST_OVER,
- &white_pattern.base,
+ &pattern.base,
NULL,
- &surface->image->base,
+ opaque,
0, 0,
0, 0,
0, 0,
- width, height);
-
- _cairo_pattern_fini (&white_pattern.base);
+ image->width,
+ image->height);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ rgb_size = 3 * image->width * image->height;
+ rgb = malloc (rgb_size);
+ if (rgb == NULL)
+ goto bail1;
i = 0;
- for (y = 0; y < height; y++) {
- pixman_bits_t *pixel = (pixman_bits_t *) (surface->image->data + y * surface->image->stride);
- for (x = 0; x < width; x++, pixel++) {
+ for (y = 0; y < image->height; y++) {
+ pixman_bits_t *pixel = (pixman_bits_t *) (image->data + y * image->stride);
+ for (x = 0; x < image->width; x++, pixel++) {
rgb[i++] = (*pixel & 0x00ff0000) >> 16;
rgb[i++] = (*pixel & 0x0000ff00) >> 8;
rgb[i++] = (*pixel & 0x000000ff) >> 0;
}
}
- compress (compressed, &compressed_size, rgb, rgb_size);
+ compressed = compress_dup (rgb, rgb_size, &compressed_size);
+ if (compressed == NULL)
+ goto bail2;
- /* Page header */
- _cairo_output_stream_printf (stream, "%%%%Page: %d\n", ++surface->pages);
+ /* matrix transforms from user space to image space. We need to
+ * transform from device space to image space to compensate for
+ * postscripts coordinate system. */
+ cairo_matrix_init (&d2i, 1, 0, 0, -1, 0, surface->height);
+ cairo_matrix_multiply (&d2i, &d2i, matrix);
- _cairo_output_stream_printf (stream, "gsave\n");
-
- /* Image header goop */
- _cairo_output_stream_printf (stream, "%f %f translate\n",
- 0.0, surface->height_in_points);
- _cairo_output_stream_printf (stream, "/DeviceRGB setcolorspace\n");
- _cairo_output_stream_printf (stream, "<<\n");
- _cairo_output_stream_printf (stream, " /ImageType 1\n");
- _cairo_output_stream_printf (stream, " /Width %d\n", width);
- _cairo_output_stream_printf (stream, " /Height %d\n", height);
- _cairo_output_stream_printf (stream, " /BitsPerComponent 8\n");
- _cairo_output_stream_printf (stream, " /Decode [ 0 1 0 1 0 1 ]\n");
- _cairo_output_stream_printf (stream, " /DataSource currentfile /FlateDecode filter\n");
- _cairo_output_stream_printf (stream, " /ImageMatrix [ 1 0 0 -1 0 1 ]\n");
- _cairo_output_stream_printf (stream, ">>\n");
- _cairo_output_stream_printf (stream, "image\n");
+ _cairo_output_stream_printf (surface->stream,
+ "/DeviceRGB setcolorspace\n"
+ "<<\n"
+ " /ImageType 1\n"
+ " /Width %d\n"
+ " /Height %d\n"
+ " /BitsPerComponent 8\n"
+ " /Decode [ 0 1 0 1 0 1 ]\n"
+ " /DataSource currentfile\n"
+ " /ImageMatrix [ %f %f %f %f %f %f ]\n"
+ ">>\n"
+ "image\n",
+ image->width,
+ image->height,
+ d2i.xx, d2i.yx,
+ d2i.xy, d2i.yy,
+ d2i.x0, d2i.y0);
/* Compressed image data */
- _cairo_output_stream_write (stream, compressed, compressed_size);
-
- _cairo_output_stream_printf (stream, "showpage\n");
+ _cairo_output_stream_write (surface->stream, rgb, rgb_size);
+ status = CAIRO_STATUS_SUCCESS;
- _cairo_output_stream_printf (stream, "grestore\n");
-
- /* Page footer */
- _cairo_output_stream_printf (stream, "%%%%EndPage\n");
+ _cairo_output_stream_printf (surface->stream,
+ "\n");
free (compressed);
- BAIL1:
+ bail2:
free (rgb);
- BAIL0:
+ bail1:
+ cairo_surface_destroy (opaque);
+ bail0:
return status;
}
+static void
+emit_solid_pattern (cairo_ps_surface_t *surface,
+ cairo_solid_pattern_t *pattern)
+{
+ if (color_is_gray (&pattern->color))
+ _cairo_output_stream_printf (surface->stream,
+ "%f setgray\n",
+ pattern->color.red);
+ else
+ _cairo_output_stream_printf (surface->stream,
+ "%f %f %f setrgbcolor\n",
+ pattern->color.red,
+ pattern->color.green,
+ pattern->color.blue);
+}
+
+static void
+emit_surface_pattern (cairo_ps_surface_t *surface,
+ cairo_surface_pattern_t *pattern)
+{
+}
+
+static void
+emit_linear_pattern (cairo_ps_surface_t *surface,
+ cairo_linear_pattern_t *pattern)
+{
+}
+
+static void
+emit_radial_pattern (cairo_ps_surface_t *surface,
+ cairo_radial_pattern_t *pattern)
+{
+}
+
+static void
+emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern)
+{
+ /* FIXME: We should keep track of what pattern is currently set in
+ * the postscript file and only emit code if we're setting a
+ * different pattern. */
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_SOLID:
+ emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
+ break;
+
+ case CAIRO_PATTERN_SURFACE:
+ emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern);
+ break;
+
+ case CAIRO_PATTERN_LINEAR:
+ emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
+ break;
+
+ case CAIRO_PATTERN_RADIAL:
+ emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern);
+ break;
+ }
+}
+
+
static cairo_int_status_t
-_cairo_ps_surface_show_page (void *abstract_surface)
+_ps_output_composite (cairo_operator_t operator,
+ cairo_pattern_t *src_pattern,
+ cairo_pattern_t *mask_pattern,
+ 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_int_status_t status;
- cairo_ps_surface_t *surface = abstract_surface;
+ ps_output_surface_t *surface = abstract_dst;
+ cairo_output_stream_t *stream = surface->parent->stream;
+ cairo_surface_pattern_t *surface_pattern;
+ cairo_status_t status;
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ if (mask_pattern) {
+ /* FIXME: Investigate how this can be done... we'll probably
+ * need pixmap fallbacks for this, though. */
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_composite: with mask\n");
+ return CAIRO_STATUS_SUCCESS;
+ }
- status = _cairo_ps_surface_copy_page (surface);
- if (status)
- return status;
+ status = CAIRO_STATUS_SUCCESS;
+ switch (src_pattern->type) {
+ case CAIRO_PATTERN_SOLID:
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_composite: solid\n");
+ break;
+
+ case CAIRO_PATTERN_SURFACE:
+ surface_pattern = (cairo_surface_pattern_t *) src_pattern;
+
+ if (src_pattern->extend != CAIRO_EXTEND_NONE) {
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_composite: repeating image\n");
+ break;
+ }
+
+
+ status = _cairo_surface_acquire_source_image (surface_pattern->surface,
+ &image,
+ &image_extra);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_composite: src_pattern not available as image\n");
+ break;
+ } else if (status) {
+ break;
+ }
+ status = emit_image (surface->parent, image, &src_pattern->matrix);
+ _cairo_surface_release_source_image (surface_pattern->surface,
+ image, image_extra);
+ break;
+
+ case CAIRO_PATTERN_LINEAR:
+ case CAIRO_PATTERN_RADIAL:
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_composite: gradient\n");
+ break;
+ }
- _cairo_ps_surface_erase (surface);
+ return status;
+}
+
+static cairo_int_status_t
+_ps_output_fill_rectangles (void *abstract_surface,
+ cairo_operator_t operator,
+ const cairo_color_t *color,
+ cairo_rectangle_t *rects,
+ int num_rects)
+{
+ ps_output_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->parent->stream;
+ cairo_solid_pattern_t solid;
+ int i;
+
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_fill_rectangles\n");
+
+ _cairo_pattern_init_solid (&solid, color);
+ emit_pattern (surface->parent, &solid.base);
+ _cairo_pattern_fini (&solid.base);
+
+ _cairo_output_stream_printf (stream, "[");
+ for (i = 0; i < num_rects; i++) {
+ _cairo_output_stream_printf (stream,
+ " %d %d %d %d",
+ rects[i].x,
+ surface->parent->height - rects[i].y - rects[i].height,
+ rects[i].width, rects[i].height);
+ }
+
+ _cairo_output_stream_printf (stream, " ] rectfill\n");
return CAIRO_STATUS_SUCCESS;
}
+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_ps_surface_set_clip_region (void *abstract_surface,
- pixman_region16_t *region)
+_ps_output_composite_trapezoids (cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ void *abstract_dst,
+ int x_src,
+ int y_src,
+ int x_dst,
+ int y_dst,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps)
{
- cairo_ps_surface_t *surface = abstract_surface;
+ ps_output_surface_t *surface = abstract_dst;
+ cairo_output_stream_t *stream = surface->parent->stream;
+ int i;
+
+ if (pattern_is_translucent (pattern))
+ return _ps_output_add_fallback_area (surface, x_dst, y_dst, width, height);
+
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_composite_trapezoids\n");
+
+ emit_pattern (surface->parent, pattern);
+
+ for (i = 0; i < num_traps; i++) {
+ double left_x1, left_x2, right_x1, right_x2, top, bottom;
+
+ 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);
+ top = surface->parent->height - _cairo_fixed_to_double (traps[i].top);
+ bottom = surface->parent->height - _cairo_fixed_to_double (traps[i].bottom);
+
+ _cairo_output_stream_printf
+ (stream,
+ "%f %f moveto %f %f lineto %f %f lineto %f %f lineto "
+ "closepath\n",
+ left_x1, top,
+ left_x2, bottom,
+ right_x2, bottom,
+ right_x1, top);
+ }
+
+ _cairo_output_stream_printf (stream,
+ "fill\n");
- return _cairo_image_surface_set_clip_region (surface->image, region);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct
+{
+ double height;
+ cairo_output_stream_t *output_stream;
+ cairo_bool_t has_current_point;
+} ps_output_path_info_t;
+
+static cairo_status_t
+_ps_output_path_move_to (void *closure, cairo_point_t *point)
+{
+ ps_output_path_info_t *info = closure;
+
+ _cairo_output_stream_printf (info->output_stream,
+ "%f %f moveto ",
+ _cairo_fixed_to_double (point->x),
+ info->height - _cairo_fixed_to_double (point->y));
+ info->has_current_point = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_path_line_to (void *closure, cairo_point_t *point)
+{
+ ps_output_path_info_t *info = closure;
+ const char *ps_operator;
+
+ if (info->has_current_point)
+ ps_operator = "lineto";
+ else
+ ps_operator = "moveto";
+
+ _cairo_output_stream_printf (info->output_stream,
+ "%f %f %s ",
+ _cairo_fixed_to_double (point->x),
+ info->height - _cairo_fixed_to_double (point->y),
+ ps_operator);
+ info->has_current_point = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_path_curve_to (void *closure,
+ cairo_point_t *b,
+ cairo_point_t *c,
+ cairo_point_t *d)
+{
+ ps_output_path_info_t *info = closure;
+
+ _cairo_output_stream_printf (info->output_stream,
+ "%f %f %f %f %f %f curveto ",
+ _cairo_fixed_to_double (b->x),
+ info->height - _cairo_fixed_to_double (b->y),
+ _cairo_fixed_to_double (c->x),
+ info->height - _cairo_fixed_to_double (c->y),
+ _cairo_fixed_to_double (d->x),
+ info->height - _cairo_fixed_to_double (d->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_path_close_path (void *closure)
+{
+ ps_output_path_info_t *info = closure;
+
+ _cairo_output_stream_printf (info->output_stream,
+ "closepath\n");
+ info->has_current_point = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
-_cairo_ps_surface_get_extents (void *abstract_surface,
- cairo_rectangle_t *rectangle)
+_ps_output_intersect_clip_path (void *abstract_surface,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
{
- cairo_ps_surface_t *surface = abstract_surface;
+ ps_output_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->parent->stream;
+ cairo_status_t status;
+ ps_output_path_info_t info;
+ const char *ps_operator;
+
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_intersect_clip_path\n");
+
+ if (path == NULL) {
+ _cairo_output_stream_printf (stream, "initclip\n");
+ return CAIRO_STATUS_SUCCESS;
+ }
- rectangle->x = 0;
- rectangle->y = 0;
+ info.output_stream = stream;
+ info.has_current_point = FALSE;
+ info.height = surface->parent->height;
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _ps_output_path_move_to,
+ _ps_output_path_line_to,
+ _ps_output_path_curve_to,
+ _ps_output_path_close_path,
+ &info);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ ps_operator = "clip";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ ps_operator = "eoclip";
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_output_stream_printf (stream,
+ "%s newpath\n",
+ ps_operator);
+
+ return status;
+}
- /* XXX: The conversion to integers here is pretty bogus, (not to
- * mention the aribitray limitation of width to a short(!). We
- * may need to come up with a better interface for get_size.
- */
- rectangle->width = (surface->width_in_points + 0.5);
- rectangle->height = (surface->height_in_points + 0.5);
+
+static cairo_int_status_t
+_ps_output_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ void *abstract_surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ ps_output_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->parent->stream;
+ cairo_font_subset_t *subset;
+ int i, subset_index;
+
+ /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */
+ if (! _cairo_scaled_font_is_ft (scaled_font))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (pattern_is_translucent (pattern))
+ return _ps_output_add_fallback_area (surface, dest_x, dest_y, width, height);
+
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_show_glyphs\n");
+
+ emit_pattern (surface->parent, pattern);
+
+ /* FIXME: Need to optimize this so we only do this sequence if the
+ * font isn't already set. */
+
+ subset = _cairo_ps_surface_get_font (surface->parent, scaled_font);
+ _cairo_output_stream_printf (stream,
+ "/f%d findfont\n"
+ "[ %f %f %f %f 0 0 ] makefont\n"
+ "setfont\n",
+ subset->font_id,
+ scaled_font->scale.xx,
+ scaled_font->scale.yx,
+ scaled_font->scale.xy,
+ scaled_font->scale.yy);
+
+ /* FIXME: Need to optimize per glyph code. Should detect when
+ * glyphs share the same baseline and when the spacing corresponds
+ * to the glyph widths. */
+
+ for (i = 0; i < num_glyphs; i++) {
+ subset_index = _cairo_font_subset_use_glyph (subset, glyphs[i].index);
+ _cairo_output_stream_printf (stream,
+ "%f %f moveto (\\%o) show\n",
+ glyphs[i].x,
+ surface->parent->height - glyphs[i].y,
+ subset_index);
+
+ }
return CAIRO_STATUS_SUCCESS;
}
-static const cairo_surface_backend_t cairo_ps_surface_backend = {
- _cairo_ps_surface_create_similar,
- _cairo_ps_surface_finish,
- _cairo_ps_surface_acquire_source_image,
+static cairo_int_status_t
+_ps_output_fill_path (cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
+{
+ ps_output_surface_t *surface = abstract_dst;
+ cairo_output_stream_t *stream = surface->parent->stream;
+ cairo_int_status_t status;
+ ps_output_path_info_t info;
+ const char *ps_operator;
+
+ _cairo_output_stream_printf (stream,
+ "%% _ps_output_fill_path\n");
+
+ emit_pattern (surface->parent, pattern);
+
+ info.output_stream = stream;
+ info.has_current_point = FALSE;
+ info.height = surface->parent->height;
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _ps_output_path_move_to,
+ _ps_output_path_line_to,
+ _ps_output_path_curve_to,
+ _ps_output_path_close_path,
+ &info);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ ps_operator = "fill";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ ps_operator = "eofill";
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_output_stream_printf (stream,
+ "%s\n", ps_operator);
+
+ return status;
+}
+
+static const cairo_surface_backend_t ps_output_backend = {
+ NULL, /* create_similar */
+ _ps_output_finish,
+ NULL, /* acquire_source_image */
NULL, /* release_source_image */
- _cairo_ps_surface_acquire_dest_image,
+ NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
- NULL, /* composite */
- NULL, /* fill_rectangles */
- NULL, /* composite_trapezoids */
- _cairo_ps_surface_copy_page,
- _cairo_ps_surface_show_page,
- _cairo_ps_surface_set_clip_region,
- NULL, /* intersect_clip_path */
- _cairo_ps_surface_get_extents,
- NULL /* show_glyphs */
+ _ps_output_composite,
+ _ps_output_fill_rectangles,
+ _ps_output_composite_trapezoids,
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ NULL, /* set_clip_region */
+ _ps_output_intersect_clip_path,
+ NULL, /* get_extents */
+ _ps_output_show_glyphs,
+ _ps_output_fill_path
};
+
+static cairo_int_status_t
+_ps_output_render_fallbacks (cairo_surface_t *surface,
+ cairo_surface_t *page)
+{
+ ps_output_surface_t *ps_output;
+ cairo_surface_t *image;
+ cairo_int_status_t status;
+ cairo_matrix_t matrix;
+ int width, height;
+
+ ps_output = (ps_output_surface_t *) surface;
+ if (ps_output->fallback_areas == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ width = ps_output->parent->width * ps_output->parent->x_dpi / 72;
+ height = ps_output->parent->height * ps_output->parent->y_dpi / 72;
+
+ image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+ if (image == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ status = _cairo_surface_fill_rectangle (image,
+ CAIRO_OPERATOR_SOURCE,
+ CAIRO_COLOR_WHITE,
+ 0, 0, width, height);
+ if (status)
+ goto bail;
+
+ status = _cairo_meta_surface_replay (page, image);
+ if (status)
+ goto bail;
+
+ matrix.xx = 1;
+ matrix.xy = 0;
+ matrix.yx = 0;
+ matrix.yy = 1;
+ matrix.x0 = 0;
+ matrix.y0 = 0;
+
+ status = emit_image (ps_output->parent,
+ (cairo_image_surface_t *) image, &matrix);
+
+ bail:
+ cairo_surface_destroy (image);
+
+ return status;
+}
+
+static cairo_surface_t *
+_ps_output_surface_create (cairo_ps_surface_t *parent)
+{
+ ps_output_surface_t *ps_output;
+
+ ps_output = malloc (sizeof (ps_output_surface_t));
+ if (ps_output == NULL)
+ return NULL;
+
+ _cairo_surface_init (&ps_output->base, &ps_output_backend);
+ ps_output->parent = parent;
+ ps_output->fallback_areas = NULL;
+
+ return &ps_output->base;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_render_page (cairo_ps_surface_t *surface,
+ cairo_surface_t *page, int page_number)
+{
+ cairo_surface_t *ps_output;
+ cairo_int_status_t status;
+
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%Page: %d\n"
+ "gsave\n",
+ page_number);
+
+ ps_output = _ps_output_surface_create (surface);
+ if (ps_output == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ status = _cairo_meta_surface_replay (page, ps_output);
+
+ _ps_output_render_fallbacks (ps_output, page);
+
+ cairo_surface_destroy (ps_output);
+
+ _cairo_output_stream_printf (surface->stream,
+ "showpage\n"
+ "grestore\n"
+ "%%%%EndPage\n");
+
+ return status;
+}