diff --git a/NEWS b/NEWS index b320af6ea..dd1c9a557 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,129 @@ +Release 1.15.4 (2016-12-9 Bryce Harrington ) +======================================================================= +This new snapshot incorporates changes over the past year since the +1.15.2 snapshot, including all the fixes from the 1.14 release series. + +Of particular note in this snapshot is a wealth of work by Adrian +Johnson to enhance PDF support, as well as numerous bug fixes provided +by him and other contributors. + +For a complete log of changes since the last release, please see: + + http://cairographics.org/releases/ChangeLog.1.15.4 + +Features +-------- +* The PDF backend has gained support for a range of widely used + features, including thumbnails, page labels, metadata, document + outlines, structured text, hyperlinks, and tags. Tags permit adding + logical info such as headings, tables, figures, etc. that facilitates + indexing, accessibility, text reflow, searching, and extraction of the + tagged items to other software. For details on this new PDF + functionality, see: + + https://lists.cairographics.org/archives/cairo/2016-June/027427.html + + +API Changes +----------- + + cairo_win32_surface_create_with_format + + Added a cairo API to set up Win32 surfaces for HDC with alpha channels. + + cairo_pdf_surface_add_outline + cairo_pdf_surface_set_metadata + cairo_pdf_surface_set_page_label + cairo_pdf_surface_set_thumbnail_size + cairo_tag_begin + cairo_tag_end + CAIRO_STATUS_TAG_ERROR + + New API for added PDF functionality (see above), and new error + status item for problems relating to PDF tagging. + + CAIRO_STATUS_WIN32_GDI_ERROR + CAIRO_STATUS_FREETYPE_ERROR + CAIRO_STATUS_PNG_ERROR + + New error status items for handling of GDI, libfreetype, and libpng + errors, respectively. + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Bug fixes from 1.15.2 (see the 1.15.2 NEWS for details) + +* Fix playback of recording surfaces into PDF surfaces, where objects + with negative coordinates were not getting drawn. To address this, + the coordinate systems for PDF and PS have been changed to match + cairo's coordinate system. This allows recording surfaces to be + emitted in cairo coordinates, and results in the same origin being + used for all operations when using the recording surface XObject. + Test cases for PDF and PS have also been updated accordingly. + (Bug #89232) + +* Fix "invalidfont" error on some printers when printing PDFs with + embedded fonts that have glyphs (such as spaces) with + num_contours == 0. (Bug #79897) + +* Fix missing glyphs such as thin dashes, which get scaled to 0 in + userspace and thus have their drawing operations culled. (Bug #94615) + +* Fix other oddities caused by variously idiosyncratic fonts. + +* Fix deadlock when destruction of a scaled font indirectly triggers + destruction of a second scaled font, causing the global cache to be + locked twice. (Bug #93891) + +* Fix X errors reported to applications when shmdt() is called before + the Attach request is processed, due to missing xcb and xlib calls. + +* Fix random failure in record-paint-alpha-clip-mast test case, caused + by an incorrect assumption that a deferred clear can be skipped. + (Bug #84330) + +* Fix crash when dealing with an XShmGetImage() failure, caused by a + double free in _get_image_surface(). (Bug #91967) + +* Fix invalid execution of ASCII85 data by the PS interpreter that the + image operator didn't use, by flushing the extraneous data after + drawing the image. (Bug #84811) + +* Fix decoding of Adobe Photoshop's inverted CMYK JPEG files in PDF + export. + +* Fix unbounded surface assertion in win32-print code. + +* Fix a data race in freed_pool discovered by Firefox's cairo usage. + The patch adads atomic int load and store functions, with relaxed + memory ordering. (Bug #90318) + +* Cleanup debugging text sent to stdout instead of log. (Bug #95227) + +* Fix build issue when using non-GNU strings utility. (Bug #88639) + +* Fix build of cairo modules as regular modules, not as versioned shared + libaries. (Bug #29319) + +* Fix build on win32 using gcc 5.4. + +* Fix build of script backend to require zlib. + +* Update test suite reference images using Debian Jessie 64-bit and + poppler current as of June, 2016. + +* Various improvements to documentation and tests, compiler warning + fixes, and an assortment of code refactoring and cleanup. + + Release 1.15.2 (2015-12-10 Bryce Harrington ) ======================================================================== This release is largely a rollup to include a variety of fixes that @@ -30,7 +156,7 @@ None Bug Fixes --------- -* All the bug fixes from 1.14.2, 1.14.4, and 14.6 +* All the bug fixes from 1.14.2, 1.14.4, and 1.14.6 * Fix xcb/xlib compilation and calls. Make image boxes behave when SHM is not available. @@ -55,7 +181,7 @@ Bug Fixes doing anything. (Bug #90984) -* Improve rendering with Quarts to better match pixman's blending and +* Improve rendering with Quartz to better match pixman's blending and filtering behavior. diff --git a/README b/README index 0be9947d5..7ee2c1878 100644 --- a/README +++ b/README @@ -121,7 +121,7 @@ Supported, "platform" surface backends quartz backend -------------- - MacOS X >= 10.5 with Xcode >= 3.0 + MacOS X >= 10.4 with Xcode >= 2.5 win32 backend ------------- diff --git a/RELEASING b/RELEASING index 0a824f0ea..424902e9b 100644 --- a/RELEASING +++ b/RELEASING @@ -2,10 +2,10 @@ Here are the steps to follow to create a new cairo release: 1) Ensure that there are no local, uncommitted/unpushed mods. - You're probably in a good state if both "git diff - HEAD" and "git log master..origin/master" give no output. Also make - sure you have libglib2.0-doc installed (else you'll get - excessive gtk-doc cross reference warnings in the next step). + You're probably in a good state if both "git diff + HEAD" and "git log master..origin/master" give no output. Also make + sure you have libglib2.0-doc installed (else you'll get + excessive gtk-doc cross reference warnings in the next step). 2) Verify that the code passes "make distcheck" @@ -38,7 +38,7 @@ Here are the steps to follow to create a new cairo release: suite passing, here's the magic env vars to set when doing 'make distcheck' and 'make release-publish' that will let you get away with it. At any cost, never ever release without - (implied) distchecking. Every time we got around it, it turned + (implied) distchecking. Every time we got around it, it turned out to be a disaster. Anyway, here's the pass code: DISPLAY= CAIRO_TEST_TARGET=" " diff --git a/boilerplate/cairo-boilerplate-win32.c b/boilerplate/cairo-boilerplate-win32.c index 4fd0a1044..4b3eda9dd 100644 --- a/boilerplate/cairo-boilerplate-win32.c +++ b/boilerplate/cairo-boilerplate-win32.c @@ -28,6 +28,9 @@ #include +#include +#include + static const cairo_user_data_key_t win32_closure_key; typedef struct _win32_target_closure { diff --git a/build/aclocal.float.m4 b/build/aclocal.float.m4 index 8f85f0862..ca14ea377 100644 --- a/build/aclocal.float.m4 +++ b/build/aclocal.float.m4 @@ -31,10 +31,10 @@ int main() { return 0; } ]])], [ -if strings - conftest$ac_exeext | grep noonsees >/dev/null ; then +if strings -a conftest$ac_exeext | grep noonsees >/dev/null ; then ax_cv_c_float_words_bigendian=yes fi -if strings - conftest$ac_exeext | grep seesnoon >/dev/null ; then +if strings -a conftest$ac_exeext | grep seesnoon >/dev/null ; then if test "$ax_cv_c_float_words_bigendian" = unknown; then ax_cv_c_float_words_bigendian=no else diff --git a/cairo-version.h b/cairo-version.h index feedcc52d..2bd308ce7 100644 --- a/cairo-version.h +++ b/cairo-version.h @@ -3,6 +3,6 @@ #define CAIRO_VERSION_MAJOR 1 #define CAIRO_VERSION_MINOR 15 -#define CAIRO_VERSION_MICRO 2 +#define CAIRO_VERSION_MICRO 5 #endif diff --git a/configure.ac b/configure.ac index 2ce19599e..93953a7f2 100644 --- a/configure.ac +++ b/configure.ac @@ -296,7 +296,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(beos, BeOS/Zeta, no, [ dnl =========================================================================== CAIRO_ENABLE_SURFACE_BACKEND(drm, DRM, no, [ - drm_REQUIRES="libudev >= 136" + drm_REQUIRES="libudev >= 136, libdrm >= 2.4" PKG_CHECK_MODULES(drm, $drm_REQUIRES, , [use_drm="no (requires $drm_REQUIRES, udev is available from git://git.kernel.org/pub/scm/linux/hotplug/udev.git)"]) ]) @@ -470,6 +470,9 @@ dnl =========================================================================== any2ppm_cs=no CAIRO_ENABLE_SURFACE_BACKEND(script, script, yes, [ any2ppm_cs=yes + # The script backend requires zlib. + use_script=$have_libz + script_NONPKGCONFIG_LIBS=-lz ]) dnl =========================================================================== diff --git a/doc/public/cairo-docs.xml b/doc/public/cairo-docs.xml index d54e63be6..73c9813f3 100644 --- a/doc/public/cairo-docs.xml +++ b/doc/public/cairo-docs.xml @@ -18,6 +18,7 @@ + Fonts diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index 4beaa0ae2..7b04ae7b3 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -68,6 +68,9 @@ cairo_image_surface_get_stride
cairo-pdf CAIRO_HAS_PDF_SURFACE +CAIRO_PDF_OUTLINE_ROOT +cairo_pdf_outline_flags_t +cairo_pdf_metadata_t cairo_pdf_surface_create cairo_pdf_surface_create_for_stream cairo_pdf_surface_restrict_to_version @@ -75,6 +78,10 @@ cairo_pdf_version_t cairo_pdf_get_versions cairo_pdf_version_to_string cairo_pdf_surface_set_size +cairo_pdf_surface_add_outline +cairo_pdf_surface_set_metadata +cairo_pdf_surface_set_page_label +cairo_pdf_surface_set_thumbnail_size
@@ -407,6 +414,14 @@ cairo_raster_source_copy_func_t cairo_raster_source_finish_func_t
+
+cairo-tag +CAIRO_TAG_DEST +CAIRO_TAG_LINK +cairo_tag_begin +cairo_tag_end +
+
cairo-matrix cairo_matrix_t diff --git a/src/.gitignore b/src/.gitignore index 32fb7333e..50020169f 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -36,3 +36,7 @@ headers-standalone !cairo-uninstalled.pc.in !cairo-features.pc.in !cairo-features-uninstalled.pc.in +.history +.history/* +.gitignore + diff --git a/src/Makefile.sources b/src/Makefile.sources index fac24d79d..b368f2774 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -279,8 +279,8 @@ _cairo_deflate_stream_sources = cairo-deflate-stream.c cairo_sources += $(_cairo_deflate_stream_sources) cairo_pdf_headers = cairo-pdf.h -cairo_pdf_private = cairo-pdf-surface-private.h -cairo_pdf_sources = cairo-pdf-surface.c +cairo_pdf_private = cairo-pdf-surface-private.h cairo-tag-stack-private.h cairo-tag-attributes-private.h +cairo_pdf_sources = cairo-pdf-surface.c cairo-pdf-interchange.c cairo-tag-stack.c cairo-tag-attributes.c cairo_svg_headers = cairo-svg.h cairo_svg_private = cairo-svg-surface-private.h diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index b4069ea8d..a968f4015 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -139,13 +139,16 @@ detach_proxy (cairo_surface_t *proxy) static cairo_int_status_t _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, - const cairo_pattern_t *pattern) + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents) { const cairo_surface_pattern_t *surface_pattern; cairo_analysis_surface_t *tmp; cairo_surface_t *source, *proxy; cairo_matrix_t p2d; cairo_status_t status, analysis_status; + cairo_bool_t surface_is_unbounded; + cairo_bool_t unused; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; @@ -167,14 +170,39 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, p2d = pattern->matrix; status = cairo_matrix_invert (&p2d); assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm); - tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm); + _cairo_analysis_surface_set_ctm (&tmp->base, &p2d); source = _cairo_surface_get_source (source, NULL); + surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT + || pattern->extend == CAIRO_EXTEND_REFLECT); status = _cairo_recording_surface_replay_and_create_regions (source, - &tmp->base); + &pattern->matrix, + &tmp->base, + surface_is_unbounded); + if (tmp->has_supported) { + surface->has_supported = TRUE; + unused = cairo_region_union (&surface->supported_region, &tmp->supported_region); + } + + if (tmp->has_unsupported) { + surface->has_unsupported = TRUE; + unused = cairo_region_union (&surface->fallback_region, &tmp->fallback_region); + } + analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS; + + if (pattern->extend != CAIRO_EXTEND_NONE) { + _cairo_unbounded_rectangle_init (extents); + } else if (source->content & CAIRO_CONTENT_ALPHA) { + status = cairo_matrix_invert (&tmp->ctm); + _cairo_matrix_transform_bounding_box_fixed (&tmp->ctm, + &tmp->page_bbox, NULL); + _cairo_box_round_to_rectangle (&tmp->page_bbox, extents); + } else { + /* black background fills entire extents */ + _cairo_surface_get_extents (source, extents); + } + detach_proxy (proxy); cairo_surface_destroy (&tmp->base); @@ -344,7 +372,7 @@ _cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; - _cairo_pattern_get_extents (source, &source_extents); + _cairo_pattern_get_extents (source, &source_extents, surface->target->is_vector); _cairo_rectangle_intersect (extents, &source_extents); } @@ -355,7 +383,7 @@ static cairo_int_status_t _cairo_analysis_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - const cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; @@ -371,12 +399,14 @@ _cairo_analysis_surface_paint (void *abstract_surface, return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + cairo_rectangle_int_t rec_extents; + backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + _cairo_rectangle_intersect (&extents, &rec_extents); + } return _add_operation (surface, &extents, backend_status); } @@ -386,7 +416,7 @@ _cairo_analysis_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - const cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; @@ -402,18 +432,24 @@ _cairo_analysis_surface_mask (void *abstract_surface, return backend_status; } + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; + cairo_rectangle_int_t rec_extents; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface; src_surface = _cairo_surface_get_source (src_surface, NULL); if (_cairo_surface_is_recording (src_surface)) { backend_source_status = - _analyze_recording_surface_pattern (surface, source); + _analyze_recording_surface_pattern (surface, source, &rec_extents); if (_cairo_int_status_is_error (backend_source_status)) return backend_source_status; + + _cairo_rectangle_intersect (&extents, &rec_extents); } } @@ -422,9 +458,11 @@ _cairo_analysis_surface_mask (void *abstract_surface, mask_surface = _cairo_surface_get_source (mask_surface, NULL); if (_cairo_surface_is_recording (mask_surface)) { backend_mask_status = - _analyze_recording_surface_pattern (surface, mask); + _analyze_recording_surface_pattern (surface, mask, &rec_extents); if (_cairo_int_status_is_error (backend_mask_status)) return backend_mask_status; + + _cairo_rectangle_intersect (&extents, &rec_extents); } } @@ -433,14 +471,10 @@ _cairo_analysis_surface_mask (void *abstract_surface, backend_mask_status); } - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; - _cairo_pattern_get_extents (mask, &mask_extents); + _cairo_pattern_get_extents (mask, &mask_extents, surface->target->is_vector); _cairo_rectangle_intersect (&extents, &mask_extents); } @@ -448,16 +482,16 @@ _cairo_analysis_surface_mask (void *abstract_surface, } static cairo_int_status_t -_cairo_analysis_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) +_cairo_analysis_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; @@ -476,12 +510,14 @@ _cairo_analysis_surface_stroke (void *abstract_surface, return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + cairo_rectangle_int_t rec_extents; + backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; @@ -508,7 +544,7 @@ _cairo_analysis_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - const cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; @@ -526,12 +562,14 @@ _cairo_analysis_surface_fill (void *abstract_surface, return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + cairo_rectangle_int_t rec_extents; + backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; @@ -588,12 +626,14 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + cairo_rectangle_int_t rec_extents; + backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, @@ -664,12 +704,14 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + cairo_rectangle_int_t rec_extents; + backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, @@ -686,6 +728,39 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, return _add_operation (surface, &extents, backend_status); } +static cairo_int_status_t +_cairo_analysis_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + + backend_status = CAIRO_INT_STATUS_SUCCESS; + if (surface->target->backend->tag != NULL) { + backend_status = + surface->target->backend->tag (surface->target, + begin, + tag_name, + attributes, + source, + stroke_style, + ctm, + ctm_inverse, + clip); + if (_cairo_int_status_is_error (backend_status)) + return backend_status; + } + + return backend_status; +} + static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, @@ -718,7 +793,9 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* fill_stroke */ _cairo_analysis_surface_show_glyphs, _cairo_analysis_surface_has_show_text_glyphs, - _cairo_analysis_surface_show_text_glyphs + _cairo_analysis_surface_show_text_glyphs, + NULL, /* get_supported_mime_types */ + _cairo_analysis_surface_tag }; cairo_surface_t * diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h index 11b2887a1..500129039 100644 --- a/src/cairo-atomic-private.h +++ b/src/cairo-atomic-private.h @@ -75,6 +75,18 @@ _cairo_atomic_int_get (cairo_atomic_int_t *x) return __atomic_load_n(x, __ATOMIC_SEQ_CST); } +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) +{ + return __atomic_load_n(x, __ATOMIC_RELAXED); +} + +static cairo_always_inline void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) +{ + __atomic_store_n(x, val, __ATOMIC_RELAXED); +} + static cairo_always_inline void * _cairo_atomic_ptr_get (void **x) { @@ -157,6 +169,18 @@ _cairo_atomic_int_get (cairo_atomic_int_t *x) return *x; } +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) +{ + return *x; +} + +static cairo_always_inline void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) +{ + *x = val; +} + static cairo_always_inline void * _cairo_atomic_ptr_get (void **x) { @@ -165,6 +189,8 @@ _cairo_atomic_ptr_get (void **x) } #else # define _cairo_atomic_int_get(x) (*x) +# define _cairo_atomic_int_get_relaxed(x) (*x) +# define _cairo_atomic_int_set_relaxed(x, val) (*x) = (val) # define _cairo_atomic_ptr_get(x) (*x) #endif @@ -200,6 +226,8 @@ typedef long long cairo_atomic_intptr_t; typedef AO_t cairo_atomic_int_t; # define _cairo_atomic_int_get(x) (AO_load_full (x)) +# define _cairo_atomic_int_get_relaxed(x) (AO_load_full (x)) +# define _cairo_atomic_int_set_relaxed(x, val) (AO_store_full ((x), (val))) # define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) # define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_full(x)) @@ -230,6 +258,8 @@ typedef unsigned long long cairo_atomic_intptr_t; typedef int32_t cairo_atomic_int_t; # define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x)) +# define _cairo_atomic_int_get_relaxed(x) *(x) +# define _cairo_atomic_int_set_relaxed(x, val) *(x) = (val) # define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x)) # define _cairo_atomic_int_dec(x) ((void) OSAtomicDecrement32Barrier (x)) @@ -288,9 +318,15 @@ _cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv); #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER cairo_private cairo_atomic_int_t _cairo_atomic_int_get (cairo_atomic_int_t *x); +cairo_private cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x); +void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val); # define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x) #else # define _cairo_atomic_int_get(x) (*x) +# define _cairo_atomic_int_get_relaxed(x) (*x) +# define _cairo_atomic_int_set_relaxed(x, val) (*x) = (val) # define _cairo_atomic_ptr_get(x) (*x) #endif diff --git a/src/cairo-atomic.c b/src/cairo-atomic.c index 909cfea49..2af50cd38 100644 --- a/src/cairo-atomic.c +++ b/src/cairo-atomic.c @@ -101,6 +101,20 @@ _cairo_atomic_int_get (cairo_atomic_intptr_t *x) return ret; } + +cairo_atomic_intptr_t +_cairo_atomic_int_get_relaxed (cairo_atomic_intptr_t *x) +{ + return _cairo_atomic_int_get (x); +} + +void +_cairo_atomic_int_set_relaxed (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t val) +{ + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + *x = val; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); +} #endif #endif diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h index b05eca59a..67607c12f 100644 --- a/src/cairo-backend-private.h +++ b/src/cairo-backend-private.h @@ -172,6 +172,9 @@ struct _cairo_backend { cairo_status_t (*copy_page) (void *cr); cairo_status_t (*show_page) (void *cr); + + cairo_status_t (*tag_begin) (void *cr, const char *tag_name, const char *attributes); + cairo_status_t (*tag_end) (void *cr, const char *tag_name); }; static inline void diff --git a/src/cairo-botor-scan-converter.c b/src/cairo-botor-scan-converter.c index 515305bf2..64883a6fa 100644 --- a/src/cairo-botor-scan-converter.c +++ b/src/cairo-botor-scan-converter.c @@ -456,7 +456,7 @@ edges_compare_x_for_y (const cairo_edge_t *a, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; + int32_t ax = 0, bx = 0; /* XXX given we have x and dx? */ @@ -2128,6 +2128,42 @@ botor_add_edge (cairo_botor_scan_converter_t *self, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_botor_scan_converter_add_edge (void *converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + cairo_botor_scan_converter_t *self = converter; + cairo_edge_t edge; + + edge.line.p1 = *p1; + edge.line.p2 = *p2; + edge.top = top; + edge.bottom = bottom; + edge.dir = dir; + + return botor_add_edge (self, &edge); +} + +cairo_status_t +_cairo_botor_scan_converter_add_polygon (cairo_botor_scan_converter_t *converter, + const cairo_polygon_t *polygon) +{ + cairo_botor_scan_converter_t *self = converter; + cairo_status_t status; + int i; + + for (i = 0; i < polygon->num_edges; i++) { + status = botor_add_edge (self, &polygon->edges[i]); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + static void _cairo_botor_scan_converter_destroy (void *converter) { diff --git a/src/cairo-box-inline.h b/src/cairo-box-inline.h index d6b994127..59e5a0d5f 100644 --- a/src/cairo-box-inline.h +++ b/src/cairo-box-inline.h @@ -57,6 +57,16 @@ _cairo_box_from_integers (cairo_box_t *box, int x, int y, int w, int h) box->p2.y = _cairo_fixed_from_int (y + h); } +static inline void +_cairo_box_from_rectangle_int (cairo_box_t *box, + const cairo_rectangle_int_t *rect) +{ + box->p1.x = _cairo_fixed_from_int (rect->x); + box->p1.y = _cairo_fixed_from_int (rect->y); + box->p2.x = _cairo_fixed_from_int (rect->x + rect->width); + box->p2.y = _cairo_fixed_from_int (rect->y + rect->height); +} + /* assumes box->p1 is top-left, p2 bottom-right */ static inline void _cairo_box_add_point (cairo_box_t *box, diff --git a/src/cairo-boxes-private.h b/src/cairo-boxes-private.h index d1f9dfcd1..fd622aec3 100644 --- a/src/cairo-boxes-private.h +++ b/src/cairo-boxes-private.h @@ -92,8 +92,7 @@ _cairo_boxes_extents (const cairo_boxes_t *boxes, cairo_private cairo_box_t * _cairo_boxes_to_array (const cairo_boxes_t *boxes, - int *num_boxes, - cairo_bool_t force_allocation); + int *num_boxes); cairo_private cairo_status_t _cairo_boxes_intersect (const cairo_boxes_t *a, diff --git a/src/cairo-boxes.c b/src/cairo-boxes.c index 63b68ddfb..6ddf81a82 100644 --- a/src/cairo-boxes.c +++ b/src/cairo-boxes.c @@ -102,6 +102,16 @@ _cairo_boxes_init_for_array (cairo_boxes_t *boxes, boxes->is_pixel_aligned = n == num_boxes; } +/** + * Computes the minimum bounding box of the given list of boxes and assign + * it to the given boxes set. It also assigns that list as the list of + * limiting boxes in the box set. + * + * @param boxes the box set to be filled (return buffer) + * @param limits array of the limiting boxes to compute the bounding + * box from + * @param num_limits length of the limits array + */ void _cairo_boxes_limit (cairo_boxes_t *boxes, const cairo_box_t *limits, @@ -265,6 +275,13 @@ _cairo_boxes_add (cairo_boxes_t *boxes, return boxes->status; } +/** + * Computes the minimum bounding box of the given box set and stores + * it in the given box. + * + * @param boxes the box set whose minimum bounding is computed + * @param box return buffer for the computed result + */ void _cairo_boxes_extents (const cairo_boxes_t *boxes, cairo_box_t *box) @@ -317,18 +334,24 @@ _cairo_boxes_clear (cairo_boxes_t *boxes) boxes->is_pixel_aligned = TRUE; } +/** + * Linearize a box set of possibly multiple chunks into one big chunk + * and returns an array of boxes + * + * @param boxes the box set to be converted + * @param num_boxes return buffer for the number of boxes (array count) + * @return pointer to the newly allocated array of boxes + * (the number o elements is given in num_boxes) + * */ cairo_box_t * _cairo_boxes_to_array (const cairo_boxes_t *boxes, - int *num_boxes, - cairo_bool_t force_allocation) + int *num_boxes) { const struct _cairo_boxes_chunk *chunk; cairo_box_t *box; int i, j; *num_boxes = boxes->num_boxes; - if (boxes->chunks.next == NULL && ! force_allocation) - return boxes->chunks.base; box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t)); if (box == NULL) { diff --git a/src/cairo-clip-boxes.c b/src/cairo-clip-boxes.c index 7bcbeb191..1d33cc892 100644 --- a/src/cairo-clip-boxes.c +++ b/src/cairo-clip-boxes.c @@ -119,11 +119,7 @@ _cairo_clip_contains_rectangle (const cairo_clip_t *clip, { cairo_box_t box; - box.p1.x = _cairo_fixed_from_int (rect->x); - box.p1.y = _cairo_fixed_from_int (rect->y); - box.p2.x = _cairo_fixed_from_int (rect->x + rect->width); - box.p2.y = _cairo_fixed_from_int (rect->y + rect->height); - + _cairo_box_from_rectangle_int (&box, rect); return _cairo_clip_contains_rectangle_box (clip, rect, &box); } @@ -268,6 +264,35 @@ _cairo_clip_intersect_box (cairo_clip_t *clip, return _cairo_clip_intersect_rectangle_box (clip, &r, box); } +/** + * copy a box set to an clip + * + * @param box the box set to copy from + * @param clip the clip to copy to (return buffer) + * @result zero if the allocation failed - the clip will be set to all-clipped + * otherwise non-zero + */ +static cairo_bool_t +_cairo_boxes_copy_to_clip (const cairo_boxes_t *boxes, cairo_clip_t *clip) +{ + /* XXX cow-boxes? */ + if (boxes->num_boxes == 1) { + clip->boxes = &clip->embedded_box; + clip->boxes[0] = boxes->chunks.base[0]; + clip->num_boxes = 1; + return TRUE; + } + + clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes); + if (unlikely (clip->boxes == NULL)) + { + _cairo_clip_set_all_clipped (clip); + return FALSE; + } + + return TRUE; +} + cairo_clip_t * _cairo_clip_intersect_boxes (cairo_clip_t *clip, const cairo_boxes_t *boxes) @@ -305,13 +330,10 @@ _cairo_clip_intersect_boxes (cairo_clip_t *clip, if (boxes->num_boxes == 0) { clip = _cairo_clip_set_all_clipped (clip); goto out; - } else if (boxes->num_boxes == 1) { - clip->boxes = &clip->embedded_box; - clip->boxes[0] = boxes->chunks.base[0]; - clip->num_boxes = 1; - } else { - clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE); } + + _cairo_boxes_copy_to_clip (boxes, clip); + _cairo_boxes_extents (boxes, &limits); _cairo_box_round_to_rectangle (&limits, &extents); @@ -347,10 +369,7 @@ _cairo_clip_intersect_rectangle (cairo_clip_t *clip, if (r->width == 0 || r->height == 0) return _cairo_clip_set_all_clipped (clip); - box.p1.x = _cairo_fixed_from_int (r->x); - box.p1.y = _cairo_fixed_from_int (r->y); - box.p2.x = _cairo_fixed_from_int (r->x + r->width); - box.p2.y = _cairo_fixed_from_int (r->y + r->height); + _cairo_box_from_rectangle_int (&box, r); return _cairo_clip_intersect_rectangle_box (clip, r, &box); } @@ -581,16 +600,8 @@ _cairo_clip_from_boxes (const cairo_boxes_t *boxes) if (clip == NULL) return _cairo_clip_set_all_clipped (clip); - /* XXX cow-boxes? */ - if(boxes->num_boxes == 1) { - clip->boxes = &clip->embedded_box; - clip->boxes[0] = boxes->chunks.base[0]; - clip->num_boxes = 1; - } else { - clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE); - if (clip->boxes == NULL) - return _cairo_clip_set_all_clipped (clip); - } + if (unlikely (! _cairo_boxes_copy_to_clip (boxes, clip))) + return clip; _cairo_boxes_extents (boxes, &extents); _cairo_box_round_to_rectangle (&extents, &clip->extents); diff --git a/src/cairo-composite-rectangles.c b/src/cairo-composite-rectangles.c index 495d20070..f102eddbc 100644 --- a/src/cairo-composite-rectangles.c +++ b/src/cairo-composite-rectangles.c @@ -97,7 +97,8 @@ _cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents, _cairo_composite_reduce_pattern (source, &extents->source_pattern); _cairo_pattern_get_extents (&extents->source_pattern.base, - &extents->source); + &extents->source, + surface->is_vector); if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) { if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source)) return FALSE; @@ -146,10 +147,8 @@ static cairo_int_status_t _cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents, const cairo_clip_t *clip) { - cairo_bool_t ret; - - ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask); - if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) + if ((!_cairo_rectangle_intersect (&extents->bounded, &extents->mask)) && + (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { @@ -318,7 +317,7 @@ _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t cairo_int_status_t _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, - cairo_surface_t*surface, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, @@ -332,7 +331,7 @@ _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents extents->original_mask_pattern = mask; _cairo_composite_reduce_pattern (mask, &extents->mask_pattern); - _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask); + _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask, surface->is_vector); return _cairo_composite_rectangles_intersect (extents, clip); } diff --git a/src/cairo-debug.c b/src/cairo-debug.c index 33d46aa3f..10933a673 100644 --- a/src/cairo-debug.c +++ b/src/cairo-debug.c @@ -237,7 +237,7 @@ _print_close (void *closure) } void -_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) +_cairo_debug_print_path (FILE *stream, const cairo_path_fixed_t *path) { cairo_status_t status; cairo_box_t box; @@ -262,7 +262,7 @@ _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) box.p1.x, box.p1.y, box.p2.x, box.p2.y); } - printf ("\n"); + fprintf (stream, "\n"); } void @@ -302,3 +302,20 @@ _cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon) } } + +void +_cairo_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix) +{ + fprintf (file, "[%g %g %g %g %g %g]\n", + matrix->xx, matrix->yx, + matrix->xy, matrix->yy, + matrix->x0, matrix->y0); +} + +void +_cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect) +{ + fprintf (file, "x: %d y: %d width: %d height: %d\n", + rect->x, rect->y, + rect->width, rect->height); +} diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c index 1e5067bf1..694eecf78 100644 --- a/src/cairo-default-context.c +++ b/src/cairo-default-context.c @@ -1155,6 +1155,24 @@ _cairo_default_context_copy_page (void *abstract_cr) return _cairo_gstate_copy_page (cr->gstate); } +static cairo_status_t +_cairo_default_context_tag_begin (void *abstract_cr, + const char *tag_name, const char *attributes) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_tag_begin (cr->gstate, tag_name, attributes); +} + +static cairo_status_t +_cairo_default_context_tag_end (void *abstract_cr, + const char *tag_name) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_tag_end (cr->gstate, tag_name); +} + static cairo_status_t _cairo_default_context_show_page (void *abstract_cr) { @@ -1437,6 +1455,9 @@ static const cairo_backend_t _cairo_default_context_backend = { _cairo_default_context_copy_page, _cairo_default_context_show_page, + + _cairo_default_context_tag_begin, + _cairo_default_context_tag_end, }; cairo_status_t diff --git a/src/cairo-device.c b/src/cairo-device.c index 585a9c1c4..965c84c65 100644 --- a/src/cairo-device.c +++ b/src/cairo-device.c @@ -159,6 +159,10 @@ _cairo_device_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: case CAIRO_STATUS_DEVICE_FINISHED: case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_device_t *) &_nil_device; @@ -190,8 +194,8 @@ _cairo_device_init (cairo_device_t *device, * @device from being destroyed until a matching call to * cairo_device_destroy() is made. * - * The number of references to a #cairo_device_t can be get using - * cairo_device_get_reference_count(). + * Use cairo_device_get_reference_count() to get the number of references + * to a #cairo_device_t. * * Return value: the referenced #cairo_device_t. * diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h index 178078ad6..1ab57ddf8 100644 --- a/src/cairo-error-private.h +++ b/src/cairo-error-private.h @@ -94,6 +94,10 @@ enum _cairo_int_status { CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION, CAIRO_INT_STATUS_DEVICE_FINISHED, CAIRO_INT_STATUS_JBIG2_GLOBAL_MISSING, + CAIRO_INT_STATUS_PNG_ERROR, + CAIRO_INT_STATUS_FREETYPE_ERROR, + CAIRO_INT_STATUS_WIN32_GDI_ERROR, + CAIRO_INT_STATUS_TAG_ERROR, CAIRO_INT_STATUS_LAST_STATUS, diff --git a/src/cairo-font-face.c b/src/cairo-font-face.c index 795951b14..e10a6eac2 100644 --- a/src/cairo-font-face.c +++ b/src/cairo-font-face.c @@ -111,8 +111,8 @@ _cairo_font_face_init (cairo_font_face_t *font_face, * @font_face from being destroyed until a matching call to * cairo_font_face_destroy() is made. * - * The number of references to a #cairo_font_face_t can be get using - * cairo_font_face_get_reference_count(). + * Use cairo_font_face_get_reference_count() to get the number of + * references to a #cairo_font_face_t. * * Return value: the referenced #cairo_font_face_t. * diff --git a/src/cairo-freed-pool-private.h b/src/cairo-freed-pool-private.h index 0ec6de3d1..8a7af523d 100644 --- a/src/cairo-freed-pool-private.h +++ b/src/cairo-freed-pool-private.h @@ -51,7 +51,7 @@ CAIRO_BEGIN_DECLS #define MAX_FREED_POOL_SIZE 16 typedef struct { void *pool[MAX_FREED_POOL_SIZE]; - int top; + cairo_atomic_int_t top; } freed_pool_t; static cairo_always_inline void * @@ -81,13 +81,13 @@ _freed_pool_get (freed_pool_t *pool) void *ptr; int i; - i = pool->top - 1; + i = _cairo_atomic_int_get_relaxed (&pool->top) - 1; if (i < 0) i = 0; ptr = _atomic_fetch (&pool->pool[i]); if (likely (ptr != NULL)) { - pool->top = i; + _cairo_atomic_int_set_relaxed (&pool->top, i); return ptr; } @@ -103,11 +103,11 @@ _freed_pool_put (freed_pool_t *pool, void *ptr) { int i; - i = pool->top; + i = _cairo_atomic_int_get_relaxed (&pool->top); if (likely (i < ARRAY_LENGTH (pool->pool) && _atomic_store (&pool->pool[i], ptr))) { - pool->top = i + 1; + _cairo_atomic_int_set_relaxed (&pool->top, i + 1); return; } diff --git a/src/cairo-freed-pool.c b/src/cairo-freed-pool.c index cfdc8e96b..5b1c4c0bb 100644 --- a/src/cairo-freed-pool.c +++ b/src/cairo-freed-pool.c @@ -50,13 +50,13 @@ _freed_pool_get_search (freed_pool_t *pool) for (i = ARRAY_LENGTH (pool->pool); i--;) { ptr = _atomic_fetch (&pool->pool[i]); if (ptr != NULL) { - pool->top = i; + _cairo_atomic_int_set_relaxed (&pool->top, i); return ptr; } } /* empty */ - pool->top = 0; + _cairo_atomic_int_set_relaxed (&pool->top, 0); return NULL; } @@ -67,13 +67,13 @@ _freed_pool_put_search (freed_pool_t *pool, void *ptr) for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { if (_atomic_store (&pool->pool[i], ptr)) { - pool->top = i + 1; + _cairo_atomic_int_set_relaxed (&pool->top, i + 1); return; } } /* full */ - pool->top = i; + _cairo_atomic_int_set_relaxed (&pool->top, i); free (ptr); } @@ -87,7 +87,7 @@ _freed_pool_reset (freed_pool_t *pool) pool->pool[i] = NULL; } - pool->top = 0; + _cairo_atomic_int_set_relaxed (&pool->top, 0); } #endif diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 3e485c5b7..74d43cc2d 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -223,7 +223,10 @@ _ft_to_cairo_error (FT_Error error) * Populate as needed. */ switch (error) { - default: return CAIRO_STATUS_NO_MEMORY; + case FT_Err_Out_Of_Memory: + return CAIRO_STATUS_NO_MEMORY; + default: + return CAIRO_STATUS_FREETYPE_ERROR; } } @@ -2205,6 +2208,55 @@ _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font, } } +static cairo_int_status_t +_cairo_ft_scaled_glyph_load_glyph (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + int load_flags, + cairo_bool_t use_em_size, + cairo_bool_t vertical_layout) +{ + FT_Error error; + cairo_status_t status; + + if (use_em_size) { + cairo_matrix_t em_size; + cairo_matrix_init_scale (&em_size, face->units_per_EM, face->units_per_EM); + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, &em_size); + } else { + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, + &scaled_font->base.scale); + } + if (unlikely (status)) + return status; + + error = FT_Load_Glyph (face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags); + /* XXX ignoring all other errors for now. They are not fatal, typically + * just a glyph-not-found. */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* + * synthesize glyphs if requested + */ +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) + FT_GlyphSlot_Embolden (face->glyph); +#endif + +#if HAVE_FT_GLYPHSLOT_OBLIQUE + if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE) + FT_GlyphSlot_Oblique (face->glyph); +#endif + + if (vertical_layout) + _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, face->glyph); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_ft_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, @@ -2215,22 +2267,17 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_GlyphSlot glyph; FT_Face face; - FT_Error error; int load_flags = scaled_font->ft_options.load_flags; FT_Glyph_Metrics *metrics; double x_factor, y_factor; cairo_bool_t vertical_layout = FALSE; - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_bool_t scaled_glyph_loaded = FALSE; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, - &scaled_font->base.scale); - if (unlikely (status)) - goto FAIL; - /* Ignore global advance unconditionally */ load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; @@ -2261,37 +2308,23 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, /* load_flags |= FT_LOAD_COLOR; */ #endif - error = FT_Load_Glyph (face, - _cairo_scaled_glyph_index(scaled_glyph), - load_flags); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - glyph = face->glyph; - /* - * synthesize glyphs if requested - */ -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) - FT_GlyphSlot_Embolden (glyph); -#endif + if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { -#if HAVE_FT_GLYPHSLOT_OBLIQUE - if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE) - FT_GlyphSlot_Oblique (glyph); -#endif + cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; - if (vertical_layout) - _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags, + !hint_metrics, + vertical_layout); + if (unlikely (status)) + goto FAIL; - if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { + glyph = face->glyph; + scaled_glyph_loaded = hint_metrics; - cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; /* * Compute font-space metrics */ @@ -2389,6 +2422,20 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { cairo_image_surface_t *surface; + if (!scaled_glyph_loaded) { + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags, + FALSE, + vertical_layout); + if (unlikely (status)) + goto FAIL; + + glyph = face->glyph; + scaled_glyph_loaded = TRUE; + } + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { status = _render_glyph_outline (face, &scaled_font->ft_options.base, &surface); @@ -2420,27 +2467,23 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, * so reload it. This will probably never occur though */ if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { - error = FT_Load_Glyph (face, - _cairo_scaled_glyph_index(scaled_glyph), - load_flags | FT_LOAD_NO_BITMAP); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + scaled_glyph_loaded = FALSE; + load_flags |= FT_LOAD_NO_BITMAP; + } + + if (!scaled_glyph_loaded) { + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags, + FALSE, + vertical_layout); + if (unlikely (status)) goto FAIL; - } -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) - FT_GlyphSlot_Embolden (glyph); -#endif -#if HAVE_FT_GLYPHSLOT_OBLIQUE - if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE) - FT_GlyphSlot_Oblique (glyph); -#endif - if (vertical_layout) - _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); + glyph = face->glyph; } + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, &path); diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c index 5b1411472..a95712e1c 100644 --- a/src/cairo-gl-composite.c +++ b/src/cairo-gl-composite.c @@ -174,7 +174,7 @@ _cairo_gl_texture_set_extend (cairo_gl_context_t *ctx, switch (extend) { case CAIRO_EXTEND_NONE: - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) wrap_mode = GL_CLAMP_TO_EDGE; else wrap_mode = GL_CLAMP_TO_BORDER; diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c index 7235d9ae1..c6aba430a 100644 --- a/src/cairo-gl-device.c +++ b/src/cairo-gl-device.c @@ -171,7 +171,7 @@ test_can_read_bgra (cairo_gl_flavor_t gl_flavor) if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) return TRUE; - assert (gl_flavor == CAIRO_GL_FLAVOR_ES); + assert (gl_flavor == CAIRO_GL_FLAVOR_ES2); /* For OpenGL ES we have to look for the specific extension and BGRA only * matches cairo's integer packed bytes on little-endian machines. */ @@ -190,7 +190,7 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx) int n; cairo_bool_t is_desktop = gl_flavor == CAIRO_GL_FLAVOR_DESKTOP; - cairo_bool_t is_gles = gl_flavor == CAIRO_GL_FLAVOR_ES; + cairo_bool_t is_gles = gl_flavor == CAIRO_GL_FLAVOR_ES2; _cairo_device_init (&ctx->base, &_cairo_gl_device_backend); @@ -402,7 +402,7 @@ _cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx, does not require an explicit multisample resolution. */ #if CAIRO_HAS_GLESV2_SURFACE if (surface->supports_msaa && _cairo_gl_msaa_compositor_enabled () && - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { _cairo_gl_ensure_msaa_gles_framebuffer (ctx, surface); } else #endif @@ -509,7 +509,7 @@ _cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx, #endif #if CAIRO_HAS_GLESV2_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, @@ -712,7 +712,7 @@ _cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx, if (_cairo_gl_surface_is_texture (surface)) { /* OpenGL ES surfaces only have either a multisample framebuffer or a * singlesample framebuffer, so we cannot switch back and forth. */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { _cairo_gl_ensure_framebuffer (ctx, surface); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); return; @@ -749,7 +749,7 @@ _cairo_gl_context_set_destination (cairo_gl_context_t *ctx, /* The decision whether or not to use multisampling happens when * we create an OpenGL ES surface, so we can never switch modes. */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) multisampling = surface->msaa_active; changing_surface = ctx->current_target != surface || surface->needs_update; diff --git a/src/cairo-gl-dispatch.c b/src/cairo-gl-dispatch.c index 76c3115ba..3e3721922 100644 --- a/src/cairo-gl-dispatch.c +++ b/src/cairo-gl-dispatch.c @@ -124,7 +124,7 @@ _cairo_gl_dispatch_init_buffers (cairo_gl_dispatch_t *dispatch, else return CAIRO_STATUS_DEVICE_ERROR; } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES && + else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 && gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) { dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; @@ -156,7 +156,7 @@ _cairo_gl_dispatch_init_shaders (cairo_gl_dispatch_t *dispatch, else return CAIRO_STATUS_DEVICE_ERROR; } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES && + else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 && gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) { dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; @@ -189,7 +189,7 @@ _cairo_gl_dispatch_init_fbo (cairo_gl_dispatch_t *dispatch, else return CAIRO_STATUS_DEVICE_ERROR; } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES && + else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 && gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) { dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; @@ -214,7 +214,7 @@ _cairo_gl_dispatch_init_multisampling (cairo_gl_dispatch_t *dispatch, /* For the multisampling table, there are two GLES versions of the * extension, so we put one in the EXT slot and one in the real ES slot.*/ cairo_gl_dispatch_name_t dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - if (gl_flavor == CAIRO_GL_FLAVOR_ES) { + if (gl_flavor == CAIRO_GL_FLAVOR_ES2) { if (_cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture")) dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; else if (_cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture")) diff --git a/src/cairo-gl-gradient.c b/src/cairo-gl-gradient.c index ac3292101..db82c23d8 100644 --- a/src/cairo-gl-gradient.c +++ b/src/cairo-gl-gradient.c @@ -282,7 +282,7 @@ _cairo_gl_gradient_create (cairo_gl_context_t *ctx, * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat' * must match 'format' in glTexImage2D. */ - if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES) + if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) internal_format = GL_BGRA; else internal_format = GL_RGBA; diff --git a/src/cairo-gl-info.c b/src/cairo-gl-info.c index acefbb910..39541aa6f 100644 --- a/src/cairo-gl-info.c +++ b/src/cairo-gl-info.c @@ -65,8 +65,8 @@ _cairo_gl_get_flavor (void) if (version == NULL) flavor = CAIRO_GL_FLAVOR_NONE; - else if (strstr (version, "OpenGL ES") != NULL) - flavor = CAIRO_GL_FLAVOR_ES; + else if (strstr (version, "OpenGL ES 2") != NULL) + flavor = CAIRO_GL_FLAVOR_ES2; else flavor = CAIRO_GL_FLAVOR_DESKTOP; diff --git a/src/cairo-gl-msaa-compositor.c b/src/cairo-gl-msaa-compositor.c index 4904b9a84..507459de3 100644 --- a/src/cairo-gl-msaa-compositor.c +++ b/src/cairo-gl-msaa-compositor.c @@ -280,7 +280,7 @@ can_use_msaa_compositor (cairo_gl_surface_t *surface, /* Multisampling OpenGL ES surfaces only maintain one multisampling framebuffer and thus must use the spans compositor to do non-antialiased rendering. */ - if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES + if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES2 && surface->supports_msaa && antialias == CAIRO_ANTIALIAS_NONE) return FALSE; diff --git a/src/cairo-gl-operand.c b/src/cairo-gl-operand.c index 1d1465a0b..ca1fa4b6b 100644 --- a/src/cairo-gl-operand.c +++ b/src/cairo-gl-operand.c @@ -658,7 +658,7 @@ _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled, * these shaders need the texture dimensions for their calculations. */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE && _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR) { diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index cb915c8cf..091e095fc 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -103,7 +103,7 @@ typedef struct _cairo_gl_surface cairo_gl_surface_t; typedef enum cairo_gl_flavor { CAIRO_GL_FLAVOR_NONE = 0, CAIRO_GL_FLAVOR_DESKTOP = 1, - CAIRO_GL_FLAVOR_ES = 2 + CAIRO_GL_FLAVOR_ES2 = 2 } cairo_gl_flavor_t; /* Indices for vertex attributes used by BindAttribLocation etc */ diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index fe975d2d7..aceb5d255 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -398,7 +398,7 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, "vec4 get_%s()\n" "{\n", rectstr, namestr, namestr, namestr, namestr); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, @@ -425,7 +425,7 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, "vec4 get_%s()\n" "{\n", namestr, namestr, rectstr, namestr, namestr); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, @@ -462,7 +462,7 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, " float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n", namestr, namestr, rectstr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, @@ -507,7 +507,7 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, " float upper_t = mix (t.y, t.x, is_valid.x);\n", namestr, namestr, rectstr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, @@ -674,7 +674,7 @@ cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE); _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { if (_cairo_gl_shader_needs_border_fade (src)) _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE); if (_cairo_gl_shader_needs_border_fade (mask)) diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index 5950fbbaf..e5e8205d5 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -922,7 +922,7 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, * alignment constraint */ if (src->stride < 0 || - (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && + (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && (src->width * cpp < src->stride - 3 || width != src->width))) { @@ -1099,7 +1099,7 @@ _cairo_gl_surface_map_to_image (void *abstract_surface, return NULL; } - if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) { + if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES2) { /* If only RGBA is supported, we must download data in a compatible * format. This means that pixman will convert the data on the CPU when * interacting with other image surfaces. For ALPHA, GLES2 does not @@ -1319,7 +1319,7 @@ _cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface) return CAIRO_INT_STATUS_SUCCESS; /* GLES surfaces do not need explicit resolution. */ - if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES) + if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES2) return CAIRO_INT_STATUS_SUCCESS; if (! _cairo_gl_surface_is_texture (surface)) diff --git a/src/cairo-gl-traps-compositor.c b/src/cairo-gl-traps-compositor.c index 125ed4eab..664a27a41 100644 --- a/src/cairo-gl-traps-compositor.c +++ b/src/cairo-gl-traps-compositor.c @@ -304,7 +304,7 @@ traps_to_operand (void *_dst, } /* GLES2 only supports RGB/RGBA when uploading */ - if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES) { + if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) { cairo_surface_pattern_t pattern; cairo_surface_t *rgba_image; diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h index b2ccc76d1..198c66998 100644 --- a/src/cairo-gstate-private.h +++ b/src/cairo-gstate-private.h @@ -323,6 +323,15 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, double width, double height); +cairo_private cairo_status_t +_cairo_gstate_tag_begin (cairo_gstate_t *gstate, + const char *tag_name, + const char *attributes); + +cairo_private cairo_status_t +_cairo_gstate_tag_end (cairo_gstate_t *gstate, + const char *tag_name); + cairo_private cairo_status_t _cairo_gstate_set_font_size (cairo_gstate_t *gstate, double size); diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 4c7eb11ce..95a4ccb5b 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1646,6 +1646,65 @@ _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) return list; } +cairo_status_t +_cairo_gstate_tag_begin (cairo_gstate_t *gstate, + const char *tag_name, const char *attributes) +{ + cairo_pattern_union_t source_pattern; + cairo_stroke_style_t style; + double dash[2]; + cairo_status_t status; + cairo_matrix_t aggregate_transform; + cairo_matrix_t aggregate_transform_inverse; + + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; + + cairo_matrix_multiply (&aggregate_transform, + &gstate->ctm, + &gstate->target->device_transform); + cairo_matrix_multiply (&aggregate_transform_inverse, + &gstate->target->device_transform_inverse, + &gstate->ctm_inverse); + + memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style)); + if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &aggregate_transform, gstate->tolerance)) { + style.dash = dash; + _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance, + &style.dash_offset, + style.dash, + &style.num_dashes); + } + + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + + return _cairo_surface_tag (gstate->target, + TRUE, /* begin */ + tag_name, + attributes ? attributes : "", + &source_pattern.base, + &style, + &aggregate_transform, + &aggregate_transform_inverse, + gstate->clip); +} + +cairo_status_t +_cairo_gstate_tag_end (cairo_gstate_t *gstate, + const char *tag_name) +{ + return _cairo_surface_tag (gstate->target, + FALSE, /* begin */ + tag_name, + NULL, /* attributes */ + NULL, /* source */ + NULL, /* stroke_style */ + NULL, /* ctm */ + NULL, /* ctm_inverse*/ + NULL); /* clip */ +} + static void _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) { diff --git a/src/cairo-image-info-private.h b/src/cairo-image-info-private.h index e64928e40..99cbbcc02 100644 --- a/src/cairo-image-info-private.h +++ b/src/cairo-image-info-private.h @@ -43,6 +43,7 @@ typedef struct _cairo_image_info { int height; int num_components; int bits_per_component; + int is_adobe_jpeg; } cairo_image_info_t; cairo_private cairo_int_status_t diff --git a/src/cairo-image-info.c b/src/cairo-image-info.c index 26e7ae5af..2ecce954a 100644 --- a/src/cairo-image-info.c +++ b/src/cairo-image-info.c @@ -67,6 +67,9 @@ #define SOF14 0xce #define SOF15 0xcf +/* Start of tag markers */ +#define APP14 0xee /* Adobe */ + static const unsigned char * _jpeg_skip_segment (const unsigned char *p) { @@ -94,6 +97,8 @@ _cairo_image_info_get_jpeg_info (cairo_image_info_t *info, { const unsigned char *p = data; + info->is_adobe_jpeg = FALSE; + while (p + 1 < data + length) { if (*p != 0xff) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -131,6 +136,18 @@ _cairo_image_info_get_jpeg_info (cairo_image_info_t *info, _jpeg_extract_info (info, p); return CAIRO_STATUS_SUCCESS; + case APP14: + /* "Adobe" tags segment indicates inverted CMYK (in + * CMYK images). */ + if (p + 12 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + info->is_adobe_jpeg = + (0 == strncmp((const char *)(p + 3), "Adobe", 5)); + + p = _jpeg_skip_segment(p); + break; + default: if (*p >= RST_begin && *p <= RST_end) { p++; @@ -206,6 +223,7 @@ _jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) info->width = get_unaligned_be32 (p + 4); info->num_components = (p[8] << 8) + p[9]; info->bits_per_component = p[10]; + info->is_adobe_jpeg = FALSE; } cairo_int_status_t @@ -283,6 +301,8 @@ _cairo_image_info_get_png_info (cairo_image_info_t *info, p += 4; info->height = get_unaligned_be32 (p); + info->is_adobe_jpeg = FALSE; + return CAIRO_STATUS_SUCCESS; } @@ -395,6 +415,7 @@ _jbig2_extract_info (cairo_image_info_t *info, const unsigned char *p) info->height = get_unaligned_be32 (p + 4); info->num_components = 1; info->bits_per_component = 1; + info->is_adobe_jpeg = FALSE; } cairo_int_status_t diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c index 4b79db965..3e3ca2814 100644 --- a/src/cairo-image-source.c +++ b/src/cairo-image-source.c @@ -1113,10 +1113,12 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, { cairo_surface_t *source, *clone, *proxy; cairo_rectangle_int_t limit; + cairo_rectangle_int_t src_limit; pixman_image_t *pixman_image; cairo_status_t status; cairo_extend_t extend; cairo_matrix_t *m, matrix; + double sx = 1.0, sy = 1.0; int tx = 0, ty = 0; TRACE ((stderr, "%s\n", __FUNCTION__)); @@ -1124,34 +1126,38 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, *ix = *iy = 0; source = _cairo_pattern_get_source (pattern, &limit); + src_limit = limit; extend = pattern->base.extend; if (_cairo_rectangle_contains_rectangle (&limit, sample)) extend = CAIRO_EXTEND_NONE; + if (extend == CAIRO_EXTEND_NONE) { if (! _cairo_rectangle_intersect (&limit, sample)) return _pixman_transparent_image (); + } - if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { - double x1, y1, x2, y2; - - matrix = pattern->base.matrix; - status = cairo_matrix_invert (&matrix); - assert (status == CAIRO_STATUS_SUCCESS); - - x1 = limit.x; - y1 = limit.y; - x2 = limit.x + limit.width; - y2 = limit.y + limit.height; - - _cairo_matrix_transform_bounding_box (&matrix, - &x1, &y1, &x2, &y2, NULL); + if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { + double x1, y1, x2, y2; - limit.x = floor (x1); - limit.y = floor (y1); - limit.width = ceil (x2) - limit.x; - limit.height = ceil (y2) - limit.y; - } + matrix = pattern->base.matrix; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + x1 = limit.x; + y1 = limit.y; + x2 = limit.x + limit.width; + y2 = limit.y + limit.height; + + _cairo_matrix_transform_bounding_box (&matrix, + &x1, &y1, &x2, &y2, NULL); + + limit.x = floor (x1); + limit.y = floor (y1); + limit.width = ceil (x2) - limit.x; + limit.height = ceil (y2) - limit.y; + sx = (double)src_limit.width / limit.width; + sy = (double)src_limit.height / limit.height; } tx = limit.x; ty = limit.y; @@ -1183,7 +1189,9 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, cairo_matrix_translate (&matrix, tx, ty); m = &matrix; } else { - /* XXX extract scale factor for repeating patterns */ + cairo_matrix_init_scale (&matrix, sx, sy); + cairo_matrix_translate (&matrix, src_limit.x/sx, src_limit.y/sy); + m = &matrix; } /* Handle recursion by returning future reads from the current image */ @@ -1199,11 +1207,22 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image); cairo_surface_destroy (clone); - *ix = -limit.x; - *iy = -limit.y; - if (extend != CAIRO_EXTEND_NONE) { + if (extend == CAIRO_EXTEND_NONE) { + *ix = -limit.x; + *iy = -limit.y; + } else { + cairo_pattern_union_t tmp_pattern; + _cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base); + matrix = pattern->base.matrix; + status = cairo_matrix_invert(&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_translate (&matrix, src_limit.x, src_limit.y); + cairo_matrix_scale (&matrix, sx, sy); + status = cairo_matrix_invert(&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_pattern_set_matrix (&tmp_pattern.base, &matrix); if (! _pixman_image_set_properties (pixman_image, - &pattern->base, extents, + &tmp_pattern.base, extents, ix, iy)) { pixman_image_unref (pixman_image); pixman_image= NULL; diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 13d6272a8..1571c549a 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -52,6 +52,7 @@ #include "cairo-recording-surface-private.h" #include "cairo-region-private.h" #include "cairo-scaled-font-private.h" +#include "cairo-surface-snapshot-inline.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" @@ -1153,79 +1154,87 @@ _cairo_image_surface_create_from_image (cairo_image_surface_t *other, return (cairo_image_surface_t *) _cairo_surface_create_in_error (status); } -cairo_image_transparency_t -_cairo_image_analyze_transparency (cairo_image_surface_t *image) +static cairo_image_transparency_t +_cairo_image_compute_transparency (cairo_image_surface_t *image) { int x, y; - - if (image->transparency != CAIRO_IMAGE_UNKNOWN) - return image->transparency; + cairo_image_transparency_t transparency; if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) - return image->transparency = CAIRO_IMAGE_IS_OPAQUE; + return CAIRO_IMAGE_IS_OPAQUE; if (image->base.is_clear) - return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + return CAIRO_IMAGE_HAS_BILEVEL_ALPHA; if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) { if (image->format == CAIRO_FORMAT_A1) { - return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + return CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } else if (image->format == CAIRO_FORMAT_A8) { for (y = 0; y < image->height; y++) { uint8_t *alpha = (uint8_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, alpha++) { if (*alpha > 0 && *alpha < 255) - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + return CAIRO_IMAGE_HAS_ALPHA; } } - return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + return CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } else { - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + return CAIRO_IMAGE_HAS_ALPHA; } } if (image->format == CAIRO_FORMAT_RGB16_565) { - image->transparency = CAIRO_IMAGE_IS_OPAQUE; return CAIRO_IMAGE_IS_OPAQUE; } if (image->format != CAIRO_FORMAT_ARGB32) - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + return CAIRO_IMAGE_HAS_ALPHA; - image->transparency = CAIRO_IMAGE_IS_OPAQUE; + transparency = CAIRO_IMAGE_IS_OPAQUE; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, pixel++) { int a = (*pixel & 0xff000000) >> 24; if (a > 0 && a < 255) { - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + return CAIRO_IMAGE_HAS_ALPHA; } else if (a == 0) { - image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } } } - return image->transparency; + return transparency; } -cairo_image_color_t -_cairo_image_analyze_color (cairo_image_surface_t *image) +cairo_image_transparency_t +_cairo_image_analyze_transparency (cairo_image_surface_t *image) { - int x, y; + if (_cairo_surface_is_snapshot (&image->base)) { + if (image->transparency == CAIRO_IMAGE_UNKNOWN) + image->transparency = _cairo_image_compute_transparency (image); - if (image->color != CAIRO_IMAGE_UNKNOWN_COLOR) - return image->color; + return image->transparency; + } + + return _cairo_image_compute_transparency (image); +} + +static cairo_image_color_t +_cairo_image_compute_color (cairo_image_surface_t *image) +{ + int x, y; + cairo_image_color_t color; if (image->format == CAIRO_FORMAT_A1) - return image->color = CAIRO_IMAGE_IS_MONOCHROME; + return CAIRO_IMAGE_IS_MONOCHROME; if (image->format == CAIRO_FORMAT_A8) - return image->color = CAIRO_IMAGE_IS_GRAYSCALE; + return CAIRO_IMAGE_IS_GRAYSCALE; if (image->format == CAIRO_FORMAT_ARGB32) { - image->color = CAIRO_IMAGE_IS_MONOCHROME; + color = CAIRO_IMAGE_IS_MONOCHROME; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); @@ -1242,16 +1251,16 @@ _cairo_image_analyze_color (cairo_image_surface_t *image) b = (b * 255 + a / 2) / a; } if (!(r == g && g == b)) - return image->color = CAIRO_IMAGE_IS_COLOR; + return CAIRO_IMAGE_IS_COLOR; else if (r > 0 && r < 255) - image->color = CAIRO_IMAGE_IS_GRAYSCALE; + color = CAIRO_IMAGE_IS_GRAYSCALE; } } - return image->color; + return color; } if (image->format == CAIRO_FORMAT_RGB24) { - image->color = CAIRO_IMAGE_IS_MONOCHROME; + color = CAIRO_IMAGE_IS_MONOCHROME; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); @@ -1260,15 +1269,28 @@ _cairo_image_analyze_color (cairo_image_surface_t *image) int g = (*pixel & 0x0000ff00) >> 8; int b = (*pixel & 0x000000ff); if (!(r == g && g == b)) - return image->color = CAIRO_IMAGE_IS_COLOR; + return CAIRO_IMAGE_IS_COLOR; else if (r > 0 && r < 255) - image->color = CAIRO_IMAGE_IS_GRAYSCALE; + color = CAIRO_IMAGE_IS_GRAYSCALE; } } + return color; + } + + return CAIRO_IMAGE_IS_COLOR; +} + +cairo_image_color_t +_cairo_image_analyze_color (cairo_image_surface_t *image) +{ + if (_cairo_surface_is_snapshot (&image->base)) { + if (image->color == CAIRO_IMAGE_UNKNOWN_COLOR) + image->color = _cairo_image_compute_color (image); + return image->color; } - return image->color = CAIRO_IMAGE_IS_COLOR; + return _cairo_image_compute_color (image); } cairo_image_surface_t * diff --git a/src/cairo-line.c b/src/cairo-line.c index cb13927bd..dc00a9cb1 100644 --- a/src/cairo-line.c +++ b/src/cairo-line.c @@ -110,9 +110,9 @@ lines_compare_x_for_y_general (const cairo_line_t *a, * should prevent that before the tessellation algorithm * begins. */ - int32_t dx; - int32_t adx, ady; - int32_t bdx, bdy; + int32_t dx = 0; + int32_t adx = 0, ady = 0; + int32_t bdx = 0, bdy = 0; enum { HAVE_NONE = 0x0, HAVE_DX = 0x1, @@ -218,7 +218,7 @@ lines_compare_x_for_y (const cairo_line_t *a, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; + int32_t ax = 0, bx = 0; if (y == a->p1.y) ax = a->p1.x; diff --git a/src/cairo-misc.c b/src/cairo-misc.c index 3c7c95986..e9b0ab6be 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -49,7 +49,7 @@ COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); * @Title: Error handling * @Short_Description: Decoding cairo's status * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(), - * cairo_font_face_status(), cairo_scaled_font_status(), + * cairo_font_face_status(), cairo_scaled_font_status(), * cairo_region_status() * * Cairo uses a single status type to represent all kinds of errors. A status @@ -158,6 +158,14 @@ cairo_status_to_string (cairo_status_t status) return "the target device has been finished"; case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: return "CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID used but no CAIRO_MIME_TYPE_JBIG2_GLOBAL data provided"; + case CAIRO_STATUS_PNG_ERROR: + return "error occurred in libpng while reading from or writing to a PNG file"; + case CAIRO_STATUS_FREETYPE_ERROR: + return "error occurred in libfreetype"; + case CAIRO_STATUS_WIN32_GDI_ERROR: + return "error occurred in the Windows Graphics Device Interface"; + case CAIRO_STATUS_TAG_ERROR: + return "invalid tag name, attributes, or nesting"; default: case CAIRO_STATUS_LAST_STATUS: return ""; diff --git a/src/cairo-paginated-private.h b/src/cairo-paginated-private.h index b827faba0..b85a5db6b 100644 --- a/src/cairo-paginated-private.h +++ b/src/cairo-paginated-private.h @@ -56,7 +56,7 @@ struct _cairo_paginated_surface_backend { * CAIRO_PAGINATED_MODE_RENDER. See more details in the * documentation for _cairo_paginated_surface_create below. */ - void + cairo_warn cairo_int_status_t (*set_paginated_mode) (void *surface, cairo_paginated_mode_t mode); @@ -77,7 +77,23 @@ struct _cairo_paginated_surface_backend { cairo_bool_t fallbacks_required); cairo_bool_t - (*supports_fine_grained_fallbacks) (void *surface); + (*supports_fine_grained_fallbacks) (void *surface); + + /* Optional. Indicates whether the page requires a thumbnail image to be + * supplied. If a thumbnail is required, set width, heigh to size required + * and return TRUE. + */ + cairo_bool_t + (*requires_thumbnail_image) (void *surface, + int *width, + int *height); + + /* If thumbbail image requested, this function will be called before + * _show_page(). + */ + cairo_warn cairo_int_status_t + (*set_thumbnail_image) (void *surface, + cairo_image_surface_t *image); }; /* A #cairo_paginated_surface_t provides a very convenient wrapper that diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index b81215a7c..e9454d416 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -290,6 +290,63 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } +static cairo_int_status_t +_paint_thumbnail_image (cairo_paginated_surface_t *surface, + int width, + int height) +{ + cairo_surface_pattern_t pattern; + cairo_rectangle_int_t extents; + double x_scale; + double y_scale; + cairo_surface_t *image = NULL; + cairo_surface_t *opaque = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + _cairo_surface_get_extents (surface->target, &extents); + x_scale = (double)width / extents.width; + y_scale = (double)height / extents.height; + + image = _cairo_paginated_surface_create_image_surface (surface, width, height); + cairo_surface_set_device_scale (image, x_scale, y_scale); + cairo_surface_set_device_offset (image, -extents.x*x_scale, -extents.y*y_scale); + status = _cairo_recording_surface_replay (surface->recording_surface, image); + if (unlikely (status)) + goto cleanup; + + /* flatten transparency */ + + opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + if (unlikely (opaque->status)) { + status = opaque->status; + goto cleanup; + } + + status = _cairo_surface_paint (opaque, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + NULL); + if (unlikely (status)) + goto cleanup; + + _cairo_pattern_init_for_surface (&pattern, image); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto cleanup; + + status = surface->backend->set_thumbnail_image (surface->target, (cairo_image_surface_t *)opaque); + + cleanup: + if (image) + cairo_surface_destroy (image); + if (opaque) + cairo_surface_destroy (opaque); + + return status; +} + static cairo_int_status_t _paint_fallback_image (cairo_paginated_surface_t *surface, cairo_rectangle_int_t *rect) @@ -352,10 +409,13 @@ _paint_page (cairo_paginated_surface_t *surface) if (unlikely (analysis->status)) return _cairo_surface_set_error (surface->target, analysis->status); - surface->backend->set_paginated_mode (surface->target, + status = surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE); + if (unlikely (status)) + goto FAIL; + status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, - analysis); + NULL, analysis, FALSE); if (status) goto FAIL; @@ -401,8 +461,10 @@ _paint_page (cairo_paginated_surface_t *surface) } if (has_supported) { - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_RENDER); + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_RENDER); + if (unlikely (status)) + goto FAIL; status = _cairo_recording_surface_replay_region (surface->recording_surface, NULL, @@ -417,8 +479,10 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_rectangle_int_t extents; cairo_bool_t is_bounded; - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_FALLBACK); + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_FALLBACK); + if (unlikely (status)) + goto FAIL; is_bounded = _cairo_surface_get_extents (surface->target, &extents); if (! is_bounded) { @@ -435,8 +499,10 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_region_t *region; int num_rects, i; - surface->backend->set_paginated_mode (surface->target, + status = surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); + if (unlikely (status)) + goto FAIL; region = _cairo_analysis_surface_get_unsupported (analysis); @@ -451,6 +517,13 @@ _paint_page (cairo_paginated_surface_t *surface) } } + if (surface->backend->requires_thumbnail_image) { + int width, height; + + if (surface->backend->requires_thumbnail_image (surface->target, &width, &height)) + _paint_thumbnail_image (surface, width, height); + } + FAIL: cairo_surface_destroy (analysis); @@ -660,6 +733,26 @@ _cairo_paginated_surface_get_supported_mime_types (void *abstract_surface) return NULL; } +static cairo_int_status_t +_cairo_paginated_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_tag (surface->recording_surface, + begin, tag_name, attributes, + source, style, + ctm, ctm_inverse, + clip); +} + static cairo_surface_t * _cairo_paginated_surface_snapshot (void *abstract_other) { @@ -714,4 +807,5 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { _cairo_paginated_surface_has_show_text_glyphs, _cairo_paginated_surface_show_text_glyphs, _cairo_paginated_surface_get_supported_mime_types, + _cairo_paginated_surface_tag, }; diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h index be8ab9fc2..ad7e2e773 100644 --- a/src/cairo-pattern-private.h +++ b/src/cairo-pattern-private.h @@ -296,7 +296,8 @@ _cairo_pattern_sampled_area (const cairo_pattern_t *pattern, cairo_private void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents); + cairo_rectangle_int_t *extents, + cairo_bool_t is_vector); cairo_private cairo_int_status_t _cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index b4bc83c0e..7c2d5d315 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -357,6 +357,7 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, /* The reference count and user_data array are unique to the copy. */ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); + cairo_list_init (&pattern->observers); return CAIRO_STATUS_SUCCESS; } @@ -396,6 +397,7 @@ _cairo_pattern_init_static_copy (cairo_pattern_t *pattern, CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); + cairo_list_init (&pattern->observers); } cairo_status_t @@ -1045,8 +1047,8 @@ cairo_pattern_create_mesh (void) * @pattern from being destroyed until a matching call to * cairo_pattern_destroy() is made. * - * The number of references to a #cairo_pattern_t can be get using - * cairo_pattern_get_reference_count(). + * Use cairo_pattern_get_reference_count() to get the number of + * references to a #cairo_pattern_t. * * Return value: the referenced #cairo_pattern_t. * @@ -1071,8 +1073,8 @@ slim_hidden_def (cairo_pattern_reference); * cairo_pattern_get_type: * @pattern: a #cairo_pattern_t * - * This function returns the type a pattern. - * See #cairo_pattern_type_t for available types. + * Get the pattern's type. See #cairo_pattern_type_t for available + * types. * * Return value: The type of @pattern. * @@ -3522,13 +3524,17 @@ _cairo_pattern_sampled_area (const cairo_pattern_t *pattern, * For unbounded patterns, the @extents will be initialized with * "infinite" extents, (minimum and maximum fixed-point values). * + * When is_vector is TRUE, avoid rounding to zero widths or heights that + * are less than 1 unit. + * * XXX: Currently, bounded gradient patterns will also return * "infinite" extents, though it would be possible to optimize these * with a little more work. **/ void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents) + cairo_rectangle_int_t *extents, + cairo_bool_t is_vector) { double x1, y1, x2, y2; int ix1, ix2, iy1, iy2; @@ -3731,6 +3737,8 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, else ix2 = _cairo_lround (x2); extents->x = ix1; extents->width = ix2 - ix1; + if (is_vector && extents->width == 0 && x1 != x2) + extents->width += 1; if (!round_y) { y1 -= 0.5; @@ -3745,6 +3753,8 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, else iy2 = _cairo_lround (y2); extents->y = iy1; extents->height = iy2 - iy1; + if (is_vector && extents->height == 0 && y1 != y2) + extents->height += 1; return; @@ -3796,7 +3806,7 @@ _cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, } } - _cairo_pattern_get_extents (pattern, extents); + _cairo_pattern_get_extents (pattern, extents, TRUE); return CAIRO_STATUS_SUCCESS; } @@ -4607,14 +4617,43 @@ static void _cairo_debug_print_surface_pattern (FILE *file, const cairo_surface_pattern_t *pattern) { - printf (" surface type: %d\n", pattern->surface->type); + const char *s; + switch (pattern->surface->type) { + case CAIRO_SURFACE_TYPE_IMAGE: s = "image"; break; + case CAIRO_SURFACE_TYPE_PDF: s = "pdf"; break; + case CAIRO_SURFACE_TYPE_PS: s = "ps"; break; + case CAIRO_SURFACE_TYPE_XLIB: s = "xlib"; break; + case CAIRO_SURFACE_TYPE_XCB: s = "xcb"; break; + case CAIRO_SURFACE_TYPE_GLITZ: s = "glitz"; break; + case CAIRO_SURFACE_TYPE_QUARTZ: s = "quartz"; break; + case CAIRO_SURFACE_TYPE_WIN32: s = "win32"; break; + case CAIRO_SURFACE_TYPE_BEOS: s = "beos"; break; + case CAIRO_SURFACE_TYPE_DIRECTFB: s = "directfb"; break; + case CAIRO_SURFACE_TYPE_SVG: s = "svg"; break; + case CAIRO_SURFACE_TYPE_OS2: s = "os2"; break; + case CAIRO_SURFACE_TYPE_WIN32_PRINTING: s = "win32_printing"; break; + case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: s = "quartz_image"; break; + case CAIRO_SURFACE_TYPE_SCRIPT: s = "script"; break; + case CAIRO_SURFACE_TYPE_QT: s = "qt"; break; + case CAIRO_SURFACE_TYPE_RECORDING: s = "recording"; break; + case CAIRO_SURFACE_TYPE_VG: s = "vg"; break; + case CAIRO_SURFACE_TYPE_GL: s = "gl"; break; + case CAIRO_SURFACE_TYPE_DRM: s = "drm"; break; + case CAIRO_SURFACE_TYPE_TEE: s = "tee"; break; + case CAIRO_SURFACE_TYPE_XML: s = "xml"; break; + case CAIRO_SURFACE_TYPE_SKIA: s = "skia"; break; + case CAIRO_SURFACE_TYPE_SUBSURFACE: s = "subsurface"; break; + case CAIRO_SURFACE_TYPE_COGL: s = "cogl"; break; + default: s = "invalid"; ASSERT_NOT_REACHED; break; + } + fprintf (file, " surface type: %s\n", s); } static void _cairo_debug_print_raster_source_pattern (FILE *file, const cairo_raster_source_pattern_t *raster) { - printf (" content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height); + fprintf (file, " content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height); } static void diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c new file mode 100644 index 000000000..bac62d530 --- /dev/null +++ b/src/cairo-pdf-interchange.c @@ -0,0 +1,1473 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson + */ + + +/* PDF Document Interchange features: + * - metadata + * - document outline + * - tagged pdf + * - hyperlinks + * - page labels + */ + +#include "cairoint.h" + +#include "cairo-pdf.h" +#include "cairo-pdf-surface-private.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +static void +write_rect_to_pdf_quad_points (cairo_output_stream_t *stream, + const cairo_rectangle_t *rect, + double surface_height) +{ + _cairo_output_stream_printf (stream, + "%f %f %f %f %f %f %f %f", + rect->x, + surface_height - rect->y, + rect->x + rect->width, + surface_height - rect->y, + rect->x + rect->width, + surface_height - (rect->y + rect->height), + rect->x, + surface_height - (rect->y + rect->height)); +} + +static void +write_rect_int_to_pdf_bbox (cairo_output_stream_t *stream, + const cairo_rectangle_int_t *rect, + double surface_height) +{ + _cairo_output_stream_printf (stream, + "%d %f %d %f", + rect->x, + surface_height - (rect->y + rect->height), + rect->x + rect->width, + surface_height - rect->y); +} + +static cairo_int_status_t +add_tree_node (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *parent, + const char *name, + cairo_pdf_struct_tree_node_t **new_node) +{ + cairo_pdf_struct_tree_node_t *node; + + node = malloc (sizeof(cairo_pdf_struct_tree_node_t)); + if (unlikely (node == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + node->name = strdup (name); + node->res = _cairo_pdf_surface_new_object (surface); + if (node->res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + node->parent = parent; + cairo_list_init (&node->children); + _cairo_array_init (&node->mcid, sizeof(struct page_mcid)); + memset (&node->annot, 0, sizeof(node->annot)); + cairo_list_init (&node->children); + + cairo_list_add_tail (&node->link, &parent->children); + + *new_node = node; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +is_leaf_node (cairo_pdf_struct_tree_node_t *node) +{ + return node->parent && cairo_list_is_empty (&node->children) ; +} + +static void +free_node (cairo_pdf_struct_tree_node_t *node) +{ + cairo_pdf_struct_tree_node_t *child, *next; + + if (!node) + return; + + cairo_list_foreach_entry_safe (child, next, cairo_pdf_struct_tree_node_t, + &node->children, link) + { + cairo_list_del (&child->link); + free_node (child); + } + free (node->name); + _cairo_array_fini (&node->mcid); + free (node); +} + +static cairo_status_t +add_mcid_to_node (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + int page, + int *mcid) +{ + struct page_mcid mcid_elem; + cairo_int_status_t status; + cairo_pdf_interchange_t *ic = &surface->interchange; + + status = _cairo_array_append (&ic->mcid_to_tree, &node); + if (unlikely (status)) + return status; + + mcid_elem.page = page; + mcid_elem.mcid = _cairo_array_num_elements (&ic->mcid_to_tree) - 1; + *mcid = mcid_elem.mcid; + return _cairo_array_append (&node->mcid, &mcid_elem); +} + +static cairo_int_status_t +cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node) +{ + struct page_mcid *mcid_elem; + int i, num_mcid, first_page; + cairo_pdf_resource_t *page_res; + cairo_pdf_struct_tree_node_t *child; + + _cairo_pdf_surface_update_object (surface, node->res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /StructElem\n" + " /S /%s\n" + " /P %d 0 R\n", + node->res.id, + node->name, + node->parent->res.id); + + if (! cairo_list_is_empty (&node->children)) { + if (cairo_list_is_singular (&node->children)) { + child = cairo_list_first_entry (&node->children, cairo_pdf_struct_tree_node_t, link); + _cairo_output_stream_printf (surface->output, " /K %d 0 R\n", child->res.id); + } else { + _cairo_output_stream_printf (surface->output, " /K [ "); + + cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, + &node->children, link) + { + _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id); + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + } else { + num_mcid = _cairo_array_num_elements (&node->mcid); + if (num_mcid > 0 ) { + mcid_elem = _cairo_array_index (&node->mcid, 0); + first_page = mcid_elem->page; + page_res = _cairo_array_index (&surface->pages, first_page - 1); + _cairo_output_stream_printf (surface->output, " /Pg %d 0 R\n", page_res->id); + + if (num_mcid == 1 && node->annot.res.id == 0) { + _cairo_output_stream_printf (surface->output, " /K %d\n", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->output, " /K [ "); + if (node->annot.res.id != 0) { + _cairo_output_stream_printf (surface->output, + "%d 0 R ", + node->annot.res.id); + } + for (i = 0; i < num_mcid; i++) { + mcid_elem = _cairo_array_index (&node->mcid, i); + page_res = _cairo_array_index (&surface->pages, mcid_elem->page - 1); + if (mcid_elem->page == first_page) { + _cairo_output_stream_printf (surface->output, "%d ", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->output, + "\n << /Type /MCR /Pg %d 0 R /MCID %d >> ", + page_res->id, + mcid_elem->mcid); + } + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + } + } + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node) +{ + cairo_pdf_resource_t res; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + int sp; + char *dest = NULL; + int i, num_rects, num_mcid; + struct page_mcid *mcid_elem; + + num_mcid = _cairo_array_num_elements (&node->mcid); + if (num_mcid == 0 ) + return status; + + mcid_elem = _cairo_array_index (&node->mcid, 0); + if (mcid_elem->page != ic->annot_page) + return status; + + num_rects = _cairo_array_num_elements (&node->annot.link_attrs.rects); + if (strcmp (node->name, CAIRO_TAG_LINK) == 0 && + node->annot.link_attrs.link_type != TAG_LINK_EMPTY && + (node->annot.extents.valid || num_rects > 0)) + { + res = _cairo_pdf_surface_new_object (surface); + + status = _cairo_array_append (&ic->parent_tree, &res); + if (unlikely (status)) + return status; + + sp = _cairo_array_num_elements (&ic->parent_tree) - 1; + + status = _cairo_array_append (&surface->page_annots, &res); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_update_object (surface, res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Annot\n" + " /Subtype /Link\n" + " /StructParent %d\n", + res.id, + sp); + + if (num_rects > 0) { + cairo_rectangle_int_t bbox_rect; + + _cairo_output_stream_printf (surface->output, + " /QuadPoints [ "); + for (i = 0; i < num_rects; i++) { + cairo_rectangle_t rectf; + cairo_rectangle_int_t recti; + + _cairo_array_copy_element (&node->annot.link_attrs.rects, i, &rectf); + _cairo_rectangle_int_from_double (&recti, &rectf); + if (i == 0) + bbox_rect = recti; + else + _cairo_rectangle_union (&bbox_rect, &recti); + + write_rect_to_pdf_quad_points (surface->output, &rectf, node->annot.page_height); + _cairo_output_stream_printf (surface->output, " "); + } + _cairo_output_stream_printf (surface->output, + "]\n" + " /Rect [ "); + write_rect_int_to_pdf_bbox (surface->output, &bbox_rect, node->annot.page_height); + _cairo_output_stream_printf (surface->output, " ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Rect [ "); + write_rect_int_to_pdf_bbox (surface->output, &node->annot.extents.extents, node->annot.page_height); + _cairo_output_stream_printf (surface->output, " ]\n"); + } + + if (node->annot.link_attrs.dest) { + status = _cairo_utf8_to_pdf_string (node->annot.link_attrs.dest, &dest); + if (unlikely (status)) + return status; + } + + if (node->annot.link_attrs.link_type == TAG_LINK_DEST) { + if (node->annot.link_attrs.dest) { + _cairo_output_stream_printf (surface->output, + " /Dest %s\n", + dest); + } else { + cairo_pdf_resource_t res; + int page = node->annot.link_attrs.page; + + if (page < 1 || page > (int)_cairo_array_num_elements (&surface->pages)) + return CAIRO_INT_STATUS_TAG_ERROR; + + _cairo_array_copy_element (&surface->pages, page - 1, &res); + _cairo_output_stream_printf (surface->output, + " /Dest [%d 0 R /XYZ %f %f 0]\n", + res.id, + node->annot.link_attrs.pos.x, + node->annot.page_height - node->annot.link_attrs.pos.y); + } + } else if (node->annot.link_attrs.link_type == TAG_LINK_URI) { + _cairo_output_stream_printf (surface->output, + " /A <<\n" + " /Type /Action\n" + " /S /URI\n" + " /URI (%s)\n" + " >>\n", + node->annot.link_attrs.uri); + } else if (node->annot.link_attrs.link_type == TAG_LINK_FILE) { + _cairo_output_stream_printf (surface->output, + " /A <<\n" + " /Type /Action\n" + " /S /GoToR\n" + " /F (%s)\n", + node->annot.link_attrs.file); + if (node->annot.link_attrs.dest) { + _cairo_output_stream_printf (surface->output, + " /D %s\n", + dest); + } else { + _cairo_output_stream_printf (surface->output, + " /D [%d %f %f ]\n", + node->annot.link_attrs.page, + node->annot.link_attrs.pos.x, + node->annot.link_attrs.pos.y); + } + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + _cairo_output_stream_printf (surface->output, + " /BS << /W 0 >>" + ">>\n" + "endobj\n"); + + status = _cairo_output_stream_get_status (surface->output); + + free (dest); + } + + return status; +} + +static cairo_int_status_t +cairo_pdf_interchange_walk_struct_tree (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + cairo_int_status_t (*func) (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node)) +{ + cairo_int_status_t status; + cairo_pdf_struct_tree_node_t *child; + + if (node->parent) { + status = func (surface, node); + if (unlikely (status)) + return status; + } + + cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, + &node->children, link) + { + status = cairo_pdf_interchange_walk_struct_tree (surface, child, func); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_struct_tree_node_t *child; + + if (cairo_list_is_empty (&ic->struct_root->children)) + return CAIRO_STATUS_SUCCESS; + + surface->struct_tree_root = _cairo_pdf_surface_new_object (surface); + ic->struct_root->res = surface->struct_tree_root; + + cairo_pdf_interchange_walk_struct_tree (surface, ic->struct_root, cairo_pdf_interchange_write_node_object); + + child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link); + _cairo_pdf_surface_update_object (surface, surface->struct_tree_root); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /StructTreeRoot\n" + " /ParentTree %d 0 R\n" + " /K [ %d 0 R ]\n" + ">>\n" + "endobj\n", + surface->struct_tree_root.id, + ic->parent_tree_res.id, + child->res.id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_page_annots (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + _cairo_array_truncate (&surface->page_annots, 0); + ic->annot_page = _cairo_array_num_elements (&surface->pages); + + cairo_pdf_interchange_walk_struct_tree (surface, ic->struct_root, cairo_pdf_interchange_write_annot); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_page_parent_elems (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_struct_tree_node_t *node; + cairo_pdf_resource_t res; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + surface->page_parent_tree = -1; + num_elems = _cairo_array_num_elements (&ic->mcid_to_tree); + if (num_elems > 0) { + res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "[\n", + res.id); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&ic->mcid_to_tree, i, &node); + _cairo_output_stream_printf (surface->output, " %d 0 R\n", node->res.id); + } + _cairo_output_stream_printf (surface->output, + "]\n" + "endobj\n"); + status = _cairo_array_append (&ic->parent_tree, &res); + surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1; + } + + return status; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_resource_t *res; + cairo_pdf_interchange_t *ic = &surface->interchange; + + num_elems = _cairo_array_num_elements (&ic->parent_tree); + if (num_elems > 0) { + ic->parent_tree_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Nums [\n", + ic->parent_tree_res.id); + for (i = 0; i < num_elems; i++) { + res = _cairo_array_index (&ic->parent_tree, i); + if (res->id) { + _cairo_output_stream_printf (surface->output, + " %d %d 0 R\n", + i, + res->id); + } + } + _cairo_output_stream_printf (surface->output, + " ]\n" + ">>\n" + "endobj\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_outline_entry_t *outline; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status; + char *name = NULL; + char *dest = NULL; + + num_elems = _cairo_array_num_elements (&ic->outline); + if (num_elems < 2) + return CAIRO_INT_STATUS_SUCCESS; + + _cairo_array_copy_element (&ic->outline, 0, &outline); + outline->res = _cairo_pdf_surface_new_object (surface); + surface->outlines_dict_res = outline->res; + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Outlines\n" + " /First %d 0 R\n" + " /Last %d 0 R\n" + " /Count %d\n" + ">>\n" + "endobj\n", + outline->res.id, + outline->first_child->res.id, + outline->last_child->res.id, + outline->count); + + for (i = 1; i < num_elems; i++) { + _cairo_array_copy_element (&ic->outline, i, &outline); + _cairo_pdf_surface_update_object (surface, outline->res); + status = _cairo_utf8_to_pdf_string (outline->name, &name); + if (unlikely (status)) + return status; + + status = _cairo_utf8_to_pdf_string (outline->dest, &dest); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Title %s\n" + " /Parent %d 0 R\n", + outline->res.id, + name, + outline->parent->res.id); + + if (outline->prev) { + _cairo_output_stream_printf (surface->output, + " /Prev %d 0 R\n", + outline->prev->res.id); + } + + if (outline->next) { + _cairo_output_stream_printf (surface->output, + " /Next %d 0 R\n", + outline->next->res.id); + } + + if (outline->first_child) { + _cairo_output_stream_printf (surface->output, + " /First %d 0 R\n" + " /Last %d 0 R\n" + " /Count %d\n", + outline->first_child->res.id, + outline->last_child->res.id, + outline->count); + } + + if (outline->flags) { + int flags = 0; + if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_ITALIC) + flags |= 1; + if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_BOLD) + flags |= 2; + _cairo_output_stream_printf (surface->output, + " /F %d\n", + flags); + } + + _cairo_output_stream_printf (surface->output, + " /Dest %s\n" + ">>\n" + "endobj\n", + dest); + free (dest); + } + + return status; +} + +/* + * Split a page label into a text prefix and numeric suffix. Leading '0's are + * included in the prefix. eg + * "3" => NULL, 3 + * "cover" => "cover", 0 + * "A-2" => "A-", 2 + * "A-002" => "A-00", 2 + */ +static char * +split_label (const char* label, int *num) +{ + int len, i; + + *num = 0; + len = strlen (label); + if (len == 0) + return NULL; + + i = len; + while (i > 0 && _cairo_isdigit (label[i-1])) + i--; + + while (i < len && label[i] == '0') + i++; + + if (i < len) + sscanf (label + i, "%d", num); + + if (i > 0) { + char *s; + s = _cairo_malloc (i + 1); + if (!s) + return NULL; + + memcpy (s, label, i); + s[i] = 0; + return s; + } + + return NULL; +} + +/* strcmp that handles NULL arguments */ +static cairo_bool_t +strcmp_null (const char *s1, const char *s2) +{ + if (s1 && s2) + return strcmp (s1, s2) == 0; + + if (!s1 && !s2) + return TRUE; + + return FALSE; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + char *label; + char *prefix; + char *prev_prefix; + int num, prev_num; + cairo_int_status_t status; + + num_elems = _cairo_array_num_elements (&surface->page_labels); + if (num_elems > 0) { + surface->page_labels_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Nums [\n", + surface->page_labels_res.id); + prefix = NULL; + prev_prefix = NULL; + num = 0; + prev_num = 0; + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->page_labels, i, &label); + if (label) { + prefix = split_label (label, &num); + } else { + prefix = NULL; + num = i + 1; + } + + if (!strcmp_null (prefix, prev_prefix) || num != prev_num + 1) { + _cairo_output_stream_printf (surface->output, " %d << ", i); + + if (num) + _cairo_output_stream_printf (surface->output, "/S /D /St %d ", num); + + if (prefix) { + char *s; + status = _cairo_utf8_to_pdf_string (prefix, &s); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "/P %s ", s); + free (s); + } + + _cairo_output_stream_printf (surface->output, ">>\n"); + } + free (prev_prefix); + prev_prefix = prefix; + prefix = NULL; + prev_num = num; + } + free (prefix); + free (prev_prefix); + _cairo_output_stream_printf (surface->output, + " ]\n" + ">>\n" + "endobj\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_collect_dest (void *entry, void *closure) +{ + cairo_pdf_named_dest_t *dest = entry; + cairo_pdf_surface_t *surface = closure; + cairo_pdf_interchange_t *ic = &surface->interchange; + + ic->sorted_dests[ic->num_dests++] = dest; +} + +static int +_dest_compare (const void *a, const void *b) +{ + const cairo_pdf_named_dest_t * const *dest_a = a; + const cairo_pdf_named_dest_t * const *dest_b = b; + + return strcmp ((*dest_a)->attrs.name, (*dest_b)->attrs.name); +} + +static cairo_int_status_t +_cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface) +{ + int i; + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (ic->num_dests == 0) { + ic->dests_res.id = 0; + return CAIRO_STATUS_SUCCESS; + } + + ic->sorted_dests = calloc (ic->num_dests, sizeof (cairo_pdf_named_dest_t *)); + if (unlikely (ic->sorted_dests == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ic->num_dests = 0; + _cairo_hash_table_foreach (ic->named_dests, _collect_dest, surface); + qsort (ic->sorted_dests, ic->num_dests, sizeof (cairo_pdf_named_dest_t *), _dest_compare); + + ic->dests_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Names [\n", + ic->dests_res.id); + for (i = 0; i < ic->num_dests; i++) { + cairo_pdf_named_dest_t *dest = ic->sorted_dests[i]; + cairo_pdf_resource_t page_res; + double x = 0; + double y = 0; + + if (dest->extents.valid) { + x = dest->extents.extents.x; + y = dest->extents.extents.y; + } + + if (dest->attrs.x_valid) + x = dest->attrs.x; + + if (dest->attrs.y_valid) + y = dest->attrs.y; + + _cairo_array_copy_element (&surface->pages, dest->page - 1, &page_res); + _cairo_output_stream_printf (surface->output, + " (%s) [ %d 0 R /XYZ %f %f ]\n", + dest->attrs.name, + page_res.id, + x, + surface->height - y); + } + _cairo_output_stream_printf (surface->output, + " ]\n" + ">>\n" + "endobj\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_names_dict (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status; + + status = _cairo_pdf_interchange_write_document_dests (surface); + if (unlikely (status)) + return status; + + surface->names_dict_res.id = 0; + if (ic->dests_res.id != 0) { + surface->names_dict_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Dests %d 0 R >>\n" + "endobj\n", + surface->names_dict_res.id, + ic->dests_res.id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + surface->docinfo_res = _cairo_pdf_surface_new_object (surface); + if (surface->docinfo_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Producer (cairo %s (http://cairographics.org))\n", + surface->docinfo_res.id, + cairo_version_string ()); + + if (ic->docinfo.title) + _cairo_output_stream_printf (surface->output, " /Title %s\n", ic->docinfo.title); + + if (ic->docinfo.author) + _cairo_output_stream_printf (surface->output, " /Author %s\n", ic->docinfo.author); + + if (ic->docinfo.subject) + _cairo_output_stream_printf (surface->output, " /Subject %s\n", ic->docinfo.subject); + + if (ic->docinfo.keywords) + _cairo_output_stream_printf (surface->output, " /Keywords %s\n", ic->docinfo.keywords); + + if (ic->docinfo.creator) + _cairo_output_stream_printf (surface->output, " /Creator %s\n", ic->docinfo.creator); + + if (ic->docinfo.create_date) + _cairo_output_stream_printf (surface->output, " /CreationDate %s\n", ic->docinfo.create_date); + + if (ic->docinfo.mod_date) + _cairo_output_stream_printf (surface->output, " /ModDate %s\n", ic->docinfo.mod_date); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static void +init_named_dest_key (cairo_pdf_named_dest_t *dest) +{ + dest->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + dest->attrs.name, + strlen (dest->attrs.name)); +} + +static cairo_bool_t +_named_dest_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_named_dest_t *a = key_a; + const cairo_pdf_named_dest_t *b = key_b; + + return strcmp (a->attrs.name, b->attrs.name) == 0; +} + +static void +_named_dest_pluck (void *entry, void *closure) +{ + cairo_pdf_named_dest_t *dest = entry; + cairo_hash_table_t *table = closure; + + _cairo_hash_table_remove (table, &dest->base); + free (dest->attrs.name); + free (dest); +} + +static cairo_int_status_t +_cairo_pdf_interchange_begin_structure_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + const char *name, + const char *attributes) +{ + int page_num, mcid; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = add_tree_node (surface, ic->current_node, name, &ic->current_node); + if (unlikely (status)) + return status; + + _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, ic->current_node); + + if (tag_type & TAG_TYPE_LINK) { + status = _cairo_tag_parse_link_attributes (attributes, &ic->current_node->annot.link_attrs); + if (unlikely (status)) + return status; + + ic->current_node->annot.page_height = surface->height; + cairo_list_add_tail (&ic->current_node->annot.extents.link, &ic->extents_list); + } + + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + ic->current_node = _cairo_tag_stack_top_elem (&ic->render_tag_stack)->data; + assert (ic->current_node != NULL); + if (is_leaf_node (ic->current_node)) { + page_num = _cairo_array_num_elements (&surface->pages); + add_mcid_to_node (surface, ic->current_node, page_num, &mcid); + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, name, mcid); + } + } + + return status; +} + +static cairo_int_status_t +_cairo_pdf_interchange_begin_dest_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + const char *name, + const char *attributes) +{ + cairo_pdf_named_dest_t *dest; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + dest = calloc (1, sizeof (cairo_pdf_named_dest_t)); + if (unlikely (dest == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_tag_parse_dest_attributes (attributes, &dest->attrs); + if (unlikely (status)) + return status; + + dest->page_height = surface->height; + dest->page = _cairo_array_num_elements (&surface->pages); + init_named_dest_key (dest); + status = _cairo_hash_table_insert (ic->named_dests, &dest->base); + if (unlikely (status)) + return status; + + _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, dest); + cairo_list_add_tail (&dest->extents.link, &ic->extents_list); + ic->num_dests++; + } + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface, + const char *name, + const char *attributes) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_tag_type_t tag_type; + cairo_pdf_interchange_t *ic = &surface->interchange; + void *ptr; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_tag_stack_push (&ic->analysis_tag_stack, name, attributes); + + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + status = _cairo_tag_stack_push (&ic->render_tag_stack, name, attributes); + _cairo_array_copy_element (&ic->push_data, ic->push_data_index++, &ptr); + _cairo_tag_stack_set_top_data (&ic->render_tag_stack, ptr); + } + + if (unlikely (status)) + return status; + + tag_type = _cairo_tag_get_type (name); + if (tag_type & TAG_TYPE_STRUCTURE) { + status = _cairo_pdf_interchange_begin_structure_tag (surface, tag_type, name, attributes); + if (unlikely (status)) + return status; + } + + if (tag_type & TAG_TYPE_DEST) { + status = _cairo_pdf_interchange_begin_dest_tag (surface, tag_type, name, attributes); + if (unlikely (status)) + return status; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + ptr = _cairo_tag_stack_top_elem (&ic->analysis_tag_stack)->data; + status = _cairo_array_append (&ic->push_data, &ptr); + } + + return status; +} + +static cairo_int_status_t +_cairo_pdf_interchange_end_structure_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + cairo_tag_stack_elem_t *elem) +{ + const cairo_pdf_struct_tree_node_t *node; + struct tag_extents *tag, *next; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + assert (elem->data != NULL); + node = elem->data; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + if (tag_type & TAG_TYPE_LINK) { + cairo_list_foreach_entry_safe (tag, next, struct tag_extents, + &ic->extents_list, link) { + if (tag == &node->annot.extents) { + cairo_list_del (&tag->link); + break; + } + } + } + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + if (is_leaf_node (ic->current_node)) { + status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + if (unlikely (status)) + return status; + } + } + + ic->current_node = ic->current_node->parent; + assert (ic->current_node != NULL); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_interchange_end_dest_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + cairo_tag_stack_elem_t *elem) +{ + struct tag_extents *tag, *next; + cairo_pdf_named_dest_t *dest; + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + assert (elem->data != NULL); + dest = (cairo_pdf_named_dest_t *) elem->data; + cairo_list_foreach_entry_safe (tag, next, struct tag_extents, + &ic->extents_list, link) { + if (tag == &dest->extents) { + cairo_list_del (&tag->link); + break; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface, + const char *name) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_tag_type_t tag_type; + cairo_tag_stack_elem_t *elem; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + status = _cairo_tag_stack_pop (&ic->analysis_tag_stack, name, &elem); + else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) + status = _cairo_tag_stack_pop (&ic->render_tag_stack, name, &elem); + + if (unlikely (status)) + return status; + + tag_type = _cairo_tag_get_type (name); + if (tag_type & TAG_TYPE_STRUCTURE) { + status = _cairo_pdf_interchange_end_structure_tag (surface, tag_type, elem); + if (unlikely (status)) + goto cleanup; + } + + if (tag_type & TAG_TYPE_DEST) { + status = _cairo_pdf_interchange_end_dest_tag (surface, tag_type, elem); + if (unlikely (status)) + goto cleanup; + } + + cleanup: + _cairo_tag_stack_free_elem (elem); + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + struct tag_extents *tag; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + cairo_list_foreach_entry (tag, struct tag_extents, &ic->extents_list, link) { + if (tag->valid) { + _cairo_rectangle_union (&tag->extents, extents); + } else { + tag->extents = *extents; + tag->valid = TRUE; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + int page_num, mcid; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + _cairo_array_truncate (&ic->mcid_to_tree, 0); + _cairo_array_truncate (&ic->push_data, 0); + ic->begin_page_node = ic->current_node; + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + ic->push_data_index = 0; + ic->current_node = ic->begin_page_node; + if (ic->end_page_node && is_leaf_node (ic->end_page_node)) { + page_num = _cairo_array_num_elements (&surface->pages); + add_mcid_to_node (surface, ic->end_page_node, page_num, &mcid); + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, + ic->end_page_node->name, + mcid); + } + } + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + ic->end_page_node = ic->current_node; + if (is_leaf_node (ic->current_node)) + status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + } + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + + status = cairo_pdf_interchange_write_page_annots (surface); + if (unlikely (status)) + return status; + + return cairo_pdf_interchange_write_page_parent_elems (surface); +} + +cairo_int_status_t +_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + status = cairo_pdf_interchange_write_parent_tree (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_struct_tree (surface); + if (unlikely (status)) + return status; + + if (_cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack) == TAG_TREE_TYPE_TAGGED) + surface->tagged = TRUE; + + status = cairo_pdf_interchange_write_outline (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_page_labels (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_names_dict (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_docinfo (surface); + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_outline_entry_t *outline_root; + cairo_int_status_t status; + + _cairo_tag_stack_init (&ic->analysis_tag_stack); + _cairo_tag_stack_init (&ic->render_tag_stack); + _cairo_array_init (&ic->push_data, sizeof(void *)); + ic->struct_root = calloc (1, sizeof(cairo_pdf_struct_tree_node_t)); + if (unlikely (ic->struct_root == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_init (&ic->struct_root->children); + _cairo_array_init (&ic->struct_root->mcid, sizeof(struct page_mcid)); + ic->current_node = ic->struct_root; + ic->begin_page_node = NULL; + ic->end_page_node = NULL; + _cairo_array_init (&ic->parent_tree, sizeof(cairo_pdf_resource_t)); + _cairo_array_init (&ic->mcid_to_tree, sizeof(cairo_pdf_struct_tree_node_t *)); + ic->parent_tree_res.id = 0; + cairo_list_init (&ic->extents_list); + ic->named_dests = _cairo_hash_table_create (_named_dest_equal); + if (unlikely (ic->named_dests == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ic->num_dests = 0; + ic->sorted_dests = NULL; + ic->dests_res.id = 0; + + _cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *)); + outline_root = calloc (1, sizeof(cairo_pdf_outline_entry_t)); + if (unlikely (outline_root == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memset (&ic->docinfo, 0, sizeof (ic->docinfo)); + status = _cairo_array_append (&ic->outline, &outline_root); + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + unsigned i; + + _cairo_tag_stack_fini (&ic->analysis_tag_stack); + _cairo_tag_stack_fini (&ic->render_tag_stack); + _cairo_array_fini (&ic->push_data); + free_node (ic->struct_root); + _cairo_array_fini (&ic->mcid_to_tree); + _cairo_array_fini (&ic->parent_tree); + _cairo_hash_table_foreach (ic->named_dests, _named_dest_pluck, ic->named_dests); + _cairo_hash_table_destroy (ic->named_dests); + free (ic->sorted_dests); + + for (i = 0; i < _cairo_array_num_elements (&ic->outline); i++) { + cairo_pdf_outline_entry_t *outline; + + _cairo_array_copy_element (&ic->outline, i, &outline); + free (outline); + } + _cairo_array_fini (&ic->outline); + free (ic->docinfo.title); + free (ic->docinfo.author); + free (ic->docinfo.subject); + free (ic->docinfo.keywords); + free (ic->docinfo.creator); + free (ic->docinfo.create_date); + free (ic->docinfo.mod_date); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface, + int parent_id, + const char *name, + const char *dest, + cairo_pdf_outline_flags_t flags, + int *id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_outline_entry_t *outline; + cairo_pdf_outline_entry_t *parent; + cairo_int_status_t status; + + if (parent_id < 0 || parent_id >= (int)_cairo_array_num_elements (&ic->outline)) + return CAIRO_STATUS_SUCCESS; + + outline = _cairo_malloc (sizeof(cairo_pdf_outline_entry_t)); + if (unlikely (outline == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + outline->res = _cairo_pdf_surface_new_object (surface); + if (outline->res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + outline->name = strdup (name); + outline->dest = strdup (dest); + outline->flags = flags; + outline->count = 0; + + _cairo_array_copy_element (&ic->outline, parent_id, &parent); + + outline->parent = parent; + outline->first_child = NULL; + outline->last_child = NULL; + outline->next = NULL; + if (parent->last_child) { + parent->last_child->next = outline; + outline->prev = parent->last_child; + parent->last_child = outline; + } else { + parent->first_child = outline; + parent->last_child = outline; + outline->prev = NULL; + } + + *id = _cairo_array_num_elements (&ic->outline); + status = _cairo_array_append (&ic->outline, &outline); + if (unlikely (status)) + return status; + + /* Update Count */ + outline = outline->parent; + while (outline) { + if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_OPEN) { + outline->count++; + } else { + outline->count--; + break; + } + outline = outline->parent; + } + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Date must be in the following format: + * + * YYYY-MM-DDThh:mm:ss[Z+-]hh:mm + * + * Only the year is required. If a field is included all preceding + * fields must be included. + */ +static char * +iso8601_to_pdf_date_string (const char *iso) +{ + char buf[40]; + const char *p; + int i; + + /* Check that utf8 contains only the characters "0123456789-T:Z+" */ + p = iso; + while (*p) { + if (!_cairo_isdigit (*p) && *p != '-' && *p != 'T' && + *p != ':' && *p != 'Z' && *p != '+') + return NULL; + p++; + } + + p = iso; + strcpy (buf, "("); + + /* YYYY (required) */ + if (strlen (p) < 4) + return NULL; + + strncat (buf, p, 4); + p += 4; + + /* -MM, -DD, Thh, :mm, :ss */ + for (i = 0; i < 5; i++) { + if (strlen (p) < 3) + goto finish; + + strncat (buf, p + 1, 2); + p += 3; + } + + /* Z, +, - */ + if (strlen (p) < 1) + goto finish; + strncat (buf, p, 1); + p += 1; + + /* hh */ + if (strlen (p) < 2) + goto finish; + + strncat (buf, p, 2); + strcat (buf, "'"); + p += 2; + + /* :mm */ + if (strlen (p) < 3) + goto finish; + + strncat (buf, p + 1, 3); + + finish: + strcat (buf, ")"); + return strdup (buf); +} + +cairo_int_status_t +_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_status_t status; + char *s = NULL; + + if (utf8) { + if (metadata == CAIRO_PDF_METADATA_CREATE_DATE || + metadata == CAIRO_PDF_METADATA_MOD_DATE) { + s = iso8601_to_pdf_date_string (utf8); + } else { + status = _cairo_utf8_to_pdf_string (utf8, &s); + if (unlikely (status)) + return status; + } + } + + switch (metadata) { + case CAIRO_PDF_METADATA_TITLE: + free (ic->docinfo.title); + ic->docinfo.title = s; + break; + case CAIRO_PDF_METADATA_AUTHOR: + free (ic->docinfo.author); + ic->docinfo.author = s; + break; + case CAIRO_PDF_METADATA_SUBJECT: + free (ic->docinfo.subject); + ic->docinfo.subject = s; + break; + case CAIRO_PDF_METADATA_KEYWORDS: + free (ic->docinfo.keywords); + ic->docinfo.keywords = s; + break; + case CAIRO_PDF_METADATA_CREATOR: + free (ic->docinfo.creator); + ic->docinfo.creator = s; + break; + case CAIRO_PDF_METADATA_CREATE_DATE: + free (ic->docinfo.create_date); + ic->docinfo.create_date = s; + break; + case CAIRO_PDF_METADATA_MOD_DATE: + free (ic->docinfo.mod_date); + ic->docinfo.mod_date = s; + break; + } + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h index 4314a042e..ed8be7f96 100644 --- a/src/cairo-pdf-operators-private.h +++ b/src/cairo-pdf-operators-private.h @@ -173,4 +173,12 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font); +cairo_private cairo_int_status_t +_cairo_pdf_operators_tag_begin (cairo_pdf_operators_t *pdf_operators, + const char *tag_name, + int mcid); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_tag_end (cairo_pdf_operators_t *pdf_operators); + #endif /* CAIRO_PDF_OPERATORS_H */ diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index dcee25f0c..b4ac253e6 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -319,7 +319,8 @@ _word_wrap_stream_write (cairo_output_stream_t *base, if (*data == '\n' || stream->column >= stream->max_column) { _cairo_output_stream_printf (stream->output, "\n"); stream->column = 0; - } else if (*data == '<') { + } + if (*data == '<') { stream->state = WRAP_STATE_HEXSTRING; } else if (*data == '(') { stream->state = WRAP_STATE_STRING; @@ -1492,9 +1493,6 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, cairo_matrix_init_scale (&invert_y_axis, 1, -1); text_matrix = scaled_font->scale; - /* Invert y axis in font space */ - cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis); - /* Invert y axis in device space */ cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); @@ -1557,4 +1555,41 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, return _cairo_output_stream_get_status (pdf_operators->stream); } +cairo_int_status_t +_cairo_pdf_operators_tag_begin (cairo_pdf_operators_t *pdf_operators, + const char *tag_name, + int mcid) +{ + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "/%s << /MCID %d >> BDC\n", + tag_name, + mcid); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +cairo_int_status_t +_cairo_pdf_operators_tag_end (cairo_pdf_operators_t *pdf_operators) +{ + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + #endif /* CAIRO_HAS_PDF_OPERATORS */ diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index 618ca4ede..a5c7d0ed3 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -48,6 +48,8 @@ #include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-path-fixed-private.h" +#include "cairo-tag-attributes-private.h" +#include "cairo-tag-stack-private.h" typedef struct _cairo_pdf_resource { unsigned int id; @@ -76,9 +78,14 @@ typedef struct _cairo_pdf_source_surface_entry { cairo_bool_t smask; cairo_pdf_resource_t surface_res; cairo_pdf_resource_t smask_res; - int width; - int height; + + /* Extents of the source surface. If bounded is false, + * extents is the ink extents. */ + cairo_bool_t bounded; cairo_rectangle_int_t extents; + + /* Union of source extents requried for all operations using this source */ + cairo_rectangle_int_t required_extents; } cairo_pdf_source_surface_entry_t; typedef struct _cairo_pdf_source_surface { @@ -97,6 +104,17 @@ typedef struct _cairo_pdf_pattern { cairo_pdf_resource_t gstate_res; cairo_operator_t operator; cairo_bool_t is_shading; + + /* PDF pattern space is the pattern matrix concatenated with the + * initial space of the parent object. If the parent object is the + * page, the intial space does not include the Y-axis flipping + * matrix emitted at the start of the page content stream. If the + * parent object is not the page content stream, the initial space + * will have a flipped Y-axis. The inverted_y_axis flag is true + * when the initial space of the parent object that is drawing + * this pattern has a flipped Y-axis. + */ + cairo_bool_t inverted_y_axis; } cairo_pdf_pattern_t; typedef enum _cairo_pdf_operation { @@ -138,6 +156,91 @@ typedef struct _cairo_pdf_jbig2_global { cairo_bool_t emitted; } cairo_pdf_jbig2_global_t; +/* cairo-pdf-interchange.c types */ + +struct page_mcid { + int page; + int mcid; +}; + +struct tag_extents { + cairo_rectangle_int_t extents; + cairo_bool_t valid; + cairo_list_t link; +}; + +typedef struct _cairo_pdf_struct_tree_node { + char *name; + cairo_pdf_resource_t res; + struct _cairo_pdf_struct_tree_node *parent; + cairo_list_t children; + cairo_array_t mcid; /* array of struct page_mcid */ + struct { + struct tag_extents extents; + cairo_pdf_resource_t res; + cairo_link_attrs_t link_attrs; + double page_height; + } annot; + cairo_list_t link; +} cairo_pdf_struct_tree_node_t; + +typedef struct _cairo_pdf_named_dest { + cairo_hash_entry_t base; + struct tag_extents extents; + cairo_dest_attrs_t attrs; + int page; + double page_height; + cairo_bool_t referenced; +} cairo_pdf_named_dest_t; + +typedef struct _cairo_pdf_outline_entry { + char *name; + char *dest; + cairo_pdf_outline_flags_t flags; + cairo_pdf_resource_t res; + struct _cairo_pdf_outline_entry *parent; + struct _cairo_pdf_outline_entry *first_child; + struct _cairo_pdf_outline_entry *last_child; + struct _cairo_pdf_outline_entry *next; + struct _cairo_pdf_outline_entry *prev; + int count; +} cairo_pdf_outline_entry_t; + +struct docinfo { + char *title; + char *author; + char *subject; + char *keywords; + char *creator; + char *create_date; + char *mod_date; +}; + +typedef struct _cairo_pdf_interchange { + cairo_tag_stack_t analysis_tag_stack; + cairo_tag_stack_t render_tag_stack; + cairo_array_t push_data; /* records analysis_tag_stack data field for each push */ + int push_data_index; + cairo_pdf_struct_tree_node_t *struct_root; + cairo_pdf_struct_tree_node_t *current_node; + cairo_pdf_struct_tree_node_t *begin_page_node; + cairo_pdf_struct_tree_node_t *end_page_node; + cairo_array_t parent_tree; /* parent tree resources */ + cairo_array_t mcid_to_tree; /* mcid to tree node mapping for current page */ + cairo_pdf_resource_t parent_tree_res; + cairo_list_t extents_list; + cairo_hash_table_t *named_dests; + int num_dests; + cairo_pdf_named_dest_t **sorted_dests; + cairo_pdf_resource_t dests_res; + int annot_page; + cairo_array_t outline; /* array of pointers to cairo_pdf_outline_entry_t; */ + struct docinfo docinfo; + +} cairo_pdf_interchange_t; + +/* pdf surface data */ + typedef struct _cairo_pdf_surface cairo_pdf_surface_t; struct _cairo_pdf_surface { @@ -149,7 +252,10 @@ struct _cairo_pdf_surface { double width; double height; + cairo_rectangle_int_t surface_extents; + cairo_bool_t surface_bounded; cairo_matrix_t cairo_to_pdf; + cairo_bool_t in_xobject; cairo_array_t objects; cairo_array_t pages; @@ -167,6 +273,7 @@ struct _cairo_pdf_surface { cairo_pdf_resource_t next_available_resource; cairo_pdf_resource_t pages_resource; + cairo_pdf_resource_t struct_tree_root; cairo_pdf_version_t pdf_version; cairo_bool_t compress_content; @@ -212,7 +319,76 @@ struct _cairo_pdf_surface { double current_color_blue; double current_color_alpha; + cairo_pdf_interchange_t interchange; + int page_parent_tree; /* -1 if not used */ + cairo_array_t page_annots; + cairo_bool_t tagged; + char *current_page_label; + cairo_array_t page_labels; + cairo_pdf_resource_t outlines_dict_res; + cairo_pdf_resource_t names_dict_res; + cairo_pdf_resource_t docinfo_res; + cairo_pdf_resource_t page_labels_res; + + int thumbnail_width; + int thumbnail_height; + cairo_image_surface_t *thumbnail_image; + cairo_surface_t *paginated_surface; }; +cairo_private cairo_pdf_resource_t +_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); + +cairo_private void +_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource); + +cairo_private cairo_int_status_t +_cairo_utf8_to_pdf_string (const char *utf8, char **str_out); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface, + const char *name, + const char *attributes); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface, + const char *name); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface, + int parent_id, + const char *name, + const char *dest, + cairo_pdf_outline_flags_t flags, + int *id); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8); + #endif /* CAIRO_PDF_SURFACE_PRIVATE_H */ diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index a6274bd07..e60d0b9a6 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -209,9 +209,6 @@ typedef struct _cairo_pdf_alpha_linear_function { double alpha2; } cairo_pdf_alpha_linear_function_t; -static cairo_pdf_resource_t -_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); - static void _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); @@ -241,9 +238,6 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); static void _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); -static cairo_pdf_resource_t -_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface); - static cairo_pdf_resource_t _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); @@ -262,7 +256,7 @@ _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); static const cairo_surface_backend_t cairo_pdf_surface_backend; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; -static cairo_pdf_resource_t +cairo_pdf_resource_t _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t resource; @@ -283,7 +277,7 @@ _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) return resource; } -static void +void _cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, cairo_pdf_resource_t resource) { @@ -300,9 +294,10 @@ _cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, { surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_pdf); + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); } static cairo_bool_t @@ -373,7 +368,13 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->output = output; surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); + cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, 1, 0, 0); + surface->in_xobject = FALSE; + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + surface->surface_bounded = TRUE; _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t)); _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t)); @@ -409,6 +410,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL2; } + surface->struct_tree_root.id = 0; surface->pdf_version = CAIRO_PDF_VERSION_1_5; surface->compress_content = TRUE; surface->pdf_stream.active = FALSE; @@ -438,6 +440,23 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface); _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE); + status = _cairo_pdf_interchange_init (surface); + if (unlikely (status)) + goto BAIL2; + + surface->page_parent_tree = -1; + _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t)); + surface->tagged = FALSE; + surface->current_page_label = NULL; + _cairo_array_init (&surface->page_labels, sizeof (char *)); + surface->outlines_dict_res.id = 0; + surface->names_dict_res.id = 0; + surface->docinfo_res.id = 0; + surface->page_labels_res.id = 0; + surface->thumbnail_width = 0; + surface->thumbnail_height = 0; + surface->thumbnail_image = NULL; + surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, @@ -706,6 +725,142 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, status = _cairo_surface_set_error (surface, status); } +/** + * CAIRO_PDF_OUTLINE_ROOT: + * + * The root outline item in cairo_pdf_surface_add_outline(). + * + * Since: 1.16 + **/ + +/** + * cairo_pdf_surface_add_outline: + * @surface: a PDF #cairo_surface_t + * @parent_id: the id of the parent item or %CAIRO_PDF_OUTLINE_ROOT if this is a top level item. + * @utf8: the name of the outline + * @dest: the name of the destination + * @flags: outline item flags + * + * Add an item to the document outline hierarchy with the name @utf8 that links to the + * destinaton @dest. Destinations are created using + * cairo_tag_begin()/cairo_tag_end() with the + * %CAIRO_TAG_DEST. The item will be a child of the item with id @parent_id. Use %CAIRO_PDF_OUTLINE_ROOT + * as the parent id of top level items. + * + * Return value: the id for the added item. + * + * Since: 1.16 + **/ +int +cairo_pdf_surface_add_outline (cairo_surface_t *surface, + int parent_id, + const char *utf8, + const char *dest, + cairo_pdf_outline_flags_t flags) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + cairo_status_t status; + int id = 0; + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return 0; + + status = _cairo_pdf_interchange_add_outline (pdf_surface, + parent_id, + utf8, + dest, + flags, + &id); + if (status) + status = _cairo_surface_set_error (surface, status); + + return id; +} + +/** + * cairo_pdf_surface_set_metadata: + * @surface: a PDF #cairo_surface_t + * @metadata: The metadata item to set. + * @utf8: metadata value + * + * Set document metadata. The %CAIRO_PDF_METADATA_CREATE_DATE and + * %CAIRO_PDF_METADATA_MOD_DATE values must be in ISO-8601 format: + * YYYY-MM-DDThh:mm:ss. An optional timezone of the form "[+/-]hh:mm" + * or "Z" for UTC time can be appended. All other metadata values can be any UTF-8 + * string. + * + * For example: + * + * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_TITLE, "My Document"); + * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2015-12-31T23:59+02:00"); + * + * + * Since: 1.16 + **/ +void +cairo_pdf_surface_set_metadata (cairo_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + cairo_status_t status; + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + status = _cairo_pdf_interchange_set_metadata (pdf_surface, metadata, utf8); + if (status) + status = _cairo_surface_set_error (surface, status); +} + +/** + * cairo_pdf_surface_set_page_label: + * @surface: a PDF #cairo_surface_t + * @utf8: The page label. + * + * Set page label for the current page. + * + * Since: 1.16 + **/ +void +cairo_pdf_surface_set_page_label (cairo_surface_t *surface, + const char *utf8) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + free (pdf_surface->current_page_label); + pdf_surface->current_page_label = utf8 ? strdup (utf8) : NULL; +} + +/** + * cairo_pdf_surface_set_thumbnail_size: + * @surface: a PDF #cairo_surface_t + * @width: Thumbnail width. + * @height: Thumbnail height + * + * Set the thumbnail image size for the current and all subsequent + * pages. Setting a width or height of 0 disables thumbnails for the + * current and subsequent pages. + * + * Since: 1.16 + **/ +void +cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface, + int width, + int height) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + pdf_surface->thumbnail_width = width; + pdf_surface->thumbnail_height = height; +} + static void _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) { @@ -735,6 +890,10 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) } _cairo_array_truncate (&surface->smask_groups, 0); _cairo_array_truncate (&surface->knockout_group, 0); + _cairo_array_truncate (&surface->page_annots, 0); + + cairo_surface_destroy (&surface->thumbnail_image->base); + surface->thumbnail_image = NULL; } static void @@ -1239,16 +1398,18 @@ _get_jpeg_image_info (cairo_surface_t *source, } static cairo_int_status_t -_get_source_surface_size (cairo_surface_t *source, - int *width, - int *height, - cairo_rectangle_int_t *extents) +_get_source_surface_extents (cairo_surface_t *source, + cairo_rectangle_int_t *extents, + cairo_bool_t *bounded, + cairo_bool_t *subsurface) { cairo_int_status_t status; cairo_image_info_t info; const unsigned char *mime_data; unsigned long mime_data_length; + *bounded = TRUE; + *subsurface = FALSE; if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { cairo_surface_t *free_me = NULL; @@ -1259,26 +1420,19 @@ _get_source_surface_size (cairo_surface_t *source, cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; *extents = sub->extents; - *width = extents->width; - *height = extents->height; + *subsurface = TRUE; } else { - cairo_rectangle_int_t surf_extents; cairo_box_t box; - cairo_bool_t bounded; - status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source, - &box, NULL); - if (unlikely (status)) { - cairo_surface_destroy (free_me); - return status; + if (! _cairo_surface_get_extents (source, extents)) { + status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source, + &box, NULL); + if (unlikely (status)) { + cairo_surface_destroy (free_me); + return status; + } + _cairo_box_round_to_rectangle (&box, extents); } - - bounded = _cairo_surface_get_extents (source, &surf_extents); - - *width = surf_extents.width; - *height = surf_extents.height; - - _cairo_box_round_to_rectangle (&box, extents); } cairo_surface_destroy (free_me); @@ -1290,8 +1444,6 @@ _get_source_surface_size (cairo_surface_t *source, status = _get_jbig2_image_info (source, &info, &mime_data, &mime_data_length); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; extents->width = info.width; extents->height = info.height; return status; @@ -1299,8 +1451,6 @@ _get_source_surface_size (cairo_surface_t *source, status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; extents->width = info.width; extents->height = info.height; return status; @@ -1308,8 +1458,6 @@ _get_source_surface_size (cairo_surface_t *source, status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; extents->width = info.width; extents->height = info.height; return status; @@ -1318,66 +1466,64 @@ _get_source_surface_size (cairo_surface_t *source, if (! _cairo_surface_get_extents (source, extents)) return CAIRO_INT_STATUS_UNSUPPORTED; - *width = extents->width; - *height = extents->height; - return CAIRO_STATUS_SUCCESS; } /** * _cairo_pdf_surface_add_source_surface: - * @surface: the pdf surface - * @source_surface: A #cairo_surface_t to use as the source surface - * @source_pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source - * @op: the operator used to composite this source - * @filter: filter type of the source pattern - * @stencil_mask: if true, the surface will be written to the PDF as an /ImageMask - * @smask: if true, only the alpha channel will be written (images only) - * @extents: extents of the operation that is using this source - * @smask_res: if not NULL, the image written will specify this resource as the smask for the image (images only) - * @surface_res: return PDF resource number of the surface - * @width: returns width of surface - * @height: returns height of surface - * @x_offset: x offset of surface - * @t_offset: y offset of surface - * @source_extents: returns extents of source (either ink extents or extents needed to cover @extents) + * @surface: [in] the pdf surface + * @source_surface: [in] A #cairo_surface_t to use as the source surface + * @source_pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source + * @op: [in] the operator used to composite this source + * @filter: [in] filter type of the source pattern + * @stencil_mask: [in] if true, the surface will be written to the PDF as an /ImageMask + * @smask: [in] if true, only the alpha channel will be written (images only) + * @extents: [in] extents of the operation that is using this source + * @smask_res: [in] if not NULL, the image written will specify this resource as the smask for + * the image (images only) + * @pdf_source: [out] return pdf_source_surface entry in hash table + * @x_offset: [out] if not NULL return x offset of surface + * @y_offset: [out] if not NULL return y offset of surface + * @source_extents: [out] if not NULL return operation extents in source space * * Add surface or raster_source pattern to list of surfaces to be * written to the PDF file when the current page is finished. Returns - * a PDF resource to reference the image. A hash table of all images - * in the PDF files (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or surface - * unique_id) to ensure surfaces with the same id are only written - * once to the PDF file. + * a PDF resource to reference the surface. A hash table of all + * surfaces in the PDF file (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or + * surface unique_id) is used to ensure surfaces with the same id are + * only written once to the PDF file. * * Only one of @source_pattern or @source_surface is to be * specified. Set the other to NULL. **/ static cairo_int_status_t -_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *source_surface, - const cairo_pattern_t *source_pattern, - cairo_operator_t op, - cairo_filter_t filter, - cairo_bool_t stencil_mask, - cairo_bool_t smask, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t *smask_res, - cairo_pdf_resource_t *surface_res, - int *width, - int *height, - double *x_offset, - double *y_offset, - cairo_rectangle_int_t *source_extents) +_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *source_surface, + const cairo_pattern_t *source_pattern, + cairo_operator_t op, + cairo_filter_t filter, + cairo_bool_t stencil_mask, + cairo_bool_t smask, + const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *smask_res, + cairo_pdf_source_surface_entry_t **pdf_source, + double *x_offset, + double *y_offset, + cairo_rectangle_int_t *source_extents) { cairo_pdf_source_surface_t src_surface; cairo_pdf_source_surface_entry_t surface_key; cairo_pdf_source_surface_entry_t *surface_entry; - cairo_int_status_t status; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_bool_t interpolate; unsigned char *unique_id = NULL; unsigned long unique_id_length = 0; cairo_image_surface_t *image; void *image_extra; + cairo_box_t box; + cairo_rectangle_int_t op_extents; + double x, y; + cairo_bool_t subsurface; switch (filter) { default: @@ -1393,8 +1539,8 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, break; } - *x_offset = 0; - *y_offset = 0; + x = 0; + y = 0; if (source_pattern) { if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern, @@ -1402,12 +1548,27 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; source_surface = &image->base; - cairo_surface_get_device_offset (source_surface, x_offset, y_offset); + cairo_surface_get_device_offset (source_surface, &x, &y); } else { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern; source_surface = surface_pattern->surface; } } + if (x_offset) + *x_offset = x; + if (y_offset) + *y_offset = y; + + /* transform operation extents to pattern space */ + op_extents = *extents; + if (source_pattern) + { + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&source_pattern->matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &op_extents); + } + if (source_extents) + *source_extents = op_extents; surface_key.id = source_surface->unique_id; surface_key.interpolate = interpolate; @@ -1417,40 +1578,36 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, _cairo_pdf_source_surface_init_key (&surface_key); surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); if (surface_entry) { - *surface_res = surface_entry->surface_res; - *width = surface_entry->width; - *height = surface_entry->height; - *source_extents = surface_entry->extents; - status = CAIRO_STATUS_SUCCESS; - } else { - status = _get_source_surface_size (source_surface, - width, - height, - source_extents); - if (unlikely(status)) - goto release_source; + if (pdf_source) + *pdf_source = surface_entry; - if (surface_key.unique_id && surface_key.unique_id_length > 0) { - unique_id = _cairo_malloc (surface_key.unique_id_length); - if (unique_id == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto release_source; - } + if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE) + _cairo_unbounded_rectangle_init (&op_extents); - unique_id_length = surface_key.unique_id_length; - memcpy (unique_id, surface_key.unique_id, unique_id_length); - } else { - unique_id = NULL; - unique_id_length = 0; - } + _cairo_rectangle_intersect (&op_extents, &surface_entry->extents); + _cairo_rectangle_union (&surface_entry->required_extents, &op_extents); + status = CAIRO_STATUS_SUCCESS; } -release_source: - if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) - _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); - - if (status || surface_entry) + if (status || surface_entry) { + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); return status; + } + + if (surface_key.unique_id && surface_key.unique_id_length > 0) { + unique_id = _cairo_malloc (surface_key.unique_id_length); + if (unique_id == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + unique_id_length = surface_key.unique_id_length; + memcpy (unique_id, surface_key.unique_id, unique_id_length); + } else { + unique_id = NULL; + unique_id_length = 0; + } surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t)); if (surface_entry == NULL) { @@ -1458,6 +1615,8 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, goto fail1; } + if (pdf_source) + *pdf_source = surface_entry; surface_entry->id = surface_key.id; surface_entry->operator = op; surface_entry->interpolate = interpolate; @@ -1465,13 +1624,29 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, surface_entry->smask = smask; surface_entry->unique_id_length = unique_id_length; surface_entry->unique_id = unique_id; - surface_entry->width = *width; - surface_entry->height = *height; - surface_entry->extents = *source_extents; if (smask_res) surface_entry->smask_res = *smask_res; else surface_entry->smask_res.id = 0; + + status = _get_source_surface_extents (source_surface, + &surface_entry->extents, + &surface_entry->bounded, + &subsurface); + if (unlikely (status)) + goto fail2; + + if (subsurface) { + *x_offset = -surface_entry->extents.x; + *y_offset = -surface_entry->extents.y; + } + + if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE) + _cairo_unbounded_rectangle_init (&op_extents); + + _cairo_rectangle_intersect (&op_extents, &surface_entry->extents); + surface_entry->required_extents = op_extents; + _cairo_pdf_source_surface_init_key (surface_entry); src_surface.hash_entry = surface_entry; @@ -1503,9 +1678,10 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, if (unlikely(status)) goto fail3; - *surface_res = surface_entry->surface_res; + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); - return status; + return CAIRO_STATUS_SUCCESS; fail3: if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) @@ -1517,7 +1693,11 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, free (surface_entry); fail1: - free (unique_id); + if (unique_id) + free (unique_id); + + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); return status; } @@ -1586,6 +1766,10 @@ _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, *pattern_res = pdf_pattern.pattern_res; *gstate_res = pdf_pattern.gstate_res; + /* If the pattern requires a gstate it will be drawn from within + * an XObject. The initial space of each XObject has an inverted + * Y-axis. */ + pdf_pattern.inverted_y_axis = pdf_pattern.gstate_res.id ? TRUE : surface->in_xobject; status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); if (unlikely (status)) { @@ -1603,9 +1787,9 @@ _get_bbox_from_extents (double surface_height, cairo_box_double_t *bbox) { bbox->p1.x = extents->x; - bbox->p1.y = surface_height - (extents->y + extents->height); + bbox->p1.y = extents->y; bbox->p2.x = extents->x + extents->width; - bbox->p2.y = surface_height - extents->y; + bbox->p2.y = extents->y + extents->height; } static cairo_int_status_t @@ -1707,6 +1891,7 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, surface->output = output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); } + _cairo_pdf_operators_reset (&surface->pdf_operators); return _cairo_output_stream_get_status (surface->output); } @@ -1967,6 +2152,9 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, resource, surface->compress_content, NULL); + _cairo_output_stream_printf (surface->output, + "1 0 0 -1 0 %f cm\n", + surface->height); } if (unlikely (status)) return status; @@ -1974,6 +2162,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, surface->content = surface->pdf_stream.self; _cairo_output_stream_printf (surface->output, "q\n"); + _cairo_pdf_operators_reset (&surface->pdf_operators); return _cairo_output_stream_get_status (surface->output); } @@ -2023,10 +2212,11 @@ _cairo_pdf_surface_finish (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; long offset; - cairo_pdf_resource_t info, catalog; + cairo_pdf_resource_t catalog; cairo_status_t status, status2; int size, i; cairo_pdf_jbig2_global_t *global; + char *label; status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS) @@ -2034,9 +2224,9 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_pdf_surface_write_pages (surface); - info = _cairo_pdf_surface_write_info (surface); - if (info.id == 0 && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_pdf_interchange_write_document_objects (surface); + if (unlikely (status)) + return status; catalog = _cairo_pdf_surface_write_catalog (surface); if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) @@ -2052,7 +2242,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) ">>\n", surface->next_available_resource.id, catalog.id, - info.id); + surface->docinfo_res.id); _cairo_output_stream_printf (surface->output, "startxref\n" @@ -2108,6 +2298,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->smask_groups); _cairo_array_fini (&surface->fonts); _cairo_array_fini (&surface->knockout_group); + _cairo_array_fini (&surface->page_annots); if (surface->font_subsets) { _cairo_scaled_font_subsets_destroy (surface->font_subsets); @@ -2123,17 +2314,26 @@ _cairo_pdf_surface_finish (void *abstract_surface) } _cairo_array_fini (&surface->jbig2_global); + size = _cairo_array_num_elements (&surface->page_labels); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->page_labels, i, &label); + free (label); + } + _cairo_array_fini (&surface->page_labels); + _cairo_array_truncate (&surface->page_surfaces, 0); _cairo_surface_clipper_reset (&surface->clipper); - return status; + return _cairo_pdf_interchange_fini (surface); } static cairo_int_status_t _cairo_pdf_surface_start_page (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_resource_t page; + cairo_int_status_t status; /* Document header */ if (! surface->header_emitted) { @@ -2157,6 +2357,15 @@ _cairo_pdf_surface_start_page (void *abstract_surface) } _cairo_pdf_group_resources_clear (&surface->resources); + surface->in_xobject = FALSE; + + page = _cairo_pdf_surface_new_object (surface); + if (page.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&surface->pages, &page); + if (unlikely (status)) + return status; return CAIRO_STATUS_SUCCESS; } @@ -2170,6 +2379,7 @@ _cairo_pdf_surface_has_fallback_images (void *abstract_surface, cairo_box_double_t bbox; surface->has_fallback_images = has_fallbacks; + surface->in_xobject = has_fallbacks; bbox.p1.x = 0; bbox.p1.y = 0; bbox.p2.x = surface->width; @@ -2187,22 +2397,47 @@ _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) return TRUE; } +static cairo_bool_t +_cairo_pdf_surface_requires_thumbnail_image (void *abstract_surface, + int *width, + int *height) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->thumbnail_width > 0 && surface->thumbnail_height > 0) { + *width = surface->thumbnail_width; + *height = surface->thumbnail_height; + return TRUE; + } + + return FALSE; +} + +static cairo_int_status_t +_cairo_pdf_surface_set_thumbnail_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + surface->thumbnail_image = (cairo_image_surface_t *)cairo_surface_reference(&image->base); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t *surface_res, - int *width, - int *height, + cairo_pdf_source_surface_entry_t **pdf_source, double *x_offset, - double *y_offset) + double *y_offset, + cairo_rectangle_int_t *source_extents) { cairo_image_surface_t *image; cairo_surface_t *pad_image; void *image_extra; cairo_int_status_t status; int w, h; - cairo_rectangle_int_t extents2; cairo_box_t box; cairo_rectangle_int_t rect; cairo_surface_pattern_t pad_pattern; @@ -2249,18 +2484,16 @@ _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surfa status = _cairo_pdf_surface_add_source_surface (surface, pad_image, NULL, - FALSE, + CAIRO_OPERATOR_OVER, /* not used for images */ source->filter, - FALSE, - FALSE, + FALSE, /* stencil mask */ + FALSE, /* smask */ extents, - NULL, - surface_res, - width, - height, + NULL, /* smask_res */ + pdf_source, x_offset, y_offset, - &extents2); + source_extents); if (unlikely (status)) goto BAIL; @@ -2924,6 +3157,7 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, " /Height %d\n" " /ColorSpace %s\n" " /Interpolate %s\n" + "%s" " /BitsPerComponent %d\n" "%s" " /Filter /DCTDecode\n", @@ -2931,6 +3165,7 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, info.height, colorspace, surface_entry->interpolate ? "true" : "false", + info.is_adobe_jpeg && info.num_components == 4 ? " /Decode [ 1 0 1 0 1 0 1 0 ]\n" : "", info.bits_per_component, smask_buf); } @@ -2992,8 +3227,11 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *pdf_source) { double old_width, old_height; + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; cairo_paginated_mode_t old_paginated_mode; cairo_surface_clipper_t old_clipper; + cairo_bool_t old_in_xobject; cairo_box_double_t bbox; cairo_int_status_t status; int alpha = 0; @@ -3007,9 +3245,9 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, cairo_recording_surface_t *recording; assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE); - extents = &pdf_source->hash_entry->extents; - width = pdf_source->hash_entry->width; - height = pdf_source->hash_entry->height; + extents = &pdf_source->hash_entry->required_extents; + width = pdf_source->hash_entry->extents.width; + height = pdf_source->hash_entry->extents.height; is_subsurface = FALSE; source = pdf_source->surface; if (_cairo_surface_is_snapshot (source)) @@ -3030,12 +3268,19 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, old_width = surface->width; old_height = surface->height; + old_in_xobject = surface->in_xobject; + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; old_paginated_mode = surface->paginated_mode; old_clipper = surface->clipper; + surface->surface_extents = *extents; _cairo_surface_clipper_init (&surface->clipper, _cairo_pdf_surface_clipper_intersect_clip_path); - _cairo_pdf_surface_set_size_internal (surface, width, height); + _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->in_xobject = TRUE; + surface->surface_extents = *extents; + surface->surface_bounded = FALSE; /* Patterns are emitted after fallback images. The paginated mode * needs to be set to _RENDER while the recording surface is replayed @@ -3044,10 +3289,10 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; _cairo_pdf_group_resources_clear (&surface->resources); if (is_subsurface) { - bbox.p1.x = 0; - bbox.p1.y = 0; - bbox.p2.x = extents->width; - bbox.p2.y = extents->height; + bbox.p1.x = extents->x; + bbox.p1.y = extents->y; + bbox.p2.x = extents->x + extents->width; + bbox.p2.y = extents->y + extents->height; } else { _get_bbox_from_extents (height, extents, &bbox); } @@ -3088,10 +3333,11 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, _cairo_surface_clipper_reset (&surface->clipper); surface->clipper = old_clipper; - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); + _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->in_xobject = old_in_xobject; surface->paginated_mode = old_paginated_mode; + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; err: cairo_surface_destroy (free_me); @@ -3115,51 +3361,53 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, { cairo_pattern_t *pattern = pdf_pattern->pattern; cairo_int_status_t status; - cairo_pdf_resource_t pattern_resource = {0}; cairo_matrix_t cairo_p2d, pdf_p2d; cairo_extend_t extend = cairo_pattern_get_extend (pattern); double xstep, ystep; cairo_rectangle_int_t pattern_extents; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ double x_offset; double y_offset; - char draw_surface[200]; - cairo_box_double_t bbox; + char draw_surface[50]; + char draw_surface2[200]; + cairo_box_double_t bbox; + cairo_matrix_t mat; + cairo_pdf_source_surface_entry_t *pdf_source; + cairo_rectangle_int_t op_extents; + assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); if (pattern->extend == CAIRO_EXTEND_PAD) { status = _cairo_pdf_surface_add_padded_image_surface (surface, pattern, &pdf_pattern->extents, - &pattern_resource, - &pattern_width, - &pattern_height, + &pdf_source, &x_offset, - &y_offset); - pattern_extents.x = 0; - pattern_extents.y = 0; - pattern_extents.width = pattern_width; - pattern_extents.height = pattern_height; + &y_offset, + &op_extents); } else { status = _cairo_pdf_surface_add_source_surface (surface, NULL, pattern, pdf_pattern->operator, pattern->filter, - FALSE, - FALSE, + FALSE, /* stencil mask */ + FALSE, /* smask */ &pdf_pattern->extents, - NULL, - &pattern_resource, - &pattern_width, - &pattern_height, + NULL, /* smask_res */ + &pdf_source, &x_offset, &y_offset, - &pattern_extents); + &op_extents); } if (unlikely (status)) return status; + pattern_extents = pdf_source->extents; + if (!pdf_source->bounded) + { + extend = CAIRO_EXTEND_NONE; + _cairo_rectangle_intersect (&pattern_extents, &op_extents); + } + switch (extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: @@ -3177,7 +3425,8 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, * repeat visibly. */ double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); @@ -3187,23 +3436,23 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); + pattern_extents.width + pattern_extents.height); } break; case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; + case CAIRO_EXTEND_REFLECT: - pattern_extents.x = 0; - pattern_extents.y = 0; - pattern_extents.width = pattern_width*2; - pattern_extents.height = pattern_height*2; - xstep = pattern_width*2; - ystep = pattern_height*2; + pattern_extents.width *= 2; + pattern_extents.height *= 2; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; - /* All the rest (if any) should have been analyzed away, so this - * case should be unreachable. */ + + /* All the rest (if any) should have been analyzed away, so this + * case should be unreachable. */ default: ASSERT_NOT_REACHED; xstep = 0; @@ -3242,12 +3491,20 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); - cairo_matrix_translate (&pdf_p2d, -x_offset, -y_offset); - cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + if (pdf_pattern->inverted_y_axis) + cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); + else + cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); + + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &mat); + cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); + if (((cairo_surface_pattern_t *)pattern)->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + { + cairo_matrix_translate (&pdf_p2d, 0.0, pdf_source->extents.height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + } - _get_bbox_from_extents (pattern_height, &pattern_extents, &bbox); + _get_bbox_from_extents (pattern_extents.height, &pattern_extents, &bbox); _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); status = _cairo_pdf_surface_open_stream (surface, &pdf_pattern->pattern_res, @@ -3265,40 +3522,59 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, pdf_p2d.xx, pdf_p2d.yx, pdf_p2d.xy, pdf_p2d.yy, pdf_p2d.x0, pdf_p2d.y0, - pattern_resource.id, - pattern_resource.id); + pdf_source->surface_res.id, + pdf_source->surface_res.id); if (unlikely (status)) return status; - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && - ((cairo_surface_pattern_t *) pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (((cairo_surface_pattern_t *) pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { snprintf(draw_surface, sizeof (draw_surface), - "/x%d Do\n", - pattern_resource.id); + "/x%d Do", + pdf_source->surface_res.id); } else { snprintf(draw_surface, sizeof (draw_surface), "q %d 0 0 %d 0 0 cm /x%d Do Q", - pattern_width, - pattern_height, - pattern_resource.id); + pdf_source->extents.width, + pdf_source->extents.height, + pdf_source->surface_res.id); } if (extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->output, - "q 0 0 %d %d re W n %s Q\n" - "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n" - "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n" - "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n", - pattern_width, pattern_height, - draw_surface, - pattern_width*2, pattern_width, pattern_height, - draw_surface, - pattern_height*2, pattern_width, pattern_height, - draw_surface, - pattern_width*2, pattern_height*2, pattern_width, pattern_height, - draw_surface); + cairo_rectangle_int_t p_extents = pdf_source->extents; + snprintf(draw_surface2, + sizeof (draw_surface2), + "%d %d %d %d re W n %s", + p_extents.x, p_extents.y, + p_extents.width, p_extents.height, + draw_surface); + + _cairo_output_stream_printf (surface->output, "q %s Q\n", draw_surface2); + + cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); + cairo_matrix_scale (&mat, -1, 1); + cairo_matrix_translate (&mat, -2*p_extents.width, 0); + cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); + _cairo_output_stream_printf (surface->output, "q "); + _cairo_output_stream_print_matrix (surface->output, &mat); + _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); + + cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, 0, -2*p_extents.height); + cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); + _cairo_output_stream_printf (surface->output, "q "); + _cairo_output_stream_print_matrix (surface->output, &mat); + _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); + + cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); + cairo_matrix_scale (&mat, -1, -1); + cairo_matrix_translate (&mat, -2*p_extents.width, -2*p_extents.height); + cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); + _cairo_output_stream_printf (surface->output, "q "); + _cairo_output_stream_print_matrix (surface->output, &mat); + _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); } else { _cairo_output_stream_printf (surface->output, " %s \n", @@ -3923,6 +4199,7 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, cairo_circle_double_t start, end; double domain[2]; cairo_int_status_t status; + cairo_matrix_t mat; assert (pattern->n_stops != 0); @@ -3937,7 +4214,13 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); + + if (pdf_pattern->inverted_y_axis) + cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); + else + cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); + + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat); if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) @@ -4069,13 +4352,19 @@ _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, cairo_pdf_shading_t shading; int i; cairo_pdf_resource_t res; + cairo_matrix_t mat; pat_to_pdf = pattern->matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); + if (pdf_pattern->inverted_y_axis) + cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); + else + cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); + + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat); status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern); if (unlikely (status)) @@ -4207,15 +4496,8 @@ _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, static cairo_int_status_t _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { - double old_width, old_height; cairo_int_status_t status; - old_width = surface->width; - old_height = surface->height; - _cairo_pdf_surface_set_size_internal (surface, - pdf_pattern->width, - pdf_pattern->height); - switch (pdf_pattern->pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: ASSERT_NOT_REACHED; @@ -4242,10 +4524,6 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern break; } - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - return status; } @@ -4257,14 +4535,12 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *smask_res, cairo_bool_t stencil_mask) { - cairo_pdf_resource_t surface_res; - int width, height; cairo_matrix_t cairo_p2d, pdf_p2d; cairo_int_status_t status; int alpha; - cairo_rectangle_int_t extents2; double x_offset; double y_offset; + cairo_pdf_source_surface_entry_t *pdf_source; if (source->extend == CAIRO_EXTEND_PAD && !(source->type == CAIRO_PATTERN_TYPE_SURFACE && @@ -4273,11 +4549,10 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_padded_image_surface (surface, source, extents, - &surface_res, - &width, - &height, + &pdf_source, &x_offset, - &y_offset); + &y_offset, + NULL); } else { status = _cairo_pdf_surface_add_source_surface (surface, NULL, @@ -4285,15 +4560,13 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, op, source->filter, stencil_mask, - FALSE, + FALSE, /* smask */ extents, smask_res, - &surface_res, - &width, - &height, + &pdf_source, &x_offset, &y_offset, - &extents2); + NULL); } if (unlikely (status)) return status; @@ -4306,12 +4579,12 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, pdf_p2d = surface->cairo_to_pdf; cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); - cairo_matrix_translate (&pdf_p2d, 0.0, height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); if (!(source->type == CAIRO_PATTERN_TYPE_SURFACE && ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)) { - cairo_matrix_scale (&pdf_p2d, width, height); + cairo_matrix_translate (&pdf_p2d, 0.0, pdf_source->extents.height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + cairo_matrix_scale (&pdf_p2d, pdf_source->extents.width, pdf_source->extents.height); } status = _cairo_pdf_operators_flush (&surface->pdf_operators); @@ -4330,15 +4603,15 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, if (stencil_mask) { _cairo_output_stream_printf (surface->output, "/x%d Do\n", - surface_res.id); + pdf_source->surface_res.id); } else { _cairo_output_stream_printf (surface->output, "/a%d gs /x%d Do\n", alpha, - surface_res.id); + pdf_source->surface_res.id); } - return _cairo_pdf_surface_add_xobject (surface, surface_res); + return _cairo_pdf_surface_add_xobject (surface, pdf_source->surface_res); } static cairo_int_status_t @@ -4603,6 +4876,13 @@ _cairo_pdf_surface_show_page (void *abstract_surface) cairo_pdf_surface_t *surface = abstract_surface; cairo_int_status_t status; + status = _cairo_array_append (&surface->page_labels, &surface->current_page_label); + surface->current_page_label = NULL; + + status = _cairo_pdf_interchange_end_page_content (surface); + if (unlikely (status)) + return status; + status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) return status; @@ -4624,17 +4904,10 @@ _cairo_pdf_surface_get_extents (void *abstract_surface, { cairo_pdf_surface_t *surface = abstract_surface; - rectangle->x = 0; - rectangle->y = 0; - - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the arbitrary limitation of width to a short(!). We - * may need to come up with a better interface for get_size. - */ - rectangle->width = ceil (surface->width); - rectangle->height = ceil (surface->height); + if (surface->surface_bounded) + *rectangle = surface->surface_extents; - return TRUE; + return surface->surface_bounded; } static void @@ -4649,28 +4922,6 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface, _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } -static cairo_pdf_resource_t -_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t info; - - info = _cairo_pdf_surface_new_object (surface); - if (info.id == 0) - return info; - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Creator (cairo %s (http://cairographics.org))\n" - " /Producer (cairo %s (http://cairographics.org))\n" - ">>\n" - "endobj\n", - info.id, - cairo_version_string (), - cairo_version_string ()); - - return info; -} - static void _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) { @@ -4701,8 +4952,8 @@ _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) "endobj\n"); } -static cairo_int_status_t -_utf8_to_pdf_string (const char *utf8, char **str_out) +cairo_int_status_t +_cairo_utf8_to_pdf_string (const char *utf8, char **str_out) { int i; int len; @@ -4767,8 +5018,12 @@ _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) + if (unlikely (status == CAIRO_INT_STATUS_INVALID_STRING)) { + utf16 = NULL; + utf16_len = 0; + } else if (unlikely (status)) { return status; + } } _cairo_output_stream_printf (surface->output, "<"); @@ -5043,14 +5298,15 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, if (subset->family_name_utf8) { char *pdf_str; - status = _utf8_to_pdf_string (subset->family_name_utf8, &pdf_str); - if (unlikely (status)) + status = _cairo_utf8_to_pdf_string (subset->family_name_utf8, &pdf_str); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + _cairo_output_stream_printf (surface->output, + " /FontFamily %s\n", + pdf_str); + free (pdf_str); + } else if (status != CAIRO_INT_STATUS_INVALID_STRING) { return status; - - _cairo_output_stream_printf (surface->output, - " /FontFamily %s\n", - pdf_str); - free (pdf_str); + } } _cairo_output_stream_printf (surface->output, @@ -5488,14 +5744,15 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, if (subset.family_name_utf8) { char *pdf_str; - status = _utf8_to_pdf_string (subset.family_name_utf8, &pdf_str); - if (unlikely (status)) + status = _cairo_utf8_to_pdf_string (subset.family_name_utf8, &pdf_str); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + _cairo_output_stream_printf (surface->output, + " /FontFamily %s\n", + pdf_str); + free (pdf_str); + } else if (status != CAIRO_INT_STATUS_INVALID_STRING) { return status; - - _cairo_output_stream_printf (surface->output, - " /FontFamily %s\n", - pdf_str); - free (pdf_str); + } } _cairo_output_stream_printf (surface->output, @@ -5859,16 +6116,16 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, "<< /Type /Font\n" " /Subtype /Type3\n" " /FontBBox [%f %f %f %f]\n" - " /FontMatrix [ 1 0 0 1 0 0 ]\n" + " /FontMatrix [ 1 0 0 -1 0 0 ]\n" " /Encoding %d 0 R\n" " /CharProcs %d 0 R\n" " /FirstChar 0\n" " /LastChar %d\n", subset_resource.id, _cairo_fixed_to_double (font_bbox.p1.x), - - _cairo_fixed_to_double (font_bbox.p2.y), + _cairo_fixed_to_double (font_bbox.p1.y), _cairo_fixed_to_double (font_bbox.p2.x), - - _cairo_fixed_to_double (font_bbox.p1.y), + _cairo_fixed_to_double (font_bbox.p2.y), encoding.id, char_procs.id, font_subset->num_glyphs - 1); @@ -5992,12 +6249,42 @@ _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Catalog\n" - " /Pages %d 0 R\n" - ">>\n" - "endobj\n", + " /Pages %d 0 R\n", catalog.id, surface->pages_resource.id); + if (surface->struct_tree_root.id != 0) { + _cairo_output_stream_printf (surface->output, + " /StructTreeRoot %d 0 R\n", + surface->struct_tree_root.id); + if (surface->tagged) { + _cairo_output_stream_printf (surface->output, + " /MarkInfo << /Marked true >>\n"); + } + } + + if (surface->outlines_dict_res.id != 0) { + _cairo_output_stream_printf (surface->output, + " /Outlines %d 0 R\n", + surface->outlines_dict_res.id); + } + + if (surface->page_labels_res.id != 0) { + _cairo_output_stream_printf (surface->output, + " /PageLabels %d 0 R\n", + surface->page_labels_res.id); + } + + if (surface->names_dict_res.id != 0) { + _cairo_output_stream_printf (surface->output, + " /Names %d 0 R\n", + surface->names_dict_res.id); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + return catalog; } @@ -6228,14 +6515,20 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { double old_width, old_height; + cairo_bool_t old_in_xobject; cairo_int_status_t status; cairo_box_double_t bbox; + cairo_rectangle_int_t old_surface_extents; old_width = surface->width; old_height = surface->height; + old_surface_extents = surface->surface_extents; + old_in_xobject = surface->in_xobject; + surface->in_xobject = TRUE; _cairo_pdf_surface_set_size_internal (surface, group->width, group->height); + _cairo_pdf_operators_reset (&surface->pdf_operators); /* _mask is a special case that requires two groups - source * and mask as well as a smask and gstate dictionary */ if (group->operation == PDF_MASK) { @@ -6295,9 +6588,12 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_close_group (surface, NULL); RESTORE_SIZE: + surface->in_xobject = old_in_xobject; _cairo_pdf_surface_set_size_internal (surface, old_width, old_height); + surface->surface_extents = old_surface_extents; + _cairo_pdf_operators_reset (&surface->pdf_operators); return status; } @@ -6353,9 +6649,14 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) { - cairo_pdf_resource_t page, knockout, res; + cairo_pdf_resource_t knockout, res, thumbnail_res; + cairo_pdf_resource_t *page; cairo_int_status_t status; - unsigned int i, len; + unsigned int i, len, page_num, num_annots; + + status = _cairo_pdf_interchange_write_page_objects (surface); + if (unlikely (status)) + return status; _cairo_pdf_group_resources_clear (&surface->resources); if (surface->has_fallback_images) { @@ -6409,10 +6710,19 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) return status; } - page = _cairo_pdf_surface_new_object (surface); - if (page.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + thumbnail_res.id = 0; + if (surface->thumbnail_image) { + cairo_pdf_source_surface_entry_t entry; + memset (&entry, 0, sizeof (entry)); + thumbnail_res = _cairo_pdf_surface_new_object (surface); + entry.surface_res = thumbnail_res; + _cairo_pdf_surface_emit_image (surface, surface->thumbnail_image, &entry); + } + + page_num = _cairo_array_num_elements (&surface->pages); + page = _cairo_array_index (&surface->pages, page_num - 1); + _cairo_pdf_surface_update_object (surface, *page); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Page\n" @@ -6425,19 +6735,42 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) " /I true\n" " /CS /DeviceRGB\n" " >>\n" - " /Resources %d 0 R\n" - ">>\n" - "endobj\n", - page.id, + " /Resources %d 0 R\n", + page->id, surface->pages_resource.id, surface->width, surface->height, surface->content.id, surface->content_resources.id); - status = _cairo_array_append (&surface->pages, &page); - if (unlikely (status)) - return status; + if (surface->page_parent_tree >= 0) { + _cairo_output_stream_printf (surface->output, + " /StructParents %d\n", + surface->page_parent_tree); + } + + num_annots = _cairo_array_num_elements (&surface->page_annots); + if (num_annots > 0) { + _cairo_output_stream_printf (surface->output, + " /Annots [ "); + for (i = 0; i < num_annots; i++) { + _cairo_array_copy_element (&surface->page_annots, i, &res); + _cairo_output_stream_printf (surface->output, + "%d 0 R ", + res.id); + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + + if (thumbnail_res.id) { + _cairo_output_stream_printf (surface->output, + " /Thumb %d 0 R\n", + thumbnail_res.id); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface); if (unlikely (status)) @@ -6681,7 +7014,11 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; - return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE); + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE); + if (unlikely (status)) + return status; + + return _cairo_pdf_interchange_begin_page_content (surface); } /* If source is an opaque image and mask is an image and both images @@ -6698,7 +7035,6 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, cairo_image_surface_t *image; void *image_extra; cairo_image_transparency_t transparency; - cairo_pdf_resource_t smask_res; int src_width, src_height; int mask_width, mask_height; double src_x_offset, src_y_offset; @@ -6707,8 +7043,8 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, double mask_x1, mask_y1, mask_x2, mask_y2; cairo_matrix_t p2u; double src_radius, mask_radius, e; - cairo_rectangle_int_t extents2; cairo_bool_t need_smask; + cairo_pdf_source_surface_entry_t *pdf_source; /* Check that source and mask are images */ @@ -6827,16 +7163,14 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, mask, op, source->filter, - FALSE, - TRUE, + FALSE, /* stencil mask */ + TRUE, /* smask */ extents, + NULL, /* smask_res */ + &pdf_source, NULL, - &smask_res, - &mask_width, - &mask_height, - &mask_x_offset, - &mask_y_offset, - &extents2); + NULL, + NULL); if (unlikely (status)) return status; } @@ -6847,7 +7181,7 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, extents, - need_smask ? &smask_res : NULL, + need_smask ? &pdf_source->surface_res : NULL, FALSE); if (unlikely (status)) return status; @@ -6960,11 +7294,15 @@ _cairo_pdf_surface_paint (void *abstract_surface, if (unlikely (status)) return status; + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + return status; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); + status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) goto cleanup; } @@ -7045,8 +7383,11 @@ _cairo_pdf_surface_paint (void *abstract_surface, goto cleanup; _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); + "%d %d %d %d re f\n", + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) @@ -7081,6 +7422,10 @@ _cairo_pdf_surface_mask (void *abstract_surface, if (unlikely (status)) return status; + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + return status; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { cairo_int_status_t source_status, mask_status; @@ -7250,6 +7595,10 @@ _cairo_pdf_surface_stroke (void *abstract_surface, goto cleanup; } + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + goto cleanup; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; @@ -7384,6 +7733,10 @@ _cairo_pdf_surface_fill (void *abstract_surface, goto cleanup; } + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + goto cleanup; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; @@ -7602,6 +7955,10 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, goto cleanup; } + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + goto cleanup; + fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, @@ -7697,6 +8054,10 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, if (unlikely (status)) return status; + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + return status; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; @@ -7834,13 +8195,48 @@ _cairo_pdf_surface_get_supported_mime_types (void *abstract_surface) return _cairo_pdf_supported_mime_types; } -static void +static cairo_int_status_t +_cairo_pdf_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status = 0; + + if (begin) + status = _cairo_pdf_interchange_tag_begin (surface, tag_name, attributes); + else + status = _cairo_pdf_interchange_tag_end (surface, tag_name); + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status; surface->paginated_mode = paginated_mode; + status = _cairo_pdf_interchange_begin_page_content (surface); + if (unlikely (status)) + return status; + + if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + } + + return CAIRO_INT_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_pdf_surface_backend = { @@ -7878,6 +8274,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_has_show_text_glyphs, _cairo_pdf_surface_show_text_glyphs, _cairo_pdf_surface_get_supported_mime_types, + _cairo_pdf_surface_tag, }; static const cairo_paginated_surface_backend_t @@ -7887,4 +8284,6 @@ cairo_pdf_surface_paginated_backend = { NULL, /* set_bounding_box */ _cairo_pdf_surface_has_fallback_images, _cairo_pdf_surface_supports_fine_grained_fallbacks, + _cairo_pdf_surface_requires_thumbnail_image, + _cairo_pdf_surface_set_thumbnail_image, }; diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 1bc8524f2..040c0ed35 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -85,6 +85,73 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points); +/** + * cairo_pdf_outline_flags_t: + * @CAIRO_PDF_OUTLINE_FLAG_OPEN: The outline item defaults to open in the PDF viewer (Since 1.16) + * @CAIRO_PDF_OUTLINE_FLAG_BOLD: The outline item is displayed by the viewer in bold text (Since 1.16) + * @CAIRO_PDF_OUTLINE_FLAG_ITALIC: The outline item is displayed by the viewer in italic text (Since 1.16) + * + * #cairo_pdf_outline_flags_t is used by the + * cairo_pdf_surface_add_outline() function specify the attributes of + * an outline item. These flags may be bitwise-or'd to produce any + * combination of flags. + * + * Since: 1.16 + **/ +typedef enum _cairo_pdf_outline_flags { + CAIRO_PDF_OUTLINE_FLAG_OPEN = 0x1, + CAIRO_PDF_OUTLINE_FLAG_BOLD = 0x2, + CAIRO_PDF_OUTLINE_FLAG_ITALIC = 0x4, +} cairo_pdf_outline_flags_t; + +#define CAIRO_PDF_OUTLINE_ROOT 0 + +cairo_public int +cairo_pdf_surface_add_outline (cairo_surface_t *surface, + int parent_id, + const char *utf8, + const char *dest, + cairo_pdf_outline_flags_t flags); + +/** + * cairo_pdf_metadata_t: + * @CAIRO_PDF_METADATA_TITLE: The document title (Since 1.16) + * @CAIRO_PDF_METADATA_AUTHOR: The document author (Since 1.16) + * @CAIRO_PDF_METADATA_SUBJECT: The document subject (Since 1.16) + * @CAIRO_PDF_METADATA_KEYWORDS: The document keywords (Since 1.16) + * @CAIRO_PDF_METADATA_CREATOR: The document creator (Since 1.16) + * @CAIRO_PDF_METADATA_CREATE_DATE: The document creation date (Since 1.16) + * @CAIRO_PDF_METADATA_MOD_DATE: The document modification date (Since 1.16) + * + * #cairo_pdf_metadata_t is used by the + * cairo_pdf_surface_set_metadata() function specify the metadata to set. + * + * Since: 1.16 + **/ +typedef enum _cairo_pdf_metadata { + CAIRO_PDF_METADATA_TITLE, + CAIRO_PDF_METADATA_AUTHOR, + CAIRO_PDF_METADATA_SUBJECT, + CAIRO_PDF_METADATA_KEYWORDS, + CAIRO_PDF_METADATA_CREATOR, + CAIRO_PDF_METADATA_CREATE_DATE, + CAIRO_PDF_METADATA_MOD_DATE, +} cairo_pdf_metadata_t; + +cairo_public void +cairo_pdf_surface_set_metadata (cairo_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8); + +cairo_public void +cairo_pdf_surface_set_page_label (cairo_surface_t *surface, + const char *utf8); + +cairo_public void +cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface, + int width, + int height); + CAIRO_END_DECLS #else /* CAIRO_HAS_PDF_SURFACE */ diff --git a/src/cairo-png.c b/src/cairo-png.c index 068617d58..562b7439f 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -136,7 +136,7 @@ png_simple_error_callback (png_structp png, /* default to the most likely error */ if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); + *error = _cairo_error (CAIRO_STATUS_PNG_ERROR); #ifdef PNG_SETJMP_SUPPORTED longjmp (png_jmpbuf (png), 1); @@ -349,7 +349,8 @@ stdio_write_func (png_structp png, png_bytep data, png_size_t size) * be allocated for the operation or * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs - * while attempting to write the file. + * while attempting to write the file, or %CAIRO_STATUS_PNG_ERROR if libpng + * returned an error. * * Since: 1.0 **/ @@ -417,7 +418,8 @@ stream_write_func (png_structp png, png_bytep data, png_size_t size) * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if * memory could not be allocated for the operation, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have - * pixel contents. + * pixel contents, or %CAIRO_STATUS_PNG_ERROR if libpng + * returned an error. * * Since: 1.0 **/ @@ -744,6 +746,7 @@ read_png (struct png_read_closure_t *png_closure) * %CAIRO_STATUS_NO_MEMORY * %CAIRO_STATUS_FILE_NOT_FOUND * %CAIRO_STATUS_READ_ERROR + * %CAIRO_STATUS_PNG_ERROR * * Alternatively, you can allow errors to propagate through the drawing * operations and check the status on the context upon completion @@ -799,6 +802,7 @@ cairo_image_surface_create_from_png (const char *filename) * * %CAIRO_STATUS_NO_MEMORY * %CAIRO_STATUS_READ_ERROR + * %CAIRO_STATUS_PNG_ERROR * * Alternatively, you can allow errors to propagate through the drawing * operations and check the status on the context upon completion diff --git a/src/cairo-polygon-intersect.c b/src/cairo-polygon-intersect.c index 8cb8fb120..9c1777f4f 100644 --- a/src/cairo-polygon-intersect.c +++ b/src/cairo-polygon-intersect.c @@ -447,7 +447,7 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; + int32_t ax = 0, bx = 0; if (y == a->edge.line.p1.y) ax = a->edge.line.p1.x; diff --git a/src/cairo-polygon-reduce.c b/src/cairo-polygon-reduce.c index ea457fe4e..da6c9ab7c 100644 --- a/src/cairo-polygon-reduce.c +++ b/src/cairo-polygon-reduce.c @@ -445,7 +445,7 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; + int32_t ax = 0, bx = 0; if (y == a->edge.line.p1.y) ax = a->edge.line.p1.x; diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h index 1d5d27d49..7cd275d73 100644 --- a/src/cairo-ps-surface-private.h +++ b/src/cairo-ps-surface-private.h @@ -66,8 +66,9 @@ typedef struct cairo_ps_surface { cairo_content_t content; double width; double height; - cairo_rectangle_int_t page_bbox; - int bbox_x1, bbox_y1, bbox_x2, bbox_y2; + cairo_point_int_t document_bbox_p1, document_bbox_p2; /* in PS coordinates */ + cairo_rectangle_int_t surface_extents; + cairo_bool_t surface_bounded; cairo_matrix_t cairo_to_ps; cairo_bool_t use_string_datasource; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index fd0d28434..1e020c056 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -84,7 +84,7 @@ #include #include -#define DEBUG_PS 0 +/* #define DEBUG_PS 1 */ #if DEBUG_PS #define DEBUG_FALLBACK(s) \ @@ -109,7 +109,7 @@ /** * CAIRO_HAS_PS_SURFACE: - * + * * Defined if the PostScript surface backend is available. * This macro can be used to conditionally compile backend-specific code. * @@ -259,10 +259,10 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) if (!has_bbox) { _cairo_output_stream_printf (surface->final_stream, "%%%%BoundingBox: %d %d %d %d\n", - surface->bbox_x1, - surface->bbox_y1, - surface->bbox_x2, - surface->bbox_y2); + surface->document_bbox_p1.x, + surface->document_bbox_p1.y, + surface->document_bbox_p2.x, + surface->document_bbox_p2.y); } _cairo_output_stream_printf (surface->final_stream, @@ -335,7 +335,10 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) " cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n" "/g { setgray } bind def\n" "/rg { setrgbcolor } bind def\n" - "/d1 { setcachedevice } bind def\n"); + "/d1 { setcachedevice } bind def\n" + "/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def\n" + "/cairo_image { image cairo_flush_ascii85_file } def\n" + "/cairo_imagemask { imagemask cairo_flush_ascii85_file } def\n"); if (!surface->eps) { _cairo_output_stream_printf (surface->final_stream, @@ -670,7 +673,7 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->final_stream, "8 dict begin\n" "/FontType 3 def\n" - "/FontMatrix [1 0 0 1 0 0] def\n" + "/FontMatrix [1 0 0 -1 0 0] def\n" "/Encoding 256 array def\n" "0 1 255 { Encoding exch /.notdef put } for\n"); @@ -879,11 +882,11 @@ _path_covers_bbox (cairo_ps_surface_t *surface, _cairo_box_round_to_rectangle (&box, &rect); /* skip trivial whole-page clips */ - if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { - if (rect.x == surface->page_bbox.x && - rect.width == surface->page_bbox.width && - rect.y == surface->page_bbox.y && - rect.height == surface->page_bbox.height) + if (_cairo_rectangle_intersect (&rect, &surface->surface_extents)) { + if (rect.x == surface->surface_extents.x && + rect.width == surface->surface_extents.width && + rect.y == surface->surface_extents.y && + rect.height == surface->surface_extents.height) { return TRUE; } @@ -1053,17 +1056,21 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->ps_level_used = CAIRO_PS_LEVEL_2; surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + surface->surface_bounded = TRUE; surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; surface->force_fallbacks = FALSE; surface->content = CAIRO_CONTENT_COLOR_ALPHA; surface->use_string_datasource = FALSE; surface->current_pattern_is_solid_color = FALSE; - - surface->page_bbox.x = 0; - surface->page_bbox.y = 0; - surface->page_bbox.width = width; - surface->page_bbox.height = height; + surface->document_bbox_p1.x = 0; + surface->document_bbox_p1.y = 0; + surface->document_bbox_p2.x = 0; + surface->document_bbox_p2.y = 0; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); @@ -1401,7 +1408,11 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, ps_surface->width = width_in_points; ps_surface->height = height_in_points; - cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); + cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); + ps_surface->surface_extents.x = 0; + ps_surface->surface_extents.y = 0; + ps_surface->surface_extents.width = ceil (ps_surface->width); + ps_surface->surface_extents.height = ceil (ps_surface->height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, &ps_surface->cairo_to_ps); status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface, @@ -1713,15 +1724,16 @@ color_is_gray (double red, double green, double blue) /** * _cairo_ps_surface_acquire_source_surface_from_pattern: - * @surface: the ps surface - * @pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source - * @extents: extents of the operation that is using this source - * @width: returns width of surface - * @height: returns height of surface - * @x_offset: returns x offset of surface - * @y_offset: returns y offset of surface - * @surface: returns surface of type image surface or recording surface - * @image_extra: returns image extra for image type surface + * @surface: [in] the ps surface + * @pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source + * @extents: [in] extents of the operation that is using this source + * @src_surface_extents: [out] return source surface extents + * @src_surface_bounded: [out] return TRUE if source surface is bounded + * @src_op_extents: [out] return operation extents in source space + * @source_surface: [out] returns surface of type image surface or recording surface + * @x_offset: [out] return x offset of surface + * @y_offset: [out] return y offset of surface + * @image_extra: [out] returns image extra for image type surface * * Acquire source surface or raster source pattern. **/ @@ -1729,20 +1741,23 @@ static cairo_status_t _cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, - int *width, - int *height, + cairo_rectangle_int_t *src_surface_extents, + cairo_bool_t *src_surface_bounded, + cairo_rectangle_int_t *src_op_extents, + cairo_surface_t **source_surface, double *x_offset, double *y_offset, - cairo_surface_t **source_surface, void **image_extra) { cairo_status_t status; cairo_image_surface_t *image; - *x_offset = *y_offset = 0; + *x_offset = 0; + *y_offset = 0; *image_extra = NULL; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_box_t bbox; cairo_surface_t *surf = ((cairo_surface_pattern_t *) pattern)->surface; if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) { @@ -1753,26 +1768,17 @@ _cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf; - *width = sub->extents.width; - *height = sub->extents.height; + *src_surface_extents = sub->extents; + *src_surface_bounded = TRUE; + *x_offset = -sub->extents.x; + *y_offset = -sub->extents.y; } else { - cairo_recording_surface_t *recording_surface; - cairo_box_t bbox; - cairo_rectangle_int_t extents; - - recording_surface = (cairo_recording_surface_t *) surf; - - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (unlikely (status)) { - cairo_surface_destroy (*image_extra); - return status; - } - - _cairo_box_round_to_rectangle (&bbox, &extents); - *width = extents.width; - *height = extents.height; + *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents); } *source_surface = surf; + _cairo_box_from_rectangle (&bbox, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &bbox, NULL); + _cairo_box_round_to_rectangle (&bbox, src_op_extents); return CAIRO_STATUS_SUCCESS; } else { @@ -1781,7 +1787,7 @@ _cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t cairo_surface_destroy (*image_extra); return status; } - } + } } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { @@ -1810,8 +1816,11 @@ _cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t break; } - *width = image->width; - *height = image->height; + src_surface_extents->x = 0; + src_surface_extents->y = 0; + src_surface_extents->width = image->width; + src_surface_extents->height = image->height; + *src_surface_bounded = TRUE; *source_surface = &image->base; return CAIRO_STATUS_SUCCESS; } @@ -1819,7 +1828,7 @@ _cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t static void _cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, - cairo_surface_t *source, + cairo_surface_t *source_surface, void *image_extra) { switch (pattern->type) { @@ -1828,13 +1837,13 @@ _cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t if (surf_pat->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { cairo_surface_destroy (image_extra); } else { - cairo_image_surface_t *image = (cairo_image_surface_t *) source; + cairo_image_surface_t *image = (cairo_image_surface_t *) source_surface; _cairo_surface_release_source_image (surf_pat->surface, image, image_extra); } } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - _cairo_raster_source_pattern_release (pattern, source); + _cairo_raster_source_pattern_release (pattern, source_surface); break; case CAIRO_PATTERN_TYPE_SOLID: @@ -1853,11 +1862,8 @@ _cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t * @surface: the ps surface * @source: The source image * @extents: extents of the operation that is using this source - * @width: returns width of padded image - * @height: returns height of padded image - * @x_offset: returns x offset of padded image - * @y_offset: returns y offset of padded image * @image: returns the padded image or NULL if padding not required to fill @extents + * @image_extents: returns extents of padded image. These extents in are in source image space. * * Creates a padded image if the source image does not fill the extents. **/ @@ -1866,11 +1872,8 @@ _cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t * cairo_image_surface_t *source, const cairo_matrix_t *source_matrix, const cairo_rectangle_int_t *extents, - int *width, - int *height, - double *x_offset, - double *y_offset, - cairo_image_surface_t **image) + cairo_image_surface_t **image, + cairo_rectangle_int_t *image_extents) { cairo_box_t box; cairo_rectangle_int_t rect; @@ -1909,10 +1912,10 @@ _cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t * NULL); _cairo_pattern_fini (&pad_pattern.base); *image = (cairo_image_surface_t *) pad_image; - *width = rect.width; - *height = rect.height; - *x_offset = rect.x; - *y_offset = rect.y; + image_extents->x = rect.x; + image_extents->y = rect.y; + image_extents->width = rect.width; + image_extents->height = rect.height; } else { *image = NULL; status = CAIRO_STATUS_SUCCESS; @@ -1926,27 +1929,31 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { - int width, height; + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_rectangle_int_t src_op_extents; + cairo_surface_t *source_surface; double x_offset, y_offset; - cairo_surface_t *source; - cairo_image_surface_t *image; void *image_extra; - cairo_int_status_t status; + cairo_image_surface_t *image; + cairo_int_status_t status; cairo_image_transparency_t transparency; status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, - &width, - &height, + &src_surface_extents, + &src_surface_bounded, + &src_op_extents, + &source_surface, &x_offset, &y_offset, - &source, &image_extra); if (unlikely (status)) return status; - image = (cairo_image_surface_t *) source; + assert (_cairo_surface_is_image (source_surface)); + image = (cairo_image_surface_t *) source_surface; if (image->base.status) return image->base.status; @@ -1973,7 +1980,7 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t ASSERT_NOT_REACHED; } - _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface, image_extra); return status; } @@ -2686,6 +2693,9 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, "] def\n"); _cairo_output_stream_printf (surface->stream, "/CairoImageDataIndex 0 def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); } if (use_mask) { @@ -2720,7 +2730,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, compress_filter); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /%s filter def\n", + " /DataSource cairo_ascii85_file /%s filter def\n", compress_filter); } @@ -2774,15 +2784,16 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, compress_filter); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /%s filter def\n", + " /DataSource cairo_ascii85_file /%s filter def\n", compress_filter); } _cairo_output_stream_printf (surface->stream, " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" "end\n" - "%s\n", + "%s%s\n", ps_image->height, + surface->use_string_datasource ? "" : "cairo_", stencil_mask ? "imagemask" : "image"); } @@ -2872,6 +2883,9 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, "] def\n"); _cairo_output_stream_printf (surface->stream, "/CairoImageDataIndex 0 def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); } _cairo_output_stream_printf (surface->stream, @@ -2898,14 +2912,15 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, " } /ASCII85Decode filter /DCTDecode filter def\n"); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n"); + " /DataSource cairo_ascii85_file /DCTDecode filter def\n"); } _cairo_output_stream_printf (surface->stream, " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" "end\n" - "image\n", - info.height); + "%simage\n", + info.height, + surface->use_string_datasource ? "" : "cairo_"); if (!surface->use_string_datasource) { /* Emit the image data as a base85-encoded string which will @@ -2923,64 +2938,42 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, static cairo_status_t _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, cairo_surface_t *recording_surface, - cairo_bool_t subsurface, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *recording_extents, + cairo_bool_t subsurface) { double old_width, old_height; + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; cairo_surface_clipper_t old_clipper; - cairo_box_t bbox; cairo_int_status_t status; old_content = surface->content; old_width = surface->width; old_height = surface->height; - old_page_bbox = surface->page_bbox; + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; old_cairo_to_ps = surface->cairo_to_ps; old_clipper = surface->clipper; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); - if (subsurface) { - surface->page_bbox.x = surface->page_bbox.y = 0; - surface->page_bbox.width = surface->width = extents->width; - surface->page_bbox.height = surface->height = extents->height; - -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_subsurface" - " (%d, %d), (%d, %d)\n", - extents->x, extents->y, - extents->width, extents->height); -#endif - - } else { - status = - _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, - NULL); - if (unlikely (status)) - return status; - - surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); - surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); - _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); - #if DEBUG_PS _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", - _cairo_fixed_to_double (bbox.p1.x), - _cairo_fixed_to_double (bbox.p1.y), - _cairo_fixed_to_double (bbox.p2.x), - _cairo_fixed_to_double (bbox.p2.y)); + "%% _cairo_ps_surface_emit_recording_surface" + " x: %d, y: %d, w: %d, h: %d subsurface: %d\n", + recording_extents->x, recording_extents->y, + recording_extents->width, recording_extents->height, + subsurface); #endif - } + surface->width = recording_extents->width; + surface->height = recording_extents->height; + surface->surface_extents = *recording_extents; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); _cairo_output_stream_printf (surface->stream, " q\n"); @@ -2989,14 +2982,14 @@ _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + recording_extents->x, + recording_extents->y, + recording_extents->width, + recording_extents->height); } status = _cairo_recording_surface_replay_region (recording_surface, - subsurface ? extents : NULL, + subsurface ? recording_extents : NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); @@ -3014,7 +3007,8 @@ _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, surface->content = old_content; surface->width = old_width; surface->height = old_height; - surface->page_bbox = old_page_bbox; + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; @@ -3068,22 +3062,23 @@ _cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, } static cairo_status_t -_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, - cairo_pattern_t *source_pattern, - cairo_surface_t *source_surface, - cairo_operator_t op, - int width, - int height, - cairo_bool_t stencil_mask) +_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, + const cairo_pattern_t *source_pattern, + cairo_surface_t *source_surface, + cairo_operator_t op, + const cairo_rectangle_int_t *src_surface_extents, + cairo_bool_t src_surface_bounded, + const cairo_rectangle_int_t *src_op_extents, + cairo_bool_t stencil_mask) { cairo_int_status_t status; if (source_pattern->type == CAIRO_PATTERN_TYPE_SURFACE && - source_pattern->extend != CAIRO_EXTEND_PAD) + source_pattern->extend != CAIRO_EXTEND_PAD && src_surface_bounded) { cairo_surface_t *surf = ((cairo_surface_pattern_t *) source_pattern)->surface; - status = _cairo_ps_surface_emit_jpeg_image (surface, surf, width, height); + status = _cairo_ps_surface_emit_jpeg_image (surface, surf, src_surface_extents->width, src_surface_extents->height); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } @@ -3091,9 +3086,9 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, if (source_surface->type == CAIRO_SURFACE_TYPE_RECORDING) { if (source_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source_surface; - status = _cairo_ps_surface_emit_recording_surface (surface, sub->target, TRUE, &sub->extents); + status = _cairo_ps_surface_emit_recording_surface (surface, sub->target, &sub->extents, TRUE); } else { - status = _cairo_ps_surface_emit_recording_surface (surface, source_surface, FALSE, NULL); + status = _cairo_ps_surface_emit_recording_surface (surface, source_surface, src_op_extents, FALSE); } } else { cairo_image_surface_t *image = (cairo_image_surface_t *) source_surface; @@ -3137,19 +3132,21 @@ _path_fixed_init_rectangle (cairo_path_fixed_t *path, static cairo_status_t _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, cairo_operator_t op, cairo_bool_t stencil_mask) { + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_rectangle_int_t src_op_extents; + cairo_surface_t *source_surface; + double x_offset, y_offset; + void *image_extra; cairo_status_t status; - int width, height; cairo_matrix_t cairo_p2d, ps_p2d; cairo_path_fixed_t path; - double x_offset, y_offset; - cairo_surface_t *source; cairo_image_surface_t *image = NULL; - void *image_extra; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) @@ -3158,9 +3155,12 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, - &width, &height, - &x_offset, &y_offset, - &source, + &src_surface_extents, + &src_surface_bounded, + &src_op_extents, + &source_surface, + &x_offset, + &y_offset, &image_extra); if (unlikely (status)) return status; @@ -3170,16 +3170,18 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { cairo_image_surface_t *img; - img = (cairo_image_surface_t *) source; + img = (cairo_image_surface_t *) source_surface; status = _cairo_ps_surface_create_padded_image_from_image (surface, img, &pattern->matrix, extents, - &width, &height, - &x_offset, &y_offset, - &image); + &image, + &src_surface_extents); if (unlikely (status)) goto release_source; + + x_offset = src_surface_extents.x; + y_offset = src_surface_extents.y; } _path_fixed_init_rectangle (&path, extents); @@ -3200,8 +3202,8 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, "%% Fallback Image: x=%f y=%f w=%d h=%d ", -cairo_p2d.x0/x_scale, -cairo_p2d.y0/y_scale, - (int)(width/x_scale), - (int)(height/y_scale)); + (int)(src_surface_extents.width/x_scale), + (int)(src_surface_extents.height/y_scale)); if (x_scale == y_scale) { _cairo_output_stream_printf (surface->stream, "res=%fppi ", @@ -3214,14 +3216,16 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, } _cairo_output_stream_printf (surface->stream, "size=%ld\n", - (long)width*height*3); + (long)src_surface_extents.width * src_surface_extents.height * 3); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", + "%d g %d %d %d %d rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, - surface->width, - surface->height); + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); } } @@ -3232,8 +3236,12 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ps_p2d = surface->cairo_to_ps; cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); cairo_matrix_translate (&ps_p2d, x_offset, y_offset); - cairo_matrix_translate (&ps_p2d, 0.0, height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + if (!(pattern->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)) + { + cairo_matrix_translate (&ps_p2d, 0.0, src_surface_extents.height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + } if (! _cairo_matrix_is_identity (&ps_p2d)) { _cairo_output_stream_printf (surface->stream, "[ "); @@ -3243,16 +3251,18 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, status = _cairo_ps_surface_emit_surface (surface, pattern, - image ? &image->base : source, + image ? &image->base : source_surface, op, - width, height, + &src_surface_extents, + image ? TRUE : src_surface_bounded, + &src_op_extents, stencil_mask); release_source: if (image) cairo_surface_destroy (&image->base); - _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface, image_extra); return status; } @@ -3264,15 +3274,17 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, cairo_operator_t op) { cairo_status_t status; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ double xstep, ystep; + cairo_rectangle_int_t pattern_extents; + cairo_bool_t bounded; cairo_matrix_t cairo_p2d, ps_p2d; cairo_bool_t old_use_string_datasource; double x_offset, y_offset; - cairo_surface_t *source; + cairo_surface_t *source_surface; cairo_image_surface_t *image = NULL; void *image_extra; + cairo_rectangle_int_t src_op_extents; + cairo_extend_t extend = cairo_pattern_get_extend (pattern); cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); @@ -3282,32 +3294,39 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, - &pattern_width, &pattern_height, + &pattern_extents, + &bounded, + &src_op_extents, + &source_surface, &x_offset, &y_offset, - &source, &image_extra); if (unlikely (status)) return status; - if (pattern->extend == CAIRO_EXTEND_PAD) { + if (extend == CAIRO_EXTEND_PAD) { cairo_image_surface_t *img; - assert (source->type == CAIRO_SURFACE_TYPE_IMAGE); - img = (cairo_image_surface_t *) source; + assert (source_surface->type == CAIRO_SURFACE_TYPE_IMAGE); + img = (cairo_image_surface_t *) source_surface; status = _cairo_ps_surface_create_padded_image_from_image (surface, img, &pattern->matrix, extents, - &pattern_width, &pattern_height, - &x_offset, &y_offset, - &image); + &image, + &pattern_extents); if (unlikely (status)) goto release_source; } if (unlikely (status)) goto release_source; - switch (pattern->extend) { + if (!bounded) + { + extend = CAIRO_EXTEND_NONE; + _cairo_rectangle_intersect (&pattern_extents, &src_op_extents); + } + + switch (extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { @@ -3324,7 +3343,8 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, * repeat visibly. */ double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); @@ -3334,16 +3354,16 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); + pattern_extents.width + pattern_extents.height); break; } case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; case CAIRO_EXTEND_REFLECT: - xstep = pattern_width*2; - ystep = pattern_height*2; + xstep = pattern_extents.width*2; + ystep = pattern_extents.height*2; break; /* All the rest (if any) should have been analyzed away, so these * cases should be unreachable. */ @@ -3354,27 +3374,38 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, } _cairo_output_stream_printf (surface->stream, - "/CairoPattern {\n"); + "/CairoPattern {\n" + "q %d %d %d %d rectclip\n", + pattern_extents.x, pattern_extents.y, + pattern_extents.width, pattern_extents.height); old_use_string_datasource = surface->use_string_datasource; surface->use_string_datasource = TRUE; if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", + "%d g %d %d %f %f rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, + pattern_extents.x, pattern_extents.y, xstep, ystep); } + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) + src_op_extents = pattern_extents; + status = _cairo_ps_surface_emit_surface (surface, pattern, - image ? &image->base : source, + image ? &image->base : source_surface, op, - pattern_width, pattern_height, FALSE); + &pattern_extents, + bounded, + &src_op_extents, + FALSE); if (unlikely (status)) goto release_source; surface->use_string_datasource = old_use_string_datasource; _cairo_output_stream_printf (surface->stream, - "} bind def\n"); + " Q } bind def\n"); _cairo_output_stream_printf (surface->stream, "<< /PatternType 1\n" @@ -3384,20 +3415,43 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, " /XStep %f /YStep %f\n", xstep, ystep); - if (pattern->extend == CAIRO_EXTEND_REFLECT) { + if (extend == CAIRO_EXTEND_REFLECT) { + cairo_matrix_t mat; + _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n" + " /BBox [%d %d %d %d]\n" " /PaintProc {\n" - " pop CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " [ 1 0 0 -1 0 %d] concat CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " CairoPattern\n" - " } bind\n", - pattern_width*2, pattern_height*2, - pattern_width*2, - pattern_height*2, - pattern_width*2); + " pop CairoPattern\n", + pattern_extents.x, + pattern_extents.y, + pattern_extents.x + pattern_extents.width*2, + pattern_extents.y + pattern_extents.height*2); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, -1, 1); + cairo_matrix_translate (&mat, -2*pattern_extents.width, 0); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, 0, -2*pattern_extents.height); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, -1, -1); + cairo_matrix_translate (&mat, -2*pattern_extents.width, -2*pattern_extents.height); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + _cairo_output_stream_printf (surface->stream, " } bind\n"); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, @@ -3405,8 +3459,11 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, xstep, ystep); } else { _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n", - pattern_width, pattern_height); + " /BBox [%d %d %d %d]\n", + pattern_extents.x, + pattern_extents.y, + pattern_extents.x + pattern_extents.width, + pattern_extents.y + pattern_extents.height); } _cairo_output_stream_printf (surface->stream, " /PaintProc { pop CairoPattern }\n"); @@ -3421,11 +3478,13 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_init_identity (&ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, surface->height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_translate (&ps_p2d, x_offset, y_offset); + if (((cairo_surface_pattern_t *)pattern)->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + { + cairo_matrix_translate (&ps_p2d, 0.0, pattern_extents.height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + } _cairo_output_stream_printf (surface->stream, "[ "); _cairo_output_stream_print_matrix (surface->stream, &ps_p2d); @@ -3437,7 +3496,7 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, if (image) cairo_surface_destroy (&image->base); - _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface, image_extra); return status; } @@ -4020,7 +4079,7 @@ _cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface, case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_ps_surface_paint_surface (surface, - (cairo_pattern_t *)source, + source, extents, op, stencil_mask); @@ -4029,8 +4088,8 @@ _cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface, case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _cairo_ps_surface_paint_gradient (surface, - source, - extents); + source, + extents); case CAIRO_PATTERN_TYPE_SOLID: default: @@ -4068,17 +4127,10 @@ _cairo_ps_surface_get_extents (void *abstract_surface, { 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 = ceil (surface->width); - rectangle->height = ceil (surface->height); + if (surface->surface_bounded) + *rectangle = surface->surface_extents; - return TRUE; + return surface->surface_bounded; } static void @@ -4162,8 +4214,11 @@ _cairo_ps_surface_paint (void *abstract_surface, if (unlikely (status)) goto cleanup_composite; - _cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n", - surface->width, surface->height); + _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); } cleanup_composite: @@ -4458,7 +4513,7 @@ _cairo_ps_surface_get_supported_mime_types (void *abstract_surface) return _cairo_ps_supported_mime_types; } -static void +static cairo_int_status_t _cairo_ps_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { @@ -4467,34 +4522,57 @@ _cairo_ps_surface_set_paginated_mode (void *abstract_surface, surface->paginated_mode = paginated_mode; - if (surface->clipper.clip != NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); - _cairo_output_stream_printf (surface->stream, "Q q\n"); - _cairo_surface_clipper_reset (&surface->clipper); + if (surface->clipper.clip != NULL) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->stream, "Q q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } } + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_surface_set_bounding_box (void *abstract_surface, - cairo_box_t *bbox) + cairo_box_t *analysis_bbox) { cairo_ps_surface_t *surface = abstract_surface; int i, num_comments; char **comments; - int x1, y1, x2, y2; cairo_bool_t has_page_media, has_page_bbox; const char *page_media; + cairo_rectangle_int_t page_bbox; + cairo_point_int_t bbox_p1, bbox_p2; /* in PS coordinates */ - x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); - y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); - x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); - y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); + _cairo_box_round_to_rectangle (analysis_bbox, &page_bbox); - surface->page_bbox.x = x1; - surface->page_bbox.y = y1; - surface->page_bbox.width = x2 - x1; - surface->page_bbox.height = y2 - y1; + /* convert to PS coordinates */ + bbox_p1.x = page_bbox.x; + bbox_p1.y = ceil(surface->height) - (page_bbox.y + page_bbox.height); + bbox_p2.x = page_bbox.x + page_bbox.width; + bbox_p2.y = ceil(surface->height) - page_bbox.y; + + if (surface->num_pages == 1) { + surface->document_bbox_p1 = bbox_p1; + surface->document_bbox_p2 = bbox_p2; + } else { + if (bbox_p1.x < surface->document_bbox_p1.x) + surface->document_bbox_p1.x = bbox_p1.x; + if (bbox_p1.y < surface->document_bbox_p1.y) + surface->document_bbox_p1.y = bbox_p1.y; + if (bbox_p2.x < surface->document_bbox_p2.x) + surface->document_bbox_p2.x = bbox_p2.x; + if (bbox_p2.y < surface->document_bbox_p2.y) + surface->document_bbox_p2.y = bbox_p2.y; + } _cairo_output_stream_printf (surface->stream, "%%%%Page: %d %d\n", @@ -4535,7 +4613,10 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, if (!has_page_bbox) { _cairo_output_stream_printf (surface->stream, "%%%%PageBoundingBox: %d %d %d %d\n", - x1, y1, x2, y2); + bbox_p1.x, + bbox_p1.y, + bbox_p2.x, + bbox_p2.y); } if (!surface->eps) { @@ -4547,27 +4628,14 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, _cairo_output_stream_printf (surface->stream, "%%%%EndPageSetup\n" - "q %d %d %d %d rectclip q\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + "q %d %d %d %d rectclip\n" + "1 0 0 -1 0 %f cm q\n", + bbox_p1.x, + bbox_p1.y, + bbox_p2.x - bbox_p1.x, + bbox_p2.y - bbox_p1.y, + ceil(surface->height)); - if (surface->num_pages == 1) { - surface->bbox_x1 = x1; - surface->bbox_y1 = y1; - surface->bbox_x2 = x2; - surface->bbox_y2 = y2; - } else { - if (x1 < surface->bbox_x1) - surface->bbox_x1 = x1; - if (y1 < surface->bbox_y1) - surface->bbox_y1 = y1; - if (x2 > surface->bbox_x2) - surface->bbox_x2 = x2; - if (y2 > surface->bbox_y2) - surface->bbox_y2 = y2; - } surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); diff --git a/src/cairo-qt-surface.cpp b/src/cairo-qt-surface.cpp index 7ddad77df..25c60a8d8 100644 --- a/src/cairo-qt-surface.cpp +++ b/src/cairo-qt-surface.cpp @@ -1216,7 +1216,7 @@ _cairo_qt_fast_fill (cairo_qt_surface_t *qs, cairo_clip_t clip, old_clip = qs->clipper.clip; - _cairo_clip_init_copy (&clip, &qs->clipper.clip); + qs->clipper.clip = _cairo_clip_copy (&clip); status = (cairo_int_status_t) _cairo_clip_clip (&clip, path, fill_rule, diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index feee61a0d..9b345e874 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -81,6 +81,14 @@ static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; +/* Not public in the least bit */ +static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL; + +/* CTFontCreateWithGraphicsFont is not available until 10.5 */ +typedef const struct __CTFontDescriptor *CTFontDescriptorRef; +static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform*, CTFontDescriptorRef) = NULL; +static CGPathRef (*CTFontCreatePathForGlyphPtr) (CTFontRef, CGGlyph, CGAffineTransform *) = NULL; + /* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ typedef struct { int ascent; @@ -125,6 +133,11 @@ quartz_font_ensure_symbols(void) CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); + CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph"); + if (!CTFontCreateWithGraphicsFontPtr || !CTFontCreatePathForGlyphPtr) + CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); + CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); @@ -140,6 +153,7 @@ quartz_font_ensure_symbols(void) CGFontGetGlyphsForUnicharsPtr && CGFontGetUnitsPerEmPtr && CGFontGetGlyphAdvancesPtr && + ((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) && (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) _cairo_quartz_font_symbols_present = TRUE; @@ -545,7 +559,6 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; CGPathRef glyphPath; - CTFontRef ctFont; cairo_path_fixed_t *path; if (glyph == INVALID_GLYPH) { @@ -560,9 +573,14 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL); - glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); - CFRelease (ctFont); + if (CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) { + CTFontRef ctFont = CTFontCreateWithGraphicsFontPtr (font_face->cgFont, 1.0, NULL, NULL); + glyphPath = CTFontCreatePathForGlyphPtr (ctFont, glyph, &textMatrix); + CFRelease (ctFont); + } else { + glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); + } + if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -744,16 +762,17 @@ _cairo_quartz_scaled_glyph_init (void *abstract_font, static unsigned long _cairo_quartz_ucs4_to_index (void *abstract_font, - uint32_t ucs4) + uint32_t ucs4) { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); - UniChar u = (UniChar) ucs4; - CGGlyph glyph; - - CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1); + CGGlyph glyph[2]; + UniChar utf16[2]; + + CFIndex len = CFStringGetSurrogatePairForLongCharacter (ucs4, utf16) ? 2 : 1; + CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len); - return glyph; + return glyph[0]; } static cairo_int_status_t diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index 3ef14c3ae..42e1f9e91 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -44,12 +44,13 @@ #include "cairo-quartz.h" #include "cairo-surface-clipper-private.h" -#ifdef CGFLOAT_DEFINED -typedef CGFloat cairo_quartz_float_t; -#else -typedef float cairo_quartz_float_t; +#ifndef CGFLOAT_DEFINED +/* On 10.4, Quartz APIs used float instead of CGFloat */ +typedef float CGFloat; #endif +typedef CGFloat cairo_quartz_float_t; + typedef enum { DO_DIRECT, DO_SHADING, @@ -57,6 +58,9 @@ typedef enum { DO_TILED_IMAGE } cairo_quartz_action_t; +/* define CTFontRef for pre-10.5 SDKs */ +typedef const struct __CTFont *CTFontRef; + typedef struct cairo_quartz_surface { cairo_surface_t base; diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h index 456c63389..c1827f5b3 100644 --- a/src/cairo-recording-surface-private.h +++ b/src/cairo-recording-surface-private.h @@ -49,6 +49,9 @@ typedef enum { CAIRO_COMMAND_STROKE, CAIRO_COMMAND_FILL, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, + + /* cairo_tag_begin()/cairo_tag_end() */ + CAIRO_COMMAND_TAG, } cairo_command_type_t; typedef enum { @@ -112,6 +115,17 @@ typedef struct _cairo_command_show_text_glyphs { cairo_scaled_font_t *scaled_font; } cairo_command_show_text_glyphs_t; +typedef struct _cairo_command_tag { + cairo_command_header_t header; + cairo_bool_t begin; + char *tag_name; + char *attributes; + cairo_pattern_union_t source; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; +} cairo_command_tag_t; + typedef union _cairo_command { cairo_command_header_t header; @@ -120,6 +134,7 @@ typedef union _cairo_command { cairo_command_stroke_t stroke; cairo_command_fill_t fill; cairo_command_show_text_glyphs_t show_text_glyphs; + cairo_command_tag_t tag; } cairo_command_t; typedef struct _cairo_recording_surface { @@ -169,7 +184,9 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target); + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded); cairo_private cairo_status_t _cairo_recording_surface_replay_region (cairo_surface_t *surface, const cairo_rectangle_int_t *surface_extents, diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index c7cd4a15e..84449522d 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -483,7 +483,16 @@ _cairo_recording_surface_finish (void *abstract_surface) cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); break; - default: + case CAIRO_COMMAND_TAG: + free (command->tag.tag_name); + if (command->tag.begin) { + free (command->tag.attributes); + _cairo_pattern_fini (&command->tag.source.base); + _cairo_stroke_style_fini (&command->tag.style); + } + break; + + default: ASSERT_NOT_REACHED; } @@ -684,21 +693,6 @@ _cairo_recording_surface_reset (cairo_recording_surface_t *surface) _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); } -static cairo_bool_t -is_identity_recording_pattern (const cairo_pattern_t *pattern) -{ - cairo_surface_t *surface; - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return FALSE; - - if (!_cairo_matrix_is_identity(&pattern->matrix)) - return FALSE; - - surface = ((cairo_surface_pattern_t *)pattern)->surface; - return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING; -} - static cairo_int_status_t _cairo_recording_surface_paint (void *abstract_surface, cairo_operator_t op, @@ -725,10 +719,6 @@ _cairo_recording_surface_paint (void *abstract_surface, (surface->base.is_clear || _cairo_pattern_is_opaque_solid (source))))) { _cairo_recording_surface_reset (surface); - if (is_identity_recording_pattern (source)) { - cairo_surface_t *src = ((cairo_surface_pattern_t *)source)->surface; - return _cairo_recording_surface_replay (src, &surface->base); - } } status = _cairo_composite_rectangles_init_for_paint (&composite, @@ -1095,6 +1085,90 @@ _cairo_recording_surface_show_text_glyphs (void *abstract_surface, return status; } +static cairo_int_status_t +_cairo_recording_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_tag_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + status = _cairo_composite_rectangles_init_for_paint (&composite, + &surface->base, + CAIRO_OPERATOR_SOURCE, + source ? source : &_cairo_pattern_black.base, + clip); + if (unlikely (status)) + return status; + + command = calloc (1, sizeof (cairo_command_tag_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status = _command_init (surface, + &command->header, CAIRO_COMMAND_TAG, CAIRO_OPERATOR_SOURCE, + &composite); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + command->begin = begin; + command->tag_name = strdup (tag_name); + if (begin) { + if (attributes) + command->attributes = strdup (attributes); + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_STRINGS; + + status = _cairo_stroke_style_init_copy (&command->style, style); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + command->ctm = *ctm; + command->ctm_inverse = *ctm_inverse; + } + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) { + if (begin) + goto CLEANUP_STRINGS; + else + goto CLEANUP_STYLE; + } + + _cairo_recording_surface_destroy_bbtree (surface); + + _cairo_composite_rectangles_fini (&composite); + return CAIRO_STATUS_SUCCESS; + + CLEANUP_STYLE: + _cairo_stroke_style_fini (&command->style); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_STRINGS: + free (command->tag_name); + free (command->attributes); + CLEANUP_COMMAND: + _cairo_clip_destroy (command->header.clip); + free (command); + CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); + return status; +} + static void _command_init_copy (cairo_recording_surface_t *surface, cairo_command_header_t *dst, @@ -1360,6 +1434,63 @@ _cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface, return status; } +static cairo_status_t +_cairo_recording_surface_copy__tag (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_tag_t *command; + cairo_status_t status; + + command = calloc (1, sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + command->begin = src->tag.begin; + command->tag_name = strdup (src->tag.tag_name); + if (src->tag.begin) { + if (src->tag.attributes) + command->attributes = strdup (src->tag.attributes); + + status = _cairo_pattern_init_copy (&command->source.base, + &src->stroke.source.base); + if (unlikely (status)) + goto err_command; + + status = _cairo_stroke_style_init_copy (&command->style, + &src->stroke.style); + if (unlikely (status)) + goto err_source; + + command->ctm = src->stroke.ctm; + command->ctm_inverse = src->stroke.ctm_inverse; + } + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) { + if (src->tag.begin) + goto err_command; + else + goto err_style; + } + + return CAIRO_STATUS_SUCCESS; + +err_style: + _cairo_stroke_style_fini (&command->style); +err_source: + _cairo_pattern_fini (&command->source.base); +err_command: + free(command->tag_name); + free(command->attributes); + free(command); +err: + return status; +} + static cairo_status_t _cairo_recording_surface_copy (cairo_recording_surface_t *dst, cairo_recording_surface_t *src) @@ -1394,6 +1525,10 @@ _cairo_recording_surface_copy (cairo_recording_surface_t *dst, status = _cairo_recording_surface_copy__glyphs (dst, command); break; + case CAIRO_COMMAND_TAG: + status = _cairo_recording_surface_copy__tag (dst, command); + break; + default: ASSERT_NOT_REACHED; } @@ -1509,6 +1644,8 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { NULL, _cairo_recording_surface_has_show_text_glyphs, _cairo_recording_surface_show_text_glyphs, + NULL, /* get_supported_mime_types */ + _cairo_recording_surface_tag, }; cairo_int_status_t @@ -1573,6 +1710,9 @@ _cairo_recording_surface_get_path (cairo_surface_t *abstract_surface, break; } + case CAIRO_COMMAND_TAG: + break; + default: ASSERT_NOT_REACHED; } @@ -1676,14 +1816,14 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, const cairo_matrix_t *surface_transform, cairo_surface_t *target, const cairo_clip_t *target_clip, + cairo_bool_t surface_is_unbounded, cairo_recording_replay_type_t type, cairo_recording_region_type_t region) { cairo_surface_wrapper_t wrapper; cairo_command_t **elements; cairo_bool_t replay_all = - type == CAIRO_RECORDING_REPLAY && - region == CAIRO_RECORDING_REGION_ALL; + type == CAIRO_RECORDING_CREATE_REGIONS || region == CAIRO_RECORDING_REGION_ALL; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_rectangle_int_t extents; cairo_bool_t use_indices = FALSE; @@ -1708,7 +1848,7 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, if (surface_extents) _cairo_surface_wrapper_intersect_extents (&wrapper, surface_extents); r = &_cairo_unbounded_rectangle; - if (! surface->unbounded) { + if (! surface->unbounded && !surface_is_unbounded) { _cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents); r = &surface->extents; } @@ -1716,7 +1856,7 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, _cairo_surface_wrapper_set_clip (&wrapper, target_clip); /* Compute the extents of the target clip in recorded device space */ - if (! _cairo_surface_wrapper_get_target_extents (&wrapper, &extents)) + if (! _cairo_surface_wrapper_get_target_extents (&wrapper, surface_is_unbounded, &extents)) goto done; surface->has_bilevel_alpha = TRUE; @@ -1870,11 +2010,23 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, } break; + case CAIRO_COMMAND_TAG: + status = _cairo_surface_wrapper_tag (&wrapper, + command->tag.begin, + command->tag.tag_name, + command->tag.attributes, + &command->tag.source.base, + &command->tag.style, + &command->tag.ctm, + &command->tag.ctm_inverse, + command->header.clip); + break; + default: ASSERT_NOT_REACHED; } - if (type == CAIRO_RECORDING_CREATE_REGIONS) { + if (type == CAIRO_RECORDING_CREATE_REGIONS && command->header.region != CAIRO_RECORDING_REGION_NATIVE) { if (status == CAIRO_INT_STATUS_SUCCESS) { command->header.region = CAIRO_RECORDING_REGION_NATIVE; } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { @@ -1977,6 +2129,18 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, command->header.clip); break; + case CAIRO_COMMAND_TAG: + status = _cairo_surface_wrapper_tag (&wrapper, + command->tag.begin, + command->tag.tag_name, + command->tag.attributes, + &command->tag.source.base, + &command->tag.style, + &command->tag.ctm, + &command->tag.ctm_inverse, + command->header.clip); + break; + default: ASSERT_NOT_REACHED; } @@ -2001,7 +2165,7 @@ _cairo_recording_surface_replay (cairo_surface_t *surface, cairo_surface_t *target) { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL, - target, NULL, + target, NULL, FALSE, CAIRO_RECORDING_REPLAY, CAIRO_RECORDING_REGION_ALL); } @@ -2013,7 +2177,7 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, const cairo_clip_t *target_clip) { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform, - target, target_clip, + target, target_clip, FALSE, CAIRO_RECORDING_REPLAY, CAIRO_RECORDING_REGION_ALL); } @@ -2026,10 +2190,13 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, */ cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target) + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded) { - return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL, + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform, target, NULL, + surface_is_unbounded, CAIRO_RECORDING_CREATE_REGIONS, CAIRO_RECORDING_REGION_ALL); } @@ -2042,7 +2209,7 @@ _cairo_recording_surface_replay_region (cairo_surface_t *surface, { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, surface_extents, NULL, - target, NULL, + target, NULL, FALSE, CAIRO_RECORDING_REPLAY, region); } diff --git a/src/cairo-region.c b/src/cairo-region.c index 6a5122510..c1d35e174 100644 --- a/src/cairo-region.c +++ b/src/cairo-region.c @@ -107,6 +107,10 @@ _cairo_region_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: case CAIRO_STATUS_DEVICE_FINISHED: case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_region_t *) &_cairo_region_nil; diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c index 74bfb9ea5..bf05fbd55 100644 --- a/src/cairo-scaled-font-subsets.c +++ b/src/cairo-scaled-font-subsets.c @@ -1281,8 +1281,12 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset utf16_len = 0; if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) + if (status == CAIRO_STATUS_INVALID_STRING) { + utf16 = NULL; + utf16_len = 0; + } else if (unlikely (status)) { goto CLEANUP_HASH; + } } if (utf16_len == 1) { diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index ac80c97d1..dff305389 100644 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -85,7 +85,7 @@ static cairo_cache_t cairo_scaled_glyph_page_cache; #define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 struct _cairo_scaled_glyph_page { cairo_cache_entry_t cache_entry; - + cairo_scaled_font_t *scaled_font; cairo_list_t link; unsigned int num_glyphs; @@ -477,7 +477,7 @@ _cairo_scaled_glyph_page_pluck (void *closure) assert (! cairo_list_is_empty (&page->link)); - scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + scaled_font = page->scaled_font; CAIRO_MUTEX_LOCK (scaled_font->mutex); _cairo_scaled_glyph_page_destroy (scaled_font, page); @@ -818,23 +818,35 @@ _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) void _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font) { + cairo_scaled_glyph_page_t *page; + CAIRO_MUTEX_LOCK (scaled_font->mutex); assert (! scaled_font->cache_frozen); assert (! scaled_font->global_cache_frozen); CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); - while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { - cairo_scaled_glyph_page_t *page = - cairo_list_first_entry (&scaled_font->glyph_pages, - cairo_scaled_glyph_page_t, - link); + cairo_list_foreach_entry (page, + cairo_scaled_glyph_page_t, + &scaled_font->glyph_pages, + link) { cairo_scaled_glyph_page_cache.size -= page->cache_entry.size; _cairo_hash_table_remove (cairo_scaled_glyph_page_cache.hash_table, (cairo_hash_entry_t *) &page->cache_entry); + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + + /* Destroy scaled_font's pages while holding its lock only, and not the + * global page cache lock. The destructor can cause us to recurse and + * end up back here for a different scaled_font. */ + + while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { + page = cairo_list_first_entry (&scaled_font->glyph_pages, + cairo_scaled_glyph_page_t, + link); _cairo_scaled_glyph_page_destroy (scaled_font, page); } - CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } @@ -1265,8 +1277,8 @@ _cairo_scaled_font_reset_static_data (void) * @scaled_font from being destroyed until a matching call to * cairo_scaled_font_destroy() is made. * - * The number of references to a #cairo_scaled_font_t can be get using - * cairo_scaled_font_get_reference_count(). + * Use cairo_scaled_font_get_reference_count() to get the number of + * references to a #cairo_scaled_font_t. * * Returns: the referenced #cairo_scaled_font_t * @@ -2824,7 +2836,7 @@ _cairo_scaled_glyph_page_can_remove (const void *closure) const cairo_scaled_glyph_page_t *page = closure; const cairo_scaled_font_t *scaled_font; - scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + scaled_font = page->scaled_font; return scaled_font->cache_frozen == 0; } @@ -2853,6 +2865,7 @@ _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, return _cairo_error (CAIRO_STATUS_NO_MEMORY); page->cache_entry.hash = (unsigned long) scaled_font; + page->scaled_font = scaled_font; page->cache_entry.size = 1; /* XXX occupancy weighting? */ page->num_glyphs = 0; diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h index b158f4d36..653183fb1 100644 --- a/src/cairo-spans-private.h +++ b/src/cairo-spans-private.h @@ -168,6 +168,10 @@ _cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, const cairo_box_t *extents, cairo_fill_rule_t fill_rule); +cairo_private cairo_status_t +_cairo_botor_scan_converter_add_polygon (cairo_botor_scan_converter_t *converter, + const cairo_polygon_t *polygon); + /* cairo-spans.c: */ cairo_private cairo_scan_converter_t * diff --git a/src/cairo-spans.c b/src/cairo-spans.c index 182390c20..59452c0ba 100644 --- a/src/cairo-spans.c +++ b/src/cairo-spans.c @@ -128,6 +128,10 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL; case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL; case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: break; } @@ -241,6 +245,10 @@ _cairo_span_renderer_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL; case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL; case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: RETURN_NIL; + case CAIRO_STATUS_PNG_ERROR: RETURN_NIL; + case CAIRO_STATUS_FREETYPE_ERROR: RETURN_NIL; + case CAIRO_STATUS_WIN32_GDI_ERROR: RETURN_NIL; + case CAIRO_STATUS_TAG_ERROR: RETURN_NIL; default: break; } diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h index 955a79ff5..bcda9aed1 100644 --- a/src/cairo-surface-backend-private.h +++ b/src/cairo-surface-backend-private.h @@ -200,6 +200,18 @@ struct _cairo_surface_backend { const char ** (*get_supported_mime_types) (void *surface); + + cairo_warn cairo_int_status_t + (*tag) (void *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip); + }; cairo_private cairo_status_t diff --git a/src/cairo-surface-observer.c b/src/cairo-surface-observer.c index 9d12fcddd..89c7525c7 100644 --- a/src/cairo-surface-observer.c +++ b/src/cairo-surface-observer.c @@ -1219,8 +1219,6 @@ _cairo_surface_observer_mark_dirty (void *abstract_surface, cairo_surface_observer_t *surface = abstract_surface; cairo_status_t status; - printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height); - status = CAIRO_STATUS_SUCCESS; if (surface->target->backend->mark_dirty_rectangle) status = surface->target->backend->mark_dirty_rectangle (surface->target, diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h index 6529ebc11..fd22bd7e6 100644 --- a/src/cairo-surface-wrapper-private.h +++ b/src/cairo-surface-wrapper-private.h @@ -159,6 +159,17 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); +cairo_private cairo_status_t +_cairo_surface_wrapper_tag (cairo_surface_wrapper_t *wrapper, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip); + cairo_private cairo_surface_t * _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, cairo_content_t content, @@ -186,6 +197,7 @@ _cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper) cairo_private cairo_bool_t _cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper, + cairo_bool_t surface_is_unbounded, cairo_rectangle_int_t *extents); CAIRO_END_DECLS diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c index 64e2d1e94..47155c3f7 100644 --- a/src/cairo-surface-wrapper.c +++ b/src/cairo-surface-wrapper.c @@ -81,9 +81,6 @@ _cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper, { cairo_matrix_init_identity (m); - if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y)) - cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y); - if (! _cairo_matrix_is_identity (&wrapper->transform)) cairo_matrix_multiply (m, &wrapper->transform, m); @@ -109,9 +106,6 @@ _cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper, assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (m, &inv, m); } - - if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y)) - cairo_matrix_translate (m, wrapper->extents.x, wrapper->extents.y); } static cairo_clip_t * @@ -507,6 +501,53 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, return status; } +cairo_status_t +_cairo_surface_wrapper_tag (cairo_surface_wrapper_t *wrapper, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t *dev_clip; + cairo_matrix_t dev_ctm = *ctm; + cairo_matrix_t dev_ctm_inverse = *ctm_inverse; + cairo_pattern_union_t source_copy; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (wrapper->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_tag (wrapper->target, + begin, tag_name, attributes, + source, stroke_style, + &dev_ctm, &dev_ctm_inverse, + dev_clip); + + _cairo_clip_destroy (dev_clip); + return status; +} + cairo_surface_t * _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, cairo_content_t content, @@ -631,12 +672,15 @@ _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) cairo_bool_t _cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper, + cairo_bool_t surface_is_unbounded, cairo_rectangle_int_t *extents) { cairo_rectangle_int_t clip; - cairo_bool_t has_clip; + cairo_bool_t has_clip = FALSE; + + if (!surface_is_unbounded) + has_clip = _cairo_surface_get_extents (wrapper->target, &clip); - has_clip = _cairo_surface_get_extents (wrapper->target, &clip); if (wrapper->clip) { if (has_clip) { if (! _cairo_rectangle_intersect (&clip, diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 46f689473..6d3ac192b 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -914,8 +914,8 @@ _cairo_surface_create_scratch (cairo_surface_t *other, * @surface from being destroyed until a matching call to * cairo_surface_destroy() is made. * - * The number of references to a #cairo_surface_t can be get using - * cairo_surface_get_reference_count(). + * Use cairo_surface_get_reference_count() to get the number of + * references to a #cairo_surface_t. * * Return value: the referenced #cairo_surface_t. * @@ -2632,6 +2632,42 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, return _cairo_surface_set_error (surface, status); } +cairo_status_t +_cairo_surface_tag (cairo_surface_t *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (unlikely (surface->status)) + return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + if (surface->backend->tag == NULL) + return CAIRO_STATUS_SUCCESS; + + if (begin) { + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + } + + status = surface->backend->tag (surface, begin, tag_name, attributes, + source, stroke_style, + ctm, ctm_inverse, clip); + + return _cairo_surface_set_error (surface, status); +} + + /** * _cairo_surface_set_resolution: * @surface: the surface @@ -2725,6 +2761,10 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: case CAIRO_STATUS_DEVICE_FINISHED: case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 2e023b313..2a020c017 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -2852,13 +2852,15 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) return status; } -static void +static cairo_int_status_t _cairo_svg_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_svg_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; + + return CAIRO_STATUS_SUCCESS; } static cairo_bool_t diff --git a/src/cairo-tag-attributes-private.h b/src/cairo-tag-attributes-private.h new file mode 100644 index 000000000..0af23dc45 --- /dev/null +++ b/src/cairo-tag-attributes-private.h @@ -0,0 +1,76 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson + */ + +#ifndef CAIRO_TAG_ATTRIBUTES_PRIVATE_H +#define CAIRO_TAG_ATTRIBUTES_PRIVATE_H + +#include "cairo-array-private.h" +#include "cairo-error-private.h" + +typedef enum { + TAG_LINK_INVALID = 0, + TAG_LINK_EMPTY, + TAG_LINK_DEST, + TAG_LINK_URI, + TAG_LINK_FILE, +} cairo_tag_link_type_t; + +typedef struct _cairo_link_attrs { + cairo_tag_link_type_t link_type; + cairo_array_t rects; + char *dest; + char *uri; + char *file; + int page; + cairo_point_double_t pos; +} cairo_link_attrs_t; + +typedef struct _cairo_dest_attrs { + char *name; + double x; + double y; + cairo_bool_t x_valid; + cairo_bool_t y_valid; + cairo_bool_t internal; +} cairo_dest_attrs_t; + +cairo_private cairo_int_status_t +_cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs); + +cairo_private cairo_int_status_t +_cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs); + +#endif /* CAIRO_TAG_ATTRIBUTES_PRIVATE_H */ diff --git a/src/cairo-tag-attributes.c b/src/cairo-tag-attributes.c new file mode 100644 index 000000000..685c26af6 --- /dev/null +++ b/src/cairo-tag-attributes.c @@ -0,0 +1,570 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson + */ + +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-list-inline.h" +#include "cairo-tag-attributes-private.h" + +#include + +typedef enum { + ATTRIBUTE_BOOL, /* Either true/false or 1/0 may be used. */ + ATTRIBUTE_INT, + ATTRIBUTE_FLOAT, /* Decimal separator is in current locale. */ + ATTRIBUTE_STRING, /* Enclose in single quotes. String escapes: + * \' - single quote + * \\ - backslash + */ +} attribute_type_t; + +typedef struct _attribute_spec { + const char *name; + attribute_type_t type; + int array_size; /* 0 = scalar, -1 = variable size array */ +} attribute_spec_t; + +/* + * name [required] Unique name of this destination (UTF-8) + * x [optional] x coordinate of destination on page. Default is x coord of + * extents of operations enclosed by the dest begin/end tags. + * y [optional] y coordinate of destination on page. Default is y coord of + * extents of operations enclosed by the dest begin/end tags. + * internal [optional] If true, the name may be optimized out of the PDF where + * possible. Default false. + */ +static attribute_spec_t _dest_attrib_spec[] = { + { "name", ATTRIBUTE_STRING }, + { "x", ATTRIBUTE_FLOAT }, + { "y", ATTRIBUTE_FLOAT }, + { "internal", ATTRIBUTE_BOOL }, + { NULL } +}; + +/* + * rect [optional] One or more rectangles to define link region. Default + * is the extents of the operations enclosed by the link begin/end tags. + * Each rectangle is specified by four array elements: x, y, width, height. + * ie the array size must be a multiple of four. + * + * Internal Links + * -------------- + * either: + * dest - name of dest tag in the PDF file to link to (UTF8) + * or + * page - Page number in the PDF file to link to + * pos - [optional] Position of destination on page. Default is 0,0. + * + * URI Links + * --------- + * uri [required] Uniform resource identifier (ASCII). + + * File Links + * ---------- + * file - [required] File name of PDF file to link to. + * either: + * dest - name of dest tag in the PDF file to link to (UTF8) + * or + * page - Page number in the PDF file to link to + * pos - [optional] Position of destination on page. Default is 0,0. + */ +static attribute_spec_t _link_attrib_spec[] = +{ + { "rect", ATTRIBUTE_FLOAT, -1 }, + { "dest", ATTRIBUTE_STRING }, + { "uri", ATTRIBUTE_STRING }, + { "file", ATTRIBUTE_STRING }, + { "page", ATTRIBUTE_INT }, + { "pos", ATTRIBUTE_FLOAT, 2 }, + { NULL } +}; + +typedef union { + cairo_bool_t b; + int i; + double f; + char *s; +} attrib_val_t; + +typedef struct _attribute { + char *name; + attribute_type_t type; + int array_len; /* 0 = scalar */ + attrib_val_t scalar; + cairo_array_t array; /* array of attrib_val_t */ + cairo_list_t link; +} attribute_t; + +static const char * +skip_space (const char *p) +{ + while (_cairo_isspace (*p)) + p++; + + return p; +} + +static const char * +parse_bool (const char *p, cairo_bool_t *b) +{ + if (*p == '1') { + *b = TRUE; + return p + 1; + } else if (*p == '0') { + *b = FALSE; + return p + 1; + } else if (strcmp (p, "true") == 0) { + *b = TRUE; + return p + 4; + } else if (strcmp (p, "false") == 0) { + *b = FALSE; + return p + 5; + } + + return NULL; +} + +static const char * +parse_int (const char *p, int *i) +{ + int n; + + if (sscanf(p, "%d%n", i, &n) > 0) + return p + n; + + return NULL; +} + +static const char * +parse_float (const char *p, double *d) +{ + int n; + + if (sscanf(p, "%lf%n", d, &n) > 0) + return p + n; + + return NULL; +} + +static const char * +decode_string (const char *p, int *len, char *s) +{ + if (*p != '\'') + return NULL; + + p++; + if (! *p) + return NULL; + + *len = 0; + while (*p) { + if (*p == '\\') { + p++; + if (*p) { + if (s) + *s++ = *p; + p++; + (*len)++; + } + } else if (*p == '\'') { + return p + 1; + } else { + if (s) + *s++ = *p; + p++; + (*len)++; + } + } + + return NULL; +} + +static const char * +parse_string (const char *p, char **s) +{ + const char *end; + int len; + + end = decode_string (p, &len, NULL); + if (!end) + return NULL; + + *s = malloc (len + 1); + decode_string (p, &len, *s); + (*s)[len] = 0; + + return end; +} + +static const char * +parse_scalar (const char *p, attribute_type_t type, attrib_val_t *scalar) +{ + switch (type) { + case ATTRIBUTE_BOOL: + return parse_bool (p, &scalar->b); + case ATTRIBUTE_INT: + return parse_int (p, &scalar->i); + case ATTRIBUTE_FLOAT: + return parse_float (p, &scalar->f); + case ATTRIBUTE_STRING: + return parse_string (p, &scalar->s); + } + + return NULL; +} + +static cairo_int_status_t +parse_array (const char *p, attribute_type_t type, cairo_array_t *array, const char **end) +{ + attrib_val_t val; + cairo_int_status_t status; + + p = skip_space (p); + if (! *p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + if (*p++ != '[') + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + while (TRUE) { + p = skip_space (p); + if (! *p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + if (*p == ']') { + *end = p + 1; + return CAIRO_INT_STATUS_SUCCESS; + } + + p = parse_scalar (p, type, &val); + if (!p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + status = _cairo_array_append (array, &val); + if (unlikely (status)) + return status; + } + + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); +} + +static cairo_int_status_t +parse_name (const char *p, const char **end, char **s) +{ + const char *p2; + char *name; + int len; + + if (! _cairo_isalpha (*p)) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + p2 = p; + while (_cairo_isalpha (*p2) || _cairo_isdigit (*p2)) + p2++; + + len = p2 - p; + name = malloc (len + 1); + if (unlikely (name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (name, p, len); + name[len] = 0; + *s = name; + *end = p2; + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +parse_attributes (const char *attributes, attribute_spec_t *attrib_def, cairo_list_t *list) +{ + attribute_spec_t *def; + attribute_t *attrib; + char *name = NULL; + cairo_int_status_t status; + const char *p = attributes; + + if (! p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + while (*p) { + p = skip_space (p); + if (! *p) + break; + + status = parse_name (p, &p, &name); + if (status) + return status; + + def = attrib_def; + while (def->name) { + if (strcmp (name, def->name) == 0) + break; + def++; + } + if (! def->name) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail1; + } + + attrib = calloc (1, sizeof (attribute_t)); + if (unlikely (attrib == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + attrib->name = name; + attrib->type = def->type; + _cairo_array_init (&attrib->array, sizeof(attrib_val_t)); + + p = skip_space (p); + if (def->type == ATTRIBUTE_BOOL && *p != '=') { + attrib->scalar.b = TRUE; + } else { + if (*p++ != '=') { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail2; + } + + if (def->array_size == 0) { + p = parse_scalar (p, def->type, &attrib->scalar); + if (!p) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail2; + } + + attrib->array_len = 0; + } else { + status = parse_array (p, def->type, &attrib->array, &p); + if (unlikely (status)) + goto fail2; + + attrib->array_len = _cairo_array_num_elements (&attrib->array); + if (def->array_size > 0 && attrib->array_len != def->array_size) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail2; + } + } + } + + cairo_list_add_tail (&attrib->link, list); + } + + return CAIRO_INT_STATUS_SUCCESS; + + fail2: + _cairo_array_fini (&attrib->array); + free (attrib); + fail1: + free (name); + + return status; +} + +static void +free_attributes_list (cairo_list_t *list) +{ + attribute_t *attr, *next; + + cairo_list_foreach_entry_safe (attr, next, attribute_t, list, link) + { + cairo_list_del (&attr->link); + free (attr->name); + _cairo_array_fini (&attr->array); + free (attr); + } +} + +static attribute_t * +find_attribute (cairo_list_t *list, const char *name) +{ + attribute_t *attr; + + cairo_list_foreach_entry (attr, attribute_t, list, link) + { + if (strcmp (attr->name, name) == 0) + return attr; + } + + return NULL; +} + +cairo_int_status_t +_cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + attrib_val_t val; + + cairo_list_init (&list); + status = parse_attributes (attributes, _link_attrib_spec, &list); + if (unlikely (status)) + return status; + + memset (link_attrs, 0, sizeof (cairo_link_attrs_t)); + _cairo_array_init (&link_attrs->rects, sizeof (cairo_rectangle_t)); + if (find_attribute (&list, "uri")) { + link_attrs->link_type = TAG_LINK_URI; + } else if (find_attribute (&list, "file")) { + link_attrs->link_type = TAG_LINK_FILE; + } else if (find_attribute (&list, "dest")) { + link_attrs->link_type = TAG_LINK_DEST; + } else if (find_attribute (&list, "page")) { + link_attrs->link_type = TAG_LINK_DEST; + } else { + link_attrs->link_type = TAG_LINK_EMPTY; + goto cleanup; + } + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "uri") == 0) { + if (link_attrs->link_type != TAG_LINK_URI) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->uri = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "file") == 0) { + if (link_attrs->link_type != TAG_LINK_FILE) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->file = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "dest") == 0) { + if (! (link_attrs->link_type == TAG_LINK_DEST || + link_attrs->link_type != TAG_LINK_FILE)) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->dest = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "page") == 0) { + if (! (link_attrs->link_type == TAG_LINK_DEST || + link_attrs->link_type != TAG_LINK_FILE)) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->page = attr->scalar.i; + + } else if (strcmp (attr->name, "pos") == 0) { + if (! (link_attrs->link_type == TAG_LINK_DEST || + link_attrs->link_type != TAG_LINK_FILE)) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + _cairo_array_copy_element (&attr->array, 0, &val); + link_attrs->pos.x = val.f; + _cairo_array_copy_element (&attr->array, 1, &val); + link_attrs->pos.y = val.f; + } else if (strcmp (attr->name, "rect") == 0) { + cairo_rectangle_t rect; + int i; + int num_elem = _cairo_array_num_elements (&attr->array); + if (num_elem == 0 || num_elem % 4 != 0) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + for (i = 0; i < num_elem; i += 4) { + _cairo_array_copy_element (&attr->array, i, &val); + rect.x = val.f; + _cairo_array_copy_element (&attr->array, i+1, &val); + rect.y = val.f; + _cairo_array_copy_element (&attr->array, i+2, &val); + rect.width = val.f; + _cairo_array_copy_element (&attr->array, i+3, &val); + rect.height = val.f; + status = _cairo_array_append (&link_attrs->rects, &rect); + if (unlikely (status)) + goto cleanup; + } + } + } + + cleanup: + free_attributes_list (&list); + if (unlikely (status)) { + free (link_attrs->dest); + free (link_attrs->uri); + free (link_attrs->file); + _cairo_array_fini (&link_attrs->rects); + } + + return status; +} + +cairo_int_status_t +_cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + + memset (dest_attrs, 0, sizeof (cairo_dest_attrs_t)); + cairo_list_init (&list); + status = parse_attributes (attributes, _dest_attrib_spec, &list); + if (unlikely (status)) + goto cleanup; + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "name") == 0) { + dest_attrs->name = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "x") == 0) { + dest_attrs->x = attr->scalar.f; + dest_attrs->x_valid = TRUE; + } else if (strcmp (attr->name, "y") == 0) { + dest_attrs->y = attr->scalar.f; + dest_attrs->y_valid = TRUE; + } else if (strcmp (attr->name, "internal") == 0) { + dest_attrs->internal = attr->scalar.b; + } + } + + if (! dest_attrs->name) + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + cleanup: + free_attributes_list (&list); + + return status; +} diff --git a/src/cairo-tag-stack-private.h b/src/cairo-tag-stack-private.h new file mode 100644 index 000000000..4af2a85de --- /dev/null +++ b/src/cairo-tag-stack-private.h @@ -0,0 +1,107 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson + */ + +#ifndef CAIRO_TAG_STACK_PRIVATE_H +#define CAIRO_TAG_STACK_PRIVATE_H + +#include "cairo-error-private.h" +#include "cairo-list-inline.h" + +/* The type of a single tag */ +typedef enum { + TAG_TYPE_INVALID = 0, + TAG_TYPE_STRUCTURE = 1, + TAG_TYPE_LINK = 2, + TAG_TYPE_DEST = 4, +} cairo_tag_type_t; + +/* The type of the structure tree. */ +typedef enum _cairo_tag_stack_structure_type { + TAG_TREE_TYPE_TAGGED, /* compliant with Tagged PDF */ + TAG_TREE_TYPE_STRUCTURE, /* valid structure but not 'Tagged PDF' compliant */ + TAG_TREE_TYPE_LINK_ONLY, /* contains Link tags only */ + TAG_TREE_TYPE_NO_TAGS, /* no tags used */ + TAG_TREE_TYPE_INVALID, /* invalid tag structure */ +} cairo_tag_stack_structure_type_t; + +typedef struct _cairo_tag_stack_elem { + char *name; + char *attributes; + void *data; + cairo_list_t link; + +} cairo_tag_stack_elem_t; + +typedef struct _cairo_tag_stack { + cairo_list_t list; + cairo_tag_stack_structure_type_t type; + int size; + +} cairo_tag_stack_t; + +cairo_private void +_cairo_tag_stack_init (cairo_tag_stack_t *stack); + +cairo_private void +_cairo_tag_stack_fini (cairo_tag_stack_t *stack); + +cairo_private cairo_tag_stack_structure_type_t +_cairo_tag_stack_get_structure_type (cairo_tag_stack_t *stack); + +cairo_private cairo_int_status_t +_cairo_tag_stack_push (cairo_tag_stack_t *stack, + const char *name, + const char *attributes); + +cairo_private void +_cairo_tag_stack_set_top_data (cairo_tag_stack_t *stack, + void *data); + +cairo_private cairo_int_status_t +_cairo_tag_stack_pop (cairo_tag_stack_t *stack, + const char *name, + cairo_tag_stack_elem_t **elem); + +cairo_private cairo_tag_stack_elem_t * +_cairo_tag_stack_top_elem (cairo_tag_stack_t *stack); + +cairo_private void +_cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem); + +cairo_private cairo_tag_type_t +_cairo_tag_get_type (const char *name); + +#endif /* CAIRO_TAG_STACK_PRIVATE_H */ diff --git a/src/cairo-tag-stack.c b/src/cairo-tag-stack.c new file mode 100644 index 000000000..858221eb2 --- /dev/null +++ b/src/cairo-tag-stack.c @@ -0,0 +1,279 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson + */ + +#include "cairoint.h" + +#include "cairo-tag-stack-private.h" + +/* Tagged PDF must have one of these tags at the top level */ +static const char * _cairo_tag_stack_tagged_pdf_top_level_element_list[] = +{ + "Document", + "Part", + "Art", + "Sect", + "Div", + NULL +}; + +/* List of valid tag names. Table numbers reference PDF 32000 */ +static const char * _cairo_tag_stack_struct_pdf_list[] = +{ + /* Table 333 - Grouping Elements */ + "Document", + "Part", + "Art", + "Sect", + "Div", + "BlockQuote", + "Caption", + "TOC", + "TOCI", + "Index", + "NonStruct", + "Private", + + /* Table 335 - Standard structure types for paragraphlike elements */ + "P", "H", + "H1", "H2", "H3", "H4", "H5", "H6", + + /* Table 336 - Standard structure types for list elements */ + "L", "LI", "Lbl", "LBody", + + /* Table 337 - Standard structure types for table elements */ + "Table", + "TR", "TH", "TD", + "THead", "TBody", "TFoot", + + /* Table 338 - Standard structure types for inline-level structure elements */ + "Span", + "Quote", + "Note", + "Reference", + "BibEntry", + "Code", + "Link", /* CAIRO_TAG_LINK */ + "Annot", + "Ruby", + "Warichu", + + /* Table 339 - Standard structure types for Ruby and Warichu elements */ + "RB", "RT", "RP", + "WT", "WP", + + /* Table 340 - Standard structure types for illustration elements */ + "Figure", + "Formula", + "Form", + + NULL +}; + +/* List of cairo specific tag names */ +static const char * _cairo_tag_stack_cairo_tag_list[] = +{ + CAIRO_TAG_DEST, + NULL +}; + +void +_cairo_tag_stack_init (cairo_tag_stack_t *stack) +{ + cairo_list_init (&stack->list); + stack->type = TAG_TREE_TYPE_NO_TAGS; + stack->size = 0; +} + +void +_cairo_tag_stack_fini (cairo_tag_stack_t *stack) +{ + while (! cairo_list_is_empty (&stack->list)) { + cairo_tag_stack_elem_t *elem; + + elem = cairo_list_first_entry (&stack->list, cairo_tag_stack_elem_t, link); + cairo_list_del (&elem->link); + free (elem->name); + free (elem->attributes); + free (elem); + } +} + +cairo_tag_stack_structure_type_t +_cairo_tag_stack_get_structure_type (cairo_tag_stack_t *stack) +{ + return stack->type; +} + +static cairo_bool_t +name_in_list (const char *name, const char **list) +{ + if (! name) + return FALSE; + + while (*list) { + if (strcmp (name, *list) == 0) + return TRUE; + list++; + } + + return FALSE; +} + +cairo_int_status_t +_cairo_tag_stack_push (cairo_tag_stack_t *stack, + const char *name, + const char *attributes) +{ + cairo_tag_stack_elem_t *elem; + + if (! name_in_list (name, _cairo_tag_stack_struct_pdf_list) && + ! name_in_list (name, _cairo_tag_stack_cairo_tag_list)) + { + stack->type = TAG_TYPE_INVALID; + return _cairo_error (CAIRO_STATUS_TAG_ERROR); + } + + if (stack->type == TAG_TREE_TYPE_NO_TAGS) { + if (name_in_list (name, _cairo_tag_stack_tagged_pdf_top_level_element_list)) + stack->type = TAG_TREE_TYPE_TAGGED; + else if (strcmp (name, "Link") == 0) + stack->type = TAG_TREE_TYPE_LINK_ONLY; + else if (name_in_list (name, _cairo_tag_stack_struct_pdf_list)) + stack->type = TAG_TREE_TYPE_STRUCTURE; + } else { + if (stack->type == TAG_TREE_TYPE_LINK_ONLY && + name_in_list (name, _cairo_tag_stack_struct_pdf_list)) + { + stack->type = TAG_TREE_TYPE_STRUCTURE; + } + } + + elem = malloc (sizeof(cairo_tag_stack_elem_t)); + if (unlikely (elem == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + elem->name = strdup (name); + if (unlikely (elem->name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (attributes) { + elem->attributes = strdup (attributes); + if (unlikely (elem->attributes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + elem->attributes = NULL; + } + + elem->data = NULL; + + cairo_list_add_tail (&elem->link, &stack->list); + stack->size++; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_private void +_cairo_tag_stack_set_top_data (cairo_tag_stack_t *stack, + void *data) +{ + cairo_tag_stack_elem_t *top; + + top = _cairo_tag_stack_top_elem (stack); + if (top) + top->data = data; +} + +cairo_int_status_t +_cairo_tag_stack_pop (cairo_tag_stack_t *stack, + const char *name, + cairo_tag_stack_elem_t **elem) +{ + cairo_tag_stack_elem_t *top; + + top = _cairo_tag_stack_top_elem (stack); + if (!top) { + stack->type = TAG_TYPE_INVALID; + return _cairo_error (CAIRO_STATUS_TAG_ERROR); + } + + cairo_list_del (&top->link); + stack->size--; + if (strcmp (top->name, name) != 0) { + stack->type = TAG_TYPE_INVALID; + _cairo_tag_stack_free_elem (top); + return _cairo_error (CAIRO_STATUS_TAG_ERROR); + } + + if (elem) + *elem = top; + else + _cairo_tag_stack_free_elem (top); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_tag_stack_elem_t * +_cairo_tag_stack_top_elem (cairo_tag_stack_t *stack) +{ + if (cairo_list_is_empty (&stack->list)) + return NULL; + + return cairo_list_last_entry (&stack->list, cairo_tag_stack_elem_t, link); +} + +void +_cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem) +{ + free (elem->name); + free (elem->attributes); + free (elem); +} + +cairo_tag_type_t +_cairo_tag_get_type (const char *name) +{ + if (! name_in_list (name, _cairo_tag_stack_struct_pdf_list) && + ! name_in_list (name, _cairo_tag_stack_cairo_tag_list)) + return TAG_TYPE_INVALID; + + if (strcmp(name, "Link") == 0) + return (TAG_TYPE_LINK | TAG_TYPE_STRUCTURE); + + if (strcmp(name, "cairo.dest") == 0) + return TAG_TYPE_DEST; + + return TAG_TYPE_STRUCTURE; +} diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c index 3aa637d6f..afd396e1b 100644 --- a/src/cairo-truetype-subset.c +++ b/src/cairo-truetype-subset.c @@ -652,16 +652,34 @@ cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, if (unlikely (status)) goto FAIL; - if (size != 0) { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + if (size > 1) { + tt_glyph_data_t *glyph_data; + int num_contours; + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_glyf, begin, buffer, &size); if (unlikely (status)) goto FAIL; - status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); - if (unlikely (status)) - goto FAIL; - } + glyph_data = (tt_glyph_data_t *) buffer; + num_contours = (int16_t)be16_to_cpu (glyph_data->num_contours); + if (num_contours < 0) { + status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); + if (unlikely (status)) + goto FAIL; + } else if (num_contours == 0) { + /* num_contours == 0 is undefined in the Opentype + * spec. There are some embedded fonts that have a + * space glyph with num_contours = 0 that fails on + * some printers. The spec requires glyphs without + * contours to have a 0 size glyph entry in the loca + * table. + * + * If num_contours == 0, truncate the glyph to 0 size. + */ + _cairo_array_truncate (&font->output, _cairo_array_num_elements (&font->output) - size); + } + } } status = cairo_truetype_font_align_output (font, &next); @@ -1291,11 +1309,14 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, /* search for glyph in segments with rangeOffset=0 */ for (i = 0; i < num_segments; i++) { + uint16_t start = be16_to_cpu (start_code[i]); + uint16_t end = be16_to_cpu (end_code[i]); + + if (start == 0xffff && end == 0xffff) + break; + c = index - be16_to_cpu (delta[i]); - if (range_offset[i] == 0 && - c >= be16_to_cpu (start_code[i]) && - c <= be16_to_cpu (end_code[i])) - { + if (range_offset[i] == 0 && c >= start && c <= end) { *ucs4 = c; goto found; } @@ -1303,9 +1324,15 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, /* search for glyph in segments with rangeOffset=1 */ for (i = 0; i < num_segments; i++) { + uint16_t start = be16_to_cpu (start_code[i]); + uint16_t end = be16_to_cpu (end_code[i]); + + if (start == 0xffff && end == 0xffff) + break; + if (range_offset[i] != 0) { uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2; - int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1; + int range_size = end - start + 1; uint16_t g_id_be = cpu_to_be16 (index); int j; @@ -1315,7 +1342,7 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, for (j = 0; j < range_size; j++) { if (glyph_ids[j] == g_id_be) { - *ucs4 = be16_to_cpu (start_code[i]) + j; + *ucs4 = start + j; goto found; } } diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c index 8c154b3ac..86c24ed4c 100644 --- a/src/cairo-type3-glyph-surface.c +++ b/src/cairo-type3-glyph-surface.c @@ -78,7 +78,6 @@ _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_bool_t ps) { cairo_type3_glyph_surface_t *surface; - cairo_matrix_t invert_y_axis; if (unlikely (stream != NULL && stream->status)) return _cairo_surface_create_in_error (stream->status); @@ -102,8 +101,6 @@ _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, * entry in the Type 3 dictionary. In the PDF backend this is an * identity matrix. */ surface->cairo_to_pdf = scaled_font->scale_inverse; - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis); _cairo_pdf_operators_init (&surface->pdf_operators, surface->stream, @@ -295,15 +292,13 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_scaled_font_t *font; - cairo_matrix_t new_ctm, invert_y_axis; + cairo_matrix_t new_ctm; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm); - cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm); + cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &scaled_font->ctm); font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &new_ctm, @@ -388,14 +383,10 @@ _cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *sur x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y); - mat.xx = image->width; - mat.xy = 0; - mat.yx = 0; - mat.yy = image->height; - mat.x0 = x; - mat.y0 = y; + cairo_matrix_init(&mat, image->width, 0, + 0, -image->height, + x, y); cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse); - mat.y0 *= -1; return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); } @@ -524,9 +515,9 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, "%f 0 %f %f %f %f d1\n", x_advance, _cairo_fixed_to_double (bbox->p1.x), - - _cairo_fixed_to_double (bbox->p2.y), + _cairo_fixed_to_double (bbox->p1.y), _cairo_fixed_to_double (bbox->p2.x), - - _cairo_fixed_to_double (bbox->p1.y)); + _cairo_fixed_to_double (bbox->p2.y)); if (status == CAIRO_INT_STATUS_SUCCESS) { cairo_output_stream_t *mem_stream; diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c index 64a3d5a77..a4969c957 100644 --- a/src/cairo-xcb-surface-render.c +++ b/src/cairo-xcb-surface-render.c @@ -3400,8 +3400,6 @@ _composite_mask_clip (void *closure, } } - dst->deferred_clear = FALSE; /* assert(trap extents == extents); */ - status = _composite_traps (&info, dst, CAIRO_OPERATOR_SOURCE, mask_pattern, dst_x, dst_y, diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 3f407c3df..908d76daf 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -189,12 +189,6 @@ _cairo_surface_is_xlib (cairo_surface_t *surface); #define CAIRO_ASSUME_PIXMAP 20 -static const XTransform identity = { { - { 1 << 16, 0x00000, 0x00000 }, - { 0x00000, 1 << 16, 0x00000 }, - { 0x00000, 0x00000, 1 << 16 }, -} }; - static Visual * _visual_for_xrender_format(Screen *screen, XRenderPictFormat *xrender_format) @@ -793,6 +787,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, _cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image); + XSync (display->display, False); old_handler = XSetErrorHandler (_noop_error_handler); success = XShmGetImage (display->display, surface->drawable, @@ -807,12 +802,14 @@ _get_image_surface (cairo_xlib_surface_t *surface, } cairo_surface_destroy (&image->base); + image = NULL; } } if (surface->use_pixmap == 0) { cairo_xlib_error_func_t old_handler; + XSync (display->display, False); old_handler = XSetErrorHandler (_noop_error_handler); ximage = XGetImage (display->display, @@ -1011,7 +1008,8 @@ _get_image_surface (cairo_xlib_surface_t *surface, cairo_device_release (&display->base); if (unlikely (status)) { - cairo_surface_destroy (&image->base); + if (image) + cairo_surface_destroy (&image->base); return _cairo_surface_create_in_error (status); } diff --git a/src/cairo.c b/src/cairo.c index e3acf4d46..eb863efdb 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -107,6 +107,220 @@ * space. **/ +/** + * SECTION:cairo-tag + * @Title: Tags and Links + * @Short_Description: Hyperlinks and document structure + * @See_Also: #cairo_pdf_surface_t + * + * The tag functions provide the ability to specify hyperlinks and + * document logical structure on supported backends. The following tags are supported: + * * [Link][link] - Create a hyperlink + * * [Destinations][dest] - Create a hyperlink destination + * * [Document Structure Tags][doc-struct] - Create PDF Document Structure + * + * # Link Tags # {#link} + * A hyperlink is specified by enclosing the hyperlink text with the %CAIRO_TAG_LINK tag. + * + * For example: + * + * cairo_tag_begin (cr, CAIRO_TAG_LINK, "uri='http://cairographics.org'"); + * cairo_move_to (cr, 50, 50); + * cairo_show_text (cr, "This is a link to the cairo website."); + * cairo_tag_end (cr, CAIRO_TAG_LINK); + * + * + * The PDF backend uses one or more rectangles to define the clickable + * area of the link. By default cairo will use the extents of the + * drawing operations enclosed by the begin/end link tags to define the + * clickable area. In some cases, such as a link split across two + * lines, the default rectangle is undesirable. + * + * @rect: [optional] The "rect" attribute allows the application to + * specify one or more rectangles that form the clickable region. The + * value of this attribute is an array of floats. Each rectangle is + * specified by four elements in the array: x, y, width, height. The + * array size must be a multiple of four. + * + * An example of creating a link with user specified clickable region: + * + * cairo_font_extents_t font_extents; + * cairo_text_extents_t text1_extents; + * cairo_text_extents_t text2_extents; + * char attribs[100]; + * const char *text1 = "This link is split"; + * const char *text2 = "across two lines"; + * + * cairo_font_extents (cr, &font_extents); + * cairo_move_to (cr, 450, 50); + * cairo_text_extents (cr, text1, &text1_extents); + * cairo_move_to (cr, 50, 70); + * cairo_text_extents (cr, text2, &text2_extents); + * sprintf (attribs, + * "rect=[%f %f %f %f %f %f %f %f] uri='http://cairographics.org'", + * text1_extents.x_bearing, + * text1_extents.y_bearing, + * text1_extents.width, + * text1_extents.height, + * text2_extents.x_bearing, + * text2_extents.y_bearing, + * text2_extents.width, + * text2_extents.height); + * + * cairo_tag_begin (cr, CAIRO_TAG_LINK, attribs); + * cairo_show_text (cr, "This is a link to the cairo website"); + * cairo_move_to (cr, 450, 50); + * cairo_show_text (cr, text1); + * cairo_move_to (cr, 50, 70); + * cairo_show_text (cr, text2); + * cairo_tag_end (cr, CAIRO_TAG_LINK); + * + * + * There are three types of links. Each type has its own attributes as detailed below. + * * [Internal Links][internal-link] - A link to a location in the same document + * * [URI Links][uri-link] - A link to a Uniform resource identifier + * * [File Links][file-link] - A link to a location in another document + * + * ## Internal Links ## {#internal-link} + * An internal link is a link to a location in the same document. The destination + * is specified with either: + * + * @dest: a UTF-8 string specifying the destination in the PDF file to link + * to. Destinations are created with the %CAIRO_TAG_DEST tag. + * + * or the two attributes: + * + * @page: An integer specifying the page number in the PDF file to link to. + * + * @pos: [optional] An array of two floats specifying the x,y position + * on the page. Default is 0,0. + * + * An example of the link attributes to link to a page and x,y position: + * + * "page=3 pos=[3.1 6.2]" + * + * + * ## URI Links ## {#uri-link} + * A URI link is a link to a Uniform Resource Identifier ([RFC 2396](http://tools.ietf.org/html/rfc2396)). + * + * A URI is specified with the following attribute: + * + * @uri: An ASCII string specifying the URI. + * + * An example of the link attributes to the cairo website: + * + * "uri='http://cairographics.org'" + * + * + * ## File Links ## {#file-link} + * A file link is a link a location in another PDF file. + * + * The file attribute (required) specifies the name of the PDF file: + * + * @file: File name of PDF file to link to. + * + * The position is specified by either: + * + * @dest: a UTF-8 string specifying the named destination in the PDF file. + * + * or + * + * @page: An integer specifying the page number in the PDF file. + * + * @pos: [optional] An array of two floats specifying the x,y position + * on the page. Default is 0,0. + * + * An example of the link attributes to PDF file: + * + * "file='document.pdf' page=16 pos=[25 40]" + * + * + * # Destination Tags # {#dest} + + * A destination is specified by enclosing the destination drawing + * operations with the %CAIRO_TAG_DEST tag. + * + * @name: [required] A UTF-8 string specifying the name of this destination. + * + * @x: [optional] A float specifying the x coordinate of destination + * position on this page. If not specified the default + * x coordinate is the left side of the extents of the + * operations enclosed by the %CAIRO_TAG_DEST begin/end tags. If + * no operations are enclosed, the x coordidate is 0. + * + * @y: [optional] A float specifying the y coordinate of destination + * position on this page. If not specified the default + * y coordinate is the top of the extents of the + * operations enclosed by the %CAIRO_TAG_DEST begin/end tags. If + * no operations are enclosed, the y coordidate is 0. + * + * @internal: A boolean that if true, the destination name may be + * ommitted from PDF where possible. In this case, links + * refer directly to the page and position instead of via + * the named destination table. Note that if this + * destination is referenced by another PDF (see [File Links][file-link]), + * this attribute must be false. Default is false. + * + * + * /* Create a hyperlink */ + * cairo_tag_begin (cr, CAIRO_TAG_LINK, "dest='mydest' internal"); + * cairo_move_to (cr, 50, 50); + * cairo_show_text (cr, "This is a hyperlink."); + * cairo_tag_end (cr, CAIRO_TAG_LINK); + * + * /* Create a destination */ + * cairo_tag_begin (cr, CAIRO_TAG_DEST, "name='mydest'"); + * cairo_move_to (cr, 50, 250); + * cairo_show_text (cr, "This paragraph is the destination of the above link."); + * cairo_tag_end (cr, CAIRO_TAG_DEST); + * + * + * # Document Structure (PDF) # {#doc-struct} + * + * The document structure tags provide a means of specifying structural information + * such as headers, paragraphs, tables, and figures. The inclusion of structural information faciliates: + * * Extraction of text and graphics for copy and paste + * * Reflow of text and graphics in the viewer + * * Proccessing text eg searching and indexing + * * Conversion to other formats + * * Accessability support + * + * The list of structure types is specified in section 14.8.4 of the + * [PDF Reference](http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf). + * + * Note the PDF "Link" structure tag is the same as the cairo %CAIRO_TAG_LINK tag. + * + * The following example creates a document structure for a document containing two section, each with + * a header and a paragraph. + * + * + * cairo_tag_begin (cr, "Document", NULL); + * + * cairo_tag_begin (cr, "Sect", NULL); + * cairo_tag_begin (cr, "H1", NULL); + * cairo_show_text (cr, "Heading 1"); + * cairo_tag_end (cr, "H1"); + * + * cairo_tag_begin (cr, "P", NULL); + * cairo_show_text (cr, "Paragraph 1"); + * cairo_tag_end (cr, "P"); + * cairo_tag_end (cr, "Sect"); + * + * cairo_tag_begin (cr, "Sect", NULL); + * cairo_tag_begin (cr, "H1", NULL); + * cairo_show_text (cr, "Heading 2"); + * cairo_tag_end (cr, "H1"); + * + * cairo_tag_begin (cr, "P", NULL); + * cairo_show_text (cr, "Paragraph 2"); + * cairo_tag_end (cr, "P"); + * cairo_tag_end (cr, "Sect"); + * + * cairo_tag_end (cr, "Document"); + * + * + **/ + #define DEFINE_NIL_CONTEXT(status) \ { \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ @@ -153,7 +367,11 @@ static const cairo_t _cairo_nil[] = { DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION), DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED), - DEFINE_NIL_CONTEXT (CAIRO_STATUS_JBIG2_GLOBAL_MISSING) + DEFINE_NIL_CONTEXT (CAIRO_STATUS_JBIG2_GLOBAL_MISSING), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_PNG_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_FREETYPE_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_WIN32_GDI_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_TAG_ERROR) }; COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1); @@ -260,8 +478,8 @@ _cairo_init (cairo_t *cr, * @cr from being destroyed until a matching call to cairo_destroy() * is made. * - * The number of references to a #cairo_t can be get using - * cairo_get_reference_count(). + * Use cairo_get_reference_count() to get the number of references to + * a #cairo_t. * * Return value: the referenced #cairo_t. * @@ -2663,6 +2881,100 @@ cairo_copy_clip_rectangle_list (cairo_t *cr) return cr->backend->clip_copy_rectangle_list (cr); } +/** + * CAIRO_TAG_DEST: + * + * Create a destination for a hyperlink. Destination tag attributes + * are detailed at [Destinations][dests]. + * + * Since: 1.16 + **/ + +/** + * CAIRO_TAG_LINK: + * + * Create hyperlink. Link tag attributes are detailed at + * [Links][links]. + * + * Since: 1.16 + **/ + +/** + * cairo_tag_begin: + * @cr: a cairo context + * @tag_name: tag name + * @attributes: tag attributes + * + * Marks the beginning of the @tag_name structure. Call + * cairo_tag_end() with the same @tag_name to mark the end of the + * structure. + * + * The attributes string is of the form "key1=value2 key2=value2 ...". + * Values may be boolean (true/false or 1/0), integer, float, string, + * or an array. + * + * String values are enclosed in single quotes + * ('). Single quotes and backslashes inside the string should be + * escaped with a backslash. + * + * Boolean values may be set to true by only + * specifying the key. eg the attribute string "key" is the equivalent + * to "key=true". + * + * Arrays are enclosed in '[]'. eg "rect=[1.2 4.3 2.0 3.0]". + * + * If no attributes are required, @attributes can be an empty string or NULL. + * + * See [Tags and Links Description][cairo-Tags-and-Links.description] + * for the list of tags and attributes. + * + * Invalid nesting of tags or invalid attributes will cause @cr to + * shutdown with a status of %CAIRO_STATUS_TAG_ERROR. + * + * See cairo_tag_end(). + * + * Since: 1.16 + **/ +void +cairo_tag_begin (cairo_t *cr, const char *tag_name, const char *attributes) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->tag_begin (cr, tag_name, attributes); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_tag_end: + * @cr: a cairo context + * @tag_name: tag name + * + * Marks the end of the @tag_name structure. + * + * Invalid nesting of tags will cause @cr to shutdown with a status of + * %CAIRO_STATUS_TAG_ERROR. + * + * See cairo_tag_begin(). + * + * Since: 1.16 + **/ +cairo_public void +cairo_tag_end (cairo_t *cr, const char *tag_name) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->tag_end (cr, tag_name); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + /** * cairo_select_font_face: * @cr: a #cairo_t diff --git a/src/cairo.h b/src/cairo.h index 3104d47e4..32fc88b17 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -292,6 +292,10 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12) * @CAIRO_STATUS_JBIG2_GLOBAL_MISSING: %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID has been used on at least one image * but no image provided %CAIRO_MIME_TYPE_JBIG2_GLOBAL (Since 1.14) + * @CAIRO_STATUS_PNG_ERROR: error occurred in libpng while reading from or writing to a PNG file (Since 1.16) + * @CAIRO_STATUS_FREETYPE_ERROR: error occurred in libfreetype (Since 1.16) + * @CAIRO_STATUS_WIN32_GDI_ERROR: error occurred in the Windows Graphics Device Interface (Since 1.16) + * @CAIRO_STATUS_TAG_ERROR: invalid tag name, attributes, or nesting (Since 1.16) * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of * status values defined in this enumeration. When using this value, note * that the version of cairo at run-time may have additional status values @@ -348,6 +352,10 @@ typedef enum _cairo_status { CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, CAIRO_STATUS_DEVICE_FINISHED, CAIRO_STATUS_JBIG2_GLOBAL_MISSING, + CAIRO_STATUS_PNG_ERROR, + CAIRO_STATUS_FREETYPE_ERROR, + CAIRO_STATUS_WIN32_GDI_ERROR, + CAIRO_STATUS_TAG_ERROR, CAIRO_STATUS_LAST_STATUS } cairo_status_t; @@ -1018,6 +1026,17 @@ cairo_copy_clip_rectangle_list (cairo_t *cr); cairo_public void cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); +/* Logical structure tagging functions */ + +#define CAIRO_TAG_DEST "cairo.dest" +#define CAIRO_TAG_LINK "Link" + +cairo_public void +cairo_tag_begin (cairo_t *cr, const char *tag_name, const char *attributes); + +cairo_public void +cairo_tag_end (cairo_t *cr, const char *tag_name); + /* Font/Text functions */ /** diff --git a/src/cairoint.h b/src/cairoint.h index f7817484b..493d46103 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -282,6 +282,12 @@ _cairo_isdigit (int c) return (c >= '0' && c <= '9'); } +static inline int cairo_const +_cairo_isalpha (int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + #include "cairo-types-private.h" #include "cairo-cache-private.h" #include "cairo-reference-count-private.h" @@ -1417,6 +1423,17 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); +cairo_private cairo_status_t +_cairo_surface_tag (cairo_surface_t *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip); + cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, @@ -1781,6 +1798,12 @@ _cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, int *out_x_offset, int *out_y_offset); +cairo_private void +_cairo_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix); + +cairo_private void +_cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect); + cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, const cairo_polygon_t *polygon, @@ -2062,7 +2085,7 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); #endif cairo_private void -_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path); +_cairo_debug_print_path (FILE *stream, const cairo_path_fixed_t *path); cairo_private void _cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon); diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c index a0d4c1cee..a9f073fe4 100644 --- a/src/test-paginated-surface.c +++ b/src/test-paginated-surface.c @@ -232,13 +232,15 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, } -static void +static cairo_int_status_t _test_paginated_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t mode) { test_paginated_surface_t *surface = abstract_surface; surface->paginated_mode = mode; + + return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t test_paginated_surface_backend = { diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c index afc0b11ef..68321c86c 100644 --- a/src/win32/cairo-win32-printing-surface.c +++ b/src/win32/cairo-win32-printing-surface.c @@ -389,7 +389,7 @@ _cairo_win32_printing_surface_analyze_operation (cairo_win32_printing_surface_t if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_recording (surface_pattern->surface)) + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } @@ -2090,13 +2090,15 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static void +static cairo_int_status_t _cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_win32_printing_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; + + return CAIRO_STATUS_SUCCESS; } static cairo_bool_t diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index e6862bd10..f7285b92e 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -122,11 +122,7 @@ _cairo_win32_print_gdi_error (const char *context) fflush (stderr); - /* We should switch off of last_status, but we'd either return - * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there - * is no CAIRO_STATUS_UNKNOWN_ERROR. - */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } cairo_bool_t diff --git a/test/Makefile.am b/test/Makefile.am index b2fcd275d..f03c21b08 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -323,6 +323,7 @@ CLEANFILES += \ ps-surface-source.out.ps \ pdf-features.pdf \ pdf-mime-data.out* \ + pdf-tagged-text.out* \ ps-features.ps \ svg-clip.svg \ svg-surface.svg \ diff --git a/test/Makefile.sources b/test/Makefile.sources index 24ded46bb..5ead2316c 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -272,7 +272,10 @@ test_sources = \ record90.c \ recordflip.c \ record-extend.c \ + record-neg-extents.c \ record-mesh.c \ + record-replay-extend.c \ + recording-ink-extents.c \ recording-surface-pattern.c \ recording-surface-extend.c \ rectangle-rounding-error.c \ @@ -351,6 +354,7 @@ test_sources = \ text-pattern.c \ text-rotate.c \ text-transform.c \ + text-unhinted-metrics.c \ text-zero-len.c \ thin-lines.c \ tighten-bounds.c \ @@ -413,7 +417,8 @@ quartz_surface_test_sources = quartz-surface-source.c pdf_surface_test_sources = \ pdf-features.c \ pdf-mime-data.c \ - pdf-surface-source.c + pdf-surface-source.c \ + pdf-tagged-text.c ps_surface_test_sources = \ ps-eps.c \ diff --git a/test/README b/test/README index f1433944b..84d7b4170 100644 --- a/test/README +++ b/test/README @@ -21,9 +21,10 @@ The test suite needs to be run before any code is committed and before any release. See below for hints and rules governing the use of the suite. The test suite is built as a single binary, which allows you to choose -individual or categories of tests to run. For example, if you want to -run all text related tests you can use: - ./cairo-test-suite text +individual or categories of tests to run. For example, to run specific tests: + ./cairo-test-suite record-neg-extents-unbounded record-neg-extents-bounded +Or if you want to run all paint.* related tests you can use: + ./cairo-test-suite paint Or if you want to check the current status of known failures: ./cairo-test-suite XFAIL Or to run a subset of tests, use the -k option to run only the tests @@ -109,10 +110,10 @@ Here are some of the relevant details: * Your system must have a copy of the DejaVu font, the sha1sum of the version used are listed in [...]. These are - "DejaVu Sans" (DejaVuSans.ttf) [1cd336329f45f241002ded61893d91e3acd04436]; - "DejaVu Sans Mono" (DejaVuSansMono.ttf) [0458c0f0fb57f3eb8ced62f26fe7c5ed4e6a9a68]; - "DejaVu Serif" (DejaVuSerif.ttf) [93502d0d0445d1fe1c9f51e51b3e0169266346ce]; - [the DejaVu fonts can be installed from the ttf-dejavu 2.33-2 Debian package] + "DejaVu Sans" (DejaVuSans.ttf) [e9831ee4fd2e1d0ac54508a548c6a449545eba3f]; + "DejaVu Sans Mono" (DejaVuSansMono.ttf) [25d854fbd0450a372615a26a8ef9a1024bd3efc6]; + "DejaVu Serif" (DejaVuSerif.ttf) [78a81850dc7883969042cf3d6dfd18eea7e43e2f]; + [the DejaVu fonts can be installed from the fonts-dejavu-core 2.34-1 Debian package] and also "Nimbus Sans L" (n019003l.pfb) [which can be found in the gsfonts Debian package]. @@ -130,7 +131,7 @@ Here are some of the relevant details: the fixes you will need to avoid false negatives from the test suite. - * To test the ps backend, you will need ghostscript version 9.04. + * To test the ps backend, you will need ghostscript version 9.06. * Testing the xlib backend is problematic since many X server drivers have bugs that are exercised by the test suite. (Or, if @@ -278,42 +279,4 @@ the pdf-specific reference image. Here are the reported poppler bugs and the tests they affect: -Poppler doesn't correctly handle gradients with transparency -https://bugs.freedesktop.org/show_bug.cgi?id=12144 --------------------------------------------------- -fill-alpha-pattern -gradient-alpha -gradient-constant-alpha -linear-gradient -linear-gradient-reflect -radial-gradient -trap-clip - -Poppler should paint images with CAIRO_EXTEND_PAD -https://bugs.freedesktop.org/show_bug.cgi?id=14578 --------------------------------------------------- -paint-source-alpha -paint-with-alpha -rotate-image-surface-paint -scale-source-surface-paint - -Incorrect clipping of group object (regression?) -https://bugs.freedesktop.org/show_bug.cgi?id=14580 --------------------------------------------------- -push-group - -spurious horizontal stripes in color gradients -https://bugs.freedesktop.org/show_bug.cgi?id=10942 --------------------------------------------------- -smask -smask-fill -smask-image-mask -smask-mask -smask-paint -smask-stroke -smask-text - -Ghostscript does not correctly render small miters -http://bugs.ghostscript.com/show_bug.cgi?id=690098 --------------------------------------------------- -miter-precision +[Newest was closed in 2009.] diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c new file mode 100644 index 000000000..74e7968ee --- /dev/null +++ b/test/pdf-tagged-text.c @@ -0,0 +1,403 @@ +/* + * Copyright © 2016 Adrian Johnson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Author: Adrian Johnson + */ + +#include "cairo-test.h" + +#include +#include +#include + +#include +#include + +/* This test checks PDF with + * - tagged text + * - hyperlinks + * - document outline + * - metadata + * - thumbnails + * - page labels + */ + +#define BASENAME "pdf-tagged-text.out" + +#define PAGE_WIDTH 595 +#define PAGE_HEIGHT 842 + +#define HEADING1_SIZE 16 +#define HEADING2_SIZE 14 +#define HEADING3_SIZE 12 +#define TEXT_SIZE 12 +#define HEADING_HEIGHT 50 +#define MARGIN 50 + +struct section { + int level; + const char *heading; + int num_paragraphs; +}; + +static const struct section contents[] = { + { 0, "Chapter 1", 1 }, + { 1, "Section 1.1", 4 }, + { 2, "Section 1.1.1", 3 }, + { 1, "Section 1.2", 2 }, + { 2, "Section 1.2.1", 4 }, + { 2, "Section 1.2.2", 4 }, + { 1, "Section 1.3", 2 }, + { 0, "Chapter 2", 1 }, + { 1, "Section 2.1", 4 }, + { 2, "Section 2.1.1", 3 }, + { 1, "Section 2.2", 2 }, + { 2, "Section 2.2.1", 4 }, + { 2, "Section 2.2.2", 4 }, + { 1, "Section 2.3", 2 }, + { 0, "Chapter 3", 1 }, + { 1, "Section 3.1", 4 }, + { 2, "Section 3.1.1", 3 }, + { 1, "Section 3.2", 2 }, + { 2, "Section 3.2.1", 4 }, + { 2, "Section 3.2.2", 4 }, + { 1, "Section 3.3", 2 }, + { 0, NULL } +}; + +static const char *ipsum_lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing" + " elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + " Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi" + " ut aliquip ex ea commodo consequat. Duis aute irure dolor in" + " reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla" + " pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa" + " qui officia deserunt mollit anim id est laborum."; + +static const char *roman_numerals[] = { + "i", "ii", "iii", "iv", "v" +}; + +#define MAX_PARAGRAPH_LINES 20 + +static int paragraph_num_lines; +static char *paragraph_text[MAX_PARAGRAPH_LINES]; +static double paragraph_height; +static double line_height; +static double y_pos; +static int outline_parents[10]; +static int page_num; + +static void +layout_paragraph (cairo_t *cr) +{ + char *text, *begin, *end, *prev_end; + cairo_text_extents_t text_extents; + cairo_font_extents_t font_extents; + + cairo_select_font_face (cr, "Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, TEXT_SIZE); + cairo_font_extents (cr, &font_extents); + line_height = font_extents.height; + paragraph_height = 0; + paragraph_num_lines = 0; + text = strdup (ipsum_lorem); + begin = text; + end = text; + prev_end = end; + while (*begin) { + end = strchr(end, ' '); + if (!end) { + paragraph_text[paragraph_num_lines++] = strdup (begin); + break; + } + *end = 0; + cairo_text_extents (cr, begin, &text_extents); + *end = ' '; + if (text_extents.width + 2*MARGIN > PAGE_WIDTH) { + int len = prev_end - begin; + char *s = malloc (len); + memcpy (s, begin, len); + s[0] = 0; + paragraph_text[paragraph_num_lines++] = s; + begin = prev_end + 1; + } + prev_end = end; + end++; + } + paragraph_height = line_height * (paragraph_num_lines + 1); + free (text); +} + +static void +draw_paragraph (cairo_t *cr) +{ + int i; + + cairo_select_font_face (cr, "Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, TEXT_SIZE); + cairo_tag_begin (cr, "P", NULL); + for (i = 0; i < paragraph_num_lines; i++) { + cairo_move_to (cr, MARGIN, y_pos); + cairo_show_text (cr, paragraph_text[i]); + y_pos += line_height; + } + cairo_tag_end (cr, "P"); + y_pos += line_height; +} + +static void +draw_page_num (cairo_surface_t *surface, cairo_t *cr, const char *prefix, int num) +{ + char buf[100]; + + buf[0] = 0; + if (prefix) + strcat (buf, prefix); + + if (num) + sprintf (buf + strlen(buf), "%d", num); + + cairo_save (cr); + cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 12); + cairo_move_to (cr, PAGE_WIDTH/2, PAGE_HEIGHT - MARGIN); + cairo_show_text (cr, buf); + cairo_restore (cr); + cairo_pdf_surface_set_page_label (surface, buf); +} + +static void +draw_contents (cairo_surface_t *surface, cairo_t *cr, const struct section *section) +{ + char buf[100]; + + sprintf(buf, "dest='%s'", section->heading); + cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + switch (section->level) { + case 0: + cairo_set_font_size(cr, HEADING1_SIZE); + break; + case 1: + cairo_set_font_size(cr, HEADING2_SIZE); + break; + case 2: + cairo_set_font_size(cr, HEADING3_SIZE); + break; + } + + if (y_pos + HEADING_HEIGHT + MARGIN > PAGE_HEIGHT) { + cairo_show_page (cr); + draw_page_num (surface, cr, roman_numerals[page_num++], 0); + y_pos = MARGIN; + } + cairo_move_to (cr, MARGIN, y_pos); + cairo_save (cr); + cairo_set_source_rgb (cr, 0, 0, 1); + cairo_tag_begin (cr, "TOCI", NULL); + cairo_tag_begin (cr, "Reference", NULL); + cairo_tag_begin (cr, CAIRO_TAG_LINK, buf); + cairo_show_text (cr, section->heading); + cairo_tag_end (cr, CAIRO_TAG_LINK); + cairo_tag_end (cr, "Reference"); + cairo_tag_end (cr, "TOCI"); + cairo_restore (cr); + y_pos += HEADING_HEIGHT; +} + +static void +draw_section (cairo_surface_t *surface, cairo_t *cr, const struct section *section) +{ + int flags, i; + char buf[100]; + + cairo_tag_begin (cr, "Sect", NULL); + sprintf(buf, "name='%s'", section->heading); + cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + if (section->level == 0) { + cairo_show_page (cr); + draw_page_num (surface, cr, NULL, page_num++); + cairo_set_font_size(cr, HEADING1_SIZE); + cairo_move_to (cr, MARGIN, MARGIN); + cairo_tag_begin (cr, "H1", NULL); + cairo_tag_begin (cr, CAIRO_TAG_DEST, buf); + cairo_show_text (cr, section->heading); + cairo_tag_end (cr, CAIRO_TAG_DEST); + cairo_tag_end (cr, "H1"); + y_pos = MARGIN + HEADING_HEIGHT; + flags = CAIRO_PDF_OUTLINE_FLAG_BOLD | CAIRO_PDF_OUTLINE_FLAG_OPEN; + outline_parents[0] = cairo_pdf_surface_add_outline (surface, + CAIRO_PDF_OUTLINE_ROOT, + section->heading, + section->heading, + flags); + } else { + if (section->level == 1) { + cairo_set_font_size(cr, HEADING2_SIZE); + flags = 0; + } else { + cairo_set_font_size(cr, HEADING3_SIZE); + flags = CAIRO_PDF_OUTLINE_FLAG_ITALIC; + } + + if (y_pos + HEADING_HEIGHT + paragraph_height + MARGIN > PAGE_HEIGHT) { + cairo_show_page (cr); + draw_page_num (surface, cr, NULL, page_num++); + y_pos = MARGIN; + } + cairo_move_to (cr, MARGIN, y_pos); + if (section->level == 1) + cairo_tag_begin (cr, "H2", NULL); + else + cairo_tag_begin (cr, "H3", NULL); + cairo_tag_begin (cr, CAIRO_TAG_DEST, buf); + cairo_show_text (cr, section->heading); + cairo_tag_end (cr, CAIRO_TAG_DEST); + if (section->level == 1) + cairo_tag_end (cr, "H2"); + else + cairo_tag_end (cr, "H3"); + y_pos += HEADING_HEIGHT; + outline_parents[section->level] = cairo_pdf_surface_add_outline (surface, + outline_parents[section->level - 1], + section->heading, + section->heading, + flags); + } + + for (i = 0; i < section->num_paragraphs; i++) { + if (y_pos + paragraph_height + MARGIN > PAGE_HEIGHT) { + cairo_show_page (cr); + draw_page_num (surface, cr, NULL, page_num++); + y_pos = MARGIN; + } + draw_paragraph (cr); + } + cairo_tag_end (cr, "Sect"); +} + +static void +draw_cover (cairo_surface_t *surface, cairo_t *cr) +{ + cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(cr, 16); + cairo_move_to (cr, PAGE_WIDTH/3, PAGE_HEIGHT/2); + cairo_tag_begin (cr, "Span", NULL); + cairo_show_text (cr, "PDF Features Test"); + cairo_tag_end (cr, "Span"); + + draw_page_num (surface, cr, "cover", 0); +} + +static void +create_document (cairo_surface_t *surface, cairo_t *cr) +{ + layout_paragraph (cr); + + cairo_pdf_surface_set_thumbnail_size (surface, PAGE_WIDTH/10, PAGE_HEIGHT/10); + + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_TITLE, "PDF Features Test"); + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_AUTHOR, "cairo test suite"); + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_SUBJECT, "cairo test"); + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_KEYWORDS, + "tags, links, outline, page labels, metadata, thumbnails"); + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATOR, "pdf-features"); + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2016-01-01T12:34:56+10:30"); + cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_MOD_DATE, "2016-06-21T05:43:21Z"); + + cairo_tag_begin (cr, "Document", NULL); + + draw_cover (surface, cr); + cairo_show_page (cr); + + page_num = 0; + draw_page_num (surface, cr, roman_numerals[page_num++], 0); + y_pos = MARGIN; + + cairo_pdf_surface_add_outline (surface, + CAIRO_PDF_OUTLINE_ROOT, + "Contents", "TOC", + CAIRO_PDF_OUTLINE_FLAG_BOLD); + + cairo_tag_begin (cr, CAIRO_TAG_DEST, "name='TOC'"); + cairo_tag_begin (cr, "TOC", NULL); + const struct section *sect = contents; + while (sect->heading) { + draw_contents (surface, cr, sect); + sect++; + } + cairo_tag_end (cr, "TOC"); + cairo_tag_end (cr, CAIRO_TAG_DEST); + + page_num = 1; + sect = contents; + while (sect->heading) { + draw_section (surface, cr, sect); + sect++; + } + + cairo_tag_end (cr, "Document"); +} + +static cairo_test_status_t +preamble (cairo_test_context_t *ctx) +{ + cairo_surface_t *surface; + cairo_t *cr; + cairo_status_t status, status2; + char *filename; + const char *path = cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : "."; + + if (! cairo_test_is_target_enabled (ctx, "pdf")) + return CAIRO_TEST_UNTESTED; + + xasprintf (&filename, "%s/%s.pdf", path, BASENAME); + surface = cairo_pdf_surface_create (filename, PAGE_WIDTH, PAGE_HEIGHT); + + cr = cairo_create (surface); + create_document (surface, cr); + + status = cairo_status (cr); + cairo_destroy (cr); + cairo_surface_finish (surface); + status2 = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + status = status2; + + cairo_surface_destroy (surface); + if (status) { + cairo_test_log (ctx, "Failed to create pdf surface for file %s: %s\n", + filename, cairo_status_to_string (status)); + return CAIRO_TEST_FAILURE; + } + + free (filename); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (pdf_tagged_text, + "Check tagged text, hyperlinks and PDF document features", + "pdf", /* keywords */ + NULL, /* requirements */ + 0, 0, + preamble, NULL) diff --git a/test/pdf2png.c b/test/pdf2png.c index 06fa05b18..23691221e 100644 --- a/test/pdf2png.c +++ b/test/pdf2png.c @@ -49,7 +49,9 @@ int main (int argc, char *argv[]) if (argc != 4) FAIL ("usage: pdf2png input_file.pdf output_file.png page"); +#if !GLIB_CHECK_VERSION(2,36,0) g_type_init (); +#endif if (g_path_is_absolute(filename)) { absolute = g_strdup (filename); diff --git a/test/record-neg-extents.c b/test/record-neg-extents.c new file mode 100644 index 000000000..29a2aa1e6 --- /dev/null +++ b/test/record-neg-extents.c @@ -0,0 +1,217 @@ +/* + * Copyright © 2016 Adrian Johnson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Adrian Johnson + */ + +#include "cairo-test.h" +#include +#include + +#define PAT_SIZE 64 +#define PAD (PAT_SIZE/8) +#define WIDTH (PAT_SIZE*4 + PAD*5) +#define HEIGHT (PAT_SIZE + PAD*2) + +/* Test case based on bug 89232 - painting a recording surface to a pdf/ps surface + * omits objects on the recording surface with negative coordinates even though + * the pattern matrix has transformed the objects to within the page extents. + * The bug is a result of pdf/ps assuming the surface extents are always + * (0,0) to (page_width, page_height). + * + * Each test has four cases of painting a recording pattern where: + * 1) recording surface origin is transformed to the center of the pattern + * 2) same as 1) but also scaled up 10x + * 3) same as 1) but also scaled down 10x + * 4) same as 1) but also rotated 45 deg + */ + + +static void +transform_extents(cairo_rectangle_t *extents, cairo_matrix_t *mat) +{ + double x1, y1, x2, y2, x, y; + +#define UPDATE_BBOX \ + x1 = x < x1 ? x : x1; \ + y1 = y < y1 ? y : y1; \ + x2 = x > x2 ? x : x2; \ + y2 = y > y2 ? y : y2; + + x = extents->x; + y = extents->y; + cairo_matrix_transform_point (mat, &x, &y); + x1 = x2 = x; + y1 = y2 = y; + + x = extents->x + extents->width; + y = extents->y; + cairo_matrix_transform_point (mat, &x, &y); + UPDATE_BBOX; + + x = extents->x; + y = extents->y + extents->height; + cairo_matrix_transform_point (mat, &x, &y); + UPDATE_BBOX; + + x = extents->x + extents->width; + y = extents->y + extents->height; + cairo_matrix_transform_point (mat, &x, &y); + UPDATE_BBOX; + + extents->x = x1; + extents->y = y1; + extents->width = x2 - extents->x; + extents->height = y2 - extents->y; + +#undef UPDATE_BBOX +} + +static cairo_pattern_t * +create_pattern (cairo_matrix_t *mat, cairo_bool_t bounded) +{ + cairo_surface_t *surf; + cairo_pattern_t *pat; + cairo_t *cr; + int border; + int square; + + if (bounded) { + cairo_rectangle_t extents = { 0, 0, PAT_SIZE, PAT_SIZE }; + transform_extents (&extents, mat); + surf = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, &extents); + } else { + surf = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + } + + cr = cairo_create (surf); + cairo_transform (cr, mat); + + border = PAT_SIZE/8; + square = (PAT_SIZE - 2*border)/2; + + cairo_rectangle (cr, 0, 0, PAT_SIZE, PAT_SIZE); + cairo_clip (cr); + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_paint (cr); + + cairo_translate (cr, border, border); + cairo_rectangle (cr, 0, 0, square, square); + cairo_set_source_rgb (cr, 0, 1, 0); + cairo_fill (cr); + + cairo_translate (cr, square, 0); + cairo_rectangle (cr, 0, 0, square, square); + cairo_set_source_rgb (cr, 0, 0, 1); + cairo_fill (cr); + + cairo_translate (cr, 0, square); + cairo_rectangle (cr, 0, 0, square, square); + cairo_set_source_rgb (cr, 0, 1, 1); + cairo_fill (cr); + + cairo_translate (cr, -square, 0); + cairo_rectangle (cr, 0, 0, square, square); + cairo_set_source_rgb (cr, 1, 1, 0); + cairo_fill (cr); + + cairo_destroy (cr); + + pat = cairo_pattern_create_for_surface (surf); + cairo_surface_destroy (surf); + cairo_pattern_set_matrix (pat, mat); + + return pat; +} + +static cairo_test_status_t +record_extents (cairo_t *cr, int width, int height, cairo_bool_t bounded) +{ + cairo_pattern_t *pat; + cairo_matrix_t mat; + + /* record surface extents (-PAT_SIZE/2, -PAT_SIZE/2) to (PAT_SIZE/2, PAT_SIZE/2) */ + cairo_translate (cr, PAD, PAD); + cairo_matrix_init_translate (&mat, -PAT_SIZE/2, -PAT_SIZE/2); + pat = create_pattern (&mat, bounded); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_paint (cr); + + /* record surface extents (-10*PAT_SIZE/2, -10*PAT_SIZE/2) to (10*PAT_SIZE/2, 10*PAT_SIZE/2) */ + cairo_translate (cr, PAT_SIZE + PAD, 0); + cairo_matrix_init_translate (&mat, -10.0*PAT_SIZE/2, -10.0*PAT_SIZE/2); + cairo_matrix_scale (&mat, 10, 10); + pat = create_pattern (&mat, bounded); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_paint (cr); + + /* record surface extents (-0.1*PAT_SIZE/2, -0.1*PAT_SIZE/2) to (0.1*PAT_SIZE/2, 0.1*PAT_SIZE/2) */ + cairo_translate (cr, PAT_SIZE + PAD, 0); + cairo_matrix_init_translate (&mat, -0.1*PAT_SIZE/2, -0.1*PAT_SIZE/2); + cairo_matrix_scale (&mat, 0.1, 0.1); + pat = create_pattern (&mat, bounded); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_paint (cr); + + /* record surface centered on (0,0) and rotated 45 deg */ + cairo_translate (cr, PAT_SIZE + PAD, 0); + cairo_matrix_init_translate (&mat, -PAT_SIZE/sqrt(2), -PAT_SIZE/sqrt(2)); + cairo_matrix_rotate (&mat, M_PI/4.0); + cairo_matrix_translate (&mat, PAT_SIZE/2, -PAT_SIZE/2); + pat = create_pattern (&mat, bounded); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_paint (cr); + + return CAIRO_TEST_SUCCESS; +} + +static cairo_test_status_t +record_neg_extents_bounded (cairo_t *cr, int width, int height) +{ + return record_extents(cr, width, height, TRUE); +} + +static cairo_test_status_t +record_neg_extents_unbounded (cairo_t *cr, int width, int height) +{ + return record_extents(cr, width, height, FALSE); +} + + +CAIRO_TEST (record_neg_extents_unbounded, + "Paint unbounded recording pattern with untransformed extents outside of target extents", + "record,transform,pattern", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, record_neg_extents_unbounded) +CAIRO_TEST (record_neg_extents_bounded, + "Paint bounded recording pattern with untransformed extents outside of target extents", + "record,transform,pattern", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, record_neg_extents_bounded) diff --git a/test/record-replay-extend.c b/test/record-replay-extend.c new file mode 100644 index 000000000..4a4123359 --- /dev/null +++ b/test/record-replay-extend.c @@ -0,0 +1,227 @@ +/* + * Copyright © 2016 Adrian Johnson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Adrian Johnson + */ + +#include "cairo-test.h" +#include +#include + +#define PAT_SIZE 32 +#define REPLAY_SIZE (PAT_SIZE*4) +#define PAD 10 +#define WIDTH (REPLAY_SIZE*4 + PAD*5) +#define HEIGHT (REPLAY_SIZE + PAD*2) + +/* Test replaying a recording surface pattern for each type of extend. */ + +static void +transform_extents(cairo_rectangle_t *extents, cairo_matrix_t *mat) +{ + double x1, y1, x2, y2, x, y; + +#define UPDATE_BBOX \ + x1 = x < x1 ? x : x1; \ + y1 = y < y1 ? y : y1; \ + x2 = x > x2 ? x : x2; \ + y2 = y > y2 ? y : y2; + + x = extents->x; + y = extents->y; + cairo_matrix_transform_point (mat, &x, &y); + x1 = x2 = x; + y1 = y2 = y; + + x = extents->x + extents->width; + y = extents->y; + cairo_matrix_transform_point (mat, &x, &y); + UPDATE_BBOX; + + x = extents->x; + y = extents->y + extents->height; + cairo_matrix_transform_point (mat, &x, &y); + UPDATE_BBOX; + + x = extents->x + extents->width; + y = extents->y + extents->height; + cairo_matrix_transform_point (mat, &x, &y); + UPDATE_BBOX; + + extents->x = x1; + extents->y = y1; + extents->width = x2 - extents->x; + extents->height = y2 - extents->y; + +#undef UPDATE_BBOX +} + +static cairo_pattern_t * +create_pattern (cairo_matrix_t *mat, cairo_extend_t extend) +{ + cairo_surface_t *surf; + cairo_pattern_t *pat; + cairo_t *cr; + cairo_rectangle_t extents = { 0, 0, PAT_SIZE, PAT_SIZE }; + + transform_extents (&extents, mat); + surf = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, &extents); + + cr = cairo_create (surf); + cairo_transform (cr, mat); + + cairo_rectangle (cr, 0, 0, PAT_SIZE/2, PAT_SIZE/2); + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_fill (cr); + + cairo_translate (cr, PAT_SIZE/2, 0); + cairo_rectangle (cr, 0, 0, PAT_SIZE/2, PAT_SIZE/2); + cairo_set_source_rgb (cr, 0, 1, 0); + cairo_fill (cr); + + cairo_translate (cr, 0, PAT_SIZE/2); + cairo_rectangle (cr, 0, 0, PAT_SIZE/2, PAT_SIZE/2); + cairo_set_source_rgb (cr, 0, 0, 1); + cairo_fill (cr); + + cairo_translate (cr, -PAT_SIZE/2, 0); + cairo_rectangle (cr, 0, 0, PAT_SIZE/2, PAT_SIZE/2); + cairo_set_source_rgb (cr, 1, 1, 0); + cairo_fill (cr); + + cairo_destroy (cr); + + pat = cairo_pattern_create_for_surface (surf); + cairo_surface_destroy (surf); + cairo_pattern_set_matrix (pat, mat); + cairo_pattern_set_extend (pat, extend); + cairo_pattern_set_filter (pat, CAIRO_FILTER_NEAREST); + + return pat; +} + +static cairo_test_status_t +record_replay_extend (cairo_t *cr, int width, int height, cairo_extend_t extend) +{ + cairo_pattern_t *pat; + cairo_matrix_t mat; + + /* record surface extents (-PAT_SIZE/2, -PAT_SIZE/2) to (PAT_SIZE/2, PAT_SIZE/2) */ + cairo_translate (cr, PAD, PAD); + cairo_matrix_init_translate (&mat, -PAT_SIZE/2, -PAT_SIZE/2); + pat = create_pattern (&mat, extend); + + /* test repeating patterns when the source is outside of the target clip */ + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + cairo_matrix_init_translate (&mat, 3*PAT_SIZE/2, 3*PAT_SIZE/2); + cairo_pattern_set_matrix (pat, &mat); + } + + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_rectangle (cr, 0, 0, REPLAY_SIZE, REPLAY_SIZE); + cairo_fill (cr); + + /* record surface extents (-2*PAT_SIZE/2, -2*PAT_SIZE/2) to (2*PAT_SIZE/2, 2*PAT_SIZE/2) */ + cairo_translate (cr, REPLAY_SIZE + PAD, 0); + cairo_matrix_init_translate (&mat, -2.0*PAT_SIZE/2, -2.0*PAT_SIZE/2); + cairo_matrix_scale (&mat, 2, 2); + pat = create_pattern (&mat, extend); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_rectangle (cr, 0, 0, REPLAY_SIZE, REPLAY_SIZE); + cairo_fill (cr); + + /* record surface extents (-0.5*PAT_SIZE/2, -0.5*PAT_SIZE/2) to (0.5*PAT_SIZE/2, 0.5*PAT_SIZE/2) */ + cairo_translate (cr, REPLAY_SIZE + PAD, 0); + cairo_matrix_init_translate (&mat, -0.5*PAT_SIZE/2, -0.5*PAT_SIZE/2); + cairo_matrix_scale (&mat, 0.5, 0.5); + pat = create_pattern (&mat, extend); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_rectangle (cr, 0, 0, REPLAY_SIZE, REPLAY_SIZE); + cairo_fill (cr); + + /* record surface centered on (0,0) and rotated 45 deg */ + cairo_translate (cr, REPLAY_SIZE + PAD, 0); + cairo_matrix_init_translate (&mat, -PAT_SIZE/sqrt(2), -PAT_SIZE/sqrt(2)); + cairo_matrix_rotate (&mat, M_PI/4.0); + cairo_matrix_translate (&mat, PAT_SIZE/2, -PAT_SIZE/2); + pat = create_pattern (&mat, extend); + cairo_set_source (cr, pat); + cairo_pattern_destroy (pat); + cairo_rectangle (cr, 0, 0, REPLAY_SIZE, REPLAY_SIZE); + cairo_fill (cr); + + return CAIRO_TEST_SUCCESS; +} + +static cairo_test_status_t +record_replay_extend_none (cairo_t *cr, int width, int height) +{ + return record_replay_extend (cr, width, height, CAIRO_EXTEND_NONE); +} + +static cairo_test_status_t +record_replay_extend_repeat (cairo_t *cr, int width, int height) +{ + return record_replay_extend (cr, width, height, CAIRO_EXTEND_REPEAT); +} + +static cairo_test_status_t +record_replay_extend_reflect (cairo_t *cr, int width, int height) +{ + return record_replay_extend (cr, width, height, CAIRO_EXTEND_REFLECT); +} + +static cairo_test_status_t +record_replay_extend_pad (cairo_t *cr, int width, int height) +{ + return record_replay_extend (cr, width, height, CAIRO_EXTEND_PAD); +} + +CAIRO_TEST (record_replay_extend_none, + "Paint recording pattern with CAIRO_EXTEND_NONE", + "record,pattern,extend", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, record_replay_extend_none) +CAIRO_TEST (record_replay_extend_repeat, + "Paint recording pattern with CAIRO_EXTEND_REPEAT", + "record,pattern,extend", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, record_replay_extend_repeat) +CAIRO_TEST (record_replay_extend_reflect, + "Paint recording pattern with CAIRO_EXTEND_REFLECT", + "record,pattern,extend", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, record_replay_extend_reflect) +CAIRO_TEST (record_replay_extend_pad, + "Paint recording pattern with CAIRO_EXTEND_PAD", + "record,pattern,extend", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, record_replay_extend_pad) diff --git a/test/recording-ink-extents.c b/test/recording-ink-extents.c new file mode 100644 index 000000000..dcc905ddf --- /dev/null +++ b/test/recording-ink-extents.c @@ -0,0 +1,172 @@ +/* + * Copyright © 2016 Adrian Johnson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Adrian Johnson + */ + +#include "cairo-test.h" + +/* Check cairo_recording_surface_ink_extents() returns correct extents. */ + + +static cairo_test_status_t +check_extents (cairo_test_context_t *cr, + cairo_surface_t *recording_surface, + const char * func_name, + double expected_x, double expected_y, double expected_w, double expected_h) +{ + double x, y, w, h; + cairo_recording_surface_ink_extents (recording_surface, &x, &y, &w, &h); + if (x != expected_x || + y != expected_y || + w != expected_w || + h != expected_h) + { + cairo_test_log (cr, + "%s: x: %f, y: %f, w: %f, h: %f\n" + " expected: x: %f, y: %f, w: %f, h: %f\n", + func_name, + x, y, w, h, + expected_x, expected_y, + expected_w, expected_h); + return CAIRO_TEST_ERROR; + } + return CAIRO_TEST_SUCCESS; +} + +static cairo_test_status_t +unbounded_fill (cairo_test_context_t *test_cr) +{ + cairo_test_status_t status; + cairo_surface_t *surface; + cairo_t *cr; + + surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + cr = cairo_create (surface); + + cairo_rectangle (cr, -300, -150, 900, 600); + cairo_fill (cr); + + cairo_destroy(cr); + + status = check_extents (test_cr, surface, __func__, + -300, -150, 900, 600); + cairo_surface_destroy (surface); + return status; +} + +static cairo_test_status_t +bounded_fill (cairo_test_context_t *test_cr) +{ + cairo_test_status_t status; + cairo_surface_t *surface; + cairo_t *cr; + cairo_rectangle_t extents = { -150, -100, 300, 200 }; + + surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, &extents); + cr = cairo_create (surface); + + cairo_rectangle (cr, -300, -300, 650, 600); + cairo_fill (cr); + + cairo_destroy(cr); + + status = check_extents (test_cr, surface, __func__, + -150, -100, 300, 200); + cairo_surface_destroy (surface); + return status; +} + +static cairo_test_status_t +unbounded_paint (cairo_test_context_t *test_cr) +{ + cairo_test_status_t status; + cairo_surface_t *surface; + cairo_t *cr; + + surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + cr = cairo_create (surface); + + cairo_paint (cr); + + cairo_destroy(cr); + + status = check_extents (test_cr, surface, __func__, + -(1 << 23), -(1 << 23), -1, -1); + cairo_surface_destroy (surface); + return status; +} + +static cairo_test_status_t +bounded_paint (cairo_test_context_t *test_cr) +{ + cairo_test_status_t status; + cairo_surface_t *surface; + cairo_t *cr; + cairo_rectangle_t extents = { -150, -100, 300, 200 }; + + surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, &extents); + cr = cairo_create (surface); + + cairo_paint (cr); + + cairo_destroy(cr); + + status = check_extents (test_cr, surface, __func__, + -150, -100, 300, 200); + cairo_surface_destroy (surface); + return status; +} + +static cairo_test_status_t +preamble (cairo_test_context_t *cr) +{ + cairo_test_status_t status; + + status = unbounded_fill (cr); + if (status != CAIRO_TEST_SUCCESS) + return status; + + status = bounded_fill (cr); + if (status != CAIRO_TEST_SUCCESS) + return status; + + status = unbounded_paint (cr); + if (status != CAIRO_TEST_SUCCESS) + return status; + + status = bounded_paint (cr); + if (status != CAIRO_TEST_SUCCESS) + return status; + + return CAIRO_TEST_SUCCESS; +} + + +CAIRO_TEST (recording_ink_extents, + "Test cairo_recording_surface_ink_extents()", + "api,recording,extents", /* keywords */ + NULL, /* requirements */ + 0, 0, + preamble, NULL) diff --git a/test/reference/arc-direction.pdf.ref.png b/test/reference/arc-direction.pdf.ref.png new file mode 100644 index 000000000..7b14d71a2 Binary files /dev/null and b/test/reference/arc-direction.pdf.ref.png differ diff --git a/test/reference/arc-direction.ref.png b/test/reference/arc-direction.ref.png index 05ff4107c..7b14d71a2 100644 Binary files a/test/reference/arc-direction.ref.png and b/test/reference/arc-direction.ref.png differ diff --git a/test/reference/arc-looping-dash.pdf.ref.png b/test/reference/arc-looping-dash.pdf.ref.png new file mode 100644 index 000000000..5195b075e Binary files /dev/null and b/test/reference/arc-looping-dash.pdf.ref.png differ diff --git a/test/reference/big-empty-box.ps.rgb24.ref.png b/test/reference/big-empty-box.ps.rgb24.ref.png new file mode 100644 index 000000000..6c2ca32f0 Binary files /dev/null and b/test/reference/big-empty-box.ps.rgb24.ref.png differ diff --git a/test/reference/big-empty-triangle.ps.rgb24.ref.png b/test/reference/big-empty-triangle.ps.rgb24.ref.png new file mode 100644 index 000000000..6c2ca32f0 Binary files /dev/null and b/test/reference/big-empty-triangle.ps.rgb24.ref.png differ diff --git a/test/reference/big-little-box.ps.rgb24.ref.png b/test/reference/big-little-box.ps.rgb24.ref.png new file mode 100644 index 000000000..c069d6fdc Binary files /dev/null and b/test/reference/big-little-box.ps.rgb24.ref.png differ diff --git a/test/reference/bitmap-font.ps.rgb24.ref.png b/test/reference/bitmap-font.ps.rgb24.ref.png new file mode 100644 index 000000000..285d74288 Binary files /dev/null and b/test/reference/bitmap-font.ps.rgb24.ref.png differ diff --git a/test/reference/bug-51910.pdf.ref.png b/test/reference/bug-51910.pdf.ref.png new file mode 100644 index 000000000..df26fe200 Binary files /dev/null and b/test/reference/bug-51910.pdf.ref.png differ diff --git a/test/reference/bug-51910.ps.ref.png b/test/reference/bug-51910.ps.ref.png new file mode 100644 index 000000000..dcac03f51 Binary files /dev/null and b/test/reference/bug-51910.ps.ref.png differ diff --git a/test/reference/bug-84115.pdf.ref.png b/test/reference/bug-84115.pdf.ref.png new file mode 100644 index 000000000..95b5058e2 Binary files /dev/null and b/test/reference/bug-84115.pdf.ref.png differ diff --git a/test/reference/bug-84115.ps.arg32.ref.png b/test/reference/bug-84115.ps.arg32.ref.png new file mode 100644 index 000000000..2c2b918be Binary files /dev/null and b/test/reference/bug-84115.ps.arg32.ref.png differ diff --git a/test/reference/bug-84115.ps.argb32.ref.png b/test/reference/bug-84115.ps.argb32.ref.png new file mode 100644 index 000000000..2c2b918be Binary files /dev/null and b/test/reference/bug-84115.ps.argb32.ref.png differ diff --git a/test/reference/bug-84115.ps.rgb24.ref.png b/test/reference/bug-84115.ps.rgb24.ref.png new file mode 100644 index 000000000..2c2b918be Binary files /dev/null and b/test/reference/bug-84115.ps.rgb24.ref.png differ diff --git a/test/reference/bug-bo-ricotz.argb32.ref.png b/test/reference/bug-bo-ricotz.argb32.ref.png new file mode 100644 index 000000000..1f7346928 Binary files /dev/null and b/test/reference/bug-bo-ricotz.argb32.ref.png differ diff --git a/test/reference/bug-bo-ricotz.pdf.rgb24.ref.png b/test/reference/bug-bo-ricotz.pdf.rgb24.ref.png new file mode 100644 index 000000000..1f7346928 Binary files /dev/null and b/test/reference/bug-bo-ricotz.pdf.rgb24.ref.png differ diff --git a/test/reference/bug-bo-ricotz.ps.ref.png b/test/reference/bug-bo-ricotz.ps.ref.png new file mode 100644 index 000000000..ce308e225 Binary files /dev/null and b/test/reference/bug-bo-ricotz.ps.ref.png differ diff --git a/test/reference/bug-extents.pdf.ref.png b/test/reference/bug-extents.pdf.ref.png new file mode 100644 index 000000000..c3d02d703 Binary files /dev/null and b/test/reference/bug-extents.pdf.ref.png differ diff --git a/test/reference/bug-source-cu.pdf.argb32.ref.png b/test/reference/bug-source-cu.pdf.argb32.ref.png new file mode 100644 index 000000000..084d1974a Binary files /dev/null and b/test/reference/bug-source-cu.pdf.argb32.ref.png differ diff --git a/test/reference/bug-source-cu.pdf.rgb24.ref.png b/test/reference/bug-source-cu.pdf.rgb24.ref.png new file mode 100644 index 000000000..0b11626e5 Binary files /dev/null and b/test/reference/bug-source-cu.pdf.rgb24.ref.png differ diff --git a/test/reference/bug-source-cu.ps.argb32.ref.png b/test/reference/bug-source-cu.ps.argb32.ref.png new file mode 100644 index 000000000..678a17daa Binary files /dev/null and b/test/reference/bug-source-cu.ps.argb32.ref.png differ diff --git a/test/reference/bug-source-cu.ps.rgb24.ref.png b/test/reference/bug-source-cu.ps.rgb24.ref.png new file mode 100644 index 000000000..678a17daa Binary files /dev/null and b/test/reference/bug-source-cu.ps.rgb24.ref.png differ diff --git a/test/reference/bug-spline.ps.ref.png b/test/reference/bug-spline.ps.ref.png new file mode 100644 index 000000000..5eb788672 Binary files /dev/null and b/test/reference/bug-spline.ps.ref.png differ diff --git a/test/reference/caps-05.pdf.ref.png b/test/reference/caps-05.pdf.ref.png new file mode 100644 index 000000000..cec500a49 Binary files /dev/null and b/test/reference/caps-05.pdf.ref.png differ diff --git a/test/reference/caps-05.ps.ref.png b/test/reference/caps-05.ps.ref.png new file mode 100644 index 000000000..48f84ae0a Binary files /dev/null and b/test/reference/caps-05.ps.ref.png differ diff --git a/test/reference/caps-1.pdf.ref.png b/test/reference/caps-1.pdf.ref.png new file mode 100644 index 000000000..cec500a49 Binary files /dev/null and b/test/reference/caps-1.pdf.ref.png differ diff --git a/test/reference/caps-1.ps.ref.png b/test/reference/caps-1.ps.ref.png new file mode 100644 index 000000000..9fce74818 Binary files /dev/null and b/test/reference/caps-1.ps.ref.png differ diff --git a/test/reference/caps-2.pdf.ref.png b/test/reference/caps-2.pdf.ref.png new file mode 100644 index 000000000..43821fe0f Binary files /dev/null and b/test/reference/caps-2.pdf.ref.png differ diff --git a/test/reference/caps-2.ps.ref.png b/test/reference/caps-2.ps.ref.png new file mode 100644 index 000000000..2bda07b91 Binary files /dev/null and b/test/reference/caps-2.ps.ref.png differ diff --git a/test/reference/caps-joins-05.pdf.ref.png b/test/reference/caps-joins-05.pdf.ref.png new file mode 100644 index 000000000..fbe839156 Binary files /dev/null and b/test/reference/caps-joins-05.pdf.ref.png differ diff --git a/test/reference/caps-joins-05.ps.ref.png b/test/reference/caps-joins-05.ps.ref.png new file mode 100644 index 000000000..e767c40fb Binary files /dev/null and b/test/reference/caps-joins-05.ps.ref.png differ diff --git a/test/reference/caps-joins-1.pdf.ref.png b/test/reference/caps-joins-1.pdf.ref.png new file mode 100644 index 000000000..fbe839156 Binary files /dev/null and b/test/reference/caps-joins-1.pdf.ref.png differ diff --git a/test/reference/caps-joins-1.ps.ref.png b/test/reference/caps-joins-1.ps.ref.png new file mode 100644 index 000000000..ef5ed372c Binary files /dev/null and b/test/reference/caps-joins-1.ps.ref.png differ diff --git a/test/reference/caps-joins-2.pdf.ref.png b/test/reference/caps-joins-2.pdf.ref.png new file mode 100644 index 000000000..70faca9dc Binary files /dev/null and b/test/reference/caps-joins-2.pdf.ref.png differ diff --git a/test/reference/caps-joins-2.ps.ref.png b/test/reference/caps-joins-2.ps.ref.png new file mode 100644 index 000000000..da7e3eeaf Binary files /dev/null and b/test/reference/caps-joins-2.ps.ref.png differ diff --git a/test/reference/caps-joins-curve.pdf.ref.png b/test/reference/caps-joins-curve.pdf.ref.png new file mode 100644 index 000000000..17d6b451d Binary files /dev/null and b/test/reference/caps-joins-curve.pdf.ref.png differ diff --git a/test/reference/caps-joins.pdf.ref.png b/test/reference/caps-joins.pdf.ref.png new file mode 100644 index 000000000..1faf366c7 Binary files /dev/null and b/test/reference/caps-joins.pdf.ref.png differ diff --git a/test/reference/caps-sub-paths.pdf.ref.png b/test/reference/caps-sub-paths.pdf.ref.png new file mode 100644 index 000000000..8a5816047 Binary files /dev/null and b/test/reference/caps-sub-paths.pdf.ref.png differ diff --git a/test/reference/caps-tails-curve.pdf.ref.png b/test/reference/caps-tails-curve.pdf.ref.png new file mode 100644 index 000000000..fa7e21fea Binary files /dev/null and b/test/reference/caps-tails-curve.pdf.ref.png differ diff --git a/test/reference/caps.pdf.ref.png b/test/reference/caps.pdf.ref.png new file mode 100644 index 000000000..8ec25032a Binary files /dev/null and b/test/reference/caps.pdf.ref.png differ diff --git a/test/reference/checkerboard.pdf.ref.png b/test/reference/checkerboard.pdf.ref.png new file mode 100644 index 000000000..3f0391ca2 Binary files /dev/null and b/test/reference/checkerboard.pdf.ref.png differ diff --git a/test/reference/clip-complex-bug61592.pdf.ref.png b/test/reference/clip-complex-bug61592.pdf.ref.png new file mode 100644 index 000000000..02abd0e76 Binary files /dev/null and b/test/reference/clip-complex-bug61592.pdf.ref.png differ diff --git a/test/reference/clip-complex-bug61592.ps.ref.png b/test/reference/clip-complex-bug61592.ps.ref.png new file mode 100644 index 000000000..b86fb1807 Binary files /dev/null and b/test/reference/clip-complex-bug61592.ps.ref.png differ diff --git a/test/reference/clip-device-offset.ps.rgb24.ref.png b/test/reference/clip-device-offset.ps.rgb24.ref.png new file mode 100644 index 000000000..241938459 Binary files /dev/null and b/test/reference/clip-device-offset.ps.rgb24.ref.png differ diff --git a/test/reference/clip-disjoint-quad.pdf.ref.png b/test/reference/clip-disjoint-quad.pdf.ref.png new file mode 100644 index 000000000..d24d910b6 Binary files /dev/null and b/test/reference/clip-disjoint-quad.pdf.ref.png differ diff --git a/test/reference/clip-disjoint-quad.ps.ref.png b/test/reference/clip-disjoint-quad.ps.ref.png new file mode 100644 index 000000000..5de2fcf01 Binary files /dev/null and b/test/reference/clip-disjoint-quad.ps.ref.png differ diff --git a/test/reference/clip-disjoint.ps.ref.png b/test/reference/clip-disjoint.ps.ref.png index 5410d0aa3..17b867f6f 100644 Binary files a/test/reference/clip-disjoint.ps.ref.png and b/test/reference/clip-disjoint.ps.ref.png differ diff --git a/test/reference/clip-fill-no-op.ps.ref.png b/test/reference/clip-fill-no-op.ps.ref.png new file mode 100644 index 000000000..b51490deb Binary files /dev/null and b/test/reference/clip-fill-no-op.ps.ref.png differ diff --git a/test/reference/clip-fill-rule-pixel-aligned.ps.argb32.ref.png b/test/reference/clip-fill-rule-pixel-aligned.ps.argb32.ref.png new file mode 100644 index 000000000..9a814b5a2 Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.ps.argb32.ref.png differ diff --git a/test/reference/clip-fill-rule-pixel-aligned.ps.rgb24.ref.png b/test/reference/clip-fill-rule-pixel-aligned.ps.rgb24.ref.png new file mode 100644 index 000000000..0b4f06883 Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.ps.rgb24.ref.png differ diff --git a/test/reference/clip-fill-rule.pdf.argb32.ref.png b/test/reference/clip-fill-rule.pdf.argb32.ref.png index 0d9938e77..d7e2f56e1 100644 Binary files a/test/reference/clip-fill-rule.pdf.argb32.ref.png and b/test/reference/clip-fill-rule.pdf.argb32.ref.png differ diff --git a/test/reference/clip-fill-rule.pdf.rgb24.ref.png b/test/reference/clip-fill-rule.pdf.rgb24.ref.png new file mode 100644 index 000000000..a7acc4fa4 Binary files /dev/null and b/test/reference/clip-fill-rule.pdf.rgb24.ref.png differ diff --git a/test/reference/clip-fill-rule.rgb24.ref.png b/test/reference/clip-fill-rule.rgb24.ref.png index e180fccbd..a7acc4fa4 100644 Binary files a/test/reference/clip-fill-rule.rgb24.ref.png and b/test/reference/clip-fill-rule.rgb24.ref.png differ diff --git a/test/reference/clip-fill.ps.ref.png b/test/reference/clip-fill.ps.ref.png new file mode 100644 index 000000000..568d2f493 Binary files /dev/null and b/test/reference/clip-fill.ps.ref.png differ diff --git a/test/reference/clip-fill.ps.xfail.png b/test/reference/clip-fill.ps.xfail.png deleted file mode 100644 index d0aeaf142..000000000 Binary files a/test/reference/clip-fill.ps.xfail.png and /dev/null differ diff --git a/test/reference/clip-image.pdf.ref.png b/test/reference/clip-image.pdf.ref.png new file mode 100644 index 000000000..8893e0f08 Binary files /dev/null and b/test/reference/clip-image.pdf.ref.png differ diff --git a/test/reference/clip-intersect.ps.ref.png b/test/reference/clip-intersect.ps.ref.png new file mode 100644 index 000000000..d4df3861d Binary files /dev/null and b/test/reference/clip-intersect.ps.ref.png differ diff --git a/test/reference/clip-operator.pdf.argb32.ref.png b/test/reference/clip-operator.pdf.argb32.ref.png index 7f8c93eaa..48554f83a 100644 Binary files a/test/reference/clip-operator.pdf.argb32.ref.png and b/test/reference/clip-operator.pdf.argb32.ref.png differ diff --git a/test/reference/clip-operator.pdf.rgb24.ref.png b/test/reference/clip-operator.pdf.rgb24.ref.png deleted file mode 100644 index fc4f431d6..000000000 Binary files a/test/reference/clip-operator.pdf.rgb24.ref.png and /dev/null differ diff --git a/test/reference/clip-operator.rgb24.ref.png b/test/reference/clip-operator.rgb24.ref.png index 7e3a640ad..c36907279 100644 Binary files a/test/reference/clip-operator.rgb24.ref.png and b/test/reference/clip-operator.rgb24.ref.png differ diff --git a/test/reference/clip-push-group.pdf.ref.png b/test/reference/clip-push-group.pdf.ref.png index 37b58c598..d6640fe45 100644 Binary files a/test/reference/clip-push-group.pdf.ref.png and b/test/reference/clip-push-group.pdf.ref.png differ diff --git a/test/reference/clip-rectilinear.ps.ref.png b/test/reference/clip-rectilinear.ps.ref.png new file mode 100644 index 000000000..9478f8624 Binary files /dev/null and b/test/reference/clip-rectilinear.ps.ref.png differ diff --git a/test/reference/clip-rotate-image-surface-paint.pdf.argb32.ref.png b/test/reference/clip-rotate-image-surface-paint.pdf.argb32.ref.png new file mode 100644 index 000000000..764a4759a Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.pdf.argb32.ref.png differ diff --git a/test/reference/clip-rotate-image-surface-paint.pdf.rgb24.ref.png b/test/reference/clip-rotate-image-surface-paint.pdf.rgb24.ref.png new file mode 100644 index 000000000..764a4759a Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.pdf.rgb24.ref.png differ diff --git a/test/reference/clip-rotate-image-surface-paint.ps.ref.png b/test/reference/clip-rotate-image-surface-paint.ps.ref.png new file mode 100644 index 000000000..7a52887dc Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.ps.ref.png differ diff --git a/test/reference/clip-stroke-no-op.ps.ref.png b/test/reference/clip-stroke-no-op.ps.ref.png new file mode 100644 index 000000000..b51490deb Binary files /dev/null and b/test/reference/clip-stroke-no-op.ps.ref.png differ diff --git a/test/reference/clip-stroke-unbounded.argb32.ref.png b/test/reference/clip-stroke-unbounded.argb32.ref.png index 7f603b70b..51bc7c47f 100644 Binary files a/test/reference/clip-stroke-unbounded.argb32.ref.png and b/test/reference/clip-stroke-unbounded.argb32.ref.png differ diff --git a/test/reference/clip-stroke-unbounded.rgb24.ref.png b/test/reference/clip-stroke-unbounded.rgb24.ref.png index 4a06c4bbc..c75ccbe2b 100644 Binary files a/test/reference/clip-stroke-unbounded.rgb24.ref.png and b/test/reference/clip-stroke-unbounded.rgb24.ref.png differ diff --git a/test/reference/clip-stroke.ps.ref.png b/test/reference/clip-stroke.ps.ref.png new file mode 100644 index 000000000..c08f333b9 Binary files /dev/null and b/test/reference/clip-stroke.ps.ref.png differ diff --git a/test/reference/clip-stroke.ps.xfail.png b/test/reference/clip-stroke.ps.xfail.png deleted file mode 100644 index cc67b0882..000000000 Binary files a/test/reference/clip-stroke.ps.xfail.png and /dev/null differ diff --git a/test/reference/clip-text.ps.ref.png b/test/reference/clip-text.ps.ref.png new file mode 100644 index 000000000..86c676912 Binary files /dev/null and b/test/reference/clip-text.ps.ref.png differ diff --git a/test/reference/clip-text.ps.xfail.png b/test/reference/clip-text.ps.xfail.png deleted file mode 100644 index b50217d88..000000000 Binary files a/test/reference/clip-text.ps.xfail.png and /dev/null differ diff --git a/test/reference/clipped-group.pdf.ref.png b/test/reference/clipped-group.pdf.ref.png index 23db5a4fd..55217cace 100644 Binary files a/test/reference/clipped-group.pdf.ref.png and b/test/reference/clipped-group.pdf.ref.png differ diff --git a/test/reference/close-path-current-point.pdf.ref.png b/test/reference/close-path-current-point.pdf.ref.png new file mode 100644 index 000000000..a162638ce Binary files /dev/null and b/test/reference/close-path-current-point.pdf.ref.png differ diff --git a/test/reference/close-path.pdf.argb32.ref.png b/test/reference/close-path.pdf.argb32.ref.png new file mode 100644 index 000000000..e57654d27 Binary files /dev/null and b/test/reference/close-path.pdf.argb32.ref.png differ diff --git a/test/reference/close-path.pdf.rgb24.ref.png b/test/reference/close-path.pdf.rgb24.ref.png new file mode 100644 index 000000000..e57654d27 Binary files /dev/null and b/test/reference/close-path.pdf.rgb24.ref.png differ diff --git a/test/reference/copy-path.pdf.ref.png b/test/reference/copy-path.pdf.ref.png new file mode 100644 index 000000000..01673eeb5 Binary files /dev/null and b/test/reference/copy-path.pdf.ref.png differ diff --git a/test/reference/culled-glyphs.ps.ref.png b/test/reference/culled-glyphs.ps.ref.png index f34fb9566..85daafa2e 100644 Binary files a/test/reference/culled-glyphs.ps.ref.png and b/test/reference/culled-glyphs.ps.ref.png differ diff --git a/test/reference/dash-caps-joins.pdf.ref.png b/test/reference/dash-caps-joins.pdf.ref.png new file mode 100644 index 000000000..55a8c87ef Binary files /dev/null and b/test/reference/dash-caps-joins.pdf.ref.png differ diff --git a/test/reference/dash-caps-joins.ps.ref.png b/test/reference/dash-caps-joins.ps.ref.png index 466bc62d7..9cfdb19db 100644 Binary files a/test/reference/dash-caps-joins.ps.ref.png and b/test/reference/dash-caps-joins.ps.ref.png differ diff --git a/test/reference/dash-curve.pdf.ref.png b/test/reference/dash-curve.pdf.ref.png new file mode 100644 index 000000000..ef4e76b39 Binary files /dev/null and b/test/reference/dash-curve.pdf.ref.png differ diff --git a/test/reference/dash-curve.ps.ref.png b/test/reference/dash-curve.ps.ref.png new file mode 100644 index 000000000..b4e6d1c9a Binary files /dev/null and b/test/reference/dash-curve.ps.ref.png differ diff --git a/test/reference/dash-curve.ps2.ref.png b/test/reference/dash-curve.ps2.ref.png deleted file mode 100644 index ffb402fe3..000000000 Binary files a/test/reference/dash-curve.ps2.ref.png and /dev/null differ diff --git a/test/reference/dash-curve.ps3.ref.png b/test/reference/dash-curve.ps3.ref.png deleted file mode 100644 index ffb402fe3..000000000 Binary files a/test/reference/dash-curve.ps3.ref.png and /dev/null differ diff --git a/test/reference/dash-infinite-loop.pdf.ref.png b/test/reference/dash-infinite-loop.pdf.ref.png new file mode 100644 index 000000000..e329b6826 Binary files /dev/null and b/test/reference/dash-infinite-loop.pdf.ref.png differ diff --git a/test/reference/dash-scale.pdf.ref.png b/test/reference/dash-scale.pdf.ref.png new file mode 100644 index 000000000..335551fa7 Binary files /dev/null and b/test/reference/dash-scale.pdf.ref.png differ diff --git a/test/reference/dash-state.pdf.ref.png b/test/reference/dash-state.pdf.ref.png new file mode 100644 index 000000000..42db6e4c0 Binary files /dev/null and b/test/reference/dash-state.pdf.ref.png differ diff --git a/test/reference/dash-zero-length.pdf.argb32.ref.png b/test/reference/dash-zero-length.pdf.argb32.ref.png new file mode 100644 index 000000000..082eb0014 Binary files /dev/null and b/test/reference/dash-zero-length.pdf.argb32.ref.png differ diff --git a/test/reference/dash-zero-length.pdf.rgb24.ref.png b/test/reference/dash-zero-length.pdf.rgb24.ref.png new file mode 100644 index 000000000..8dcfa5464 Binary files /dev/null and b/test/reference/dash-zero-length.pdf.rgb24.ref.png differ diff --git a/test/reference/degenerate-curve-to.pdf.ref.png b/test/reference/degenerate-curve-to.pdf.ref.png new file mode 100644 index 000000000..1a1b7e49b Binary files /dev/null and b/test/reference/degenerate-curve-to.pdf.ref.png differ diff --git a/test/reference/degenerate-linear-gradient.pdf.ref.png b/test/reference/degenerate-linear-gradient.pdf.ref.png new file mode 100644 index 000000000..faeff6cd9 Binary files /dev/null and b/test/reference/degenerate-linear-gradient.pdf.ref.png differ diff --git a/test/reference/degenerate-path.pdf.argb32.ref.png b/test/reference/degenerate-path.pdf.argb32.ref.png new file mode 100644 index 000000000..c9dd80912 Binary files /dev/null and b/test/reference/degenerate-path.pdf.argb32.ref.png differ diff --git a/test/reference/degenerate-path.pdf.rgb24.ref.png b/test/reference/degenerate-path.pdf.rgb24.ref.png new file mode 100644 index 000000000..65d5a532e Binary files /dev/null and b/test/reference/degenerate-path.pdf.rgb24.ref.png differ diff --git a/test/reference/degenerate-pen.pdf.argb32.ref.png b/test/reference/degenerate-pen.pdf.argb32.ref.png new file mode 100644 index 000000000..24e9a4f32 Binary files /dev/null and b/test/reference/degenerate-pen.pdf.argb32.ref.png differ diff --git a/test/reference/degenerate-pen.pdf.rgb24.ref.png b/test/reference/degenerate-pen.pdf.rgb24.ref.png new file mode 100644 index 000000000..24e9a4f32 Binary files /dev/null and b/test/reference/degenerate-pen.pdf.rgb24.ref.png differ diff --git a/test/reference/degenerate-radial-gradient.pdf.ref.png b/test/reference/degenerate-radial-gradient.pdf.ref.png new file mode 100644 index 000000000..ba47eb1a9 Binary files /dev/null and b/test/reference/degenerate-radial-gradient.pdf.ref.png differ diff --git a/test/reference/degenerate-rel-curve-to.pdf.ref.png b/test/reference/degenerate-rel-curve-to.pdf.ref.png new file mode 100644 index 000000000..5b3ef49ba Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.pdf.ref.png differ diff --git a/test/reference/device-offset-fractional.pdf.ref.png b/test/reference/device-offset-fractional.pdf.ref.png new file mode 100644 index 000000000..7c070937d Binary files /dev/null and b/test/reference/device-offset-fractional.pdf.ref.png differ diff --git a/test/reference/device-offset-positive.ps.rgb24.ref.png b/test/reference/device-offset-positive.ps.rgb24.ref.png new file mode 100644 index 000000000..dcdd3324c Binary files /dev/null and b/test/reference/device-offset-positive.ps.rgb24.ref.png differ diff --git a/test/reference/device-offset.ps.rgb24.ref.png b/test/reference/device-offset.ps.rgb24.ref.png new file mode 100644 index 000000000..f19acba17 Binary files /dev/null and b/test/reference/device-offset.ps.rgb24.ref.png differ diff --git a/test/reference/extend-pad-border.pdf.ref.png b/test/reference/extend-pad-border.pdf.ref.png index f4fc524ac..c22743e8f 100644 Binary files a/test/reference/extend-pad-border.pdf.ref.png and b/test/reference/extend-pad-border.pdf.ref.png differ diff --git a/test/reference/fallback.pdf.ref.png b/test/reference/fallback.pdf.ref.png new file mode 100644 index 000000000..2e4a4578f Binary files /dev/null and b/test/reference/fallback.pdf.ref.png differ diff --git a/test/reference/fallback.ps.ref.png b/test/reference/fallback.ps.ref.png new file mode 100644 index 000000000..99c83e328 Binary files /dev/null and b/test/reference/fallback.ps.ref.png differ diff --git a/test/reference/fill-alpha.ps.argb32.ref.png b/test/reference/fill-alpha.ps.argb32.ref.png index 8d70d53f8..24bd3b18a 100644 Binary files a/test/reference/fill-alpha.ps.argb32.ref.png and b/test/reference/fill-alpha.ps.argb32.ref.png differ diff --git a/test/reference/fill-and-stroke-alpha-add.pdf.ref.png b/test/reference/fill-and-stroke-alpha-add.pdf.ref.png new file mode 100644 index 000000000..609ed1f77 Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.pdf.ref.png differ diff --git a/test/reference/fill-and-stroke-alpha.pdf.ref.png b/test/reference/fill-and-stroke-alpha.pdf.ref.png new file mode 100644 index 000000000..e1746d5d5 Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.pdf.ref.png differ diff --git a/test/reference/fill-and-stroke.pdf.argb32.ref.png b/test/reference/fill-and-stroke.pdf.argb32.ref.png new file mode 100644 index 000000000..81ec2f8da Binary files /dev/null and b/test/reference/fill-and-stroke.pdf.argb32.ref.png differ diff --git a/test/reference/fill-and-stroke.pdf.rgb24.ref.png b/test/reference/fill-and-stroke.pdf.rgb24.ref.png new file mode 100644 index 000000000..f0b2be519 Binary files /dev/null and b/test/reference/fill-and-stroke.pdf.rgb24.ref.png differ diff --git a/test/reference/fill-degenerate-sort-order.argb32.ref.png b/test/reference/fill-degenerate-sort-order.argb32.ref.png index bed9b5ccd..c43bcd527 100644 Binary files a/test/reference/fill-degenerate-sort-order.argb32.ref.png and b/test/reference/fill-degenerate-sort-order.argb32.ref.png differ diff --git a/test/reference/fill-degenerate-sort-order.pdf.ref.png b/test/reference/fill-degenerate-sort-order.pdf.ref.png new file mode 100644 index 000000000..20ce4b836 Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.pdf.ref.png differ diff --git a/test/reference/fill-degenerate-sort-order.rgb24.ref.png b/test/reference/fill-degenerate-sort-order.rgb24.ref.png index 3f26e302d..35ebdbd13 100644 Binary files a/test/reference/fill-degenerate-sort-order.rgb24.ref.png and b/test/reference/fill-degenerate-sort-order.rgb24.ref.png differ diff --git a/test/reference/fill-empty.ps.rgb24.ref.png b/test/reference/fill-empty.ps.rgb24.ref.png new file mode 100644 index 000000000..dc7a8a0e4 Binary files /dev/null and b/test/reference/fill-empty.ps.rgb24.ref.png differ diff --git a/test/reference/fill-image.ps.ref.png b/test/reference/fill-image.ps.ref.png index 97137015e..1b845bc3e 100644 Binary files a/test/reference/fill-image.ps.ref.png and b/test/reference/fill-image.ps.ref.png differ diff --git a/test/reference/fill-missed-stop.pdf.argb32.ref.png b/test/reference/fill-missed-stop.pdf.argb32.ref.png index 7d56e3e8c..8251b29da 100644 Binary files a/test/reference/fill-missed-stop.pdf.argb32.ref.png and b/test/reference/fill-missed-stop.pdf.argb32.ref.png differ diff --git a/test/reference/fill-missed-stop.pdf.rgb24.ref.png b/test/reference/fill-missed-stop.pdf.rgb24.ref.png new file mode 100644 index 000000000..e9921667e Binary files /dev/null and b/test/reference/fill-missed-stop.pdf.rgb24.ref.png differ diff --git a/test/reference/fill-rule.pdf.ref.png b/test/reference/fill-rule.pdf.ref.png new file mode 100644 index 000000000..67af05127 Binary files /dev/null and b/test/reference/fill-rule.pdf.ref.png differ diff --git a/test/reference/fill-rule.rgb24.ref.png b/test/reference/fill-rule.rgb24.ref.png index 25023a28c..70bde2de7 100644 Binary files a/test/reference/fill-rule.rgb24.ref.png and b/test/reference/fill-rule.rgb24.ref.png differ diff --git a/test/reference/finer-grained-fallbacks.pdf.argb32.ref.png b/test/reference/finer-grained-fallbacks.pdf.argb32.ref.png new file mode 100644 index 000000000..f5edc140f Binary files /dev/null and b/test/reference/finer-grained-fallbacks.pdf.argb32.ref.png differ diff --git a/test/reference/finer-grained-fallbacks.pdf.rgb24.ref.png b/test/reference/finer-grained-fallbacks.pdf.rgb24.ref.png new file mode 100644 index 000000000..8ee9a5e62 Binary files /dev/null and b/test/reference/finer-grained-fallbacks.pdf.rgb24.ref.png differ diff --git a/test/reference/finer-grained-fallbacks.ps.argb32.ref.png b/test/reference/finer-grained-fallbacks.ps.argb32.ref.png new file mode 100644 index 000000000..261c46036 Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps.argb32.ref.png differ diff --git a/test/reference/finer-grained-fallbacks.ps.rgb24.ref.png b/test/reference/finer-grained-fallbacks.ps.rgb24.ref.png new file mode 100644 index 000000000..8ee9a5e62 Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps.rgb24.ref.png differ diff --git a/test/reference/finer-grained-fallbacks.ps2.argb32.ref.png b/test/reference/finer-grained-fallbacks.ps2.argb32.ref.png deleted file mode 100644 index 19c132f68..000000000 Binary files a/test/reference/finer-grained-fallbacks.ps2.argb32.ref.png and /dev/null differ diff --git a/test/reference/finer-grained-fallbacks.ps2.ref.png b/test/reference/finer-grained-fallbacks.ps2.ref.png deleted file mode 100644 index 1744100c9..000000000 Binary files a/test/reference/finer-grained-fallbacks.ps2.ref.png and /dev/null differ diff --git a/test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png b/test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png deleted file mode 100644 index 3f94a3a02..000000000 Binary files a/test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png and /dev/null differ diff --git a/test/reference/finer-grained-fallbacks.ps3.argb32.ref.png b/test/reference/finer-grained-fallbacks.ps3.argb32.ref.png deleted file mode 100644 index 19c132f68..000000000 Binary files a/test/reference/finer-grained-fallbacks.ps3.argb32.ref.png and /dev/null differ diff --git a/test/reference/finer-grained-fallbacks.ps3.ref.png b/test/reference/finer-grained-fallbacks.ps3.ref.png deleted file mode 100644 index 1744100c9..000000000 Binary files a/test/reference/finer-grained-fallbacks.ps3.ref.png and /dev/null differ diff --git a/test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png b/test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png deleted file mode 100644 index 3f94a3a02..000000000 Binary files a/test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png and /dev/null differ diff --git a/test/reference/font-matrix-translation.ps.ref.png b/test/reference/font-matrix-translation.ps.ref.png new file mode 100644 index 000000000..472e60f9e Binary files /dev/null and b/test/reference/font-matrix-translation.ps.ref.png differ diff --git a/test/reference/font-matrix-translation.ps2.argb32.ref.png b/test/reference/font-matrix-translation.ps2.argb32.ref.png deleted file mode 100644 index 41d05a07d..000000000 Binary files a/test/reference/font-matrix-translation.ps2.argb32.ref.png and /dev/null differ diff --git a/test/reference/font-matrix-translation.ps2.rgb24.ref.png b/test/reference/font-matrix-translation.ps2.rgb24.ref.png deleted file mode 100644 index 41d05a07d..000000000 Binary files a/test/reference/font-matrix-translation.ps2.rgb24.ref.png and /dev/null differ diff --git a/test/reference/font-matrix-translation.ps3.argb32.ref.png b/test/reference/font-matrix-translation.ps3.argb32.ref.png deleted file mode 100644 index 41d05a07d..000000000 Binary files a/test/reference/font-matrix-translation.ps3.argb32.ref.png and /dev/null differ diff --git a/test/reference/font-matrix-translation.ps3.rgb24.ref.png b/test/reference/font-matrix-translation.ps3.rgb24.ref.png deleted file mode 100644 index 41d05a07d..000000000 Binary files a/test/reference/font-matrix-translation.ps3.rgb24.ref.png and /dev/null differ diff --git a/test/reference/ft-show-glyphs-positioning.pdf.ref.png b/test/reference/ft-show-glyphs-positioning.pdf.ref.png index 0d62fd378..4f8d9f741 100644 Binary files a/test/reference/ft-show-glyphs-positioning.pdf.ref.png and b/test/reference/ft-show-glyphs-positioning.pdf.ref.png differ diff --git a/test/reference/ft-show-glyphs-positioning.ps.ref.png b/test/reference/ft-show-glyphs-positioning.ps.ref.png new file mode 100644 index 000000000..f4e57201d Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.ps.ref.png differ diff --git a/test/reference/ft-show-glyphs-positioning.ps2.ref.png b/test/reference/ft-show-glyphs-positioning.ps2.ref.png deleted file mode 100644 index c5fbf30b5..000000000 Binary files a/test/reference/ft-show-glyphs-positioning.ps2.ref.png and /dev/null differ diff --git a/test/reference/ft-show-glyphs-positioning.ps3.ref.png b/test/reference/ft-show-glyphs-positioning.ps3.ref.png deleted file mode 100644 index c5fbf30b5..000000000 Binary files a/test/reference/ft-show-glyphs-positioning.ps3.ref.png and /dev/null differ diff --git a/test/reference/ft-show-glyphs-table.ps.ref.png b/test/reference/ft-show-glyphs-table.ps.ref.png new file mode 100644 index 000000000..35a9986eb Binary files /dev/null and b/test/reference/ft-show-glyphs-table.ps.ref.png differ diff --git a/test/reference/ft-show-glyphs-table.ps2.ref.png b/test/reference/ft-show-glyphs-table.ps2.ref.png deleted file mode 100644 index 5143663fc..000000000 Binary files a/test/reference/ft-show-glyphs-table.ps2.ref.png and /dev/null differ diff --git a/test/reference/ft-show-glyphs-table.ps3.ref.png b/test/reference/ft-show-glyphs-table.ps3.ref.png deleted file mode 100644 index 5143663fc..000000000 Binary files a/test/reference/ft-show-glyphs-table.ps3.ref.png and /dev/null differ diff --git a/test/reference/ft-text-vertical-layout-type3.pdf.ref.png b/test/reference/ft-text-vertical-layout-type3.pdf.ref.png index a05ec1d74..cec2547ec 100644 Binary files a/test/reference/ft-text-vertical-layout-type3.pdf.ref.png and b/test/reference/ft-text-vertical-layout-type3.pdf.ref.png differ diff --git a/test/reference/ft-text-vertical-layout-type3.ps.ref.png b/test/reference/ft-text-vertical-layout-type3.ps.ref.png index f8aafa2d4..11aed7da1 100644 Binary files a/test/reference/ft-text-vertical-layout-type3.ps.ref.png and b/test/reference/ft-text-vertical-layout-type3.ps.ref.png differ diff --git a/test/reference/ft-text-vertical-layout-type3.ref.png b/test/reference/ft-text-vertical-layout-type3.ref.png index 5f64d8825..0db1c0c2e 100644 Binary files a/test/reference/ft-text-vertical-layout-type3.ref.png and b/test/reference/ft-text-vertical-layout-type3.ref.png differ diff --git a/test/reference/glyph-cache-pressure.ps.ref.png b/test/reference/glyph-cache-pressure.ps.ref.png new file mode 100644 index 000000000..e537bc535 Binary files /dev/null and b/test/reference/glyph-cache-pressure.ps.ref.png differ diff --git a/test/reference/glyph-cache-pressure.ps2.ref.png b/test/reference/glyph-cache-pressure.ps2.ref.png deleted file mode 100644 index 88fa4478b..000000000 Binary files a/test/reference/glyph-cache-pressure.ps2.ref.png and /dev/null differ diff --git a/test/reference/glyph-cache-pressure.ps3.ref.png b/test/reference/glyph-cache-pressure.ps3.ref.png deleted file mode 100644 index 88fa4478b..000000000 Binary files a/test/reference/glyph-cache-pressure.ps3.ref.png and /dev/null differ diff --git a/test/reference/gradient-alpha.pdf.argb32.ref.png b/test/reference/gradient-alpha.pdf.argb32.ref.png new file mode 100644 index 000000000..48ab382c1 Binary files /dev/null and b/test/reference/gradient-alpha.pdf.argb32.ref.png differ diff --git a/test/reference/gradient-alpha.pdf.rgb24.ref.png b/test/reference/gradient-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..27b9d209b Binary files /dev/null and b/test/reference/gradient-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/gradient-constant-alpha.pdf.argb32.ref.png b/test/reference/gradient-constant-alpha.pdf.argb32.ref.png new file mode 100644 index 000000000..ad2fbdaa5 Binary files /dev/null and b/test/reference/gradient-constant-alpha.pdf.argb32.ref.png differ diff --git a/test/reference/gradient-constant-alpha.pdf.rgb24.ref.png b/test/reference/gradient-constant-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..b1cae2d8d Binary files /dev/null and b/test/reference/gradient-constant-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/gradient-zero-stops-mask.ps.rgb24.ref.png b/test/reference/gradient-zero-stops-mask.ps.rgb24.ref.png new file mode 100644 index 000000000..21465ce4a Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.ps.rgb24.ref.png differ diff --git a/test/reference/gradient-zero-stops.ps.rgb24.png b/test/reference/gradient-zero-stops.ps.rgb24.png new file mode 100644 index 000000000..21465ce4a Binary files /dev/null and b/test/reference/gradient-zero-stops.ps.rgb24.png differ diff --git a/test/reference/group-unaligned.ps.ref.png b/test/reference/group-unaligned.ps.ref.png index f10899879..2cd04f416 100644 Binary files a/test/reference/group-unaligned.ps.ref.png and b/test/reference/group-unaligned.ps.ref.png differ diff --git a/test/reference/halo-transform.ps.ref.png b/test/reference/halo-transform.ps.ref.png index 89e1f7629..f1d2e2bf1 100644 Binary files a/test/reference/halo-transform.ps.ref.png and b/test/reference/halo-transform.ps.ref.png differ diff --git a/test/reference/halo.ps.ref.png b/test/reference/halo.ps.ref.png index 1426d2e0c..f8d025f55 100644 Binary files a/test/reference/halo.ps.ref.png and b/test/reference/halo.ps.ref.png differ diff --git a/test/reference/huge-linear.pdf.ref.png b/test/reference/huge-linear.pdf.ref.png index 8313470cc..cdafafa24 100644 Binary files a/test/reference/huge-linear.pdf.ref.png and b/test/reference/huge-linear.pdf.ref.png differ diff --git a/test/reference/huge-radial.pdf.ref.png b/test/reference/huge-radial.pdf.ref.png new file mode 100644 index 000000000..8aa088b44 Binary files /dev/null and b/test/reference/huge-radial.pdf.ref.png differ diff --git a/test/reference/huge-radial.ref.png b/test/reference/huge-radial.ref.png index 541bb309a..8246ffffc 100644 Binary files a/test/reference/huge-radial.ref.png and b/test/reference/huge-radial.ref.png differ diff --git a/test/reference/image-surface-source.ps.argb32.ref.png b/test/reference/image-surface-source.ps.argb32.ref.png new file mode 100644 index 000000000..b69f5bb9d Binary files /dev/null and b/test/reference/image-surface-source.ps.argb32.ref.png differ diff --git a/test/reference/image-surface-source.ps.rgb24.ref.png b/test/reference/image-surface-source.ps.rgb24.ref.png new file mode 100644 index 000000000..d9af4f862 Binary files /dev/null and b/test/reference/image-surface-source.ps.rgb24.ref.png differ diff --git a/test/reference/image-surface-source.ps2.ref.png b/test/reference/image-surface-source.ps2.ref.png deleted file mode 100644 index 10231581b..000000000 Binary files a/test/reference/image-surface-source.ps2.ref.png and /dev/null differ diff --git a/test/reference/image-surface-source.ps3.ref.png b/test/reference/image-surface-source.ps3.ref.png deleted file mode 100644 index 10231581b..000000000 Binary files a/test/reference/image-surface-source.ps3.ref.png and /dev/null differ diff --git a/test/reference/infinite-join.pdf.ref.png b/test/reference/infinite-join.pdf.ref.png new file mode 100644 index 000000000..f40b4a487 Binary files /dev/null and b/test/reference/infinite-join.pdf.ref.png differ diff --git a/test/reference/inverse-text.ps.ref.png b/test/reference/inverse-text.ps.ref.png new file mode 100644 index 000000000..07071747a Binary files /dev/null and b/test/reference/inverse-text.ps.ref.png differ diff --git a/test/reference/joins-loop.ps.ref.png b/test/reference/joins-loop.ps.ref.png new file mode 100644 index 000000000..37b833a43 Binary files /dev/null and b/test/reference/joins-loop.ps.ref.png differ diff --git a/test/reference/joins-star.ps.ref.png b/test/reference/joins-star.ps.ref.png new file mode 100644 index 000000000..44ad65a92 Binary files /dev/null and b/test/reference/joins-star.ps.ref.png differ diff --git a/test/reference/joins.pdf.argb32.ref.png b/test/reference/joins.pdf.argb32.ref.png new file mode 100644 index 000000000..7ad9fb92a Binary files /dev/null and b/test/reference/joins.pdf.argb32.ref.png differ diff --git a/test/reference/joins.pdf.rgb24.ref.png b/test/reference/joins.pdf.rgb24.ref.png new file mode 100644 index 000000000..7ad9fb92a Binary files /dev/null and b/test/reference/joins.pdf.rgb24.ref.png differ diff --git a/test/reference/large-font.ps.ref.png b/test/reference/large-font.ps.ref.png new file mode 100644 index 000000000..b4fba170e Binary files /dev/null and b/test/reference/large-font.ps.ref.png differ diff --git a/test/reference/leaky-dashed-stroke.pdf.ref.png b/test/reference/leaky-dashed-stroke.pdf.ref.png new file mode 100644 index 000000000..6d3d9818a Binary files /dev/null and b/test/reference/leaky-dashed-stroke.pdf.ref.png differ diff --git a/test/reference/leaky-polygon.pdf.ref.png b/test/reference/leaky-polygon.pdf.ref.png new file mode 100644 index 000000000..db32c0c8e Binary files /dev/null and b/test/reference/leaky-polygon.pdf.ref.png differ diff --git a/test/reference/line-width-large-overlap-offset.ps.ref.png b/test/reference/line-width-large-overlap-offset.ps.ref.png new file mode 100644 index 000000000..3c3464bed Binary files /dev/null and b/test/reference/line-width-large-overlap-offset.ps.ref.png differ diff --git a/test/reference/line-width-large-overlap-rotated.ps.ref.png b/test/reference/line-width-large-overlap-rotated.ps.ref.png new file mode 100644 index 000000000..35f384733 Binary files /dev/null and b/test/reference/line-width-large-overlap-rotated.ps.ref.png differ diff --git a/test/reference/line-width-overlap-offset.ps.ref.png b/test/reference/line-width-overlap-offset.ps.ref.png new file mode 100644 index 000000000..f869ca240 Binary files /dev/null and b/test/reference/line-width-overlap-offset.ps.ref.png differ diff --git a/test/reference/line-width-overlap-rotated.ps.ref.png b/test/reference/line-width-overlap-rotated.ps.ref.png new file mode 100644 index 000000000..b63bfd41e Binary files /dev/null and b/test/reference/line-width-overlap-rotated.ps.ref.png differ diff --git a/test/reference/line-width-scale.pdf.ref.png b/test/reference/line-width-scale.pdf.ref.png new file mode 100644 index 000000000..9aa8b1aac Binary files /dev/null and b/test/reference/line-width-scale.pdf.ref.png differ diff --git a/test/reference/line-width-tolerance.pdf.ref.png b/test/reference/line-width-tolerance.pdf.ref.png new file mode 100644 index 000000000..f890a52ed Binary files /dev/null and b/test/reference/line-width-tolerance.pdf.ref.png differ diff --git a/test/reference/line-width-tolerance.ps.ref.png b/test/reference/line-width-tolerance.ps.ref.png new file mode 100644 index 000000000..34a2d9738 Binary files /dev/null and b/test/reference/line-width-tolerance.ps.ref.png differ diff --git a/test/reference/line-width.pdf.ref.png b/test/reference/line-width.pdf.ref.png new file mode 100644 index 000000000..357565206 Binary files /dev/null and b/test/reference/line-width.pdf.ref.png differ diff --git a/test/reference/linear-gradient-extend.pdf.ref.png b/test/reference/linear-gradient-extend.pdf.ref.png new file mode 100644 index 000000000..4c3764cd3 Binary files /dev/null and b/test/reference/linear-gradient-extend.pdf.ref.png differ diff --git a/test/reference/linear-gradient-large.pdf.ref.png b/test/reference/linear-gradient-large.pdf.ref.png new file mode 100644 index 000000000..ae995a992 Binary files /dev/null and b/test/reference/linear-gradient-large.pdf.ref.png differ diff --git a/test/reference/linear-gradient-large.ps.ref.png b/test/reference/linear-gradient-large.ps.ref.png new file mode 100644 index 000000000..038df5df3 Binary files /dev/null and b/test/reference/linear-gradient-large.ps.ref.png differ diff --git a/test/reference/linear-gradient-one-stop.ps.rgb24.ref.png b/test/reference/linear-gradient-one-stop.ps.rgb24.ref.png new file mode 100644 index 000000000..efc12ee71 Binary files /dev/null and b/test/reference/linear-gradient-one-stop.ps.rgb24.ref.png differ diff --git a/test/reference/linear-gradient-subset.pdf.ref.png b/test/reference/linear-gradient-subset.pdf.ref.png new file mode 100644 index 000000000..eaf8d2976 Binary files /dev/null and b/test/reference/linear-gradient-subset.pdf.ref.png differ diff --git a/test/reference/linear-gradient-subset.ref.png b/test/reference/linear-gradient-subset.ref.png index 8e95d10f6..fa0f62d11 100644 Binary files a/test/reference/linear-gradient-subset.ref.png and b/test/reference/linear-gradient-subset.ref.png differ diff --git a/test/reference/linear-gradient.pdf.ref.png b/test/reference/linear-gradient.pdf.ref.png new file mode 100644 index 000000000..62f74ddb3 Binary files /dev/null and b/test/reference/linear-gradient.pdf.ref.png differ diff --git a/test/reference/linear-gradient.ps3.ref.png b/test/reference/linear-gradient.ps3.ref.png index c2fa71b11..fb83dbaa1 100644 Binary files a/test/reference/linear-gradient.ps3.ref.png and b/test/reference/linear-gradient.ps3.ref.png differ diff --git a/test/reference/linear-gradient.ref.png b/test/reference/linear-gradient.ref.png index 32c99a4a3..ad6d8900d 100644 Binary files a/test/reference/linear-gradient.ref.png and b/test/reference/linear-gradient.ref.png differ diff --git a/test/reference/linear-uniform.ps.ref.png b/test/reference/linear-uniform.ps.ref.png new file mode 100644 index 000000000..0faced8c3 Binary files /dev/null and b/test/reference/linear-uniform.ps.ref.png differ diff --git a/test/reference/long-dashed-lines.pdf.ref.png b/test/reference/long-dashed-lines.pdf.ref.png new file mode 100644 index 000000000..8c203a4ca Binary files /dev/null and b/test/reference/long-dashed-lines.pdf.ref.png differ diff --git a/test/reference/mask-ctm.ps.rgb24.ref.png b/test/reference/mask-ctm.ps.rgb24.ref.png new file mode 100644 index 000000000..de3fa097f Binary files /dev/null and b/test/reference/mask-ctm.ps.rgb24.ref.png differ diff --git a/test/reference/mask-surface-ctm.ps.rgb24.ref.png b/test/reference/mask-surface-ctm.ps.rgb24.ref.png new file mode 100644 index 000000000..de3fa097f Binary files /dev/null and b/test/reference/mask-surface-ctm.ps.rgb24.ref.png differ diff --git a/test/reference/mask-transformed-image.pdf.ref.png b/test/reference/mask-transformed-image.pdf.ref.png index 33ec27997..49c6be071 100644 Binary files a/test/reference/mask-transformed-image.pdf.ref.png and b/test/reference/mask-transformed-image.pdf.ref.png differ diff --git a/test/reference/mask-transformed-image.ps.ref.png b/test/reference/mask-transformed-image.ps.ref.png new file mode 100644 index 000000000..fa8482aad Binary files /dev/null and b/test/reference/mask-transformed-image.ps.ref.png differ diff --git a/test/reference/mask-transformed-similar.pdf.ref.png b/test/reference/mask-transformed-similar.pdf.ref.png index e8d387903..339c71ee1 100644 Binary files a/test/reference/mask-transformed-similar.pdf.ref.png and b/test/reference/mask-transformed-similar.pdf.ref.png differ diff --git a/test/reference/mask-transformed-similar.ps.ref.png b/test/reference/mask-transformed-similar.ps.ref.png new file mode 100644 index 000000000..f0e7a24f6 Binary files /dev/null and b/test/reference/mask-transformed-similar.ps.ref.png differ diff --git a/test/reference/mask.argb32.ref.png b/test/reference/mask.argb32.ref.png index a4c683ccc..bc142c2ef 100644 Binary files a/test/reference/mask.argb32.ref.png and b/test/reference/mask.argb32.ref.png differ diff --git a/test/reference/mask.pdf.argb32.ref.png b/test/reference/mask.pdf.argb32.ref.png index 33769ee16..80fd75903 100644 Binary files a/test/reference/mask.pdf.argb32.ref.png and b/test/reference/mask.pdf.argb32.ref.png differ diff --git a/test/reference/mask.pdf.rgb24.ref.png b/test/reference/mask.pdf.rgb24.ref.png index dbd49a816..6b95459cd 100644 Binary files a/test/reference/mask.pdf.rgb24.ref.png and b/test/reference/mask.pdf.rgb24.ref.png differ diff --git a/test/reference/mask.rgb24.ref.png b/test/reference/mask.rgb24.ref.png index 1bd18334f..c7962a3c8 100644 Binary files a/test/reference/mask.rgb24.ref.png and b/test/reference/mask.rgb24.ref.png differ diff --git a/test/reference/mesh-pattern-conical.pdf.ref.png b/test/reference/mesh-pattern-conical.pdf.ref.png new file mode 100644 index 000000000..4c830b150 Binary files /dev/null and b/test/reference/mesh-pattern-conical.pdf.ref.png differ diff --git a/test/reference/mesh-pattern-conical.ps.ref.png b/test/reference/mesh-pattern-conical.ps.ref.png new file mode 100644 index 000000000..426428a9a Binary files /dev/null and b/test/reference/mesh-pattern-conical.ps.ref.png differ diff --git a/test/reference/mesh-pattern-control-points.pdf.ref.png b/test/reference/mesh-pattern-control-points.pdf.ref.png new file mode 100644 index 000000000..f614a2c08 Binary files /dev/null and b/test/reference/mesh-pattern-control-points.pdf.ref.png differ diff --git a/test/reference/mesh-pattern-control-points.ps.ref.png b/test/reference/mesh-pattern-control-points.ps.ref.png new file mode 100644 index 000000000..f37184a42 Binary files /dev/null and b/test/reference/mesh-pattern-control-points.ps.ref.png differ diff --git a/test/reference/mesh-pattern-fold.pdf.ref.png b/test/reference/mesh-pattern-fold.pdf.ref.png new file mode 100644 index 000000000..8cf5eb467 Binary files /dev/null and b/test/reference/mesh-pattern-fold.pdf.ref.png differ diff --git a/test/reference/mesh-pattern-fold.ps.ref.png b/test/reference/mesh-pattern-fold.ps.ref.png new file mode 100644 index 000000000..d44404df0 Binary files /dev/null and b/test/reference/mesh-pattern-fold.ps.ref.png differ diff --git a/test/reference/mesh-pattern-overlap.pdf.ref.png b/test/reference/mesh-pattern-overlap.pdf.ref.png new file mode 100644 index 000000000..0403b0470 Binary files /dev/null and b/test/reference/mesh-pattern-overlap.pdf.ref.png differ diff --git a/test/reference/mesh-pattern-overlap.ps.ref.png b/test/reference/mesh-pattern-overlap.ps.ref.png new file mode 100644 index 000000000..a90e5330f Binary files /dev/null and b/test/reference/mesh-pattern-overlap.ps.ref.png differ diff --git a/test/reference/mesh-pattern-transformed.pdf.ref.png b/test/reference/mesh-pattern-transformed.pdf.ref.png new file mode 100644 index 000000000..faf3f1375 Binary files /dev/null and b/test/reference/mesh-pattern-transformed.pdf.ref.png differ diff --git a/test/reference/mesh-pattern-transformed.ps.ref.png b/test/reference/mesh-pattern-transformed.ps.ref.png new file mode 100644 index 000000000..d8a89b79c Binary files /dev/null and b/test/reference/mesh-pattern-transformed.ps.ref.png differ diff --git a/test/reference/mesh-pattern.pdf.ref.png b/test/reference/mesh-pattern.pdf.ref.png new file mode 100644 index 000000000..10502258f Binary files /dev/null and b/test/reference/mesh-pattern.pdf.ref.png differ diff --git a/test/reference/mesh-pattern.ps.ref.png b/test/reference/mesh-pattern.ps.ref.png new file mode 100644 index 000000000..812432ce8 Binary files /dev/null and b/test/reference/mesh-pattern.ps.ref.png differ diff --git a/test/reference/new-sub-path.pdf.argb32.ref.png b/test/reference/new-sub-path.pdf.argb32.ref.png index 41fe1314e..44a70eb58 100644 Binary files a/test/reference/new-sub-path.pdf.argb32.ref.png and b/test/reference/new-sub-path.pdf.argb32.ref.png differ diff --git a/test/reference/new-sub-path.pdf.rgb24.ref.png b/test/reference/new-sub-path.pdf.rgb24.ref.png new file mode 100644 index 000000000..73f26b2a2 Binary files /dev/null and b/test/reference/new-sub-path.pdf.rgb24.ref.png differ diff --git a/test/reference/nil-surface.ps.rgb24.ref.png b/test/reference/nil-surface.ps.rgb24.ref.png new file mode 100644 index 000000000..7d5589c1d Binary files /dev/null and b/test/reference/nil-surface.ps.rgb24.ref.png differ diff --git a/test/reference/operator-alpha-alpha.pdf.ref.png b/test/reference/operator-alpha-alpha.pdf.ref.png new file mode 100644 index 000000000..fc173cb18 Binary files /dev/null and b/test/reference/operator-alpha-alpha.pdf.ref.png differ diff --git a/test/reference/operator-alpha-alpha.ps.ref.png b/test/reference/operator-alpha-alpha.ps.ref.png new file mode 100644 index 000000000..fd12e9cd6 Binary files /dev/null and b/test/reference/operator-alpha-alpha.ps.ref.png differ diff --git a/test/reference/operator-clear.pdf.argb32.ref.png b/test/reference/operator-clear.pdf.argb32.ref.png new file mode 100644 index 000000000..88255feeb Binary files /dev/null and b/test/reference/operator-clear.pdf.argb32.ref.png differ diff --git a/test/reference/operator-clear.rgb24.ref.png b/test/reference/operator-clear.rgb24.ref.png index 533b49aa0..1e89ccdfb 100644 Binary files a/test/reference/operator-clear.rgb24.ref.png and b/test/reference/operator-clear.rgb24.ref.png differ diff --git a/test/reference/operator-source.argb32.ref.png b/test/reference/operator-source.argb32.ref.png index 5fd5c43b8..49d2f270e 100644 Binary files a/test/reference/operator-source.argb32.ref.png and b/test/reference/operator-source.argb32.ref.png differ diff --git a/test/reference/operator-source.rgb24.ref.png b/test/reference/operator-source.rgb24.ref.png index c7846e5a7..38472f4ea 100644 Binary files a/test/reference/operator-source.rgb24.ref.png and b/test/reference/operator-source.rgb24.ref.png differ diff --git a/test/reference/over-above-source.ps3.argb32.ref.png b/test/reference/over-above-source.ps3.argb32.ref.png index 7c90d0867..2c3e9fafc 100644 Binary files a/test/reference/over-above-source.ps3.argb32.ref.png and b/test/reference/over-above-source.ps3.argb32.ref.png differ diff --git a/test/reference/over-around-source.ps3.argb32.ref.png b/test/reference/over-around-source.ps3.argb32.ref.png index 43917597d..1fe11d980 100644 Binary files a/test/reference/over-around-source.ps3.argb32.ref.png and b/test/reference/over-around-source.ps3.argb32.ref.png differ diff --git a/test/reference/over-between-source.ps3.argb32.ref.png b/test/reference/over-between-source.ps3.argb32.ref.png index dd95940ae..05dfb63b7 100644 Binary files a/test/reference/over-between-source.ps3.argb32.ref.png and b/test/reference/over-between-source.ps3.argb32.ref.png differ diff --git a/test/reference/overlapping-boxes.ps.argb32.ref.png b/test/reference/overlapping-boxes.ps.argb32.ref.png new file mode 100644 index 000000000..65cb55021 Binary files /dev/null and b/test/reference/overlapping-boxes.ps.argb32.ref.png differ diff --git a/test/reference/overlapping-boxes.ps.rgb24.ref.png b/test/reference/overlapping-boxes.ps.rgb24.ref.png new file mode 100644 index 000000000..0edeafa00 Binary files /dev/null and b/test/reference/overlapping-boxes.ps.rgb24.ref.png differ diff --git a/test/reference/overlapping-glyphs.pdf.argb32.ref.png b/test/reference/overlapping-glyphs.pdf.argb32.ref.png new file mode 100644 index 000000000..708d54acd Binary files /dev/null and b/test/reference/overlapping-glyphs.pdf.argb32.ref.png differ diff --git a/test/reference/overlapping-glyphs.pdf.rgb24.ref.png b/test/reference/overlapping-glyphs.pdf.rgb24.ref.png new file mode 100644 index 000000000..ff9e1c85c Binary files /dev/null and b/test/reference/overlapping-glyphs.pdf.rgb24.ref.png differ diff --git a/test/reference/overlapping-glyphs.ps.rgb24.ref.png b/test/reference/overlapping-glyphs.ps.rgb24.ref.png new file mode 100644 index 000000000..c4fd14661 Binary files /dev/null and b/test/reference/overlapping-glyphs.ps.rgb24.ref.png differ diff --git a/test/reference/paint-source-alpha.pdf.ref.png b/test/reference/paint-source-alpha.pdf.ref.png new file mode 100644 index 000000000..487b8b622 Binary files /dev/null and b/test/reference/paint-source-alpha.pdf.ref.png differ diff --git a/test/reference/paint-with-alpha-clip-mask.pdf.ref.png b/test/reference/paint-with-alpha-clip-mask.pdf.ref.png new file mode 100644 index 000000000..35630c783 Binary files /dev/null and b/test/reference/paint-with-alpha-clip-mask.pdf.ref.png differ diff --git a/test/reference/paint-with-alpha-clip.pdf.ref.png b/test/reference/paint-with-alpha-clip.pdf.ref.png new file mode 100644 index 000000000..c3dabc5ce Binary files /dev/null and b/test/reference/paint-with-alpha-clip.pdf.ref.png differ diff --git a/test/reference/paint-with-alpha-solid-clip.pdf.ref.png b/test/reference/paint-with-alpha-solid-clip.pdf.ref.png new file mode 100644 index 000000000..034ba6d9b Binary files /dev/null and b/test/reference/paint-with-alpha-solid-clip.pdf.ref.png differ diff --git a/test/reference/paint-with-alpha.pdf.ref.png b/test/reference/paint-with-alpha.pdf.ref.png new file mode 100644 index 000000000..487b8b622 Binary files /dev/null and b/test/reference/paint-with-alpha.pdf.ref.png differ diff --git a/test/reference/partial-clip-text-bottom.ps.ref.png b/test/reference/partial-clip-text-bottom.ps.ref.png new file mode 100644 index 000000000..cda2b3d82 Binary files /dev/null and b/test/reference/partial-clip-text-bottom.ps.ref.png differ diff --git a/test/reference/partial-clip-text-left.ps.ref.png b/test/reference/partial-clip-text-left.ps.ref.png new file mode 100644 index 000000000..a717f7b23 Binary files /dev/null and b/test/reference/partial-clip-text-left.ps.ref.png differ diff --git a/test/reference/partial-clip-text-right.ps.ref.png b/test/reference/partial-clip-text-right.ps.ref.png new file mode 100644 index 000000000..a88ef8276 Binary files /dev/null and b/test/reference/partial-clip-text-right.ps.ref.png differ diff --git a/test/reference/partial-clip-text-top.ps.ref.png b/test/reference/partial-clip-text-top.ps.ref.png index 049bba58f..3f0ca42f3 100644 Binary files a/test/reference/partial-clip-text-top.ps.ref.png and b/test/reference/partial-clip-text-top.ps.ref.png differ diff --git a/test/reference/pass-through.ps.rgb24.ref.png b/test/reference/pass-through.ps.rgb24.ref.png new file mode 100644 index 000000000..e98cb9ed8 Binary files /dev/null and b/test/reference/pass-through.ps.rgb24.ref.png differ diff --git a/test/reference/path-stroke-twice.pdf.ref.png b/test/reference/path-stroke-twice.pdf.ref.png new file mode 100644 index 000000000..10763bfd8 Binary files /dev/null and b/test/reference/path-stroke-twice.pdf.ref.png differ diff --git a/test/reference/pdf-surface-source.ps.argb32.ref.png b/test/reference/pdf-surface-source.ps.argb32.ref.png new file mode 100644 index 000000000..8b51d9bd7 Binary files /dev/null and b/test/reference/pdf-surface-source.ps.argb32.ref.png differ diff --git a/test/reference/pdf-surface-source.ps.rgb24.ref.png b/test/reference/pdf-surface-source.ps.rgb24.ref.png new file mode 100644 index 000000000..26cbb57c4 Binary files /dev/null and b/test/reference/pdf-surface-source.ps.rgb24.ref.png differ diff --git a/test/reference/pixman-downscale-best-24.pdf.ref.png b/test/reference/pixman-downscale-best-24.pdf.ref.png new file mode 100644 index 000000000..c6178045a Binary files /dev/null and b/test/reference/pixman-downscale-best-24.pdf.ref.png differ diff --git a/test/reference/pixman-downscale-best-24.ps.ref.png b/test/reference/pixman-downscale-best-24.ps.ref.png new file mode 100644 index 000000000..e861b900a Binary files /dev/null and b/test/reference/pixman-downscale-best-24.ps.ref.png differ diff --git a/test/reference/pixman-downscale-best-24.ref.png b/test/reference/pixman-downscale-best-24.ref.png index df0f9c0d8..184a64b74 100644 Binary files a/test/reference/pixman-downscale-best-24.ref.png and b/test/reference/pixman-downscale-best-24.ref.png differ diff --git a/test/reference/pixman-downscale-best-96.ps.ref.png b/test/reference/pixman-downscale-best-96.ps.ref.png new file mode 100644 index 000000000..ae867d689 Binary files /dev/null and b/test/reference/pixman-downscale-best-96.ps.ref.png differ diff --git a/test/reference/pixman-downscale-bilinear-24.ps.ref.png b/test/reference/pixman-downscale-bilinear-24.ps.ref.png new file mode 100644 index 000000000..e861b900a Binary files /dev/null and b/test/reference/pixman-downscale-bilinear-24.ps.ref.png differ diff --git a/test/reference/pixman-downscale-bilinear-96.ps.ref.png b/test/reference/pixman-downscale-bilinear-96.ps.ref.png new file mode 100644 index 000000000..ae867d689 Binary files /dev/null and b/test/reference/pixman-downscale-bilinear-96.ps.ref.png differ diff --git a/test/reference/pixman-downscale-good-24.ps.ref.png b/test/reference/pixman-downscale-good-24.ps.ref.png new file mode 100644 index 000000000..e861b900a Binary files /dev/null and b/test/reference/pixman-downscale-good-24.ps.ref.png differ diff --git a/test/reference/pixman-downscale-good-96.ps.ref.png b/test/reference/pixman-downscale-good-96.ps.ref.png new file mode 100644 index 000000000..ae867d689 Binary files /dev/null and b/test/reference/pixman-downscale-good-96.ps.ref.png differ diff --git a/test/reference/pixman-rotate.ps.argb32.ref.png b/test/reference/pixman-rotate.ps.argb32.ref.png index 0e916883d..e65385c29 100644 Binary files a/test/reference/pixman-rotate.ps.argb32.ref.png and b/test/reference/pixman-rotate.ps.argb32.ref.png differ diff --git a/test/reference/pixman-rotate.ps.rgb24.ref.png b/test/reference/pixman-rotate.ps.rgb24.ref.png new file mode 100644 index 000000000..59a0f436a Binary files /dev/null and b/test/reference/pixman-rotate.ps.rgb24.ref.png differ diff --git a/test/reference/ps-surface-source.ps.argb32.ref.png b/test/reference/ps-surface-source.ps.argb32.ref.png new file mode 100644 index 000000000..8b51d9bd7 Binary files /dev/null and b/test/reference/ps-surface-source.ps.argb32.ref.png differ diff --git a/test/reference/ps-surface-source.ps.rgb24.ref.png b/test/reference/ps-surface-source.ps.rgb24.ref.png new file mode 100644 index 000000000..26cbb57c4 Binary files /dev/null and b/test/reference/ps-surface-source.ps.rgb24.ref.png differ diff --git a/test/reference/pthread-show-text.pdf.ref.png b/test/reference/pthread-show-text.pdf.ref.png index bb72fc27b..9da49858b 100644 Binary files a/test/reference/pthread-show-text.pdf.ref.png and b/test/reference/pthread-show-text.pdf.ref.png differ diff --git a/test/reference/pthread-show-text.ps.ref.png b/test/reference/pthread-show-text.ps.ref.png index 807b73ff3..7137e49f4 100644 Binary files a/test/reference/pthread-show-text.ps.ref.png and b/test/reference/pthread-show-text.ps.ref.png differ diff --git a/test/reference/radial-gradient-extend.pdf.ref.png b/test/reference/radial-gradient-extend.pdf.ref.png new file mode 100644 index 000000000..fb7878535 Binary files /dev/null and b/test/reference/radial-gradient-extend.pdf.ref.png differ diff --git a/test/reference/random-clip.pdf.argb32.ref.png b/test/reference/random-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..dc5c288cd Binary files /dev/null and b/test/reference/random-clip.pdf.argb32.ref.png differ diff --git a/test/reference/random-clip.pdf.rgb24.ref.png b/test/reference/random-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..dc5c288cd Binary files /dev/null and b/test/reference/random-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/random-clip.ps.argb32.ref.png b/test/reference/random-clip.ps.argb32.ref.png new file mode 100644 index 000000000..531172fbc Binary files /dev/null and b/test/reference/random-clip.ps.argb32.ref.png differ diff --git a/test/reference/random-clip.ps.rgb24.ref.png b/test/reference/random-clip.ps.rgb24.ref.png new file mode 100644 index 000000000..dc5c288cd Binary files /dev/null and b/test/reference/random-clip.ps.rgb24.ref.png differ diff --git a/test/reference/random-clip.ref.png b/test/reference/random-clip.ref.png index de7a6052c..dc5c288cd 100644 Binary files a/test/reference/random-clip.ref.png and b/test/reference/random-clip.ref.png differ diff --git a/test/reference/random-intersections-curves-eo.pdf.argb32.ref.png b/test/reference/random-intersections-curves-eo.pdf.argb32.ref.png new file mode 100644 index 000000000..d13660fde Binary files /dev/null and b/test/reference/random-intersections-curves-eo.pdf.argb32.ref.png differ diff --git a/test/reference/random-intersections-curves-eo.pdf.ref.png b/test/reference/random-intersections-curves-eo.pdf.ref.png deleted file mode 100644 index befa3c859..000000000 Binary files a/test/reference/random-intersections-curves-eo.pdf.ref.png and /dev/null differ diff --git a/test/reference/random-intersections-curves-eo.pdf.rgb24.ref.png b/test/reference/random-intersections-curves-eo.pdf.rgb24.ref.png new file mode 100644 index 000000000..a11e7120d Binary files /dev/null and b/test/reference/random-intersections-curves-eo.pdf.rgb24.ref.png differ diff --git a/test/reference/random-intersections-curves-nz.pdf.argb32.ref.png b/test/reference/random-intersections-curves-nz.pdf.argb32.ref.png new file mode 100644 index 000000000..ab6225c8a Binary files /dev/null and b/test/reference/random-intersections-curves-nz.pdf.argb32.ref.png differ diff --git a/test/reference/random-intersections-curves-nz.pdf.rgb24.ref.png b/test/reference/random-intersections-curves-nz.pdf.rgb24.ref.png new file mode 100644 index 000000000..71c92e21a Binary files /dev/null and b/test/reference/random-intersections-curves-nz.pdf.rgb24.ref.png differ diff --git a/test/reference/random-intersections-eo.pdf.ref.png b/test/reference/random-intersections-eo.pdf.ref.png new file mode 100644 index 000000000..43e184202 Binary files /dev/null and b/test/reference/random-intersections-eo.pdf.ref.png differ diff --git a/test/reference/random-intersections-eo.ref.png b/test/reference/random-intersections-eo.ref.png index ccd3f80d1..2641c0069 100644 Binary files a/test/reference/random-intersections-eo.ref.png and b/test/reference/random-intersections-eo.ref.png differ diff --git a/test/reference/random-intersections-nonzero.pdf.ref.png b/test/reference/random-intersections-nonzero.pdf.ref.png new file mode 100644 index 000000000..fce490972 Binary files /dev/null and b/test/reference/random-intersections-nonzero.pdf.ref.png differ diff --git a/test/reference/random-intersections-nonzero.ref.png b/test/reference/random-intersections-nonzero.ref.png index 6f02ea0d8..cc748d452 100644 Binary files a/test/reference/random-intersections-nonzero.ref.png and b/test/reference/random-intersections-nonzero.ref.png differ diff --git a/test/reference/record-extend-none.ps.ref.png b/test/reference/record-extend-none.ps.ref.png new file mode 100644 index 000000000..4d05beb35 Binary files /dev/null and b/test/reference/record-extend-none.ps.ref.png differ diff --git a/test/reference/record-extend-pad.ps.ref.png b/test/reference/record-extend-pad.ps.ref.png new file mode 100644 index 000000000..0431d02a2 Binary files /dev/null and b/test/reference/record-extend-pad.ps.ref.png differ diff --git a/test/reference/record-extend-reflect.ps.ref.png b/test/reference/record-extend-reflect.ps.ref.png new file mode 100644 index 000000000..abdf27b9c Binary files /dev/null and b/test/reference/record-extend-reflect.ps.ref.png differ diff --git a/test/reference/record-extend-repeat.ps.ref.png b/test/reference/record-extend-repeat.ps.ref.png new file mode 100644 index 000000000..1f06744ef Binary files /dev/null and b/test/reference/record-extend-repeat.ps.ref.png differ diff --git a/test/reference/record-fill-alpha.pdf.ref.png b/test/reference/record-fill-alpha.pdf.ref.png new file mode 100644 index 000000000..8f72f68f4 Binary files /dev/null and b/test/reference/record-fill-alpha.pdf.ref.png differ diff --git a/test/reference/record-fill-alpha.ref.png b/test/reference/record-fill-alpha.ref.png index 25c1ac68f..8f72f68f4 100644 Binary files a/test/reference/record-fill-alpha.ref.png and b/test/reference/record-fill-alpha.ref.png differ diff --git a/test/reference/record-mesh.pdf.argb32.ref.png b/test/reference/record-mesh.pdf.argb32.ref.png new file mode 100644 index 000000000..015553650 Binary files /dev/null and b/test/reference/record-mesh.pdf.argb32.ref.png differ diff --git a/test/reference/record-mesh.pdf.rgb24.ref.png b/test/reference/record-mesh.pdf.rgb24.ref.png new file mode 100644 index 000000000..f69db6e06 Binary files /dev/null and b/test/reference/record-mesh.pdf.rgb24.ref.png differ diff --git a/test/reference/record-mesh.ps.ref.png b/test/reference/record-mesh.ps.ref.png new file mode 100644 index 000000000..015553650 Binary files /dev/null and b/test/reference/record-mesh.ps.ref.png differ diff --git a/test/reference/record-mesh.ref.png b/test/reference/record-mesh.ref.png index 6e781b1a4..015553650 100644 Binary files a/test/reference/record-mesh.ref.png and b/test/reference/record-mesh.ref.png differ diff --git a/test/reference/record-neg-extents-bounded.pdf.argb32.ref.png b/test/reference/record-neg-extents-bounded.pdf.argb32.ref.png new file mode 100644 index 000000000..159283651 Binary files /dev/null and b/test/reference/record-neg-extents-bounded.pdf.argb32.ref.png differ diff --git a/test/reference/record-neg-extents-bounded.pdf.rgb24.ref.png b/test/reference/record-neg-extents-bounded.pdf.rgb24.ref.png new file mode 100644 index 000000000..78feffbba Binary files /dev/null and b/test/reference/record-neg-extents-bounded.pdf.rgb24.ref.png differ diff --git a/test/reference/record-neg-extents-bounded.ref.png b/test/reference/record-neg-extents-bounded.ref.png new file mode 100644 index 000000000..6d29da199 Binary files /dev/null and b/test/reference/record-neg-extents-bounded.ref.png differ diff --git a/test/reference/record-neg-extents-unbounded.pdf.argb32.ref.png b/test/reference/record-neg-extents-unbounded.pdf.argb32.ref.png new file mode 100644 index 000000000..159283651 Binary files /dev/null and b/test/reference/record-neg-extents-unbounded.pdf.argb32.ref.png differ diff --git a/test/reference/record-neg-extents-unbounded.pdf.rgb24.ref.png b/test/reference/record-neg-extents-unbounded.pdf.rgb24.ref.png new file mode 100644 index 000000000..78feffbba Binary files /dev/null and b/test/reference/record-neg-extents-unbounded.pdf.rgb24.ref.png differ diff --git a/test/reference/record-neg-extents-unbounded.ref.png b/test/reference/record-neg-extents-unbounded.ref.png new file mode 100644 index 000000000..5218be502 Binary files /dev/null and b/test/reference/record-neg-extents-unbounded.ref.png differ diff --git a/test/reference/record-paint-alpha-clip-mask.pdf.argb32.ref.png b/test/reference/record-paint-alpha-clip-mask.pdf.argb32.ref.png new file mode 100644 index 000000000..0233e44f5 Binary files /dev/null and b/test/reference/record-paint-alpha-clip-mask.pdf.argb32.ref.png differ diff --git a/test/reference/record-paint-alpha-clip-mask.pdf.rgb24.ref.png b/test/reference/record-paint-alpha-clip-mask.pdf.rgb24.ref.png new file mode 100644 index 000000000..e57369f2d Binary files /dev/null and b/test/reference/record-paint-alpha-clip-mask.pdf.rgb24.ref.png differ diff --git a/test/reference/record-paint-alpha-clip-mask.ref.png b/test/reference/record-paint-alpha-clip-mask.ref.png index 4ee4c41ac..0233e44f5 100644 Binary files a/test/reference/record-paint-alpha-clip-mask.ref.png and b/test/reference/record-paint-alpha-clip-mask.ref.png differ diff --git a/test/reference/record-paint-alpha-clip.pdf.argb32.ref.png b/test/reference/record-paint-alpha-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..330399670 Binary files /dev/null and b/test/reference/record-paint-alpha-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record-paint-alpha-clip.pdf.rgb24.ref.png b/test/reference/record-paint-alpha-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..c3dabc5ce Binary files /dev/null and b/test/reference/record-paint-alpha-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record-paint-alpha-solid-clip.pdf.ref.png b/test/reference/record-paint-alpha-solid-clip.pdf.ref.png new file mode 100644 index 000000000..034ba6d9b Binary files /dev/null and b/test/reference/record-paint-alpha-solid-clip.pdf.ref.png differ diff --git a/test/reference/record-replay-extend-none.pdf.argb32.ref.png b/test/reference/record-replay-extend-none.pdf.argb32.ref.png new file mode 100644 index 000000000..6ca0f1009 Binary files /dev/null and b/test/reference/record-replay-extend-none.pdf.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-none.pdf.rgb24.ref.png b/test/reference/record-replay-extend-none.pdf.rgb24.ref.png new file mode 100644 index 000000000..3cbe86952 Binary files /dev/null and b/test/reference/record-replay-extend-none.pdf.rgb24.ref.png differ diff --git a/test/reference/record-replay-extend-none.ps.argb32.ref.png b/test/reference/record-replay-extend-none.ps.argb32.ref.png new file mode 100644 index 000000000..4b2f85fe9 Binary files /dev/null and b/test/reference/record-replay-extend-none.ps.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-none.ps.rgb24.ref.png b/test/reference/record-replay-extend-none.ps.rgb24.ref.png new file mode 100644 index 000000000..a89a45f35 Binary files /dev/null and b/test/reference/record-replay-extend-none.ps.rgb24.ref.png differ diff --git a/test/reference/record-replay-extend-none.ref.png b/test/reference/record-replay-extend-none.ref.png new file mode 100644 index 000000000..204c76502 Binary files /dev/null and b/test/reference/record-replay-extend-none.ref.png differ diff --git a/test/reference/record-replay-extend-pad.pdf.argb32.ref.png b/test/reference/record-replay-extend-pad.pdf.argb32.ref.png new file mode 100644 index 000000000..74d304385 Binary files /dev/null and b/test/reference/record-replay-extend-pad.pdf.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-pad.ps.rgb24.ref.png b/test/reference/record-replay-extend-pad.ps.rgb24.ref.png new file mode 100644 index 000000000..cd0248669 Binary files /dev/null and b/test/reference/record-replay-extend-pad.ps.rgb24.ref.png differ diff --git a/test/reference/record-replay-extend-pad.ref.png b/test/reference/record-replay-extend-pad.ref.png new file mode 100644 index 000000000..7acd8af49 Binary files /dev/null and b/test/reference/record-replay-extend-pad.ref.png differ diff --git a/test/reference/record-replay-extend-reflect.pdf.argb32.ref.png b/test/reference/record-replay-extend-reflect.pdf.argb32.ref.png new file mode 100644 index 000000000..d7845abd0 Binary files /dev/null and b/test/reference/record-replay-extend-reflect.pdf.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-reflect.pdf.rgb24.ref.png b/test/reference/record-replay-extend-reflect.pdf.rgb24.ref.png new file mode 100644 index 000000000..2c47015b5 Binary files /dev/null and b/test/reference/record-replay-extend-reflect.pdf.rgb24.ref.png differ diff --git a/test/reference/record-replay-extend-reflect.ps.argb32.ref.png b/test/reference/record-replay-extend-reflect.ps.argb32.ref.png new file mode 100644 index 000000000..32d26b0b1 Binary files /dev/null and b/test/reference/record-replay-extend-reflect.ps.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-reflect.ref.png b/test/reference/record-replay-extend-reflect.ref.png new file mode 100644 index 000000000..de53e5ca4 Binary files /dev/null and b/test/reference/record-replay-extend-reflect.ref.png differ diff --git a/test/reference/record-replay-extend-repeat.pdf.argb32.ref.png b/test/reference/record-replay-extend-repeat.pdf.argb32.ref.png new file mode 100644 index 000000000..4df031389 Binary files /dev/null and b/test/reference/record-replay-extend-repeat.pdf.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-repeat.pdf.rgb24.ref.png b/test/reference/record-replay-extend-repeat.pdf.rgb24.ref.png new file mode 100644 index 000000000..7efac20bd Binary files /dev/null and b/test/reference/record-replay-extend-repeat.pdf.rgb24.ref.png differ diff --git a/test/reference/record-replay-extend-repeat.ps.argb32.ref.png b/test/reference/record-replay-extend-repeat.ps.argb32.ref.png new file mode 100644 index 000000000..15d3d55bd Binary files /dev/null and b/test/reference/record-replay-extend-repeat.ps.argb32.ref.png differ diff --git a/test/reference/record-replay-extend-repeat.ps.rgb24.ref.png b/test/reference/record-replay-extend-repeat.ps.rgb24.ref.png new file mode 100644 index 000000000..fa25d484d Binary files /dev/null and b/test/reference/record-replay-extend-repeat.ps.rgb24.ref.png differ diff --git a/test/reference/record-replay-extend-repeat.ref.png b/test/reference/record-replay-extend-repeat.ref.png new file mode 100644 index 000000000..5a95d86f5 Binary files /dev/null and b/test/reference/record-replay-extend-repeat.ref.png differ diff --git a/test/reference/record-select-font-face.pdf.argb32.ref.png b/test/reference/record-select-font-face.pdf.argb32.ref.png new file mode 100644 index 000000000..63c7cca8a Binary files /dev/null and b/test/reference/record-select-font-face.pdf.argb32.ref.png differ diff --git a/test/reference/record-select-font-face.pdf.rgb24.ref.png b/test/reference/record-select-font-face.pdf.rgb24.ref.png new file mode 100644 index 000000000..bb2f69f90 Binary files /dev/null and b/test/reference/record-select-font-face.pdf.rgb24.ref.png differ diff --git a/test/reference/record-select-font-face.ps.ref.png b/test/reference/record-select-font-face.ps.ref.png new file mode 100644 index 000000000..ab63c85ed Binary files /dev/null and b/test/reference/record-select-font-face.ps.ref.png differ diff --git a/test/reference/record-select-font-face.ref.png b/test/reference/record-select-font-face.ref.png index 1334a9a01..63c7cca8a 100644 Binary files a/test/reference/record-select-font-face.ref.png and b/test/reference/record-select-font-face.ref.png differ diff --git a/test/reference/record-self-intersecting.pdf.rgb24.ref.png b/test/reference/record-self-intersecting.pdf.rgb24.ref.png new file mode 100644 index 000000000..83eeaad58 Binary files /dev/null and b/test/reference/record-self-intersecting.pdf.rgb24.ref.png differ diff --git a/test/reference/record-self-intersecting.ps.ref.png b/test/reference/record-self-intersecting.ps.ref.png new file mode 100644 index 000000000..84fde0171 Binary files /dev/null and b/test/reference/record-self-intersecting.ps.ref.png differ diff --git a/test/reference/record-text-transform.pdf.argb32.ref.png b/test/reference/record-text-transform.pdf.argb32.ref.png new file mode 100644 index 000000000..8e74785f5 Binary files /dev/null and b/test/reference/record-text-transform.pdf.argb32.ref.png differ diff --git a/test/reference/record-text-transform.pdf.rgb24.ref.png b/test/reference/record-text-transform.pdf.rgb24.ref.png new file mode 100644 index 000000000..ffde12fdc Binary files /dev/null and b/test/reference/record-text-transform.pdf.rgb24.ref.png differ diff --git a/test/reference/record-text-transform.ps.argb32.ref.png b/test/reference/record-text-transform.ps.argb32.ref.png new file mode 100644 index 000000000..1aaaea58a Binary files /dev/null and b/test/reference/record-text-transform.ps.argb32.ref.png differ diff --git a/test/reference/record-text-transform.ps.rgb24.ref.png b/test/reference/record-text-transform.ps.rgb24.ref.png new file mode 100644 index 000000000..1aaaea58a Binary files /dev/null and b/test/reference/record-text-transform.ps.rgb24.ref.png differ diff --git a/test/reference/record-text-transform.ref.png b/test/reference/record-text-transform.ref.png index 4603bc528..8e74785f5 100644 Binary files a/test/reference/record-text-transform.ref.png and b/test/reference/record-text-transform.ref.png differ diff --git a/test/reference/record1414x-fill-alpha.pdf.argb32.ref.png b/test/reference/record1414x-fill-alpha.pdf.argb32.ref.png new file mode 100644 index 000000000..8d892d00b Binary files /dev/null and b/test/reference/record1414x-fill-alpha.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-fill-alpha.pdf.rgb24.ref.png b/test/reference/record1414x-fill-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..c38e2435e Binary files /dev/null and b/test/reference/record1414x-fill-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-fill-alpha.ps.ref.png b/test/reference/record1414x-fill-alpha.ps.ref.png new file mode 100644 index 000000000..444f353b0 Binary files /dev/null and b/test/reference/record1414x-fill-alpha.ps.ref.png differ diff --git a/test/reference/record1414x-fill-alpha.ref.png b/test/reference/record1414x-fill-alpha.ref.png index 8e9f3226a..fb0b320b5 100644 Binary files a/test/reference/record1414x-fill-alpha.ref.png and b/test/reference/record1414x-fill-alpha.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip-mask.pdf.argb32.ref.png b/test/reference/record1414x-paint-alpha-clip-mask.pdf.argb32.ref.png new file mode 100644 index 000000000..5bb4bbd1c Binary files /dev/null and b/test/reference/record1414x-paint-alpha-clip-mask.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip-mask.pdf.rgb24.ref.png b/test/reference/record1414x-paint-alpha-clip-mask.pdf.rgb24.ref.png new file mode 100644 index 000000000..b50b99280 Binary files /dev/null and b/test/reference/record1414x-paint-alpha-clip-mask.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip-mask.ps.ref.png b/test/reference/record1414x-paint-alpha-clip-mask.ps.ref.png new file mode 100644 index 000000000..835bf0451 Binary files /dev/null and b/test/reference/record1414x-paint-alpha-clip-mask.ps.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip-mask.ref.png b/test/reference/record1414x-paint-alpha-clip-mask.ref.png index e381b73aa..78442bdbc 100644 Binary files a/test/reference/record1414x-paint-alpha-clip-mask.ref.png and b/test/reference/record1414x-paint-alpha-clip-mask.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip.pdf.argb32.ref.png b/test/reference/record1414x-paint-alpha-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..31a2143b9 Binary files /dev/null and b/test/reference/record1414x-paint-alpha-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip.pdf.rgb24.ref.png b/test/reference/record1414x-paint-alpha-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..1e6e1f674 Binary files /dev/null and b/test/reference/record1414x-paint-alpha-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-clip.ps.ref.png b/test/reference/record1414x-paint-alpha-clip.ps.ref.png new file mode 100644 index 000000000..13b1163e0 Binary files /dev/null and b/test/reference/record1414x-paint-alpha-clip.ps.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-solid-clip.pdf.argb32.ref.png b/test/reference/record1414x-paint-alpha-solid-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..21b02b87d Binary files /dev/null and b/test/reference/record1414x-paint-alpha-solid-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-solid-clip.pdf.rgb24.ref.png b/test/reference/record1414x-paint-alpha-solid-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..d3f7e57de Binary files /dev/null and b/test/reference/record1414x-paint-alpha-solid-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-paint-alpha-solid-clip.ps.ref.png b/test/reference/record1414x-paint-alpha-solid-clip.ps.ref.png new file mode 100644 index 000000000..811c8aa30 Binary files /dev/null and b/test/reference/record1414x-paint-alpha-solid-clip.ps.ref.png differ diff --git a/test/reference/record1414x-paint-alpha.pdf.argb32.ref.png b/test/reference/record1414x-paint-alpha.pdf.argb32.ref.png new file mode 100644 index 000000000..5d5d79798 Binary files /dev/null and b/test/reference/record1414x-paint-alpha.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-paint-alpha.pdf.rgb24.ref.png b/test/reference/record1414x-paint-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..6a80a830b Binary files /dev/null and b/test/reference/record1414x-paint-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-paint-alpha.ps.ref.png b/test/reference/record1414x-paint-alpha.ps.ref.png new file mode 100644 index 000000000..7460886e1 Binary files /dev/null and b/test/reference/record1414x-paint-alpha.ps.ref.png differ diff --git a/test/reference/record1414x-select-font-face.pdf.argb32.ref.png b/test/reference/record1414x-select-font-face.pdf.argb32.ref.png new file mode 100644 index 000000000..ac30b23a3 Binary files /dev/null and b/test/reference/record1414x-select-font-face.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-select-font-face.pdf.rgb24.ref.png b/test/reference/record1414x-select-font-face.pdf.rgb24.ref.png new file mode 100644 index 000000000..cdc3ce850 Binary files /dev/null and b/test/reference/record1414x-select-font-face.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-select-font-face.ps.argb32.ref.png b/test/reference/record1414x-select-font-face.ps.argb32.ref.png new file mode 100644 index 000000000..37da5e1ed Binary files /dev/null and b/test/reference/record1414x-select-font-face.ps.argb32.ref.png differ diff --git a/test/reference/record1414x-select-font-face.ps.rgb24.ref.png b/test/reference/record1414x-select-font-face.ps.rgb24.ref.png new file mode 100644 index 000000000..97b19be2a Binary files /dev/null and b/test/reference/record1414x-select-font-face.ps.rgb24.ref.png differ diff --git a/test/reference/record1414x-select-font-face.ref.png b/test/reference/record1414x-select-font-face.ref.png index 6c52067b1..ac30b23a3 100644 Binary files a/test/reference/record1414x-select-font-face.ref.png and b/test/reference/record1414x-select-font-face.ref.png differ diff --git a/test/reference/record1414x-self-intersecting.pdf.argb32.ref.png b/test/reference/record1414x-self-intersecting.pdf.argb32.ref.png new file mode 100644 index 000000000..62f91c9e7 Binary files /dev/null and b/test/reference/record1414x-self-intersecting.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-self-intersecting.pdf.rgb24.ref.png b/test/reference/record1414x-self-intersecting.pdf.rgb24.ref.png new file mode 100644 index 000000000..5fa2debd5 Binary files /dev/null and b/test/reference/record1414x-self-intersecting.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-self-intersecting.ps.argb32.ref.png b/test/reference/record1414x-self-intersecting.ps.argb32.ref.png new file mode 100644 index 000000000..c6f483e59 Binary files /dev/null and b/test/reference/record1414x-self-intersecting.ps.argb32.ref.png differ diff --git a/test/reference/record1414x-self-intersecting.ps.rgb24.ref.png b/test/reference/record1414x-self-intersecting.ps.rgb24.ref.png new file mode 100644 index 000000000..d7e2af564 Binary files /dev/null and b/test/reference/record1414x-self-intersecting.ps.rgb24.ref.png differ diff --git a/test/reference/record1414x-text-transform.pdf.argb32.ref.png b/test/reference/record1414x-text-transform.pdf.argb32.ref.png new file mode 100644 index 000000000..60de60a80 Binary files /dev/null and b/test/reference/record1414x-text-transform.pdf.argb32.ref.png differ diff --git a/test/reference/record1414x-text-transform.pdf.rgb24.ref.png b/test/reference/record1414x-text-transform.pdf.rgb24.ref.png new file mode 100644 index 000000000..9b90c9799 Binary files /dev/null and b/test/reference/record1414x-text-transform.pdf.rgb24.ref.png differ diff --git a/test/reference/record1414x-text-transform.ps.ref.png b/test/reference/record1414x-text-transform.ps.ref.png new file mode 100644 index 000000000..bbf4f5208 Binary files /dev/null and b/test/reference/record1414x-text-transform.ps.ref.png differ diff --git a/test/reference/record1414x-text-transform.ref.png b/test/reference/record1414x-text-transform.ref.png index 3bb8b1212..ef3c96726 100644 Binary files a/test/reference/record1414x-text-transform.ref.png and b/test/reference/record1414x-text-transform.ref.png differ diff --git a/test/reference/record2x-fill-alpha.pdf.argb32.ref.png b/test/reference/record2x-fill-alpha.pdf.argb32.ref.png new file mode 100644 index 000000000..c4d796533 Binary files /dev/null and b/test/reference/record2x-fill-alpha.pdf.argb32.ref.png differ diff --git a/test/reference/record2x-fill-alpha.pdf.rgb24.ref.png b/test/reference/record2x-fill-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..9bbe8b85c Binary files /dev/null and b/test/reference/record2x-fill-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-fill-alpha.ps.ref.png b/test/reference/record2x-fill-alpha.ps.ref.png new file mode 100644 index 000000000..c4d796533 Binary files /dev/null and b/test/reference/record2x-fill-alpha.ps.ref.png differ diff --git a/test/reference/record2x-fill-alpha.ref.png b/test/reference/record2x-fill-alpha.ref.png index 91787bd9b..9bbe8b85c 100644 Binary files a/test/reference/record2x-fill-alpha.ref.png and b/test/reference/record2x-fill-alpha.ref.png differ diff --git a/test/reference/record2x-paint-alpha-clip-mask.pdf.argb32.ref.png b/test/reference/record2x-paint-alpha-clip-mask.pdf.argb32.ref.png new file mode 100644 index 000000000..dd1ca05f9 Binary files /dev/null and b/test/reference/record2x-paint-alpha-clip-mask.pdf.argb32.ref.png differ diff --git a/test/reference/record2x-paint-alpha-clip-mask.pdf.rgb24.ref.png b/test/reference/record2x-paint-alpha-clip-mask.pdf.rgb24.ref.png new file mode 100644 index 000000000..f7a20d360 Binary files /dev/null and b/test/reference/record2x-paint-alpha-clip-mask.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-paint-alpha-clip-mask.ps.ref.png b/test/reference/record2x-paint-alpha-clip-mask.ps.ref.png new file mode 100644 index 000000000..dd1ca05f9 Binary files /dev/null and b/test/reference/record2x-paint-alpha-clip-mask.ps.ref.png differ diff --git a/test/reference/record2x-paint-alpha-clip-mask.ref.png b/test/reference/record2x-paint-alpha-clip-mask.ref.png index dd1ca05f9..938440b21 100644 Binary files a/test/reference/record2x-paint-alpha-clip-mask.ref.png and b/test/reference/record2x-paint-alpha-clip-mask.ref.png differ diff --git a/test/reference/record2x-paint-alpha-clip.pdf.argb32.ref.png b/test/reference/record2x-paint-alpha-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..56dcc79ee Binary files /dev/null and b/test/reference/record2x-paint-alpha-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record2x-paint-alpha-clip.pdf.rgb24.ref.png b/test/reference/record2x-paint-alpha-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..2dc5ee4ba Binary files /dev/null and b/test/reference/record2x-paint-alpha-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-paint-alpha-solid-clip.pdf.argb32.ref.png b/test/reference/record2x-paint-alpha-solid-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..449bc5a97 Binary files /dev/null and b/test/reference/record2x-paint-alpha-solid-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record2x-paint-alpha-solid-clip.pdf.rgb24.ref.png b/test/reference/record2x-paint-alpha-solid-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..b22ac6d38 Binary files /dev/null and b/test/reference/record2x-paint-alpha-solid-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-paint-alpha.pdf.argb32.ref.png b/test/reference/record2x-paint-alpha.pdf.argb32.ref.png new file mode 100644 index 000000000..f53c7f9cd Binary files /dev/null and b/test/reference/record2x-paint-alpha.pdf.argb32.ref.png differ diff --git a/test/reference/record2x-paint-alpha.pdf.rgb24.ref.png b/test/reference/record2x-paint-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..41b8cf1b8 Binary files /dev/null and b/test/reference/record2x-paint-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-select-font-face.pdf.rgb24.ref.png b/test/reference/record2x-select-font-face.pdf.rgb24.ref.png new file mode 100644 index 000000000..ed64d7c6a Binary files /dev/null and b/test/reference/record2x-select-font-face.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-select-font-face.ps.ref.png b/test/reference/record2x-select-font-face.ps.ref.png new file mode 100644 index 000000000..71eee7c42 Binary files /dev/null and b/test/reference/record2x-select-font-face.ps.ref.png differ diff --git a/test/reference/record2x-select-font-face.ref.png b/test/reference/record2x-select-font-face.ref.png index 7a99795e4..32c1367fb 100644 Binary files a/test/reference/record2x-select-font-face.ref.png and b/test/reference/record2x-select-font-face.ref.png differ diff --git a/test/reference/record2x-text-transform.pdf.argb32.ref.png b/test/reference/record2x-text-transform.pdf.argb32.ref.png new file mode 100644 index 000000000..9811c1b5f Binary files /dev/null and b/test/reference/record2x-text-transform.pdf.argb32.ref.png differ diff --git a/test/reference/record2x-text-transform.pdf.rgb24.ref.png b/test/reference/record2x-text-transform.pdf.rgb24.ref.png new file mode 100644 index 000000000..35b5ce912 Binary files /dev/null and b/test/reference/record2x-text-transform.pdf.rgb24.ref.png differ diff --git a/test/reference/record2x-text-transform.ps.ref.png b/test/reference/record2x-text-transform.ps.ref.png new file mode 100644 index 000000000..f56280268 Binary files /dev/null and b/test/reference/record2x-text-transform.ps.ref.png differ diff --git a/test/reference/record2x-text-transform.ref.png b/test/reference/record2x-text-transform.ref.png index 6c21785ef..ff521ab8a 100644 Binary files a/test/reference/record2x-text-transform.ref.png and b/test/reference/record2x-text-transform.ref.png differ diff --git a/test/reference/record90-fill-alpha.pdf.ref.png b/test/reference/record90-fill-alpha.pdf.ref.png new file mode 100644 index 000000000..167d7be65 Binary files /dev/null and b/test/reference/record90-fill-alpha.pdf.ref.png differ diff --git a/test/reference/record90-fill-alpha.pdf.rgb24.ref.png b/test/reference/record90-fill-alpha.pdf.rgb24.ref.png new file mode 100644 index 000000000..ee01f5f8a Binary files /dev/null and b/test/reference/record90-fill-alpha.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-fill-alpha.ref.png b/test/reference/record90-fill-alpha.ref.png index bf3b260a2..167d7be65 100644 Binary files a/test/reference/record90-fill-alpha.ref.png and b/test/reference/record90-fill-alpha.ref.png differ diff --git a/test/reference/record90-paint-alpha-clip-mask.pdf.argb32.ref.png b/test/reference/record90-paint-alpha-clip-mask.pdf.argb32.ref.png new file mode 100644 index 000000000..3ddf95c8b Binary files /dev/null and b/test/reference/record90-paint-alpha-clip-mask.pdf.argb32.ref.png differ diff --git a/test/reference/record90-paint-alpha-clip-mask.pdf.rgb24.ref.png b/test/reference/record90-paint-alpha-clip-mask.pdf.rgb24.ref.png new file mode 100644 index 000000000..e6bc20868 Binary files /dev/null and b/test/reference/record90-paint-alpha-clip-mask.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-paint-alpha-clip.pdf.argb32.ref.png b/test/reference/record90-paint-alpha-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..b7703d47d Binary files /dev/null and b/test/reference/record90-paint-alpha-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record90-paint-alpha-clip.pdf.rgb24.ref.png b/test/reference/record90-paint-alpha-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..aac3c29f2 Binary files /dev/null and b/test/reference/record90-paint-alpha-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-paint-alpha-clip.ref.png b/test/reference/record90-paint-alpha-clip.ref.png index 3fae802ac..b7703d47d 100644 Binary files a/test/reference/record90-paint-alpha-clip.ref.png and b/test/reference/record90-paint-alpha-clip.ref.png differ diff --git a/test/reference/record90-paint-alpha-solid-clip.pdf.argb32.ref.png b/test/reference/record90-paint-alpha-solid-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..17bff57c5 Binary files /dev/null and b/test/reference/record90-paint-alpha-solid-clip.pdf.argb32.ref.png differ diff --git a/test/reference/record90-paint-alpha-solid-clip.pdf.rgb24.ref.png b/test/reference/record90-paint-alpha-solid-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..260be2199 Binary files /dev/null and b/test/reference/record90-paint-alpha-solid-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-paint-alpha.ps.ref.png b/test/reference/record90-paint-alpha.ps.ref.png new file mode 100644 index 000000000..7b7998ebc Binary files /dev/null and b/test/reference/record90-paint-alpha.ps.ref.png differ diff --git a/test/reference/record90-select-font-face.pdf.rgb24.ref.png b/test/reference/record90-select-font-face.pdf.rgb24.ref.png new file mode 100644 index 000000000..463875d54 Binary files /dev/null and b/test/reference/record90-select-font-face.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-select-font-face.ps.ref.png b/test/reference/record90-select-font-face.ps.ref.png new file mode 100644 index 000000000..58e6625bd Binary files /dev/null and b/test/reference/record90-select-font-face.ps.ref.png differ diff --git a/test/reference/record90-select-font-face.ref.png b/test/reference/record90-select-font-face.ref.png index 189a3154d..13ed998f2 100644 Binary files a/test/reference/record90-select-font-face.ref.png and b/test/reference/record90-select-font-face.ref.png differ diff --git a/test/reference/record90-self-intersecting.pdf.rgb24.ref.png b/test/reference/record90-self-intersecting.pdf.rgb24.ref.png new file mode 100644 index 000000000..3a960c7c6 Binary files /dev/null and b/test/reference/record90-self-intersecting.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-self-intersecting.ps.ref.png b/test/reference/record90-self-intersecting.ps.ref.png new file mode 100644 index 000000000..3bcc7fbed Binary files /dev/null and b/test/reference/record90-self-intersecting.ps.ref.png differ diff --git a/test/reference/record90-self-intersecting.ref.png b/test/reference/record90-self-intersecting.ref.png index 15ce4c005..f4f34ce3b 100644 Binary files a/test/reference/record90-self-intersecting.ref.png and b/test/reference/record90-self-intersecting.ref.png differ diff --git a/test/reference/record90-text-transform.pdf.rgb24.ref.png b/test/reference/record90-text-transform.pdf.rgb24.ref.png new file mode 100644 index 000000000..96ff2c466 Binary files /dev/null and b/test/reference/record90-text-transform.pdf.rgb24.ref.png differ diff --git a/test/reference/record90-text-transform.ps.ref.png b/test/reference/record90-text-transform.ps.ref.png new file mode 100644 index 000000000..2e0bbfbf8 Binary files /dev/null and b/test/reference/record90-text-transform.ps.ref.png differ diff --git a/test/reference/record90-text-transform.ref.png b/test/reference/record90-text-transform.ref.png index 22f6c1f0c..e8fa7225f 100644 Binary files a/test/reference/record90-text-transform.ref.png and b/test/reference/record90-text-transform.ref.png differ diff --git a/test/reference/recordflip-fill-alpha.ref.png b/test/reference/recordflip-fill-alpha.ref.png index 289a91505..c80c351ba 100644 Binary files a/test/reference/recordflip-fill-alpha.ref.png and b/test/reference/recordflip-fill-alpha.ref.png differ diff --git a/test/reference/recordflip-paint-alpha-clip-mask.pdf.rgb24.ref.png b/test/reference/recordflip-paint-alpha-clip-mask.pdf.rgb24.ref.png new file mode 100644 index 000000000..c9eaf2dd6 Binary files /dev/null and b/test/reference/recordflip-paint-alpha-clip-mask.pdf.rgb24.ref.png differ diff --git a/test/reference/recordflip-paint-alpha-clip-mask.ref.png b/test/reference/recordflip-paint-alpha-clip-mask.ref.png index 842fa35ae..b824723e3 100644 Binary files a/test/reference/recordflip-paint-alpha-clip-mask.ref.png and b/test/reference/recordflip-paint-alpha-clip-mask.ref.png differ diff --git a/test/reference/recordflip-paint-alpha-clip.pdf.rgb24.ref.png b/test/reference/recordflip-paint-alpha-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..22f7dfe80 Binary files /dev/null and b/test/reference/recordflip-paint-alpha-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/recordflip-select-font-face.pdf.rgb24.ref.png b/test/reference/recordflip-select-font-face.pdf.rgb24.ref.png new file mode 100644 index 000000000..36606176a Binary files /dev/null and b/test/reference/recordflip-select-font-face.pdf.rgb24.ref.png differ diff --git a/test/reference/recordflip-select-font-face.ps.argb32.ref.png b/test/reference/recordflip-select-font-face.ps.argb32.ref.png new file mode 100644 index 000000000..086e5e941 Binary files /dev/null and b/test/reference/recordflip-select-font-face.ps.argb32.ref.png differ diff --git a/test/reference/recordflip-select-font-face.ps.rgb24.ref.png b/test/reference/recordflip-select-font-face.ps.rgb24.ref.png new file mode 100644 index 000000000..086e5e941 Binary files /dev/null and b/test/reference/recordflip-select-font-face.ps.rgb24.ref.png differ diff --git a/test/reference/recordflip-select-font-face.ref.png b/test/reference/recordflip-select-font-face.ref.png index eb710858c..bedb1ca20 100644 Binary files a/test/reference/recordflip-select-font-face.ref.png and b/test/reference/recordflip-select-font-face.ref.png differ diff --git a/test/reference/recordflip-self-intersecting.pdf.rgb24.ref.png b/test/reference/recordflip-self-intersecting.pdf.rgb24.ref.png new file mode 100644 index 000000000..05eb9f6ad Binary files /dev/null and b/test/reference/recordflip-self-intersecting.pdf.rgb24.ref.png differ diff --git a/test/reference/recordflip-self-intersecting.ps.ref.png b/test/reference/recordflip-self-intersecting.ps.ref.png new file mode 100644 index 000000000..84fde0171 Binary files /dev/null and b/test/reference/recordflip-self-intersecting.ps.ref.png differ diff --git a/test/reference/recordflip-text-transform.pdf.rgb24.ref.png b/test/reference/recordflip-text-transform.pdf.rgb24.ref.png new file mode 100644 index 000000000..a5826a1ad Binary files /dev/null and b/test/reference/recordflip-text-transform.pdf.rgb24.ref.png differ diff --git a/test/reference/recordflip-text-transform.ps.argb32.ref.png b/test/reference/recordflip-text-transform.ps.argb32.ref.png new file mode 100644 index 000000000..beed4bf18 Binary files /dev/null and b/test/reference/recordflip-text-transform.ps.argb32.ref.png differ diff --git a/test/reference/recordflip-text-transform.ps.rgb24.ref.png b/test/reference/recordflip-text-transform.ps.rgb24.ref.png new file mode 100644 index 000000000..beed4bf18 Binary files /dev/null and b/test/reference/recordflip-text-transform.ps.rgb24.ref.png differ diff --git a/test/reference/recordflip-text-transform.ref.png b/test/reference/recordflip-text-transform.ref.png index 31784d735..ccf5e3367 100644 Binary files a/test/reference/recordflip-text-transform.ref.png and b/test/reference/recordflip-text-transform.ref.png differ diff --git a/test/reference/recordflip-whole-fill-alpha.ref.png b/test/reference/recordflip-whole-fill-alpha.ref.png index 289a91505..69035ef5f 100644 Binary files a/test/reference/recordflip-whole-fill-alpha.ref.png and b/test/reference/recordflip-whole-fill-alpha.ref.png differ diff --git a/test/reference/recordflip-whole-paint-alpha-clip-mask.ref.png b/test/reference/recordflip-whole-paint-alpha-clip-mask.ref.png index 842fa35ae..6f14ad3b4 100644 Binary files a/test/reference/recordflip-whole-paint-alpha-clip-mask.ref.png and b/test/reference/recordflip-whole-paint-alpha-clip-mask.ref.png differ diff --git a/test/reference/recordflip-whole-select-font-face.ref.png b/test/reference/recordflip-whole-select-font-face.ref.png index eb710858c..bedb1ca20 100644 Binary files a/test/reference/recordflip-whole-select-font-face.ref.png and b/test/reference/recordflip-whole-select-font-face.ref.png differ diff --git a/test/reference/recordflip-whole-text-transform.ref.png b/test/reference/recordflip-whole-text-transform.ref.png index 31784d735..ccf5e3367 100644 Binary files a/test/reference/recordflip-whole-text-transform.ref.png and b/test/reference/recordflip-whole-text-transform.ref.png differ diff --git a/test/reference/recording-surface-extend-none.pdf.argb32.ref.png b/test/reference/recording-surface-extend-none.pdf.argb32.ref.png new file mode 100644 index 000000000..812f9ee50 Binary files /dev/null and b/test/reference/recording-surface-extend-none.pdf.argb32.ref.png differ diff --git a/test/reference/recording-surface-extend-none.pdf.rgb24.ref.png b/test/reference/recording-surface-extend-none.pdf.rgb24.ref.png new file mode 100644 index 000000000..d7bf21b26 Binary files /dev/null and b/test/reference/recording-surface-extend-none.pdf.rgb24.ref.png differ diff --git a/test/reference/recording-surface-extend-none.ps.argb32.ref.png b/test/reference/recording-surface-extend-none.ps.argb32.ref.png new file mode 100644 index 000000000..d2db40116 Binary files /dev/null and b/test/reference/recording-surface-extend-none.ps.argb32.ref.png differ diff --git a/test/reference/recording-surface-extend-none.ps.rgb24.ref.png b/test/reference/recording-surface-extend-none.ps.rgb24.ref.png new file mode 100644 index 000000000..1a5b908a8 Binary files /dev/null and b/test/reference/recording-surface-extend-none.ps.rgb24.ref.png differ diff --git a/test/reference/recording-surface-extend-reflect.pdf.argb32.ref.png b/test/reference/recording-surface-extend-reflect.pdf.argb32.ref.png new file mode 100644 index 000000000..a980fd53b Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.pdf.argb32.ref.png differ diff --git a/test/reference/recording-surface-extend-reflect.pdf.rgb24.ref.png b/test/reference/recording-surface-extend-reflect.pdf.rgb24.ref.png new file mode 100644 index 000000000..45ca3c61d Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.pdf.rgb24.ref.png differ diff --git a/test/reference/recording-surface-extend-reflect.ps.argb32.ref.png b/test/reference/recording-surface-extend-reflect.ps.argb32.ref.png new file mode 100644 index 000000000..d9f3573d1 Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.ps.argb32.ref.png differ diff --git a/test/reference/recording-surface-extend-reflect.ps.rgb24.ref.png b/test/reference/recording-surface-extend-reflect.ps.rgb24.ref.png new file mode 100644 index 000000000..9e43713b2 Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.ps.rgb24.ref.png differ diff --git a/test/reference/recording-surface-extend-repeat.pdf.argb32.ref.png b/test/reference/recording-surface-extend-repeat.pdf.argb32.ref.png new file mode 100644 index 000000000..8ff6587aa Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.pdf.argb32.ref.png differ diff --git a/test/reference/recording-surface-extend-repeat.pdf.rgb24.ref.png b/test/reference/recording-surface-extend-repeat.pdf.rgb24.ref.png new file mode 100644 index 000000000..e1dccd930 Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.pdf.rgb24.ref.png differ diff --git a/test/reference/recording-surface-extend-repeat.ps.argb32.ref.png b/test/reference/recording-surface-extend-repeat.ps.argb32.ref.png new file mode 100644 index 000000000..b12aa1763 Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.ps.argb32.ref.png differ diff --git a/test/reference/recording-surface-extend-repeat.ps.rgb24.ref.png b/test/reference/recording-surface-extend-repeat.ps.rgb24.ref.png new file mode 100644 index 000000000..870027b11 Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.ps.rgb24.ref.png differ diff --git a/test/reference/recording-surface-over.pdf.argb32.ref.png b/test/reference/recording-surface-over.pdf.argb32.ref.png index a06386b12..812f9ee50 100644 Binary files a/test/reference/recording-surface-over.pdf.argb32.ref.png and b/test/reference/recording-surface-over.pdf.argb32.ref.png differ diff --git a/test/reference/recording-surface-over.pdf.rgb24.ref.png b/test/reference/recording-surface-over.pdf.rgb24.ref.png index bf69f9ea3..d7bf21b26 100644 Binary files a/test/reference/recording-surface-over.pdf.rgb24.ref.png and b/test/reference/recording-surface-over.pdf.rgb24.ref.png differ diff --git a/test/reference/recording-surface-over.ps.argb32.ref.png b/test/reference/recording-surface-over.ps.argb32.ref.png index ac6632316..d2db40116 100644 Binary files a/test/reference/recording-surface-over.ps.argb32.ref.png and b/test/reference/recording-surface-over.ps.argb32.ref.png differ diff --git a/test/reference/recording-surface-over.ps.rgb24.ref.png b/test/reference/recording-surface-over.ps.rgb24.ref.png index fab338235..1a5b908a8 100644 Binary files a/test/reference/recording-surface-over.ps.rgb24.ref.png and b/test/reference/recording-surface-over.ps.rgb24.ref.png differ diff --git a/test/reference/recording-surface-source.pdf.argb32.ref.png b/test/reference/recording-surface-source.pdf.argb32.ref.png new file mode 100644 index 000000000..d2db40116 Binary files /dev/null and b/test/reference/recording-surface-source.pdf.argb32.ref.png differ diff --git a/test/reference/recording-surface-source.pdf.rgb24.ref.png b/test/reference/recording-surface-source.pdf.rgb24.ref.png new file mode 100644 index 000000000..1a5b908a8 Binary files /dev/null and b/test/reference/recording-surface-source.pdf.rgb24.ref.png differ diff --git a/test/reference/recording-surface-source.ps.argb32.ref.png b/test/reference/recording-surface-source.ps.argb32.ref.png new file mode 100644 index 000000000..d2db40116 Binary files /dev/null and b/test/reference/recording-surface-source.ps.argb32.ref.png differ diff --git a/test/reference/recording-surface-source.ps.rgb24.ref.png b/test/reference/recording-surface-source.ps.rgb24.ref.png new file mode 100644 index 000000000..1a5b908a8 Binary files /dev/null and b/test/reference/recording-surface-source.ps.rgb24.ref.png differ diff --git a/test/reference/rectilinear-dash-scale-unaligned.pdf.ref.png b/test/reference/rectilinear-dash-scale-unaligned.pdf.ref.png new file mode 100644 index 000000000..b05e29358 Binary files /dev/null and b/test/reference/rectilinear-dash-scale-unaligned.pdf.ref.png differ diff --git a/test/reference/rectilinear-dash-scale-unaligned.ps.ref.png b/test/reference/rectilinear-dash-scale-unaligned.ps.ref.png new file mode 100644 index 000000000..b55a18dcc Binary files /dev/null and b/test/reference/rectilinear-dash-scale-unaligned.ps.ref.png differ diff --git a/test/reference/reflected-stroke.pdf.ref.png b/test/reference/reflected-stroke.pdf.ref.png new file mode 100644 index 000000000..efae740da Binary files /dev/null and b/test/reference/reflected-stroke.pdf.ref.png differ diff --git a/test/reference/rotate-clip-image-surface-paint.pdf.argb32.png b/test/reference/rotate-clip-image-surface-paint.pdf.argb32.png new file mode 100644 index 000000000..591e9a003 Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.pdf.argb32.png differ diff --git a/test/reference/rotate-clip-image-surface-paint.pdf.rgb24.png b/test/reference/rotate-clip-image-surface-paint.pdf.rgb24.png new file mode 100644 index 000000000..c1e357e94 Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.pdf.rgb24.png differ diff --git a/test/reference/rotate-clip-image-surface-paint.ps.argb32.png b/test/reference/rotate-clip-image-surface-paint.ps.argb32.png new file mode 100644 index 000000000..159a5158a Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.ps.argb32.png differ diff --git a/test/reference/rotated-clip.pdf.ref.png b/test/reference/rotated-clip.pdf.ref.png new file mode 100644 index 000000000..fd8707ae4 Binary files /dev/null and b/test/reference/rotated-clip.pdf.ref.png differ diff --git a/test/reference/rotated-clip.ps.argb32.png b/test/reference/rotated-clip.ps.argb32.png new file mode 100644 index 000000000..e26d11878 Binary files /dev/null and b/test/reference/rotated-clip.ps.argb32.png differ diff --git a/test/reference/rotated-clip.ps.argb32.ref.png b/test/reference/rotated-clip.ps.argb32.ref.png new file mode 100644 index 000000000..e26d11878 Binary files /dev/null and b/test/reference/rotated-clip.ps.argb32.ref.png differ diff --git a/test/reference/rotated-clip.ps.ref.png b/test/reference/rotated-clip.ps.ref.png deleted file mode 100644 index a2a0aceda..000000000 Binary files a/test/reference/rotated-clip.ps.ref.png and /dev/null differ diff --git a/test/reference/rotated-clip.ps.rgb24.ref.png b/test/reference/rotated-clip.ps.rgb24.ref.png new file mode 100644 index 000000000..f5468c7de Binary files /dev/null and b/test/reference/rotated-clip.ps.rgb24.ref.png differ diff --git a/test/reference/scale-offset-image.pdf.argb32.ref.png b/test/reference/scale-offset-image.pdf.argb32.ref.png deleted file mode 100644 index 74abfaecb..000000000 Binary files a/test/reference/scale-offset-image.pdf.argb32.ref.png and /dev/null differ diff --git a/test/reference/scale-offset-image.pdf.ref.png b/test/reference/scale-offset-image.pdf.ref.png new file mode 100644 index 000000000..588c961fd Binary files /dev/null and b/test/reference/scale-offset-image.pdf.ref.png differ diff --git a/test/reference/scale-offset-image.pdf.rgb24.ref.png b/test/reference/scale-offset-image.pdf.rgb24.ref.png deleted file mode 100644 index 74abfaecb..000000000 Binary files a/test/reference/scale-offset-image.pdf.rgb24.ref.png and /dev/null differ diff --git a/test/reference/scale-offset-image.ref.png b/test/reference/scale-offset-image.ref.png index ab1ced830..085029f7b 100644 Binary files a/test/reference/scale-offset-image.ref.png and b/test/reference/scale-offset-image.ref.png differ diff --git a/test/reference/scale-offset-similar.pdf.argb32.ref.png b/test/reference/scale-offset-similar.pdf.argb32.ref.png deleted file mode 100644 index c1e27653f..000000000 Binary files a/test/reference/scale-offset-similar.pdf.argb32.ref.png and /dev/null differ diff --git a/test/reference/scale-offset-similar.pdf.ref.png b/test/reference/scale-offset-similar.pdf.ref.png new file mode 100644 index 000000000..f2af6add7 Binary files /dev/null and b/test/reference/scale-offset-similar.pdf.ref.png differ diff --git a/test/reference/scale-offset-similar.pdf.rgb24.ref.png b/test/reference/scale-offset-similar.pdf.rgb24.ref.png deleted file mode 100644 index c1e27653f..000000000 Binary files a/test/reference/scale-offset-similar.pdf.rgb24.ref.png and /dev/null differ diff --git a/test/reference/scale-offset-similar.ref.png b/test/reference/scale-offset-similar.ref.png index 8b3649a33..085029f7b 100644 Binary files a/test/reference/scale-offset-similar.ref.png and b/test/reference/scale-offset-similar.ref.png differ diff --git a/test/reference/self-intersecting.ref.png b/test/reference/self-intersecting.ref.png deleted file mode 100644 index d554d83ee..000000000 Binary files a/test/reference/self-intersecting.ref.png and /dev/null differ diff --git a/test/reference/shape-sierpinski.pdf.argb32.ref.png b/test/reference/shape-sierpinski.pdf.argb32.ref.png deleted file mode 100644 index 4e70fbd60..000000000 Binary files a/test/reference/shape-sierpinski.pdf.argb32.ref.png and /dev/null differ diff --git a/test/reference/shape-sierpinski.pdf.ref.png b/test/reference/shape-sierpinski.pdf.ref.png new file mode 100644 index 000000000..786d8d201 Binary files /dev/null and b/test/reference/shape-sierpinski.pdf.ref.png differ diff --git a/test/reference/shape-sierpinski.pdf.rgb24.ref.png b/test/reference/shape-sierpinski.pdf.rgb24.ref.png deleted file mode 100644 index 4e70fbd60..000000000 Binary files a/test/reference/shape-sierpinski.pdf.rgb24.ref.png and /dev/null differ diff --git a/test/reference/simple-edge.ref.png b/test/reference/simple-edge.ref.png index 4757b0a54..8ba7dd3d6 100644 Binary files a/test/reference/simple-edge.ref.png and b/test/reference/simple-edge.ref.png differ diff --git a/test/reference/smask-fill.pdf.ref.png b/test/reference/smask-fill.pdf.ref.png index cfd40b07c..420295ab8 100644 Binary files a/test/reference/smask-fill.pdf.ref.png and b/test/reference/smask-fill.pdf.ref.png differ diff --git a/test/reference/smask-image-mask.pdf.ref.png b/test/reference/smask-image-mask.pdf.ref.png index 7ac43e483..03201af6a 100644 Binary files a/test/reference/smask-image-mask.pdf.ref.png and b/test/reference/smask-image-mask.pdf.ref.png differ diff --git a/test/reference/smask-mask.pdf.ref.png b/test/reference/smask-mask.pdf.ref.png index 59c97407e..4d25ac94a 100644 Binary files a/test/reference/smask-mask.pdf.ref.png and b/test/reference/smask-mask.pdf.ref.png differ diff --git a/test/reference/smask-paint.pdf.ref.png b/test/reference/smask-paint.pdf.ref.png index 623a92dae..9d4c6cee0 100644 Binary files a/test/reference/smask-paint.pdf.ref.png and b/test/reference/smask-paint.pdf.ref.png differ diff --git a/test/reference/smask-stroke.pdf.ref.png b/test/reference/smask-stroke.pdf.ref.png new file mode 100644 index 000000000..f6ddb1939 Binary files /dev/null and b/test/reference/smask-stroke.pdf.ref.png differ diff --git a/test/reference/smask-text.pdf.ref.png b/test/reference/smask-text.pdf.ref.png index fa4905627..6b94f4acc 100644 Binary files a/test/reference/smask-text.pdf.ref.png and b/test/reference/smask-text.pdf.ref.png differ diff --git a/test/reference/smask-text.ps.ref.png b/test/reference/smask-text.ps.ref.png new file mode 100644 index 000000000..4e0066567 Binary files /dev/null and b/test/reference/smask-text.ps.ref.png differ diff --git a/test/reference/smask-text.ps2.ref.png b/test/reference/smask-text.ps2.ref.png deleted file mode 100644 index ae61325cb..000000000 Binary files a/test/reference/smask-text.ps2.ref.png and /dev/null differ diff --git a/test/reference/smask-text.ps3.ref.png b/test/reference/smask-text.ps3.ref.png deleted file mode 100644 index ae61325cb..000000000 Binary files a/test/reference/smask-text.ps3.ref.png and /dev/null differ diff --git a/test/reference/smask.pdf.ref.png b/test/reference/smask.pdf.ref.png new file mode 100644 index 000000000..2bc721c52 Binary files /dev/null and b/test/reference/smask.pdf.ref.png differ diff --git a/test/reference/smask.ps3.ref.png b/test/reference/smask.ps3.ref.png new file mode 100644 index 000000000..fb1d74e1c Binary files /dev/null and b/test/reference/smask.ps3.ref.png differ diff --git a/test/reference/spline-decomposition.pdf.ref.png b/test/reference/spline-decomposition.pdf.ref.png index 5afa09498..99fd1d637 100644 Binary files a/test/reference/spline-decomposition.pdf.ref.png and b/test/reference/spline-decomposition.pdf.ref.png differ diff --git a/test/reference/spline-decomposition.ps.ref.png b/test/reference/spline-decomposition.ps.ref.png index 51e2938ac..7f3a8e6d6 100644 Binary files a/test/reference/spline-decomposition.ps.ref.png and b/test/reference/spline-decomposition.ps.ref.png differ diff --git a/test/reference/stroke-clipped.ps.ref.png b/test/reference/stroke-clipped.ps.ref.png new file mode 100644 index 000000000..a4b2aa6a1 Binary files /dev/null and b/test/reference/stroke-clipped.ps.ref.png differ diff --git a/test/reference/stroke-ctm-caps.pdf.ref.png b/test/reference/stroke-ctm-caps.pdf.ref.png new file mode 100644 index 000000000..dbc6415dc Binary files /dev/null and b/test/reference/stroke-ctm-caps.pdf.ref.png differ diff --git a/test/reference/stroke-image.pdf.ref.png b/test/reference/stroke-image.pdf.ref.png index 790369cb4..e0babce9d 100644 Binary files a/test/reference/stroke-image.pdf.ref.png and b/test/reference/stroke-image.pdf.ref.png differ diff --git a/test/reference/stroke-image.ps.ref.png b/test/reference/stroke-image.ps.ref.png index 71889acfd..8f0a52269 100644 Binary files a/test/reference/stroke-image.ps.ref.png and b/test/reference/stroke-image.ps.ref.png differ diff --git a/test/reference/stroke-pattern.pdf.argb32.ref.png b/test/reference/stroke-pattern.pdf.argb32.ref.png new file mode 100644 index 000000000..788e1fcf3 Binary files /dev/null and b/test/reference/stroke-pattern.pdf.argb32.ref.png differ diff --git a/test/reference/stroke-pattern.pdf.rgb24.ref.png b/test/reference/stroke-pattern.pdf.rgb24.ref.png new file mode 100644 index 000000000..788e1fcf3 Binary files /dev/null and b/test/reference/stroke-pattern.pdf.rgb24.ref.png differ diff --git a/test/reference/surface-pattern-big-scale-down.ps.ref.png b/test/reference/surface-pattern-big-scale-down.ps.ref.png index 13fb09362..b9a5463ca 100644 Binary files a/test/reference/surface-pattern-big-scale-down.ps.ref.png and b/test/reference/surface-pattern-big-scale-down.ps.ref.png differ diff --git a/test/reference/surface-pattern-scale-down-extend-none.ps.ref.png b/test/reference/surface-pattern-scale-down-extend-none.ps.ref.png new file mode 100644 index 000000000..d3f90ab1a Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.ps.ref.png differ diff --git a/test/reference/surface-pattern-scale-down.pdf.ref.png b/test/reference/surface-pattern-scale-down.pdf.ref.png index 1e32a44bb..c574f3093 100644 Binary files a/test/reference/surface-pattern-scale-down.pdf.ref.png and b/test/reference/surface-pattern-scale-down.pdf.ref.png differ diff --git a/test/reference/surface-pattern-scale-down.ps.ref.png b/test/reference/surface-pattern-scale-down.ps.ref.png new file mode 100644 index 000000000..419f5f84e Binary files /dev/null and b/test/reference/surface-pattern-scale-down.ps.ref.png differ diff --git a/test/reference/surface-pattern-scale-down.ps2.ref.png b/test/reference/surface-pattern-scale-down.ps2.ref.png deleted file mode 100644 index 5fb6395ce..000000000 Binary files a/test/reference/surface-pattern-scale-down.ps2.ref.png and /dev/null differ diff --git a/test/reference/surface-pattern-scale-down.ps3.ref.png b/test/reference/surface-pattern-scale-down.ps3.ref.png deleted file mode 100644 index 5fb6395ce..000000000 Binary files a/test/reference/surface-pattern-scale-down.ps3.ref.png and /dev/null differ diff --git a/test/reference/surface-pattern-scale-down.ref.png b/test/reference/surface-pattern-scale-down.ref.png index 8bb58a2a1..239a6fd6c 100644 Binary files a/test/reference/surface-pattern-scale-down.ref.png and b/test/reference/surface-pattern-scale-down.ref.png differ diff --git a/test/reference/surface-pattern-scale-up.pdf.ref.png b/test/reference/surface-pattern-scale-up.pdf.ref.png index 593d058aa..fa04bcfcc 100644 Binary files a/test/reference/surface-pattern-scale-up.pdf.ref.png and b/test/reference/surface-pattern-scale-up.pdf.ref.png differ diff --git a/test/reference/surface-pattern-scale-up.ps.ref.png b/test/reference/surface-pattern-scale-up.ps.ref.png new file mode 100644 index 000000000..b70ecb190 Binary files /dev/null and b/test/reference/surface-pattern-scale-up.ps.ref.png differ diff --git a/test/reference/surface-pattern-scale-up.ps2.ref.png b/test/reference/surface-pattern-scale-up.ps2.ref.png deleted file mode 100644 index f2eac7a78..000000000 Binary files a/test/reference/surface-pattern-scale-up.ps2.ref.png and /dev/null differ diff --git a/test/reference/surface-pattern-scale-up.ps3.ref.png b/test/reference/surface-pattern-scale-up.ps3.ref.png deleted file mode 100644 index f2eac7a78..000000000 Binary files a/test/reference/surface-pattern-scale-up.ps3.ref.png and /dev/null differ diff --git a/test/reference/surface-pattern.pdf.ref.png b/test/reference/surface-pattern.pdf.ref.png new file mode 100644 index 000000000..42c42df49 Binary files /dev/null and b/test/reference/surface-pattern.pdf.ref.png differ diff --git a/test/reference/surface-pattern.pdf.xfail.png b/test/reference/surface-pattern.pdf.xfail.png deleted file mode 100644 index fadc2c240..000000000 Binary files a/test/reference/surface-pattern.pdf.xfail.png and /dev/null differ diff --git a/test/reference/surface-pattern.ps.xfail.png b/test/reference/surface-pattern.ps.ref.png similarity index 100% rename from test/reference/surface-pattern.ps.xfail.png rename to test/reference/surface-pattern.ps.ref.png diff --git a/test/reference/svg-surface-source.ps.rgb24.ref.png b/test/reference/svg-surface-source.ps.rgb24.ref.png new file mode 100644 index 000000000..26cbb57c4 Binary files /dev/null and b/test/reference/svg-surface-source.ps.rgb24.ref.png differ diff --git a/test/reference/text-glyph-range.pdf.ref.png b/test/reference/text-glyph-range.pdf.ref.png new file mode 100644 index 000000000..06588e945 Binary files /dev/null and b/test/reference/text-glyph-range.pdf.ref.png differ diff --git a/test/reference/text-glyph-range.ps.ref.png b/test/reference/text-glyph-range.ps.ref.png index 96bc85a34..605472254 100644 Binary files a/test/reference/text-glyph-range.ps.ref.png and b/test/reference/text-glyph-range.ps.ref.png differ diff --git a/test/reference/text-pattern.pdf.argb32.ref.png b/test/reference/text-pattern.pdf.argb32.ref.png index 5eef739dc..f8abd9ea7 100644 Binary files a/test/reference/text-pattern.pdf.argb32.ref.png and b/test/reference/text-pattern.pdf.argb32.ref.png differ diff --git a/test/reference/text-pattern.pdf.rgb24.ref.png b/test/reference/text-pattern.pdf.rgb24.ref.png index 27a1195e8..679fbd008 100644 Binary files a/test/reference/text-pattern.pdf.rgb24.ref.png and b/test/reference/text-pattern.pdf.rgb24.ref.png differ diff --git a/test/reference/text-pattern.ps3.argb32.ref.png b/test/reference/text-pattern.ps3.argb32.ref.png index 411a531b2..98021a54d 100644 Binary files a/test/reference/text-pattern.ps3.argb32.ref.png and b/test/reference/text-pattern.ps3.argb32.ref.png differ diff --git a/test/reference/text-pattern.ps3.rgb24.ref.png b/test/reference/text-pattern.ps3.rgb24.ref.png index f696a9926..afcaba822 100644 Binary files a/test/reference/text-pattern.ps3.rgb24.ref.png and b/test/reference/text-pattern.ps3.rgb24.ref.png differ diff --git a/test/reference/text-rotate.pdf.ref.png b/test/reference/text-rotate.pdf.ref.png index b533075c4..3b1c95e50 100644 Binary files a/test/reference/text-rotate.pdf.ref.png and b/test/reference/text-rotate.pdf.ref.png differ diff --git a/test/reference/text-rotate.ps.ref.png b/test/reference/text-rotate.ps.ref.png index c68d02dae..a0f8fbe37 100644 Binary files a/test/reference/text-rotate.ps.ref.png and b/test/reference/text-rotate.ps.ref.png differ diff --git a/test/reference/text-rotate.ref.png b/test/reference/text-rotate.ref.png index 432de3123..ac8b68166 100644 Binary files a/test/reference/text-rotate.ref.png and b/test/reference/text-rotate.ref.png differ diff --git a/test/reference/text-transform.pdf.argb32.ref.png b/test/reference/text-transform.pdf.argb32.ref.png index 7a2f3a72a..441c1cd53 100644 Binary files a/test/reference/text-transform.pdf.argb32.ref.png and b/test/reference/text-transform.pdf.argb32.ref.png differ diff --git a/test/reference/text-transform.pdf.rgb24.ref.png b/test/reference/text-transform.pdf.rgb24.ref.png index 7a2f3a72a..441c1cd53 100644 Binary files a/test/reference/text-transform.pdf.rgb24.ref.png and b/test/reference/text-transform.pdf.rgb24.ref.png differ diff --git a/test/reference/text-transform.ps.ref.png b/test/reference/text-transform.ps.ref.png new file mode 100644 index 000000000..c5fdb304c Binary files /dev/null and b/test/reference/text-transform.ps.ref.png differ diff --git a/test/reference/text-transform.ps2.ref.png b/test/reference/text-transform.ps2.ref.png deleted file mode 100644 index 07896b302..000000000 Binary files a/test/reference/text-transform.ps2.ref.png and /dev/null differ diff --git a/test/reference/text-transform.ps3.ref.png b/test/reference/text-transform.ps3.ref.png deleted file mode 100644 index 07896b302..000000000 Binary files a/test/reference/text-transform.ps3.ref.png and /dev/null differ diff --git a/test/reference/text-unhinted-metrics.pdf.ref.png b/test/reference/text-unhinted-metrics.pdf.ref.png new file mode 100644 index 000000000..ac8952175 Binary files /dev/null and b/test/reference/text-unhinted-metrics.pdf.ref.png differ diff --git a/test/reference/text-unhinted-metrics.ps.ref.png b/test/reference/text-unhinted-metrics.ps.ref.png new file mode 100644 index 000000000..d34ea3478 Binary files /dev/null and b/test/reference/text-unhinted-metrics.ps.ref.png differ diff --git a/test/reference/text-unhinted-metrics.ref.png b/test/reference/text-unhinted-metrics.ref.png new file mode 100644 index 000000000..7bfe2f651 Binary files /dev/null and b/test/reference/text-unhinted-metrics.ref.png differ diff --git a/test/reference/tiger.pdf.ref.png b/test/reference/tiger.pdf.ref.png new file mode 100644 index 000000000..cc641e290 Binary files /dev/null and b/test/reference/tiger.pdf.ref.png differ diff --git a/test/reference/tiger.ps.ref.png b/test/reference/tiger.ps.ref.png new file mode 100644 index 000000000..09729b243 Binary files /dev/null and b/test/reference/tiger.ps.ref.png differ diff --git a/test/reference/tiger.ref.png b/test/reference/tiger.ref.png index b8b21758b..58e047119 100644 Binary files a/test/reference/tiger.ref.png and b/test/reference/tiger.ref.png differ diff --git a/test/reference/tighten-bounds.pdf.ref.png b/test/reference/tighten-bounds.pdf.ref.png new file mode 100644 index 000000000..8511caeee Binary files /dev/null and b/test/reference/tighten-bounds.pdf.ref.png differ diff --git a/test/reference/tighten-bounds.ps.ref.png b/test/reference/tighten-bounds.ps.ref.png new file mode 100644 index 000000000..8511caeee Binary files /dev/null and b/test/reference/tighten-bounds.ps.ref.png differ diff --git a/test/reference/transforms.pdf.ref.png b/test/reference/transforms.pdf.ref.png new file mode 100644 index 000000000..d6bff0a02 Binary files /dev/null and b/test/reference/transforms.pdf.ref.png differ diff --git a/test/reference/trap-clip.argb32.ref.png b/test/reference/trap-clip.argb32.ref.png index 08e6c68a5..b929d94c1 100644 Binary files a/test/reference/trap-clip.argb32.ref.png and b/test/reference/trap-clip.argb32.ref.png differ diff --git a/test/reference/trap-clip.pdf.argb32.ref.png b/test/reference/trap-clip.pdf.argb32.ref.png new file mode 100644 index 000000000..fca20c459 Binary files /dev/null and b/test/reference/trap-clip.pdf.argb32.ref.png differ diff --git a/test/reference/trap-clip.pdf.rgb24.ref.png b/test/reference/trap-clip.pdf.rgb24.ref.png new file mode 100644 index 000000000..fca20c459 Binary files /dev/null and b/test/reference/trap-clip.pdf.rgb24.ref.png differ diff --git a/test/reference/trap-clip.rgb24.ref.png b/test/reference/trap-clip.rgb24.ref.png index 9c51d62eb..2c06d0153 100644 Binary files a/test/reference/trap-clip.rgb24.ref.png and b/test/reference/trap-clip.rgb24.ref.png differ diff --git a/test/reference/twin.pdf.argb32.ref.png b/test/reference/twin.pdf.argb32.ref.png new file mode 100644 index 000000000..f10ad5c71 Binary files /dev/null and b/test/reference/twin.pdf.argb32.ref.png differ diff --git a/test/reference/twin.pdf.rgb24.ref.png b/test/reference/twin.pdf.rgb24.ref.png new file mode 100644 index 000000000..0964f5c14 Binary files /dev/null and b/test/reference/twin.pdf.rgb24.ref.png differ diff --git a/test/reference/twin.ps.ref.png b/test/reference/twin.ps.ref.png index 25c71b440..a92e4fb62 100644 Binary files a/test/reference/twin.ps.ref.png and b/test/reference/twin.ps.ref.png differ diff --git a/test/reference/unbounded-operator.pdf.argb32.ref.png b/test/reference/unbounded-operator.pdf.argb32.ref.png index 4aa476de4..6e5302469 100644 Binary files a/test/reference/unbounded-operator.pdf.argb32.ref.png and b/test/reference/unbounded-operator.pdf.argb32.ref.png differ diff --git a/test/reference/unbounded-operator.pdf.rgb24.ref.png b/test/reference/unbounded-operator.pdf.rgb24.ref.png new file mode 100644 index 000000000..1a1f9d1a7 Binary files /dev/null and b/test/reference/unbounded-operator.pdf.rgb24.ref.png differ diff --git a/test/reference/unbounded-operator.rgb24.ref.png b/test/reference/unbounded-operator.rgb24.ref.png index c9b5b3456..1a1f9d1a7 100644 Binary files a/test/reference/unbounded-operator.rgb24.ref.png and b/test/reference/unbounded-operator.rgb24.ref.png differ diff --git a/test/reference/unclosed-strokes.pdf.ref.png b/test/reference/unclosed-strokes.pdf.ref.png new file mode 100644 index 000000000..17b161f54 Binary files /dev/null and b/test/reference/unclosed-strokes.pdf.ref.png differ diff --git a/test/reference/unclosed-strokes.ps.ref.png b/test/reference/unclosed-strokes.ps.ref.png new file mode 100644 index 000000000..ee9324bd4 Binary files /dev/null and b/test/reference/unclosed-strokes.ps.ref.png differ diff --git a/test/reference/user-font-proxy.pdf.argb32.ref.png b/test/reference/user-font-proxy.pdf.argb32.ref.png index cffa9edb7..9d61720e7 100644 Binary files a/test/reference/user-font-proxy.pdf.argb32.ref.png and b/test/reference/user-font-proxy.pdf.argb32.ref.png differ diff --git a/test/reference/user-font-proxy.pdf.ref.png b/test/reference/user-font-proxy.pdf.ref.png deleted file mode 100644 index afe7cb0dc..000000000 Binary files a/test/reference/user-font-proxy.pdf.ref.png and /dev/null differ diff --git a/test/reference/user-font-proxy.ps.ref.png b/test/reference/user-font-proxy.ps.ref.png index a7b348b6a..76ba45be1 100644 Binary files a/test/reference/user-font-proxy.ps.ref.png and b/test/reference/user-font-proxy.ps.ref.png differ diff --git a/test/reference/user-font-rescale.pdf.rgb24.ref.png b/test/reference/user-font-rescale.pdf.rgb24.ref.png new file mode 100644 index 000000000..542e3370d Binary files /dev/null and b/test/reference/user-font-rescale.pdf.rgb24.ref.png differ diff --git a/test/reference/user-font-rescale.ps.ref.png b/test/reference/user-font-rescale.ps.ref.png index 1ee4b1303..42e816e9c 100644 Binary files a/test/reference/user-font-rescale.ps.ref.png and b/test/reference/user-font-rescale.ps.ref.png differ diff --git a/test/reference/user-font.pdf.ref.png b/test/reference/user-font.pdf.ref.png index de864074a..5dc028eb3 100644 Binary files a/test/reference/user-font.pdf.ref.png and b/test/reference/user-font.pdf.ref.png differ diff --git a/test/reference/user-font.ps.ref.png b/test/reference/user-font.ps.ref.png index 63f289698..3a674c30a 100644 Binary files a/test/reference/user-font.ps.ref.png and b/test/reference/user-font.ps.ref.png differ diff --git a/test/reference/world-map-fill.pdf.argb32.ref.png b/test/reference/world-map-fill.pdf.argb32.ref.png new file mode 100644 index 000000000..cd828fd7c Binary files /dev/null and b/test/reference/world-map-fill.pdf.argb32.ref.png differ diff --git a/test/reference/world-map-fill.pdf.rgb24.ref.png b/test/reference/world-map-fill.pdf.rgb24.ref.png new file mode 100644 index 000000000..cd828fd7c Binary files /dev/null and b/test/reference/world-map-fill.pdf.rgb24.ref.png differ diff --git a/test/reference/world-map-fill.ps.argb32.ref.png b/test/reference/world-map-fill.ps.argb32.ref.png new file mode 100644 index 000000000..86f323a29 Binary files /dev/null and b/test/reference/world-map-fill.ps.argb32.ref.png differ diff --git a/test/reference/world-map-fill.ps.rgb24.ref.png b/test/reference/world-map-fill.ps.rgb24.ref.png new file mode 100644 index 000000000..86f323a29 Binary files /dev/null and b/test/reference/world-map-fill.ps.rgb24.ref.png differ diff --git a/test/reference/world-map-stroke.pdf.argb32.ref.png b/test/reference/world-map-stroke.pdf.argb32.ref.png new file mode 100644 index 000000000..05ac7e30f Binary files /dev/null and b/test/reference/world-map-stroke.pdf.argb32.ref.png differ diff --git a/test/reference/world-map-stroke.pdf.rgb24.ref.png b/test/reference/world-map-stroke.pdf.rgb24.ref.png new file mode 100644 index 000000000..05ac7e30f Binary files /dev/null and b/test/reference/world-map-stroke.pdf.rgb24.ref.png differ diff --git a/test/reference/world-map-stroke.ps.argb32.ref.png b/test/reference/world-map-stroke.ps.argb32.ref.png new file mode 100644 index 000000000..f846294d9 Binary files /dev/null and b/test/reference/world-map-stroke.ps.argb32.ref.png differ diff --git a/test/reference/world-map-stroke.ps.rgb24.ref.png b/test/reference/world-map-stroke.ps.rgb24.ref.png new file mode 100644 index 000000000..f846294d9 Binary files /dev/null and b/test/reference/world-map-stroke.ps.rgb24.ref.png differ diff --git a/test/reference/world-map.pdf.argb32.ref.png b/test/reference/world-map.pdf.argb32.ref.png new file mode 100644 index 000000000..3f7df8a9e Binary files /dev/null and b/test/reference/world-map.pdf.argb32.ref.png differ diff --git a/test/reference/world-map.pdf.rgb24.ref.png b/test/reference/world-map.pdf.rgb24.ref.png new file mode 100644 index 000000000..3f7df8a9e Binary files /dev/null and b/test/reference/world-map.pdf.rgb24.ref.png differ diff --git a/test/reference/world-map.ps.argb32.ref.png b/test/reference/world-map.ps.argb32.ref.png new file mode 100644 index 000000000..c12220d22 Binary files /dev/null and b/test/reference/world-map.ps.argb32.ref.png differ diff --git a/test/reference/world-map.ps.rgb24.ref.png b/test/reference/world-map.ps.rgb24.ref.png new file mode 100644 index 000000000..c12220d22 Binary files /dev/null and b/test/reference/world-map.ps.rgb24.ref.png differ diff --git a/test/reference/zero-mask.ref.png b/test/reference/zero-mask.ref.png deleted file mode 100644 index ffae8d995..000000000 Binary files a/test/reference/zero-mask.ref.png and /dev/null differ diff --git a/test/svg2png.c b/test/svg2png.c index 1eecaf7b2..9a5c8c045 100644 --- a/test/svg2png.c +++ b/test/svg2png.c @@ -37,9 +37,13 @@ int main (int argc, char *argv[]) { GError *error = NULL; - GdkPixbuf *pixbuf; + RsvgHandle *handle; + RsvgDimensionData dimensions; const char *filename = argv[1]; const char *output_filename = argv[2]; + cairo_surface_t *surface; + cairo_t *cr; + cairo_status_t status; if (argc != 3) FAIL ("usage: svg2png input_file.svg output_file.png"); @@ -51,14 +55,37 @@ int main (int argc, char *argv[]) error = NULL; rsvg_set_default_dpi (72.0); - pixbuf = rsvg_pixbuf_from_file (filename, &error); - if (error != NULL) + + handle = rsvg_handle_new_from_file (filename, &error); + if (!handle) + FAIL (error->message); + + rsvg_handle_get_dimensions (handle, &dimensions); + + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + dimensions.width, dimensions.height); + cr = cairo_create (surface); + cairo_surface_destroy (surface); + + cairo_set_source_rgb (cr, 1,1,1); + cairo_paint (cr); + cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); + + if (!rsvg_handle_render_cairo (handle, cr)) FAIL (error->message); - gdk_pixbuf_save (pixbuf, output_filename, "png", &error, NULL); - if (error != NULL) + cairo_pop_group_to_source (cr); + cairo_paint (cr); + + status = cairo_surface_write_to_png (cairo_get_target (cr), + output_filename); + cairo_destroy (cr); + if (status) + FAIL (cairo_status_to_string (status)); + + if (!rsvg_handle_close (handle, &error)) FAIL (error->message); - g_object_unref (pixbuf); + g_object_unref (handle); return 0; } diff --git a/test/text-unhinted-metrics.c b/test/text-unhinted-metrics.c new file mode 100644 index 000000000..352ab4ef2 --- /dev/null +++ b/test/text-unhinted-metrics.c @@ -0,0 +1,73 @@ +/* + * Copyright © 2016 Adrian Johnson + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Adrian Johnson + */ + +/* Test CAIRO_HINT_METRICS_OFF with a TrueType font. + * + * Based on the test case in https://lists.cairographics.org/archives/cairo/2016-April/027334.html + */ + +#include "cairo-test.h" + +#define WIDTH 100 +#define HEIGHT 100 + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_font_options_t *font_options; + double size, y; + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY "DejaVu Sans Mono", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + + font_options = cairo_font_options_create(); + cairo_get_font_options (cr, font_options); + cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); + cairo_set_font_options (cr, font_options); + cairo_font_options_destroy (font_options); + + y = 0.0; + cairo_set_source_rgb (cr, 0, 0, 0); + for (size = 8.0; size <= 10.0; size += 0.25) { + cairo_set_font_size (cr, size); + y += 10.0; + cairo_move_to (cr, 5, y); + cairo_show_text (cr, "abcdefghijklm"); + } + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (text_unhinted_metrics, + "Test CAIRO_HINT_METRICS_OFF", + "text, font", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, draw) diff --git a/util/cairo-fdr/Makefile.am b/util/cairo-fdr/Makefile.am index 34215a659..5cd542219 100644 --- a/util/cairo-fdr/Makefile.am +++ b/util/cairo-fdr/Makefile.am @@ -9,7 +9,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src \ cairo_fdr_la_SOURCES = fdr.c cairo_fdr_la_CPPFLAGS = $(AM_CPPFLAGS) cairo_fdr_la_CFLAGS = $(CAIRO_CFLAGS) -cairo_fdr_la_LDFLAGS = -module -no-undefined +cairo_fdr_la_LDFLAGS = -module -no-undefined -avoid-version if CAIRO_HAS_DL cairo_fdr_la_LIBADD = -ldl endif diff --git a/util/cairo-gobject/cairo-gobject-enums.c b/util/cairo-gobject/cairo-gobject-enums.c index 0a7c95d29..0c5069406 100644 --- a/util/cairo-gobject/cairo-gobject-enums.c +++ b/util/cairo-gobject/cairo-gobject-enums.c @@ -50,7 +50,12 @@ cairo_gobject_status_get_type (void) { CAIRO_STATUS_DEVICE_TYPE_MISMATCH, "CAIRO_STATUS_DEVICE_TYPE_MISMATCH", "device-type-mismatch" }, { CAIRO_STATUS_DEVICE_ERROR, "CAIRO_STATUS_DEVICE_ERROR", "device-error" }, { CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, "CAIRO_STATUS_INVALID_MESH_CONSTRUCTION", "invalid-mesh-construction" }, - { CAIRO_STATUS_LAST_STATUS, "CAIRO_STATUS_LAST_STATUS", "last-status" }, + { CAIRO_STATUS_DEVICE_FINISHED, "CAIRO_STATUS_DEVICE_FINISHED", "device-finished" }, + { CAIRO_STATUS_JBIG2_GLOBAL_MISSING, "CAIRO_STATUS_JBIG2_GLOBAL_MISSING", "jbig2-global_missing" }, + { CAIRO_STATUS_PNG_ERROR, "CAIRO_STATUS_PNG_ERROR", "png-error" }, + { CAIRO_STATUS_FREETYPE_ERROR, "CAIRO_STATUS_FREETYPE_ERROR", "freetype-error" }, + { CAIRO_STATUS_LAST_STATUS, "CAIRO_STATUS_LAST_STATUS", "last-status" }, + { CAIRO_STATUS_WIN32_GDI_ERROR, "CAIRO_STATUS_WIN32_GDI_ERROR", "win32-gdi-error" }, { 0, NULL, NULL } }; GType type = g_enum_register_static (g_intern_static_string ("cairo_status_t"), values); diff --git a/util/cairo-script/cairo-script-private.h b/util/cairo-script/cairo-script-private.h index 8d158d600..da846dcb2 100644 --- a/util/cairo-script/cairo-script-private.h +++ b/util/cairo-script/cairo-script-private.h @@ -219,6 +219,16 @@ typedef enum _csi_status { CSI_STATUS_INVALID_CLUSTERS = CAIRO_STATUS_INVALID_CLUSTERS, CSI_STATUS_INVALID_SLANT = CAIRO_STATUS_INVALID_SLANT, CSI_STATUS_INVALID_WEIGHT = CAIRO_STATUS_INVALID_WEIGHT, + CSI_STATUS_INVALID_SIZE = CAIRO_STATUS_INVALID_SIZE, + CSI_STATUS_USER_FONT_NOT_IMPLEMENTED = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, + CSI_STATUS_DEVICE_TYPE_MISMATCH = CAIRO_STATUS_DEVICE_TYPE_MISMATCH, + CSI_STATUS_DEVICE_ERROR = CAIRO_STATUS_DEVICE_ERROR, + CSI_STATUS_INVALID_MESH_CONSTRUCTION = CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, + CSI_STATUS_DEVICE_FINISHED = CAIRO_STATUS_DEVICE_FINISHED, + CSI_STATUS_JBIG2_GLOBAL_MISSING = CAIRO_STATUS_JBIG2_GLOBAL_MISSING, + CSI_STATUS_PNG_ERROR = CAIRO_STATUS_PNG_ERROR, + CSI_STATUS_FREETYPE_ERROR = CAIRO_STATUS_FREETYPE_ERROR, + CSI_STATUS_WIN32_GDI_ERROR = CAIRO_STATUS_WIN32_GDI_ERROR, /* cairo-script-interpreter specific errors */ diff --git a/util/cairo-sphinx/Makefile.am b/util/cairo-sphinx/Makefile.am index 10bc10c27..3c1126320 100644 --- a/util/cairo-sphinx/Makefile.am +++ b/util/cairo-sphinx/Makefile.am @@ -11,7 +11,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src \ cairo_sphinx_la_SOURCES = fdr.c cairo_sphinx_la_CPPFLAGS = $(AM_CPPFLAGS) cairo_sphinx_la_CFLAGS = $(CAIRO_CFLAGS) -cairo_sphinx_la_LDFLAGS = -module -no-undefined +cairo_sphinx_la_LDFLAGS = -module -no-undefined -avoid-version if CAIRO_HAS_DL cairo_sphinx_la_LIBADD = -ldl endif diff --git a/util/cairo-trace/Makefile.am b/util/cairo-trace/Makefile.am index 3278abe42..a0091f882 100644 --- a/util/cairo-trace/Makefile.am +++ b/util/cairo-trace/Makefile.am @@ -11,7 +11,7 @@ libcairo_trace_la_SOURCES = trace.c libcairo_trace_la_CPPFLAGS = -DCAIRO_TRACE_OUTDIR="\"$(cairooutdir)\"" \ $(AM_CPPFLAGS) libcairo_trace_la_CFLAGS = $(CAIRO_CFLAGS) $(real_pthread_CFLAGS) -libcairo_trace_la_LDFLAGS = -no-undefined +libcairo_trace_la_LDFLAGS = -module -no-undefined -avoid-version libcairo_trace_la_LIBADD = $(real_pthread_LIBS) -lz if CAIRO_HAS_DL diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c index d5d76689c..3c056134e 100644 --- a/util/cairo-trace/trace.c +++ b/util/cairo-trace/trace.c @@ -1582,6 +1582,10 @@ _status_to_string (cairo_status_t status) f(INVALID_MESH_CONSTRUCTION); f(DEVICE_FINISHED); f(JBIG2_GLOBAL_MISSING); + f(PNG_ERROR); + f(FREETYPE_ERROR); + f(WIN32_GDI_ERROR); + f(TAG_ERROR); case CAIRO_STATUS_LAST_STATUS: break; }