cairo-ps-surface.c [plain text]
#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>
static const cairo_surface_backend_t cairo_ps_surface_backend;
typedef struct cairo_ps_surface {
cairo_surface_t base;
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;
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_surface_t *
cairo_ps_surface_create (const char *filename,
double width_in_points,
double height_in_points)
{
cairo_output_stream_t *stream;
stream = _cairo_output_stream_create_for_file (filename);
if (stream == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t*) &_cairo_surface_nil;
}
return _cairo_ps_surface_create_for_stream_internal (stream,
width_in_points,
height_in_points);
}
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_output_stream_t *stream;
stream = _cairo_output_stream_create (write_func, closure);
if (stream == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
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;
}
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;
}
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, -1.0);
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;
if (! _cairo_scaled_font_is_ft (scaled_font))
return NULL;
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;
_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);
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);
_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
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;
#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);
}
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;
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 = _cairo_compress_lzw (rgb, rgb_size, &compressed_size);
if (compressed == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto bail2;
}
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);
_cairo_output_stream_write_base85_string (surface->stream, (char *)compressed, compressed_size);
status = CAIRO_STATUS_SUCCESS;
_cairo_output_stream_printf (surface->stream,
"~>\n");
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)
{
}
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)
{
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) {
_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;
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);
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);
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);
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,
_cairo_ps_surface_finish,
NULL,
NULL,
NULL,
NULL,
NULL,
_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,
_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,
#endif
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
_cairo_ps_surface_fill,
NULL
};