cairo/src/cairo-ps-surface.c
Carl Worth dd67cf6616 Implement proper cairo-style error-handling for cairo_output_stream_t.
The cairo_output_stream_t object already had an internal status value,
but it was annoyingly returning status values from various functions.
It also was missing proper shutdown-on-error as well as nil-create
semantics.

This fixes those shortcomings and adjusts all callers for the new
semantics, (leading to simpler and more correct calling
code---particularly in the case of cairo-base85-stream.c).
2006-04-04 10:45:38 -07:00

1348 lines
36 KiB
C

/* 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
* 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):
* Carl D. Worth <cworth@cworth.org>
* Kristian Høgsberg <krh@redhat.com>
* Keith Packard <keithp@keithp.com>
*/
#include "cairoint.h"
#include "cairo-ps.h"
#include "cairo-font-subset-private.h"
#include "cairo-paginated-surface-private.h"
#include "cairo-ft-private.h"
#include <time.h>
#include <zlib.h>
/* TODO:
*
* - Add document structure convention comments where appropriate.
*
* - 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 {
cairo_surface_t base;
/* PS-specific fields */
cairo_output_stream_t *stream;
double width;
double height;
double x_dpi;
double y_dpi;
cairo_bool_t need_start_page;
int num_pages;
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
cairo_array_t fonts;
#endif
} cairo_ps_surface_t;
#define PS_SURFACE_DPI_DEFAULT 300.0
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
static cairo_int_status_t
_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface);
#endif
static void
_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
{
time_t now;
now = time (NULL);
_cairo_output_stream_printf (surface->stream,
"%%!PS-Adobe-3.0\n"
"%%%%Creator: cairo (http://cairographics.org)\n"
"%%%%CreationDate: %s"
"%%%%Pages: (atend)\n"
"%%%%BoundingBox: %f %f %f %f\n",
ctime (&now),
0.0, 0.0,
surface->width,
surface->height);
_cairo_output_stream_printf (surface->stream,
"%%%%DocumentData: Clean7Bit\n"
"%%%%LanguageLevel: 2\n"
"%%%%Orientation: Portrait\n"
"%%%%EndComments\n");
}
static void
_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
{
_cairo_output_stream_printf (surface->stream,
"%%%%Trailer\n"
"%%%%Pages: %d\n"
"%%%%EOF\n",
surface->num_pages);
}
static cairo_surface_t *
_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
double width,
double height)
{
cairo_ps_surface_t *surface;
surface = malloc (sizeof (cairo_ps_surface_t));
if (surface == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t*) &_cairo_surface_nil;
}
_cairo_surface_init (&surface->base, &cairo_ps_surface_backend);
surface->stream = stream;
surface->width = width;
surface->height = height;
surface->x_dpi = PS_SURFACE_DPI_DEFAULT;
surface->y_dpi = PS_SURFACE_DPI_DEFAULT;
#if DONE_ADDING_DEVICE_SCALE_SUPPORT_AFTER_SWITCHING_TO_PAGINATED
surface->base.device_x_scale = surface->x_dpi / 72.0;
surface->base.device_y_scale = surface->y_dpi / 72.0;
#endif
surface->need_start_page = TRUE;
surface->num_pages = 0;
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
_cairo_array_init (&surface->fonts, sizeof (cairo_font_subset_t *));
#endif
_cairo_ps_surface_emit_header (surface);
return _cairo_paginated_surface_create (&surface->base,
CAIRO_CONTENT_COLOR_ALPHA,
width, height);
}
/**
* cairo_ps_surface_create:
* @filename: a filename for the PS output (must be writable)
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PostScript surface of the specified size in points to be
* written to @filename.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
**/
cairo_surface_t *
cairo_ps_surface_create (const char *filename,
double width_in_points,
double height_in_points)
{
cairo_status_t status;
cairo_output_stream_t *stream;
stream = _cairo_output_stream_create_for_file (filename);
status = _cairo_output_stream_get_status (stream);
if (status) {
_cairo_error (status);
return (cairo_surface_t*) &_cairo_surface_nil;
}
return _cairo_ps_surface_create_for_stream_internal (stream,
width_in_points,
height_in_points);
}
/**
* cairo_ps_surface_create_for_stream:
* @write: a #cairo_write_func_t to accept the output data
* @closure: the closure argument for @write
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PostScript surface of the specified size in points to be
* written incrementally to the stream represented by @write and
* @closure.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*/
cairo_surface_t *
cairo_ps_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width_in_points,
double height_in_points)
{
cairo_status_t status;
cairo_output_stream_t *stream;
stream = _cairo_output_stream_create (write_func, NULL, closure);
status = _cairo_output_stream_get_status (stream);
if (status) {
_cairo_error (status);
return (cairo_surface_t*) &_cairo_surface_nil;
}
return _cairo_ps_surface_create_for_stream_internal (stream,
width_in_points,
height_in_points);
}
static cairo_bool_t
_cairo_surface_is_ps (cairo_surface_t *surface)
{
return surface->backend == &cairo_ps_surface_backend;
}
/**
* cairo_ps_surface_set_dpi:
* @surface: a postscript cairo_surface_t
* @x_dpi: horizontal dpi
* @y_dpi: vertical dpi
*
* Set the horizontal and vertical resolution for image fallbacks.
* When the ps backend needs to fall back to image overlays, it will
* use this resolution. These DPI values are not used for any other
* purpose, (in particular, they do not have any bearing on the size
* passed to cairo_ps_surface_create() nor on the CTM).
**/
void
cairo_ps_surface_set_dpi (cairo_surface_t *surface,
double x_dpi,
double y_dpi)
{
cairo_surface_t *target;
cairo_ps_surface_t *ps_surface;
if (! _cairo_surface_is_paginated (surface)) {
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
target = _cairo_paginated_surface_get_target (surface);
if (! _cairo_surface_is_ps (surface)) {
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
ps_surface = (cairo_ps_surface_t *) target;
ps_surface->x_dpi = x_dpi;
ps_surface->y_dpi = y_dpi;
#if DONE_ADDING_DEVICE_SCALE_SUPPORT_AFTER_SWITCHING_TO_PAGINATED
ps_surface->base.device_x_scale = ps_surface->x_dpi / 72.0;
ps_surface->base.device_y_scale = ps_surface->y_dpi / 72.0;
#endif
}
/* XXX */
static cairo_status_t
_cairo_ps_surface_finish (void *abstract_surface)
{
cairo_ps_surface_t *surface = abstract_surface;
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
_cairo_ps_surface_write_font_subsets (surface);
#endif
_cairo_ps_surface_emit_footer (surface);
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
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);
#endif
_cairo_output_stream_destroy (surface->stream);
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_ps_surface_start_page (cairo_ps_surface_t *surface)
{
_cairo_output_stream_printf (surface->stream,
"%%%%Page: %d\n",
++surface->num_pages);
_cairo_output_stream_printf (surface->stream,
"gsave %f %f translate %f %f scale \n",
0.0, surface->height,
1.0/surface->base.device_x_scale,
-1.0/surface->base.device_y_scale);
surface->need_start_page = FALSE;
}
static void
_cairo_ps_surface_end_page (cairo_ps_surface_t *surface)
{
_cairo_output_stream_printf (surface->stream,
"grestore\n");
surface->need_start_page = TRUE;
}
static cairo_int_status_t
_cairo_ps_surface_copy_page (void *abstract_surface)
{
cairo_ps_surface_t *surface = abstract_surface;
_cairo_ps_surface_end_page (surface);
_cairo_output_stream_printf (surface->stream, "copypage\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_ps_surface_show_page (void *abstract_surface)
{
cairo_ps_surface_t *surface = abstract_surface;
_cairo_ps_surface_end_page (surface);
_cairo_output_stream_printf (surface->stream, "showpage\n");
surface->need_start_page = TRUE;
return CAIRO_STATUS_SUCCESS;
}
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
static cairo_font_subset_t *
_cairo_ps_surface_get_font (cairo_ps_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_status_t status;
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;
status = _cairo_array_append (&surface->fonts, &subset);
if (status) {
_cairo_font_subset_destroy (subset);
return NULL;
}
return subset;
}
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);
}
return CAIRO_STATUS_SUCCESS;
}
#endif
/* XXX: This function wil go away in favor of the new "analysis mode"
* of cairo_paginated_surface_t */
static cairo_int_status_t
_cairo_ps_surface_add_fallback_area (cairo_ps_surface_t *surface,
int x, int y,
unsigned int width,
unsigned int height)
{
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
color_is_translucent (const cairo_color_t *color)
{
return color->alpha < 0.999;
}
static cairo_bool_t
format_is_translucent (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_ARGB32:
return TRUE;
case CAIRO_FORMAT_RGB24:
return FALSE;
case CAIRO_FORMAT_A8:
return TRUE;
case CAIRO_FORMAT_A1:
return TRUE;
}
return TRUE;
}
static cairo_bool_t
surface_is_translucent (const cairo_surface_t *surface)
{
if (_cairo_surface_is_image (surface)) {
const cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
return format_is_translucent (image_surface->format);
}
return TRUE;
}
static cairo_bool_t
gradient_is_translucent (const cairo_gradient_pattern_t *gradient)
{
return TRUE; /* XXX no gradient support */
#if 0
int i;
for (i = 0; i < gradient->n_stops; i++)
if (color_is_translucent (&gradient->stops[i].color))
return TRUE;
return FALSE;
#endif
}
static cairo_bool_t
pattern_is_translucent (const cairo_pattern_t *abstract_pattern)
{
const cairo_pattern_union_t *pattern;
pattern = (cairo_pattern_union_t *) abstract_pattern;
switch (pattern->base.type) {
case CAIRO_PATTERN_TYPE_SOLID:
return color_is_translucent (&pattern->solid.color);
case CAIRO_PATTERN_TYPE_SURFACE:
return surface_is_translucent (pattern->surface.surface);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return gradient_is_translucent (&pattern->gradient.base);
}
ASSERT_NOT_REACHED;
return FALSE;
}
static cairo_bool_t
operator_always_opaque (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
return TRUE;
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
return FALSE;
case CAIRO_OPERATOR_DEST:
return TRUE;
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
return FALSE;
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_ADD:
case CAIRO_OPERATOR_SATURATE:
return FALSE;
}
return FALSE;
}
static cairo_bool_t
operator_always_translucent (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
return FALSE;
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
return FALSE;
case CAIRO_OPERATOR_DEST:
return FALSE;
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
return FALSE;
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_ADD:
case CAIRO_OPERATOR_SATURATE:
return TRUE;
}
return TRUE;
}
static cairo_bool_t
color_operation_needs_fallback (cairo_operator_t op,
const cairo_color_t *color)
{
if (operator_always_opaque (op))
return FALSE;
if (operator_always_translucent (op))
return TRUE;
return color_is_translucent (color);
}
static cairo_bool_t
pattern_type_supported (const cairo_pattern_t *pattern)
{
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
return TRUE;
return FALSE;
}
static cairo_bool_t
pattern_operation_needs_fallback (cairo_operator_t op,
const cairo_pattern_t *pattern)
{
if (! pattern_type_supported (pattern))
return TRUE;
if (operator_always_opaque (op))
return FALSE;
if (operator_always_translucent (op))
return TRUE;
return pattern_is_translucent (pattern);
}
/* PS Output - this section handles output of the parts of the meta
* surface we can render natively in PS. */
static cairo_status_t
emit_image (cairo_ps_surface_t *surface,
cairo_image_surface_t *image,
cairo_matrix_t *matrix)
{
cairo_status_t status;
unsigned char *rgb, *compressed;
unsigned long rgb_size, compressed_size;
cairo_surface_t *opaque;
cairo_image_surface_t *opaque_image;
cairo_pattern_union_t pattern;
cairo_matrix_t d2i;
int x, y, i;
cairo_output_stream_t *base85_stream;
/* PostScript can not represent the alpha channel, so we blend the
current image over a white RGB surface to eliminate it. */
if (image->base.status)
return image->base.status;
if (image->format != CAIRO_FORMAT_RGB24) {
opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
image->width,
image->height);
if (opaque->status) {
status = CAIRO_STATUS_NO_MEMORY;
goto bail0;
}
_cairo_pattern_init_for_surface (&pattern.surface, &image->base);
_cairo_surface_fill_rectangle (opaque,
CAIRO_OPERATOR_SOURCE,
CAIRO_COLOR_WHITE,
0, 0, image->width, image->height);
_cairo_surface_composite (CAIRO_OPERATOR_OVER,
&pattern.base,
NULL,
opaque,
0, 0,
0, 0,
0, 0,
image->width,
image->height);
_cairo_pattern_fini (&pattern.base);
opaque_image = (cairo_image_surface_t *) opaque;
} else {
opaque = &image->base;
opaque_image = image;
}
rgb_size = 3 * opaque_image->width * opaque_image->height;
rgb = malloc (rgb_size);
if (rgb == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto bail1;
}
i = 0;
for (y = 0; y < opaque_image->height; y++) {
pixman_bits_t *pixel = (pixman_bits_t *) (opaque_image->data + y * opaque_image->stride);
for (x = 0; x < opaque_image->width; x++, pixel++) {
rgb[i++] = (*pixel & 0x00ff0000) >> 16;
rgb[i++] = (*pixel & 0x0000ff00) >> 8;
rgb[i++] = (*pixel & 0x000000ff) >> 0;
}
}
compressed_size = rgb_size;
compressed = _cairo_lzw_compress (rgb, &compressed_size);
if (compressed == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto bail2;
}
/* 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, 0);
cairo_matrix_multiply (&d2i, &d2i, matrix);
_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 /ASCII85Decode filter /LZWDecode filter \n"
" /ImageMatrix [ %f %f %f %f %f %f ]\n"
">>\n"
"image\n",
opaque_image->width,
opaque_image->height,
d2i.xx, d2i.yx,
d2i.xy, d2i.yy,
d2i.x0, d2i.y0);
/* Compressed image data (Base85 encoded) */
base85_stream = _cairo_base85_stream_create (surface->stream);
_cairo_output_stream_write (base85_stream, compressed, compressed_size);
_cairo_output_stream_close (base85_stream);
status = _cairo_output_stream_get_status (base85_stream);
_cairo_output_stream_destroy (base85_stream);
bail3:
free (compressed);
bail2:
free (rgb);
bail1:
if (opaque_image != image)
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)
{
/* XXX: NYI */
}
static void
emit_linear_pattern (cairo_ps_surface_t *surface,
cairo_linear_pattern_t *pattern)
{
/* XXX: NYI */
}
static void
emit_radial_pattern (cairo_ps_surface_t *surface,
cairo_radial_pattern_t *pattern)
{
/* XXX: NYI */
}
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_TYPE_SOLID:
emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_SURFACE:
emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern);
break;
}
}
static cairo_int_status_t
_cairo_ps_surface_composite (cairo_operator_t op,
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_ps_surface_t *surface = abstract_dst;
cairo_output_stream_t *stream = surface->stream;
cairo_surface_pattern_t *surface_pattern;
cairo_status_t status;
cairo_image_surface_t *image;
void *image_extra;
if (surface->need_start_page)
_cairo_ps_surface_start_page (surface);
if (mask_pattern) {
/* FIXME: Investigate how this can be done... we'll probably
* need pixmap fallbacks for this, though. */
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_composite: with mask\n");
goto bail;
}
status = CAIRO_STATUS_SUCCESS;
switch (src_pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_composite: solid\n");
goto bail;
case CAIRO_PATTERN_TYPE_SURFACE:
surface_pattern = (cairo_surface_pattern_t *) src_pattern;
if (src_pattern->extend != CAIRO_EXTEND_NONE) {
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_composite: repeating image\n");
goto bail;
}
status = _cairo_surface_acquire_source_image (surface_pattern->surface,
&image,
&image_extra);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_composite: src_pattern not available as image\n");
goto bail;
} else if (status) {
break;
}
status = emit_image (surface, image, &src_pattern->matrix);
_cairo_surface_release_source_image (surface_pattern->surface,
image, image_extra);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_composite: gradient\n");
goto bail;
}
return status;
bail:
return _cairo_ps_surface_add_fallback_area (surface, dst_x, dst_y, width, height);
}
static cairo_int_status_t
_cairo_ps_surface_fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_t *rects,
int num_rects)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_solid_pattern_t solid;
int i;
if (!num_rects)
return CAIRO_STATUS_SUCCESS;
if (surface->need_start_page)
_cairo_ps_surface_start_page (surface);
if (color_operation_needs_fallback (op, color)) {
int min_x = rects[0].x;
int min_y = rects[0].y;
int max_x = rects[0].x + rects[0].width;
int max_y = rects[0].y + rects[0].height;
for (i = 1; i < num_rects; i++) {
if (rects[i].x < min_x) min_x = rects[i].x;
if (rects[i].y < min_y) min_y = rects[i].y;
if (rects[i].x + rects[i].width > max_x) max_x = rects[i].x + rects[i].width;
if (rects[i].y + rects[i].height > max_y) max_y = rects[i].y + rects[i].height;
}
return _cairo_ps_surface_add_fallback_area (surface, min_x, min_y, max_x - min_x, max_y - min_y);
}
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_fill_rectangles\n");
_cairo_pattern_init_solid (&solid, color);
emit_pattern (surface, &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, rects[i].y,
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_composite_trapezoids (cairo_operator_t op,
cairo_pattern_t *pattern,
void *abstract_dst,
cairo_antialias_t antialias,
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;
cairo_output_stream_t *stream = surface->stream;
int i;
if (pattern_operation_needs_fallback (op, pattern))
return _cairo_ps_surface_add_fallback_area (surface, x_dst, y_dst, width, height);
if (surface->need_start_page)
_cairo_ps_surface_start_page (surface);
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_composite_trapezoids\n");
emit_pattern (surface, 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 = _cairo_fixed_to_double (traps[i].top);
bottom = _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_STATUS_SUCCESS;
}
typedef struct
{
cairo_output_stream_t *output_stream;
cairo_bool_t has_current_point;
} cairo_ps_surface_path_info_t;
static cairo_status_t
_cairo_ps_surface_path_move_to (void *closure, cairo_point_t *point)
{
cairo_ps_surface_path_info_t *info = closure;
_cairo_output_stream_printf (info->output_stream,
"%f %f moveto ",
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
info->has_current_point = TRUE;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_ps_surface_path_line_to (void *closure, cairo_point_t *point)
{
cairo_ps_surface_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),
_cairo_fixed_to_double (point->y),
ps_operator);
info->has_current_point = TRUE;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_ps_surface_path_curve_to (void *closure,
cairo_point_t *b,
cairo_point_t *c,
cairo_point_t *d)
{
cairo_ps_surface_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),
_cairo_fixed_to_double (b->y),
_cairo_fixed_to_double (c->x),
_cairo_fixed_to_double (c->y),
_cairo_fixed_to_double (d->x),
_cairo_fixed_to_double (d->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_ps_surface_path_close_path (void *closure)
{
cairo_ps_surface_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_intersect_clip_path (void *abstract_surface,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_status_t status;
cairo_ps_surface_path_info_t info;
const char *ps_operator;
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_intersect_clip_path\n");
if (path == NULL) {
_cairo_output_stream_printf (stream, "initclip\n");
return CAIRO_STATUS_SUCCESS;
}
info.output_stream = stream;
info.has_current_point = FALSE;
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_ps_surface_path_move_to,
_cairo_ps_surface_path_line_to,
_cairo_ps_surface_path_curve_to,
_cairo_ps_surface_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;
}
static cairo_int_status_t
_cairo_ps_surface_get_extents (void *abstract_surface,
cairo_rectangle_t *rectangle)
{
cairo_ps_surface_t *surface = abstract_surface;
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_extents.
*/
rectangle->width = (int) ceil (surface->width);
rectangle->height = (int) ceil (surface->height);
return CAIRO_STATUS_SUCCESS;
}
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
static cairo_int_status_t
_cairo_ps_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t op,
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_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_font_subset_t *subset;
int i, subset_index;
if (surface->fallback)
return CAIRO_STATUS_SUCCESS;
if (surface->need_start_page)
_cairo_ps_surface_start_page (surface);
/* 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 (surface->fallback)
return CAIRO_STATUS_SUCCESS;
if (pattern_operation_needs_fallback (op, pattern))
return _cairo_ps_surface_add_fallback_area (surface, dest_x, dest_y, width, height);
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_old_show_glyphs\n");
emit_pattern (surface, 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, 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,
glyphs[i].y,
subset_index);
}
return CAIRO_STATUS_SUCCESS;
}
#endif
static cairo_int_status_t
_cairo_ps_surface_fill (void *abstract_surface,
cairo_operator_t op,
cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_int_status_t status;
cairo_ps_surface_path_info_t info;
const char *ps_operator;
if (pattern_operation_needs_fallback (op, source))
return _cairo_ps_surface_add_fallback_area (surface,
0, 0,
surface->width,
surface->height);
if (surface->need_start_page)
_cairo_ps_surface_start_page (surface);
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_fill\n");
emit_pattern (surface, source);
info.output_stream = stream;
info.has_current_point = FALSE;
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_ps_surface_path_move_to,
_cairo_ps_surface_path_line_to,
_cairo_ps_surface_path_curve_to,
_cairo_ps_surface_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 cairo_ps_surface_backend = {
CAIRO_SURFACE_TYPE_PS,
NULL, /* 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,
#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED
_cairo_ps_surface_old_show_glyphs,
#else
NULL, /* old_show_glyphs */
#endif
NULL, /* get_font_options */
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
/* Here are the drawing functions */
NULL, /* paint */
NULL, /* mask */
NULL, /* stroke */
_cairo_ps_surface_fill,
NULL /* show_glyphs */
};