From 2dfe32a0ff9272ade481796f0f25fd83863c7600 Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Sun, 17 Feb 2008 16:45:15 +1030 Subject: [PATCH] Implement PDF fill-stroke --- src/cairo-meta-surface.c | 15 ++++- src/cairo-pdf-operators-private.h | 8 +++ src/cairo-pdf-operators.c | 51 ++++++++++++++++- src/cairo-pdf-surface.c | 92 +++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 2 deletions(-) diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c index 0fa25443e..152dd1501 100644 --- a/src/cairo-meta-surface.c +++ b/src/cairo-meta-surface.c @@ -744,7 +744,12 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, cairo_command_t *stroke_command; stroke_command = (i < num_elements - 1) ? elements[i + 1] : NULL; - + if (stroke_command != NULL && + type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) + { + if (stroke_command->header.region != region) + stroke_command = NULL; + } if (stroke_command != NULL && stroke_command->header.type == CAIRO_COMMAND_STROKE && _cairo_path_fixed_is_equal (dev_path, _cairo_command_get_path (stroke_command))) { @@ -778,6 +783,14 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, stroke_command->stroke.tolerance * tolerance_multiplier, stroke_command->stroke.antialias); i++; + if (type == CAIRO_META_CREATE_REGIONS) { + if (status == CAIRO_STATUS_SUCCESS) { + stroke_command->header.region = CAIRO_META_REGION_NATIVE; + } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { + stroke_command->header.region = CAIRO_META_REGION_IMAGE_FALLBACK; + status = CAIRO_STATUS_SUCCESS; + } + } } else status = _cairo_surface_fill (target, command->fill.op, diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h index fe4b8427f..5957a8986 100644 --- a/src/cairo-pdf-operators-private.h +++ b/src/cairo-pdf-operators-private.h @@ -97,6 +97,14 @@ _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule); +cairo_int_status_t +_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse); + cairo_private cairo_int_status_t _cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t *pdf_operators, cairo_glyph_t *glyphs, diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index d998619bb..7b66ebbab 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -3,7 +3,7 @@ * * Copyright © 2004 Red Hat, Inc * Copyright © 2006 Red Hat, Inc - * Copyright © 2007 Adrian Johnson + * Copyright © 2007, 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -612,6 +612,55 @@ _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, return _cairo_output_stream_get_status (pdf_operators->stream); } +cairo_int_status_t +_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse) +{ + const char *pdf_operator; + cairo_status_t status; + cairo_matrix_t m; + + status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + if (status) + return status; + + cairo_matrix_multiply (&m, ctm, &pdf_operators->cairo_to_pdf); + _cairo_output_stream_printf (pdf_operators->stream, + "q %f %f %f %f %f %f cm\r\n", + m.xx, m.yx, m.xy, m.yy, + m.x0, m.y0); + + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + ctm_inverse, + style->line_cap); + if (status) + return status; + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + pdf_operator = "B"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + pdf_operator = "B*"; + break; + default: + ASSERT_NOT_REACHED; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "%s Q\r\n", + pdf_operator); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + #define GLYPH_POSITION_TOLERANCE 0.001 cairo_int_status_t diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 26fc8c1ad..822bd432d 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -4455,6 +4455,97 @@ _cairo_pdf_surface_fill (void *abstract_surface, return _cairo_output_stream_get_status (surface->output); } +static cairo_int_status_t +_cairo_pdf_surface_fill_stroke (void *abstract_surface, + cairo_operator_t fill_op, + cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + cairo_pattern_t *stroke_source, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *stroke_ctm, + cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; + + /* During analysis we return unsupported and let the _fill and + * _stroke functions that are on the fallback path do the analysis + * for us. During render we may still encounter unsupported + * combinations of fill/stroke patterns. However we can return + * unsupported anytime to let the _fill and _stroke functions take + * over. + */ + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Fill-stroke with patterns requiring an SMask are not currently + * implemented. Non opaque stroke patterns are not supported + * because the PDF fill-stroke operator does not blend a + * transparent stroke with the fill. + */ + if (fill_source->type == CAIRO_PATTERN_TYPE_LINEAR || + fill_source->type == CAIRO_PATTERN_TYPE_RADIAL) + { + if (!_cairo_pattern_is_opaque (fill_source)) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (!_cairo_pattern_is_opaque (stroke_source)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + fill_pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, + &fill_pattern_res, + &gstate_res); + if (status) + return status; + + assert (gstate_res.id == 0); + + stroke_pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, + stroke_source, + &stroke_pattern_res, + &gstate_res); + if (status) + return status; + + assert (gstate_res.id == 0); + + /* As PDF has separate graphics state for fill and stroke we can + * select both at the same time */ + status = _cairo_pdf_surface_select_pattern (surface, fill_source, + fill_pattern_res, FALSE); + if (status) + return status; + + status = _cairo_pdf_surface_select_pattern (surface, stroke_source, + stroke_pattern_res, TRUE); + if (status) + return status; + + status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, + path, + fill_rule, + stroke_style, + stroke_ctm, + stroke_ctm_inverse); + if (status) + return status; + + _cairo_pdf_surface_unselect_pattern (surface); + + return _cairo_output_stream_get_status (surface->output); +} + static cairo_int_status_t _cairo_pdf_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, @@ -4572,6 +4663,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* is_compatible */ NULL, /* reset */ + _cairo_pdf_surface_fill_stroke, }; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend = {