diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cairo-analysis-surface.c | 112 | ||||
-rw-r--r-- | src/cairo-array-private.h | 7 | ||||
-rw-r--r-- | src/cairo-array.c | 36 | ||||
-rw-r--r-- | src/cairo-image-source.c | 2 | ||||
-rw-r--r-- | src/cairo-paginated-surface.c | 20 | ||||
-rw-r--r-- | src/cairo-pdf-interchange.c | 1427 | ||||
-rw-r--r-- | src/cairo-pdf-operators.c | 14 | ||||
-rw-r--r-- | src/cairo-pdf-surface-private.h | 199 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 433 | ||||
-rw-r--r-- | src/cairo-quartz-surface.c | 3 | ||||
-rw-r--r-- | src/cairo-recording-surface-private.h | 24 | ||||
-rw-r--r-- | src/cairo-recording-surface.c | 76 | ||||
-rw-r--r-- | src/cairo-spans-compositor.c | 2 | ||||
-rw-r--r-- | src/cairo-surface-backend-private.h | 25 | ||||
-rw-r--r-- | src/cairo-tag-attributes-private.h | 30 | ||||
-rw-r--r-- | src/cairo-tag-attributes.c | 145 | ||||
-rw-r--r-- | src/cairo-tag-stack-private.h | 9 | ||||
-rw-r--r-- | src/cairo-tag-stack.c | 37 | ||||
-rw-r--r-- | src/cairo-traps-compositor.c | 2 | ||||
-rw-r--r-- | src/cairo-types-private.h | 11 | ||||
-rw-r--r-- | src/cairo-xcb-surface-render.c | 3 | ||||
-rw-r--r-- | src/cairo-xlib-source.c | 3 | ||||
-rw-r--r-- | src/cairo.h | 2 | ||||
-rw-r--r-- | src/cairoint.h | 1 |
24 files changed, 2156 insertions, 467 deletions
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index 0e7ba8a38..6889be38f 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -263,7 +263,8 @@ static cairo_int_status_t _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, - unsigned int *regions_id) + unsigned int *regions_id, + cairo_analysis_source_t source_type) { const cairo_surface_pattern_t *surface_pattern; cairo_analysis_surface_t *tmp; @@ -273,6 +274,7 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, cairo_int_status_t analysis_status = CAIRO_INT_STATUS_SUCCESS; cairo_bool_t surface_is_unbounded; cairo_bool_t unused; + cairo_bool_t replay_all; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; @@ -288,7 +290,7 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, tmp = (cairo_analysis_surface_t *) _cairo_analysis_surface_create (surface->target, surface->create_region_ids); if (unlikely (tmp->base.status)) { - status =tmp->base.status; + status = tmp->base.status; goto cleanup1; } proxy = attach_proxy (source, &tmp->base); @@ -298,7 +300,6 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, assert (status == CAIRO_INT_STATUS_SUCCESS); _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); @@ -307,22 +308,54 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, status = _cairo_recording_surface_region_array_attach (source, regions_id); if (unlikely (status)) goto cleanup2; + } + + replay_all = FALSE; + if (surface->target->backend->analyze_recording_surface) { + status = surface->target->backend->analyze_recording_surface ( + surface->target, + surface_pattern, + surface->create_region_ids ? *regions_id : 0, + source_type, + TRUE); + if (status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + /* Ensure all commands are replayed even if previously + * replayed and assigned to a region.*/ + replay_all = TRUE; + status = CAIRO_INT_STATUS_SUCCESS; + } + if (unlikely (status)) + goto cleanup3; + } + if (surface->create_region_ids) { status = _cairo_recording_surface_replay_and_create_regions (source, *regions_id, &pattern->matrix, &tmp->base, - surface_is_unbounded); + surface_is_unbounded, + replay_all); if (unlikely (status)) - goto cleanup2; + goto cleanup3; } else { - status = _cairo_recording_surface_replay_with_clip (source, - &pattern->matrix, - &tmp->base, - NULL, /* target clip */ - surface_is_unbounded); + status = _cairo_recording_surface_replay_with_transform (source, + &pattern->matrix, + &tmp->base, + surface_is_unbounded, + replay_all); if (unlikely (status)) - goto cleanup2; + goto cleanup3; + } + + if (surface->target->backend->analyze_recording_surface) { + status = surface->target->backend->analyze_recording_surface ( + surface->target, + surface_pattern, + surface->create_region_ids ? *regions_id : 0, + source_type, + FALSE); + if (unlikely (status)) + goto cleanup3; } /* black background or mime data fills entire extents */ @@ -339,7 +372,7 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) status = CAIRO_INT_STATUS_SUCCESS; if (unlikely (status)) - goto cleanup2; + goto cleanup3; } } @@ -363,6 +396,10 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, _cairo_box_round_to_rectangle (&tmp->page_bbox, extents); } + cleanup3: + if (surface->create_region_ids && unlikely (status)) { + _cairo_recording_surface_region_array_remove (source, *regions_id); + } cleanup2: detach_proxy (proxy); cleanup1: @@ -454,7 +491,8 @@ _cairo_analysis_surface_paint (void *abstract_surface, backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents, - &surface->source_region_id); + &surface->source_region_id, + CAIRO_ANALYSIS_SOURCE_PAINT); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -500,7 +538,8 @@ _cairo_analysis_surface_mask (void *abstract_surface, _analyze_recording_surface_pattern (surface, source, &rec_extents, - &surface->source_region_id); + &surface->source_region_id, + CAIRO_ANALYSIS_SOURCE_MASK); if (_cairo_int_status_is_error (backend_source_status)) return backend_source_status; @@ -516,7 +555,8 @@ _cairo_analysis_surface_mask (void *abstract_surface, _analyze_recording_surface_pattern (surface, mask, &rec_extents, - &surface->mask_region_id); + &surface->mask_region_id, + CAIRO_ANALYSIS_MASK_MASK); if (_cairo_int_status_is_error (backend_mask_status)) return backend_mask_status; @@ -578,7 +618,8 @@ _cairo_analysis_surface_stroke (void *abstract_surface, backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents, - &surface->source_region_id); + &surface->source_region_id, + CAIRO_ANALYSIS_SOURCE_STROKE); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -633,7 +674,8 @@ _cairo_analysis_surface_fill (void *abstract_surface, backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents, - &surface->source_region_id); + &surface->source_region_id, + CAIRO_ANALYSIS_SOURCE_FILL); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -703,7 +745,8 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents, - &surface->source_region_id); + &surface->source_region_id, + CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -787,7 +830,8 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, _analyze_recording_surface_pattern (surface, source, &rec_extents, - &surface->source_region_id); + &surface->source_region_id, + CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -839,6 +883,25 @@ _cairo_analysis_surface_supports_color_glyph (void *abstract_sur return TRUE; } +static cairo_int_status_t +_cairo_analysis_surface_command_id (void *abstract_surface, + unsigned int recording_id, + unsigned int command_id) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + + backend_status = CAIRO_INT_STATUS_SUCCESS; + if (surface->target->backend->command_id != NULL) { + backend_status = + surface->target->backend->command_id (surface->target, + recording_id, + command_id); + } + + return backend_status; +} + static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, @@ -874,7 +937,9 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { _cairo_analysis_surface_show_text_glyphs, NULL, /* get_supported_mime_types */ _cairo_analysis_surface_tag, - _cairo_analysis_surface_supports_color_glyph + _cairo_analysis_surface_supports_color_glyph, + NULL, /* analyze_recording_surface */ + _cairo_analysis_surface_command_id, }; cairo_surface_t * @@ -1135,7 +1200,12 @@ static const cairo_surface_backend_t cairo_null_surface_backend = { NULL, /* fill_stroke */ _show_glyphs_return_success, /* show_glyphs */ NULL, /* has_show_text_glyphs */ - NULL /* show_text_glyphs */ + NULL, /* show_text_glyphs */ + NULL, /* get_supported_mime_types */ + NULL, /* tag */ + NULL, /* supports_color_glyph */ + NULL, /* analyze_recording_surface */ + NULL, /* command_id*/ }; cairo_surface_t * diff --git a/src/cairo-array-private.h b/src/cairo-array-private.h index 8e779d41a..ff5b51f50 100644 --- a/src/cairo-array-private.h +++ b/src/cairo-array-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California @@ -88,6 +89,12 @@ _cairo_array_size (const cairo_array_t *array); cairo_private void _cairo_array_sort (const cairo_array_t *array, int (*compar)(const void *, const void *)); +cairo_private cairo_bool_t +_cairo_array_pop_element (cairo_array_t *array, void *dst); + +cairo_private void * +_cairo_array_last_element (cairo_array_t *array); + CAIRO_END_DECLS #endif /* CAIRO_ARRAY_PRIVATE_H */ diff --git a/src/cairo-array.c b/src/cairo-array.c index 1c91b7c73..064b63f29 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -542,3 +542,39 @@ _cairo_array_sort (const cairo_array_t *array, int (*compar)(const void *, const { qsort (array->elements, array->num_elements, array->element_size, compar); } + +/** + * _cairo_array_pop_element: + * @array: a #cairo_array_t + * Returns: A TRUE if element successfully popped, FALSE if the array is empty. + * + * Copy the last element out of the array from index @index into the + * location pointed to by @dst and remove the element from the array. + **/ +cairo_bool_t +_cairo_array_pop_element (cairo_array_t *array, void *dst) +{ + if (array->num_elements > 0) { + _cairo_array_copy_element (array, array->num_elements - 1, dst); + array->num_elements--; + return TRUE; + } + + return FALSE; +} + +/** + * _cairo_array_top_element: + * @array: a #cairo_array_t + * Returns: A pointer to the last of object or NULL if array is empty. + * + * Get the pointer to the last element of of the array. + **/ +void * +_cairo_array_last_element (cairo_array_t *array) +{ + if (array->num_elements > 0) + return _cairo_array_index (array, array->num_elements - 1); + + return NULL; +} diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c index aafdaeded..b8c1c88f5 100644 --- a/src/cairo-image-source.c +++ b/src/cairo-image-source.c @@ -1225,7 +1225,7 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, /* Handle recursion by returning future reads from the current image */ proxy = attach_proxy (source, clone); - status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL, FALSE); + status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL); if (clone->foreground_used) dst->base.foreground_used = clone->foreground_used; detach_proxy (source, proxy); diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index e079a9a28..ac24745e3 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -403,6 +403,7 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_int_status_t status; cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; unsigned int regions_id = 0; + cairo_bool_t replay_all; if (unlikely (surface->target->status)) return surface->target->status; @@ -416,13 +417,20 @@ _paint_page (cairo_paginated_surface_t *surface) if (unlikely (status)) goto FAIL; + replay_all = FALSE; + if (surface->target->backend->analyze_recording_surface && + _cairo_recording_surface_has_tags (surface->recording_surface)) + { + replay_all = TRUE; + } + status = _cairo_recording_surface_region_array_attach (surface->recording_surface, ®ions_id); if (status) goto FAIL; status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, regions_id, - NULL, analysis, FALSE); + NULL, analysis, FALSE, replay_all); if (status) goto FAIL; @@ -467,12 +475,12 @@ _paint_page (cairo_paginated_surface_t *surface) has_finegrained_fallback = FALSE; } - if (has_supported) { - status = surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_RENDER); - if (unlikely (status)) - goto FAIL; + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_RENDER); + if (unlikely (status)) + goto FAIL; + if (has_supported) { status = _cairo_recording_surface_replay_region (surface->recording_surface, regions_id, NULL, diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c index cb80151d1..2f97b081c 100644 --- a/src/cairo-pdf-interchange.c +++ b/src/cairo-pdf-interchange.c @@ -52,6 +52,8 @@ #include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-output-stream-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-snapshot-inline.h" #include <time.h> @@ -62,6 +64,275 @@ #define gmtime_r(T, BUF) (*(BUF) = *gmtime (T)) #endif +/* #define DEBUG_PDF_INTERCHANGE 1 */ + +#if DEBUG_PDF_INTERCHANGE +static void +print_tree (cairo_pdf_surface_t *surface, cairo_pdf_struct_tree_node_t *node); + +static void +print_command (cairo_pdf_command_t *command, int indent); + +static void +print_command_list(cairo_pdf_command_list_t *command_list); +#endif + +static void +_cairo_pdf_command_init_key (cairo_pdf_command_entry_t *key) +{ + key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE, (uintptr_t)key->recording_id); + key->base.hash = _cairo_hash_uintptr (key->base.hash, (uintptr_t)key->command_id); +} + +static cairo_bool_t +_cairo_pdf_command_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_command_entry_t *a = key_a; + const cairo_pdf_command_entry_t *b = key_b; + + return a->recording_id == b->recording_id && a->command_id == b->command_id; +} + +static void +_cairo_pdf_command_pluck (void *entry, void *closure) +{ + cairo_pdf_command_entry_t *dest = entry; + cairo_hash_table_t *table = closure; + + _cairo_hash_table_remove (table, &dest->base); + free (dest); +} + +static cairo_pdf_struct_tree_node_t * +lookup_node_for_command (cairo_pdf_surface_t *surface, + unsigned int recording_id, + unsigned int command_id) +{ + cairo_pdf_command_entry_t entry_key; + cairo_pdf_command_entry_t *entry; + cairo_pdf_interchange_t *ic = &surface->interchange; + + entry_key.recording_id = recording_id; + entry_key.command_id = command_id; + _cairo_pdf_command_init_key (&entry_key); + entry = _cairo_hash_table_lookup (ic->command_to_node_map, &entry_key.base); + assert (entry != NULL); + return entry->node; +} + +static cairo_int_status_t +command_list_add (cairo_pdf_surface_t *surface, + unsigned int command_id, + cairo_pdf_operation_t flags) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_t command; + cairo_int_status_t status; + + unsigned num_elements = _cairo_array_num_elements (&ic->current_commands->commands); + if (command_id > num_elements) { + void *elements; + unsigned additional_elements = command_id - num_elements; + status = _cairo_array_allocate (&ic->current_commands->commands, additional_elements, &elements); + if (unlikely (status)) + return status; + memset (elements, 0, additional_elements * sizeof(cairo_pdf_command_t)); + } + + command.group = NULL; + command.node = NULL; + command.command_id = command_id; + command.mcid_index = 0; + command.flags = flags; + return _cairo_array_append (&ic->current_commands->commands, &command); +} + +static cairo_int_status_t +command_list_push_group (cairo_pdf_surface_t *surface, + unsigned int command_id, + cairo_surface_t *recording_surface, + unsigned int region_id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_t *command; + cairo_pdf_command_list_t *group; + cairo_pdf_recording_surface_commands_t recording_commands; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + group = _cairo_malloc (sizeof(cairo_pdf_command_list_t)); + _cairo_array_init (&group->commands, sizeof(cairo_pdf_command_t)); + group->parent = ic->current_commands; + + command_list_add (surface, command_id, PDF_GROUP); + command = _cairo_array_index (&ic->current_commands->commands, command_id); + command->group = group; + ic->current_commands = group; + + recording_commands.recording_surface = recording_surface; + recording_commands.command_list = group; + recording_commands.region_id = region_id; + status = _cairo_array_append (&ic->recording_surface_commands, &recording_commands); + + return status; +} + +static void +command_list_pop_group (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + ic->current_commands = ic->current_commands->parent; +} + +static cairo_bool_t +command_list_is_group (cairo_pdf_surface_t *surface, + unsigned int command_id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_t *command; + unsigned num_elements = _cairo_array_num_elements (&ic->current_commands->commands); + + if (command_id >= num_elements) + return FALSE; + + command = _cairo_array_index (&ic->current_commands->commands, command_id); + return command->flags == PDF_GROUP; +} + + +/* Is there any content between current command and next + * begin/end/group? */ +static cairo_bool_t +command_list_has_content (cairo_pdf_surface_t *surface, + unsigned int command_id, + unsigned int *content_command_id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_t *command; + unsigned i; + unsigned num_elements = _cairo_array_num_elements (&ic->current_commands->commands); + + for (i = command_id + 1; i < num_elements; i++) { + command = _cairo_array_index (&ic->current_commands->commands, i); + switch (command->flags) { + case PDF_CONTENT: + if (content_command_id) + *content_command_id = i; + return TRUE; + break; + case PDF_BEGIN: + case PDF_END: + case PDF_GROUP: + return FALSE; + case PDF_NONE: + break; + } + } + return FALSE; +} + +static void +command_list_set_mcid (cairo_pdf_surface_t *surface, + unsigned int command_id, + cairo_pdf_struct_tree_node_t *node, + int mcid_index) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_t *command; + + command = _cairo_array_index (&ic->current_commands->commands, command_id); + command->node = node; + command->mcid_index = mcid_index; +} + +static void +command_list_set_current_recording_commands (cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface, + unsigned int region_id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + unsigned i; + cairo_pdf_recording_surface_commands_t *commands; + unsigned num_elements = _cairo_array_num_elements (&ic->recording_surface_commands); + + for (i = 0; i < num_elements; i++) { + commands = _cairo_array_index (&ic->recording_surface_commands, i); + if (commands->region_id == region_id) { + ic->current_commands = commands->command_list; + return; + } + } + ASSERT_NOT_REACHED; /* recording_surface not found */ +} + +static void +update_mcid_order (cairo_pdf_surface_t *surface, + cairo_pdf_command_list_t *command_list) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_t *command; + cairo_pdf_page_mcid_t *mcid_elem; + unsigned i; + unsigned num_elements = _cairo_array_num_elements (&command_list->commands); + + for (i = 0; i < num_elements; i++) { + command = _cairo_array_index (&command_list->commands, i); + if (command->node) { + mcid_elem = _cairo_array_index (&command->node->mcid, command->mcid_index); + mcid_elem->order = ic->mcid_order++; + } + + if (command->group) + update_mcid_order (surface, command->group); + } +} + +static void +_cairo_pdf_content_tag_init_key (cairo_pdf_content_tag_t *key) +{ + key->base.hash = _cairo_hash_string (key->node->attributes.content.id); +} + +static cairo_bool_t +_cairo_pdf_content_tag_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_content_tag_t *a = key_a; + const cairo_pdf_content_tag_t *b = key_b; + + return strcmp (a->node->attributes.content.id, b->node->attributes.content.id) == 0; +} + +static void +_cairo_pdf_content_tag_pluck (void *entry, void *closure) +{ + cairo_pdf_content_tag_t *content_tag = entry; + cairo_hash_table_t *table = closure; + + _cairo_hash_table_remove (table, &content_tag->base); + free (content_tag); +} + +static cairo_status_t +lookup_content_node_for_ref_node (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *ref_node, + cairo_pdf_struct_tree_node_t **node) +{ + cairo_pdf_content_tag_t entry_key; + cairo_pdf_content_tag_t *entry; + cairo_pdf_interchange_t *ic = &surface->interchange; + + entry_key.node = ref_node; + _cairo_pdf_content_tag_init_key (&entry_key); + entry = _cairo_hash_table_lookup (ic->content_tag_map, &entry_key.base); + if (!entry) { + return _cairo_tag_error ("CONTENT_REF ref='%s' not found", + ref_node->attributes.content_ref.ref); + } + + *node = entry->node; + return CAIRO_STATUS_SUCCESS; +} + static void write_rect_to_pdf_quad_points (cairo_output_stream_t *stream, const cairo_rectangle_t *rect, @@ -96,9 +367,11 @@ static cairo_int_status_t add_tree_node (cairo_pdf_surface_t *surface, cairo_pdf_struct_tree_node_t *parent, const char *name, + const char *attributes, cairo_pdf_struct_tree_node_t **new_node) { cairo_pdf_struct_tree_node_t *node; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; node = _cairo_malloc (sizeof(cairo_pdf_struct_tree_node_t)); if (unlikely (node == NULL)) @@ -111,21 +384,26 @@ add_tree_node (cairo_pdf_surface_t *surface, node->parent = parent; cairo_list_init (&node->children); - _cairo_array_init (&node->mcid, sizeof(struct page_mcid)); - node->annot_res.id = 0; + _cairo_array_init (&node->mcid, sizeof (cairo_pdf_page_mcid_t)); + node->annot = NULL; node->extents.valid = FALSE; - cairo_list_init (&node->extents.link); cairo_list_add_tail (&node->link, &parent->children); - *new_node = node; - return CAIRO_STATUS_SUCCESS; -} + if (strcmp (node->name, CAIRO_TAG_CONTENT) == 0) { + node->type = PDF_NODE_CONTENT; + status = _cairo_tag_parse_content_attributes (attributes, &node->attributes.content); + } else if (strcmp (node->name, CAIRO_TAG_CONTENT_REF) == 0) { + node->type = PDF_NODE_CONTENT_REF; + status = _cairo_tag_parse_content_ref_attributes (attributes, &node->attributes.content_ref); + } else if (strcmp (node->name, "Artifact") == 0) { + node->type = PDF_NODE_ARTIFACT; + } else { + node->type = PDF_NODE_STRUCT; + } -static cairo_bool_t -is_leaf_node (cairo_pdf_struct_tree_node_t *node) -{ - return node->parent && cairo_list_is_empty (&node->children) ; + *new_node = node; + return status; } static void @@ -137,23 +415,30 @@ free_node (cairo_pdf_struct_tree_node_t *node) return; cairo_list_foreach_entry_safe (child, next, cairo_pdf_struct_tree_node_t, - &node->children, link) + &node->children, + link) { cairo_list_del (&child->link); free_node (child); } free (node->name); _cairo_array_fini (&node->mcid); + if (node->type == PDF_NODE_CONTENT) + _cairo_tag_free_content_attributes (&node->attributes.content); + + if (node->type == PDF_NODE_CONTENT_REF) + _cairo_tag_free_content_ref_attributes (&node->attributes.content_ref); + free (node); } static cairo_status_t add_mcid_to_node (cairo_pdf_surface_t *surface, cairo_pdf_struct_tree_node_t *node, - int page, + unsigned int command_id, int *mcid) { - struct page_mcid mcid_elem; + cairo_pdf_page_mcid_t mcid_elem; cairo_int_status_t status; cairo_pdf_interchange_t *ic = &surface->interchange; @@ -161,12 +446,33 @@ add_mcid_to_node (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - mcid_elem.page = page; + mcid_elem.order = -1; + mcid_elem.page = _cairo_array_num_elements (&surface->pages); + mcid_elem.xobject_res = ic->current_recording_surface_res; mcid_elem.mcid = _cairo_array_num_elements (&ic->mcid_to_tree) - 1; + mcid_elem.child_node = NULL; + command_list_set_mcid (surface, command_id, node, _cairo_array_num_elements (&node->mcid)); *mcid = mcid_elem.mcid; return _cairo_array_append (&node->mcid, &mcid_elem); } +static cairo_status_t +add_child_to_mcid_array (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + unsigned int command_id, + cairo_pdf_struct_tree_node_t *child) +{ + cairo_pdf_page_mcid_t mcid_elem; + + mcid_elem.order = -1; + mcid_elem.page = 0; + mcid_elem.xobject_res.id = 0; + mcid_elem.mcid = 0; + mcid_elem.child_node = child; + command_list_set_mcid (surface, command_id, node, _cairo_array_num_elements (&node->mcid)); + return _cairo_array_append (&node->mcid, &mcid_elem); +} + static cairo_int_status_t add_annotation (cairo_pdf_surface_t *surface, cairo_pdf_struct_tree_node_t *node, @@ -177,7 +483,7 @@ add_annotation (cairo_pdf_surface_t *surface, cairo_pdf_interchange_t *ic = &surface->interchange; cairo_pdf_annotation_t *annot; - annot = malloc (sizeof(cairo_pdf_annotation_t)); + annot = _cairo_malloc (sizeof (cairo_pdf_annotation_t)); if (unlikely (annot == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -187,8 +493,16 @@ add_annotation (cairo_pdf_surface_t *surface, return status; } + if (annot->link_attrs.link_page == 0) + annot->link_attrs.link_page = _cairo_array_num_elements (&surface->pages); + annot->node = node; + annot->res = _cairo_pdf_surface_new_object (surface); + if (annot->res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + node->annot = annot; status = _cairo_array_append (&ic->annots, &annot); return status; @@ -197,10 +511,7 @@ add_annotation (cairo_pdf_surface_t *surface, static void free_annotation (cairo_pdf_annotation_t *annot) { - _cairo_array_fini (&annot->link_attrs.rects); - free (annot->link_attrs.dest); - free (annot->link_attrs.uri); - free (annot->link_attrs.file); + _cairo_tag_free_link_attributes (&annot->link_attrs); free (annot); } @@ -220,15 +531,72 @@ cairo_pdf_interchange_clear_annotations (cairo_pdf_surface_t *surface) _cairo_array_truncate (&ic->annots, 0); } +static void +cairo_pdf_interchange_write_node_mcid (cairo_pdf_surface_t *surface, + cairo_pdf_page_mcid_t *mcid_elem, + int page) +{ + cairo_pdf_page_info_t *page_info; + + page_info = _cairo_array_index (&surface->pages, mcid_elem->page - 1); + if (mcid_elem->page == page && mcid_elem->xobject_res.id == 0) { + _cairo_output_stream_printf (surface->object_stream.stream, "%d ", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, + "\n << /Type /MCR "); + if (mcid_elem->page != page) { + _cairo_output_stream_printf (surface->object_stream.stream, + "/Pg %d 0 R ", + page_info->page_res.id); + } + if (mcid_elem->xobject_res.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + "/Stm %d 0 R ", + mcid_elem->xobject_res.id); + } + _cairo_output_stream_printf (surface->object_stream.stream, + "/MCID %d >> ", + mcid_elem->mcid); + } +} + +static int +_mcid_order_compare (const void *a, + const void *b) +{ + const cairo_pdf_page_mcid_t *mcid_a = a; + const cairo_pdf_page_mcid_t *mcid_b = b; + + if (mcid_a->order < mcid_b->order) + return -1; + else if (mcid_a->order > mcid_b->order) + return 1; + else + return 0; +} + static cairo_int_status_t cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface, - cairo_pdf_struct_tree_node_t *node) + cairo_pdf_struct_tree_node_t *node, + int depth) { - 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_page_mcid_t *mcid_elem, *child_mcid_elem; + unsigned i, j, num_mcid; + int first_page = 0; + cairo_pdf_page_info_t *page_info; cairo_int_status_t status; + cairo_bool_t has_children = FALSE; + + /* The Root node is written in cairo_pdf_interchange_write_struct_tree(). */ + if (!node->parent) + return CAIRO_STATUS_SUCCESS; + + if (node->type == PDF_NODE_CONTENT || + node->type == PDF_NODE_CONTENT_REF || + node->type == PDF_NODE_ARTIFACT) + { + return CAIRO_STATUS_SUCCESS; + } status = _cairo_pdf_surface_object_begin (surface, node->res); if (unlikely (status)) @@ -241,57 +609,98 @@ cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface node->name, node->parent->res.id); - if (! cairo_list_is_empty (&node->children)) { - if (cairo_list_is_singular (&node->children) && node->annot_res.id == 0) { - child = cairo_list_first_entry (&node->children, cairo_pdf_struct_tree_node_t, link); - _cairo_output_stream_printf (surface->object_stream.stream, " /K %d 0 R\n", child->res.id); - } else { - _cairo_output_stream_printf (surface->object_stream.stream, " /K [ "); - if (node->annot_res.id != 0) { + /* Write /K entry (children of this StructElem) */ + num_mcid = _cairo_array_num_elements (&node->mcid); + if (num_mcid > 0 ) { + _cairo_array_sort (&node->mcid, _mcid_order_compare); + /* Find the first MCID element and use the page number to set /Pg */ + for (i = 0; i < num_mcid; i++) { + mcid_elem = _cairo_array_index (&node->mcid, i); + assert (mcid_elem->order != -1); + if (mcid_elem->child_node) { + if (mcid_elem->child_node->type == PDF_NODE_CONTENT_REF) { + cairo_pdf_struct_tree_node_t *content_node; + status = lookup_content_node_for_ref_node (surface, mcid_elem->child_node, &content_node); + if (status) + return status; + + /* CONTENT_REF will not have child nodes */ + if (_cairo_array_num_elements (&content_node->mcid) > 0) { + child_mcid_elem = _cairo_array_index (&content_node->mcid, 0); + first_page = child_mcid_elem->page; + page_info = _cairo_array_index (&surface->pages, first_page - 1); + _cairo_output_stream_printf (surface->object_stream.stream, + " /Pg %d 0 R\n", + page_info->page_res.id); + has_children = TRUE; + break; + } + } else { + has_children = TRUE; + } + } else { + first_page = mcid_elem->page; + page_info = _cairo_array_index (&surface->pages, first_page - 1); _cairo_output_stream_printf (surface->object_stream.stream, - "<< /Type /OBJR /Obj %d 0 R >> ", - node->annot_res.id); - } - cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, - &node->children, link) - { - _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", child->res.id); + " /Pg %d 0 R\n", + page_info->page_res.id); + has_children = TRUE; + break; } - _cairo_output_stream_printf (surface->object_stream.stream, "]\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->object_stream.stream, " /Pg %d 0 R\n", page_res->id); - - if (num_mcid == 1 && node->annot_res.id == 0) { - _cairo_output_stream_printf (surface->object_stream.stream, " /K %d\n", mcid_elem->mcid); - } else { - _cairo_output_stream_printf (surface->object_stream.stream, " /K [ "); - if (node->annot_res.id != 0) { - _cairo_output_stream_printf (surface->object_stream.stream, - "<< /Type /OBJR /Obj %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->object_stream.stream, "%d ", mcid_elem->mcid); + + if (has_children || node->annot) { + _cairo_output_stream_printf (surface->object_stream.stream, " /K "); + + if (num_mcid > 1 || node->annot) + _cairo_output_stream_printf (surface->object_stream.stream, "[ "); + + for (i = 0; i < num_mcid; i++) { + if (node->annot) { + if (node->annot->link_attrs.link_page != first_page) { + page_info = _cairo_array_index (&surface->pages, node->annot->link_attrs.link_page - 1); + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /OBJR /Pg %d 0 R /Obj %d 0 R >> ", + page_info->page_res.id, + node->annot->res.id); } else { _cairo_output_stream_printf (surface->object_stream.stream, - "\n << /Type /MCR /Pg %d 0 R /MCID %d >> ", - page_res->id, - mcid_elem->mcid); + "<< /Type /OBJR /Obj %d 0 R >> ", + node->annot->res.id); + } + } + mcid_elem = _cairo_array_index (&node->mcid, i); + if (mcid_elem->child_node) { + if (mcid_elem->child_node->type == PDF_NODE_CONTENT_REF) { + cairo_pdf_struct_tree_node_t *content_node; + status = lookup_content_node_for_ref_node (surface, mcid_elem->child_node, &content_node); + if (status) + return status; + + assert (content_node->type == PDF_NODE_CONTENT); + + /* CONTENT_REF will not have child nodes */ + for (j = 0; j < _cairo_array_num_elements (&content_node->mcid); j++) { + child_mcid_elem = _cairo_array_index (&content_node->mcid, j); + cairo_pdf_interchange_write_node_mcid (surface, child_mcid_elem, first_page); + } + } else if (mcid_elem->child_node->type != PDF_NODE_CONTENT) { + _cairo_output_stream_printf (surface->object_stream.stream, + " %d 0 R ", + mcid_elem->child_node->res.id); } + } else { + cairo_pdf_interchange_write_node_mcid (surface, mcid_elem, first_page); } - _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); } + + if (num_mcid > 1 || node->annot) + _cairo_output_stream_printf (surface->object_stream.stream, "]"); } + + _cairo_output_stream_printf (surface->object_stream.stream, "\n"); } + _cairo_output_stream_printf (surface->object_stream.stream, ">>\n"); @@ -324,7 +733,7 @@ _named_dest_pluck (void *entry, void *closure) cairo_hash_table_t *table = closure; _cairo_hash_table_remove (table, &dest->base); - free (dest->attrs.name); + _cairo_tag_free_dest_attributes (&dest->attrs); free (dest); } @@ -335,21 +744,20 @@ cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface, double x, double y) { - cairo_pdf_resource_t res; - double height; + cairo_pdf_page_info_t *page_info; + + page_info = _cairo_array_index (&surface->pages, page - 1); - _cairo_array_copy_element (&surface->page_heights, page - 1, &height); - _cairo_array_copy_element (&surface->pages, page - 1, &res); if (has_pos) { _cairo_output_stream_printf (surface->object_stream.stream, "[%d 0 R /XYZ %f %f 0]\n", - res.id, + page_info->page_res.id, x, - height - y); + page_info->height - y); } else { _cairo_output_stream_printf (surface->object_stream.stream, "[%d 0 R /XYZ null null 0]\n", - res.id); + page_info->page_res.id); } return CAIRO_STATUS_SUCCESS; @@ -601,7 +1009,8 @@ cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface, static cairo_int_status_t cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface, - cairo_pdf_annotation_t *annot) + cairo_pdf_annotation_t *annot, + cairo_bool_t struct_parents) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_pdf_interchange_t *ic = &surface->interchange; @@ -621,23 +1030,19 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface, sp = _cairo_array_num_elements (&ic->parent_tree) - 1; - node->annot_res = _cairo_pdf_surface_new_object (surface); - if (node->annot_res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_array_append (&surface->page_annots, &node->annot_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_object_begin (surface, node->annot_res); + status = _cairo_pdf_surface_object_begin (surface, annot->res); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->object_stream.stream, "<< /Type /Annot\n" - " /Subtype /Link\n" - " /StructParent %d\n", - sp); + " /Subtype /Link\n"); + + if (struct_parents) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /StructParent %d\n", + sp); + } height = surface->height; if (num_rects > 0) { @@ -676,7 +1081,7 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_printf (surface->object_stream.stream, - " /BS << /W 0 >>" + " /BS << /W 0 >>\n" ">>\n"); _cairo_pdf_surface_object_end (surface); @@ -689,25 +1094,27 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface, 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)) + int depth, + cairo_int_status_t (*func) (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + int depth)) { cairo_int_status_t status; cairo_pdf_struct_tree_node_t *child; - if (node->parent) { - status = func (surface, node); - if (unlikely (status)) - return status; - } + status = func (surface, node, depth); + if (unlikely (status)) + return status; + depth++; cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, &node->children, link) { - status = cairo_pdf_interchange_walk_struct_tree (surface, child, func); + status = cairo_pdf_interchange_walk_struct_tree (surface, child, depth, func); if (unlikely (status)) return status; } + depth--; return CAIRO_STATUS_SUCCESS; } @@ -722,15 +1129,12 @@ cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface) if (cairo_list_is_empty (&ic->struct_root->children)) return CAIRO_STATUS_SUCCESS; - surface->struct_tree_root = _cairo_pdf_surface_new_object (surface); - if (surface->struct_tree_root.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - 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); + status = cairo_pdf_interchange_walk_struct_tree (surface, + ic->struct_root, + 0, + cairo_pdf_interchange_write_node_object); + if (unlikely (status)) + return status; status = _cairo_pdf_surface_object_begin (surface, surface->struct_tree_root); if (unlikely (status)) @@ -750,6 +1154,9 @@ cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface) cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, &ic->struct_root->children, link) { + if (child->type == PDF_NODE_CONTENT || child->type == PDF_NODE_ARTIFACT) + continue; + _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", child->res.id); } _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); @@ -763,18 +1170,31 @@ cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface) } static cairo_int_status_t -cairo_pdf_interchange_write_page_annots (cairo_pdf_surface_t *surface) +cairo_pdf_interchange_write_annots (cairo_pdf_surface_t *surface, + cairo_bool_t struct_parents) { cairo_pdf_interchange_t *ic = &surface->interchange; - int num_elems, i; + int num_elems, i, page_num; + cairo_pdf_page_info_t *page_info; + cairo_pdf_annotation_t *annot; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; num_elems = _cairo_array_num_elements (&ic->annots); for (i = 0; i < num_elems; i++) { - cairo_pdf_annotation_t * annot; - _cairo_array_copy_element (&ic->annots, i, &annot); - status = cairo_pdf_interchange_write_annot (surface, annot); + page_num = annot->link_attrs.link_page; + if (page_num > (int)_cairo_array_num_elements (&surface->pages)) { + return _cairo_tag_error ("Link attribute: \"link_page=%d\" page exceeds page count (%d)", + page_num, + _cairo_array_num_elements (&surface->pages)); + } + + page_info = _cairo_array_index (&surface->pages, page_num - 1); + status = _cairo_array_append (&page_info->annots, &annot->res); + if (status) + return status; + + status = cairo_pdf_interchange_write_annot (surface, annot, struct_parents); if (unlikely (status)) return status; } @@ -783,43 +1203,72 @@ cairo_pdf_interchange_write_page_annots (cairo_pdf_surface_t *surface) } static cairo_int_status_t -cairo_pdf_interchange_write_page_parent_elems (cairo_pdf_surface_t *surface) +cairo_pdf_interchange_write_content_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); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_pdf_surface_object_begin (surface, res); - if (unlikely (status)) - return status; + status = _cairo_pdf_surface_object_begin (surface, ic->content_parent_res); + if (unlikely (status)) + return status; - _cairo_output_stream_printf (surface->object_stream.stream, + _cairo_output_stream_printf (surface->object_stream.stream, "[\n"); - for (i = 0; i < num_elems; i++) { - _cairo_array_copy_element (&ic->mcid_to_tree, i, &node); - _cairo_output_stream_printf (surface->object_stream.stream, " %d 0 R\n", node->res.id); - } - _cairo_output_stream_printf (surface->object_stream.stream, - "]\n"); - _cairo_pdf_surface_object_end (surface); - - status = _cairo_array_append (&ic->parent_tree, &res); - surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1; + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&ic->mcid_to_tree, i, &node); + _cairo_output_stream_printf (surface->object_stream.stream, " %d 0 R\n", node->res.id); } + _cairo_output_stream_printf (surface->object_stream.stream, + "]\n"); + _cairo_pdf_surface_object_end (surface); return status; } static cairo_int_status_t +cairo_pdf_interchange_apply_extents_from_content_ref (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + int depth) +{ + cairo_int_status_t status; + + if (node->type != PDF_NODE_CONTENT_REF) + return CAIRO_STATUS_SUCCESS; + + cairo_pdf_struct_tree_node_t *content_node; + status = lookup_content_node_for_ref_node (surface, node, &content_node); + if (status) + return status; + + /* Merge extents with all parent nodes */ + node = node->parent; + while (node) { + if (node->extents.valid) { + _cairo_rectangle_union (&node->extents.extents, &content_node->extents.extents); + } else { + node->extents = content_node->extents; + } + node = node->parent; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_update_extents (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + return cairo_pdf_interchange_walk_struct_tree (surface, + ic->struct_root, + 0, + cairo_pdf_interchange_apply_extents_from_content_ref); +} + +static cairo_int_status_t cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface) { int num_elems, i; @@ -1178,6 +1627,7 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface) int i; cairo_pdf_interchange_t *ic = &surface->interchange; cairo_int_status_t status; + cairo_pdf_page_info_t *page_info; if (ic->num_dests == 0) { ic->dests_res.id = 0; @@ -1205,10 +1655,8 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface) "<< /Names [\n"); 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; - double height; if (dest->attrs.internal) continue; @@ -1224,14 +1672,13 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface) if (dest->attrs.y_valid) y = dest->attrs.y; - _cairo_array_copy_element (&surface->pages, dest->page - 1, &page_res); - _cairo_array_copy_element (&surface->page_heights, dest->page - 1, &height); + page_info = _cairo_array_index (&surface->pages, dest->page - 1); _cairo_output_stream_printf (surface->object_stream.stream, " (%s) [%d 0 R /XYZ %f %f 0]\n", dest->attrs.name, - page_res.id, + page_info->page_res.id, x, - height - y); + page_info->height - y); } _cairo_output_stream_printf (surface->object_stream.stream, " ]\n" @@ -1345,32 +1792,79 @@ _cairo_pdf_interchange_begin_structure_tag (cairo_pdf_surface_t *surface, const char *name, const char *attributes) { - int page_num, mcid; + int mcid; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_command_entry_t *command_entry; + cairo_pdf_struct_tree_node_t *parent_node; + unsigned int content_command_id; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - status = add_tree_node (surface, ic->current_node, name, &ic->current_node); + ic->content_emitted = FALSE; + status = add_tree_node (surface, ic->current_analyze_node, name, attributes, &ic->current_analyze_node); + if (unlikely (status)) + return status; + + status = command_list_add (surface, ic->command_id, PDF_BEGIN); if (unlikely (status)) return status; - _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, ic->current_node); + /* Add to command_id to node map. */ + command_entry = _cairo_malloc (sizeof(cairo_pdf_command_entry_t)); + command_entry->recording_id = ic->recording_id; + command_entry->command_id = ic->command_id; + command_entry->node = ic->current_analyze_node; + _cairo_pdf_command_init_key (command_entry); + status = _cairo_hash_table_insert (ic->command_to_node_map, &command_entry->base); + if (unlikely(status)) + return status; if (tag_type & TAG_TYPE_LINK) { - status = add_annotation (surface, ic->current_node, name, attributes); + status = add_annotation (surface, ic->current_analyze_node, name, attributes); if (unlikely (status)) return status; + } - cairo_list_add_tail (&ic->current_node->extents.link, &ic->extents_list); + if (ic->current_analyze_node->type == PDF_NODE_CONTENT) { + cairo_pdf_content_tag_t *content = _cairo_malloc (sizeof(cairo_pdf_content_tag_t)); + content->node = ic->current_analyze_node; + _cairo_pdf_content_tag_init_key (content); + status = _cairo_hash_table_insert (ic->content_tag_map, &content->base); + if (unlikely (status)) + return status; } + ic->content_emitted = FALSE; + } 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); + if (ic->marked_content_open) { + status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + ic->marked_content_open = FALSE; + if (unlikely (status)) + return status; + } + + ic->current_render_node = lookup_node_for_command (surface, ic->recording_id, ic->command_id); + if (ic->current_render_node->type == PDF_NODE_ARTIFACT) { + if (command_list_has_content (surface, ic->command_id, NULL)) { + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, name, -1); + ic->marked_content_open = TRUE; + } + } else if (ic->current_render_node->type == PDF_NODE_CONTENT_REF) { + parent_node = ic->current_render_node->parent; + add_child_to_mcid_array (surface, parent_node, ic->command_id, ic->current_render_node); + } else { + parent_node = ic->current_render_node->parent; + add_child_to_mcid_array (surface, parent_node, ic->command_id, ic->current_render_node); + if (command_list_has_content (surface, ic->command_id, &content_command_id)) { + add_mcid_to_node (surface, ic->current_render_node, content_command_id, &mcid); + const char *tag_name = name; + if (ic->current_render_node->type == PDF_NODE_CONTENT) + tag_name = ic->current_render_node->attributes.content.tag_name; + + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, tag_name, mcid); + ic->marked_content_open = TRUE; + } } } @@ -1402,15 +1896,13 @@ _cairo_pdf_interchange_begin_dest_tag (cairo_pdf_surface_t *surface, 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)) - { + if (unlikely (status)) { free (dest->attrs.name); free (dest); 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++; } @@ -1425,22 +1917,21 @@ _cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface, cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_tag_type_t tag_type; cairo_pdf_interchange_t *ic = &surface->interchange; - void *ptr; + + if (ic->ignore_current_surface) + return CAIRO_STATUS_SUCCESS; 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) { + if (tag_type & (TAG_TYPE_STRUCTURE|TAG_TYPE_CONTENT|TAG_TYPE_CONTENT_REF|TAG_TYPE_ARTIFACT)) { status = _cairo_pdf_interchange_begin_structure_tag (surface, tag_type, name, attributes); if (unlikely (status)) return status; @@ -1452,11 +1943,6 @@ _cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface, 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; } @@ -1465,59 +1951,39 @@ _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; + int mcid; + unsigned int content_command_id; - 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->extents) { - cairo_list_del (&tag->link); - break; - } - } - } + assert (ic->current_analyze_node->parent != NULL); + status = command_list_add (surface, ic->command_id, PDF_END); + if (unlikely (status)) + return status; + + ic->content_emitted = FALSE; + ic->current_analyze_node = ic->current_analyze_node->parent; + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { - if (is_leaf_node (ic->current_node)) { + if (ic->marked_content_open) { status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + ic->marked_content_open = FALSE; 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; - } + ic->current_render_node = ic->current_render_node->parent; + if (ic->current_render_node->parent && + command_list_has_content (surface, ic->command_id, &content_command_id)) + { + add_mcid_to_node (surface, ic->current_render_node, content_command_id, &mcid); + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, + ic->current_render_node->name, mcid); + ic->marked_content_open = TRUE; } } - return CAIRO_STATUS_SUCCESS; + return status; } cairo_int_status_t @@ -1529,27 +1995,25 @@ _cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface, cairo_tag_type_t tag_type; cairo_tag_stack_elem_t *elem; - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + if (ic->ignore_current_surface) + return CAIRO_STATUS_SUCCESS; + + 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); + } 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) { + if (tag_type & (TAG_TYPE_STRUCTURE|TAG_TYPE_CONTENT|TAG_TYPE_CONTENT_REF|TAG_TYPE_ARTIFACT)) { 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); @@ -1557,52 +2021,320 @@ _cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface, } cairo_int_status_t +_cairo_pdf_interchange_command_id (cairo_pdf_surface_t *surface, + unsigned int recording_id, + unsigned int command_id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + int mcid; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + ic->recording_id = recording_id; + ic->command_id = command_id; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER && ic->current_render_node) { + /* TODO If the group does not have tags we don't need to close the current tag. */ + if (command_list_is_group (surface, command_id)) { + if (ic->marked_content_open) { + status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + ic->marked_content_open = FALSE; + } + if (command_list_has_content (surface, command_id, NULL)) { + ic->render_next_command_has_content = TRUE; + } + } else if (ic->render_next_command_has_content) { + add_mcid_to_node (surface, ic->current_render_node, ic->command_id, &mcid); + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, + ic->current_render_node->name, mcid); + ic->marked_content_open = TRUE; + ic->render_next_command_has_content = FALSE; + } + } + + return status; +} + +/* Check if this use of recording surface is or will need to be part of the the struct tree */ +cairo_bool_t +_cairo_pdf_interchange_struct_tree_requires_recording_surface ( + cairo_pdf_surface_t *surface, + const cairo_surface_pattern_t *recording_surface_pattern, + cairo_analysis_source_t source_type) +{ + cairo_surface_t *recording_surface = recording_surface_pattern->surface; + cairo_surface_t *free_me = NULL; + cairo_bool_t requires_recording = FALSE; + + if (recording_surface_pattern->base.extend != CAIRO_EXTEND_NONE) + return FALSE; + + if (_cairo_surface_is_snapshot (recording_surface)) + free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); + + if (_cairo_recording_surface_has_tags (recording_surface)) { + /* Check if tags are to be ignored in this source */ + switch (source_type) { + case CAIRO_ANALYSIS_SOURCE_PAINT: + case CAIRO_ANALYSIS_SOURCE_FILL: + requires_recording = TRUE; + break; + case CAIRO_ANALYSIS_SOURCE_MASK: /* TODO: allow SOURCE_MASK with solid MASK_MASK */ + case CAIRO_ANALYSIS_MASK_MASK: + case CAIRO_ANALYSIS_SOURCE_STROKE: + case CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS: + case CAIRO_ANALYSIS_SOURCE_NONE: + break; + } + } + + cairo_surface_destroy (free_me); + return requires_recording; +} + +/* Called at the start of a recording group during analyze. This will + * be called during the analysis of the drawing operation. */ +cairo_int_status_t +_cairo_pdf_interchange_recording_source_surface_begin ( + cairo_pdf_surface_t *surface, + const cairo_surface_pattern_t *recording_surface_pattern, + unsigned int region_id, + cairo_analysis_source_t source_type) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_recording_surface_stack_entry_t element; + cairo_int_status_t status; + + /* A new recording surface is being replayed */ + ic->ignore_current_surface = TRUE; + if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface, + recording_surface_pattern, + source_type)) + { + ic->ignore_current_surface = FALSE; + } + + element.ignore_surface = ic->ignore_current_surface; + element.current_node = ic->current_analyze_node; + ic->content_emitted = FALSE; + + /* Push to stack so that the current source identifiers can be + * restored after this recording surface has ended. */ + status = _cairo_array_append (&ic->recording_surface_stack, &element); + if (status) + return status; + + if (ic->ignore_current_surface) + return CAIRO_STATUS_SUCCESS; + + status = command_list_push_group (surface, ic->command_id, recording_surface_pattern->surface, region_id); + if (unlikely (status)) + return status; + + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; +} + +/* Called at the end of a recording group during analyze. */ +cairo_int_status_t +_cairo_pdf_interchange_recording_source_surface_end ( + cairo_pdf_surface_t *surface, + const cairo_surface_pattern_t *recording_surface_pattern, + unsigned int region_id, + cairo_analysis_source_t source_type) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_recording_surface_stack_entry_t element; + cairo_recording_surface_stack_entry_t *element_ptr; + + if (!ic->ignore_current_surface) + command_list_pop_group (surface); + + if (_cairo_array_pop_element (&ic->recording_surface_stack, &element)) { + element_ptr = _cairo_array_last_element (&ic->recording_surface_stack); + if (element_ptr) { + ic->ignore_current_surface = element_ptr->ignore_surface; + assert (ic->current_analyze_node == element_ptr->current_node); + } else { + /* Back at the page content. */ + ic->ignore_current_surface = FALSE; + } + ic->content_emitted = FALSE; + return CAIRO_STATUS_SUCCESS; + } + ASSERT_NOT_REACHED; /* _recording_source_surface_begin/end mismatch */ + + return CAIRO_STATUS_SUCCESS; +} + +/* Called at the start of a recording group during render. This will + * be called after the end of page content. */ +cairo_int_status_t +_cairo_pdf_interchange_emit_recording_surface_begin (cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface, + int region_id, + cairo_pdf_resource_t surface_resource, + int *struct_parents) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status; + + /* When + * _cairo_pdf_interchange_struct_tree_requires_recording_surface() + * is false, the region_id of the recording surface is set to 0. + */ + if (region_id == 0) { + ic->ignore_current_surface = TRUE; + return CAIRO_STATUS_SUCCESS; + } + + command_list_set_current_recording_commands (surface, recording_surface, region_id); + + ic->ignore_current_surface = FALSE; + _cairo_array_truncate (&ic->mcid_to_tree, 0); + ic->current_recording_surface_res = surface_resource; + + ic->content_parent_res = _cairo_pdf_surface_new_object (surface); + if (ic->content_parent_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&ic->parent_tree, &ic->content_parent_res); + if (unlikely (status)) + return status; + + *struct_parents = _cairo_array_num_elements (&ic->parent_tree) - 1; + + ic->render_next_command_has_content = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +/* Called at the end of a recording group during render. */ +cairo_int_status_t +_cairo_pdf_interchange_emit_recording_surface_end (cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (ic->ignore_current_surface) + return CAIRO_STATUS_SUCCESS; + + ic->current_recording_surface_res.id = 0; + return cairo_pdf_interchange_write_content_parent_elems (surface); +} + +static void _add_operation_extents_to_dest_tag (cairo_tag_stack_elem_t *elem, + void *closure) +{ + const cairo_rectangle_int_t *extents = (const cairo_rectangle_int_t *) closure; + cairo_pdf_named_dest_t *dest; + + if (_cairo_tag_get_type (elem->name) & TAG_TYPE_DEST) { + if (elem->data) { + dest = (cairo_pdf_named_dest_t *) elem->data; + if (dest->extents.valid) { + _cairo_rectangle_union (&dest->extents.extents, extents); + } else { + dest->extents.extents = *extents; + dest->extents.valid = TRUE; + } + } + } +} + +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; + /* Add extents to current node and all DEST tags on the stack */ 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); + if (ic->current_analyze_node) { + if (ic->current_analyze_node->extents.valid) { + _cairo_rectangle_union (&ic->current_analyze_node->extents.extents, extents); } else { - tag->extents = *extents; - tag->valid = TRUE; + ic->current_analyze_node->extents.extents = *extents; + ic->current_analyze_node->extents.valid = TRUE; } } + + _cairo_tag_stack_foreach (&ic->analysis_tag_stack, + _add_operation_extents_to_dest_tag, + (void*)extents); } return CAIRO_STATUS_SUCCESS; } cairo_int_status_t +_cairo_pdf_interchange_add_content (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (ic->ignore_current_surface) + return CAIRO_STATUS_SUCCESS; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = command_list_add (surface, ic->command_id, PDF_CONTENT); + if (unlikely (status)) + return status; + } + + return status; +} + +/* Called at the start of 1emiting the page content during analyze or render */ +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; + int mcid; + unsigned int content_command_id; + cairo_pdf_command_list_t *page_commands; 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; + status = _cairo_array_allocate (&ic->page_commands, 1, (void**)&page_commands); + if (unlikely (status)) + return status; + + _cairo_array_init (&page_commands->commands, sizeof(cairo_pdf_command_t)); + page_commands->parent = NULL; + ic->current_commands = page_commands; + ic->ignore_current_surface = FALSE; } 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); + ic->current_commands = _cairo_array_last_element (&ic->page_commands); + /* Each page has its own parent tree to map MCID to nodes. */ + _cairo_array_truncate (&ic->mcid_to_tree, 0); + ic->ignore_current_surface = FALSE; + ic->content_parent_res = _cairo_pdf_surface_new_object (surface); + if (ic->content_parent_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&ic->parent_tree, &ic->content_parent_res); + if (unlikely (status)) + return status; + + surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1; + + if (ic->next_page_render_node && ic->next_page_render_node->parent && + command_list_has_content (surface, -1, &content_command_id)) + { + add_mcid_to_node (surface, ic->next_page_render_node, content_command_id, &mcid); + const char *tag_name = ic->next_page_render_node->name; + if (ic->next_page_render_node->type == PDF_NODE_CONTENT) + tag_name = ic->next_page_render_node->attributes.content.tag_name; + + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, tag_name, mcid); + ic->marked_content_open = TRUE; } + ic->render_next_command_has_content = FALSE; } return status; } +/* Called at the end of emiting the page content during analyze or render */ cairo_int_status_t _cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface) { @@ -1610,9 +2342,12 @@ _cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface) 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)) + /* If a content tag is open across pages, the old page needs an EMC emitted. */ + if (ic->marked_content_open) { status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + ic->marked_content_open = FALSE; + } + ic->next_page_render_node = ic->current_render_node; } return status; @@ -1621,15 +2356,7 @@ _cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface) 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; - - cairo_pdf_interchange_clear_annotations (surface); - - return cairo_pdf_interchange_write_page_parent_elems (surface); + return cairo_pdf_interchange_write_content_parent_elems (surface); } cairo_int_status_t @@ -1638,15 +2365,41 @@ _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; cairo_tag_stack_structure_type_t tag_type; + cairo_bool_t write_struct_tree = FALSE; + + status = cairo_pdf_interchange_update_extents (surface); + if (unlikely (status)) + return status; tag_type = _cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack); if (tag_type == TAG_TREE_TYPE_TAGGED || tag_type == TAG_TREE_TYPE_STRUCTURE || - tag_type == TAG_TREE_TYPE_LINK_ONLY) { + tag_type == TAG_TREE_TYPE_LINK_ONLY) + { + write_struct_tree = TRUE; + } + + status = cairo_pdf_interchange_write_annots (surface, write_struct_tree); + if (unlikely (status)) + return status; + + if (write_struct_tree) { + surface->struct_tree_root = _cairo_pdf_surface_new_object (surface); + if (surface->struct_tree_root.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ic->struct_root->res = surface->struct_tree_root; status = cairo_pdf_interchange_write_parent_tree (surface); if (unlikely (status)) return status; + unsigned num_pages = _cairo_array_num_elements (&ic->page_commands); + for (unsigned i = 0; i < num_pages; i++) { + cairo_pdf_command_list_t *command_list; + command_list = _cairo_array_index (&ic->page_commands, i); + update_mcid_order (surface, command_list); + } + status = cairo_pdf_interchange_write_struct_tree (surface); if (unlikely (status)) return status; @@ -1724,16 +2477,27 @@ _cairo_pdf_interchange_init (cairo_pdf_surface_t *surface) _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); + ic->struct_root->res.id = 0; 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->struct_root->mcid, sizeof(cairo_pdf_page_mcid_t)); + + ic->current_analyze_node = ic->struct_root; + ic->current_render_node = NULL; + ic->next_page_render_node = ic->struct_root; + _cairo_array_init (&ic->recording_surface_stack, sizeof(cairo_recording_surface_stack_entry_t)); + ic->current_recording_surface_res.id = 0; + ic->command_to_node_map = _cairo_hash_table_create (_cairo_pdf_command_equal); + if (unlikely (ic->command_to_node_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ic->content_tag_map = _cairo_hash_table_create (_cairo_pdf_content_tag_equal); + if (unlikely (ic->content_tag_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + _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 *)); _cairo_array_init (&ic->annots, sizeof(cairo_pdf_annotation_t *)); @@ -1743,9 +2507,18 @@ _cairo_pdf_interchange_init (cairo_pdf_surface_t *surface) if (unlikely (ic->named_dests == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_array_init (&ic->page_commands, sizeof(cairo_pdf_command_list_t)); + ic->current_commands = NULL; + _cairo_array_init (&ic->recording_surface_commands, sizeof(cairo_pdf_recording_surface_commands_t)); + ic->num_dests = 0; ic->sorted_dests = NULL; ic->dests_res.id = 0; + ic->ignore_current_surface = FALSE; + ic->content_emitted = FALSE; + ic->marked_content_open = FALSE; + ic->render_next_command_has_content = FALSE; + ic->mcid_order = 0; _cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *)); outline_root = calloc (1, sizeof(cairo_pdf_outline_entry_t)); @@ -1772,9 +2545,7 @@ _cairo_pdf_interchange_free_outlines (cairo_pdf_surface_t *surface) _cairo_array_copy_element (&ic->outline, i, &outline); free (outline->name); - free (outline->link_attrs.dest); - free (outline->link_attrs.uri); - free (outline->link_attrs.file); + _cairo_tag_free_link_attributes (&outline->link_attrs); free (outline); } _cairo_array_fini (&ic->outline); @@ -1789,14 +2560,46 @@ _cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface) _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_pdf_interchange_clear_annotations (surface); _cairo_array_fini (&ic->annots); + + _cairo_array_fini (&ic->recording_surface_stack); _cairo_array_fini (&ic->parent_tree); + + _cairo_hash_table_foreach (ic->command_to_node_map, + _cairo_pdf_command_pluck, + ic->command_to_node_map); + _cairo_hash_table_destroy (ic->command_to_node_map); + _cairo_hash_table_foreach (ic->named_dests, _named_dest_pluck, ic->named_dests); _cairo_hash_table_destroy (ic->named_dests); + + _cairo_hash_table_foreach (ic->content_tag_map, _cairo_pdf_content_tag_pluck, ic->content_tag_map); + _cairo_hash_table_destroy(ic->content_tag_map); + + free_node (ic->struct_root); + + num_elems = _cairo_array_num_elements (&ic->recording_surface_commands); + for (i = 0; i < num_elems; i++) { + cairo_pdf_recording_surface_commands_t *recording_command; + cairo_pdf_command_list_t *command_list; + + recording_command = _cairo_array_index (&ic->recording_surface_commands, i); + command_list = recording_command->command_list; + _cairo_array_fini (&command_list->commands); + free (command_list); + } + _cairo_array_fini (&ic->recording_surface_commands); + + num_elems = _cairo_array_num_elements (&ic->page_commands); + for (i = 0; i < num_elems; i++) { + cairo_pdf_command_list_t *command_list; + command_list = _cairo_array_index (&ic->page_commands, i); + _cairo_array_fini (&command_list->commands); + } + _cairo_array_fini (&ic->page_commands); + free (ic->sorted_dests); _cairo_pdf_interchange_free_outlines (surface); free (ic->docinfo.title); @@ -2074,3 +2877,123 @@ _cairo_pdf_interchange_set_custom_metadata (cairo_pdf_surface_t *surface, return status; } + +#if DEBUG_PDF_INTERCHANGE +static cairo_int_status_t +print_node (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + int depth) +{ + if (node == NULL) { + printf("%*sNode: ptr: NULL\n", depth*2, ""); + } else if (node == surface->interchange.struct_root) { + printf("%*sNode: ptr: %p root\n", depth*2, "", node); + } else { + printf("%*sNode: ptr: %p name: '%s'\n", depth*2, "", node, node->name); + } + depth++; + printf("%*sType: ", depth*2, ""); + switch (node->type) { + case PDF_NODE_STRUCT: + printf("STRUCT\n"); + break; + case PDF_NODE_CONTENT: + printf("CONTENT\n"); + printf("%*sContent.id: %s\n", depth*2, "", node->attributes.content.id); + printf("%*sContent.tag_name: %s\n", depth*2, "", node->attributes.content.tag_name); + break; + case PDF_NODE_CONTENT_REF: + printf("CONTENT_REF\n"); + printf("%*sContent_Ref.ref: %s\n", depth*2, "", node->attributes.content_ref.ref); + break; + case PDF_NODE_ARTIFACT: + printf("ARTIFACT\n"); + break; + } + printf("%*sres: %d\n", depth*2, "", node->res.id); + printf("%*sparent: %p\n", depth*2, "", node->parent); + printf("%*sannot:", depth*2, ""); + if (node->annot) + printf(" node: %p res: %d", node->annot->node, node->annot->res.id); + printf("\n"); + printf("%*sextents: ", depth*2, ""); + if (node->extents.valid) { + printf("x: %d y: %d w: %d h: %d\n", + node->extents.extents.x, + node->extents.extents.y, + node->extents.extents.width, + node->extents.extents.height); + } else { + printf("not valid\n"); + } + + printf("%*smcid: ", depth*2, ""); + int num_mcid = _cairo_array_num_elements (&node->mcid); + for (int i = 0; i < num_mcid; i++) { + cairo_pdf_page_mcid_t *mcid_elem = _cairo_array_index (&node->mcid, i); + if (mcid_elem->child_node) { + printf("(order: %d, %p) ", mcid_elem->order, mcid_elem->child_node); + } else { + printf("(order: %d, pg: %d, xobject_res: %d, mcid: %d) ", + mcid_elem->order, mcid_elem->page, mcid_elem->xobject_res.id, mcid_elem->mcid); + } + } + printf("\n"); + return CAIRO_STATUS_SUCCESS; +} + +static void +print_tree (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node) +{ + printf("Structure Tree:\n"); + cairo_pdf_interchange_walk_struct_tree (surface, node, 0, print_node); +} + +static void +print_command (cairo_pdf_command_t *command, int indent) +{ + printf("%*s%d ", indent*2, "", command->command_id); + switch (command->flags) { + case PDF_CONTENT: + printf("CONTENT"); + break; + case PDF_BEGIN: + printf("BEGIN"); + break; + case PDF_END: + printf("END"); + break; + case PDF_GROUP: + printf("GROUP: %p", command->group); + break; + case PDF_NONE: + printf("NONE"); + break; + } + printf(" node: %p index: %d\n", command->node, command->mcid_index); +} + +static void +print_commands (cairo_pdf_command_list_t *command_list, int indent) +{ + cairo_pdf_command_t *command; + unsigned i; + unsigned num_elements = _cairo_array_num_elements (&command_list->commands); + + for (i = 0; i < num_elements; i++) { + command = _cairo_array_index (&command_list->commands, i); + print_command (command, indent); + if (command->flags == PDF_GROUP) + print_commands (command->group, indent + 1); + } +} + +static void +print_command_list(cairo_pdf_command_list_t *command_list) +{ + printf("Command List: %p\n", command_list); + print_commands (command_list, 0); + printf("end\n"); +} +#endif diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index 328e893d7..176f45b48 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -1568,10 +1568,16 @@ _cairo_pdf_operators_tag_begin (cairo_pdf_operators_t *pdf_operators, return status; } - _cairo_output_stream_printf (pdf_operators->stream, - "/%s << /MCID %d >> BDC\n", - tag_name, - mcid); + if (mcid >= 0) { + _cairo_output_stream_printf (pdf_operators->stream, + "/%s << /MCID %d >> BDC\n", + tag_name, + mcid); + } else { + _cairo_output_stream_printf (pdf_operators->stream, + "/%s BMC\n", + tag_name); + } return _cairo_output_stream_get_status (pdf_operators->stream); } diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index c97affe6b..13ccc5001 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -74,6 +74,10 @@ typedef struct _cairo_pdf_source_surface_entry { unsigned char *unique_id; unsigned long unique_id_length; cairo_operator_t operator; + + /* If not 0, this is the recording surface region id of the source surface. */ + int region_id; + cairo_bool_t interpolate; cairo_bool_t stencil_mask; cairo_bool_t smask; @@ -110,6 +114,9 @@ typedef struct _cairo_pdf_pattern { cairo_operator_t operator; cairo_bool_t is_shading; + /* Index into nodes array in cairo_pdf_surface_node_entry_t or -1 if not used */ + int region_id; + /* PDF pattern space is the pattern matrix concatenated with the * initial space of the parent object. If the parent object is the * page, the initial space does not include the Y-axis flipping @@ -161,38 +168,87 @@ typedef struct _cairo_pdf_jbig2_global { cairo_bool_t emitted; } cairo_pdf_jbig2_global_t; -/* cairo-pdf-interchange.c types */ +typedef struct _cairo_pdf_page_info { + double width; + double height; + cairo_pdf_resource_t page_res; + cairo_pdf_resource_t content; + cairo_pdf_resource_t resources; + cairo_pdf_resource_t thumbnail; + cairo_array_t annots; /* <cairo_pdf_resource_t> */ + int struct_parents; +} cairo_pdf_page_info_t; -struct page_mcid { - int page; - int mcid; -}; -struct tag_extents { +/* cairo-pdf-interchange.c types */ + +typedef struct _pdf_tag_extents { cairo_rectangle_int_t extents; cairo_bool_t valid; - cairo_list_t link; -}; +} cairo_pdf_tag_extents_t; + +typedef struct _pdf_page_mcid { + int order; + int page; + cairo_pdf_resource_t xobject_res; /* 0 if not in an XObject */ + int mcid; + struct _cairo_pdf_struct_tree_node *child_node; +} cairo_pdf_page_mcid_t; + +/* The non PDF_NODE_STRUCT types are excluded from the struct tree embedded in + * the PDF. */ +typedef enum _cairo_pdf_tree_node_type { + PDF_NODE_STRUCT, + PDF_NODE_CONTENT, + PDF_NODE_CONTENT_REF, + PDF_NODE_ARTIFACT, +} cairo_pdf_tree_node_type_t; typedef struct _cairo_pdf_struct_tree_node { + cairo_hash_entry_t hash; + cairo_pdf_tree_node_type_t type; 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 */ - cairo_pdf_resource_t annot_res; /* 0 if no annot */ - struct tag_extents extents; - cairo_list_t link; + cairo_array_t mcid; /* array of cairo_pdf_page_mcid_t */ + struct _cairo_pdf_annotation *annot; + cairo_pdf_tag_extents_t extents; + + union { + cairo_content_attrs_t content; /* type == PDF_NODE_CONTENT */ + cairo_content_ref_attrs_t content_ref; /* type == PDF_NODE_CONTENT_REF */ + } attributes; + + cairo_list_t link; /* linked list of parent's children */ } cairo_pdf_struct_tree_node_t; +typedef struct _cairo_pdf_command_entry { + cairo_hash_entry_t base; + unsigned int recording_id; + unsigned int command_id; + cairo_pdf_struct_tree_node_t *node; +} cairo_pdf_command_entry_t; + +typedef struct _cairo_recording_surface_stack_entry { + cairo_bool_t ignore_surface; + cairo_pdf_struct_tree_node_t *current_node; +} cairo_recording_surface_stack_entry_t; + +typedef struct _cairo_pdf_content_tag { + cairo_hash_entry_t base; + cairo_pdf_struct_tree_node_t *node; +} cairo_pdf_content_tag_t; + typedef struct _cairo_pdf_annotation { cairo_pdf_struct_tree_node_t *node; /* node containing the annotation */ cairo_link_attrs_t link_attrs; + cairo_pdf_resource_t res; } cairo_pdf_annotation_t; typedef struct _cairo_pdf_named_dest { cairo_hash_entry_t base; - struct tag_extents extents; + cairo_pdf_tag_extents_t extents; cairo_dest_attrs_t attrs; int page; } cairo_pdf_named_dest_t; @@ -233,19 +289,65 @@ struct metadata { char *value; }; +typedef enum _cairo_pdf_operation_flags_t { + PDF_NONE = 0, + PDF_CONTENT, + PDF_BEGIN, + PDF_END, + PDF_GROUP, +} cairo_pdf_operation_flags_t; + +typedef struct _pdf_command_list { + cairo_array_t commands; + struct _pdf_command_list *parent; +} cairo_pdf_command_list_t; + +typedef struct _pdf_operation { + cairo_pdf_command_list_t *group; + cairo_pdf_struct_tree_node_t *node; + unsigned int command_id; + int mcid_index; + cairo_pdf_operation_flags_t flags; +} cairo_pdf_command_t; + +typedef struct _pdf_recording_surface_commands { + cairo_surface_t *recording_surface; + cairo_pdf_command_list_t *command_list; + unsigned int region_id; +} cairo_pdf_recording_surface_commands_t; + 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 */ + + /* Current position in the tree during the analysis stage and across + * pages as each page adds to the tree */ + cairo_pdf_struct_tree_node_t *current_analyze_node; + + /* Currently open tag content containing content. NULL if no content tag open. + * A content containg tag may be open across pages */ + cairo_pdf_struct_tree_node_t *current_render_node; + cairo_pdf_struct_tree_node_t *next_page_render_node; + + cairo_array_t recording_surface_stack; /* cairo_recording_surface_stack_entry_t */ + cairo_pdf_resource_t current_recording_surface_res; + cairo_hash_table_t *command_to_node_map; /* <cairo_pdf_surface_node_entry_t> */ + cairo_bool_t ignore_current_surface; + cairo_hash_table_t *content_tag_map; /* <char*,cairo_pdf_content_tag_t> */ + + cairo_array_t parent_tree; /* <cairo_pdf_resource_t> */ cairo_array_t annots; /* array of pointers to cairo_pdf_annotation_t */ + cairo_pdf_resource_t content_parent_res; cairo_pdf_resource_t parent_tree_res; + + /* mcid to tree node for current page or group */ + cairo_array_t mcid_to_tree; /* <cairo_pdf_struct_tree_node_t *> */ + + cairo_array_t page_commands; /* <cairo_pdf_command_list_t> */ + cairo_pdf_command_list_t *current_commands; /* <cairo_pdf_command_list_t> */ + cairo_array_t recording_surface_commands; /* <cairo_pdf_recording_surface_commands_t> */ + cairo_list_t extents_list; cairo_hash_table_t *named_dests; int num_dests; @@ -255,6 +357,12 @@ typedef struct _cairo_pdf_interchange { cairo_array_t outline; /* array of pointers to cairo_pdf_outline_entry_t; */ struct docinfo docinfo; cairo_array_t custom_metadata; /* array of struct metadata */ + cairo_bool_t content_emitted; + cairo_bool_t marked_content_open; + unsigned int recording_id; + unsigned int command_id; + cairo_bool_t render_next_command_has_content; + int mcid_order; } cairo_pdf_interchange_t; @@ -283,18 +391,18 @@ struct _cairo_pdf_surface { cairo_matrix_t cairo_to_pdf; cairo_bool_t in_xobject; - cairo_array_t objects; - cairo_array_t pages; + cairo_array_t objects; /* cairo_pdf_resource_t - list of every resource in the PDF */ + cairo_array_t pages; /* <cairo_pdf_page_info_t> */ cairo_array_t rgb_linear_functions; cairo_array_t alpha_linear_functions; cairo_array_t page_patterns; /* cairo_pdf_pattern_t */ cairo_array_t page_surfaces; /* cairo_pdf_source_surface_t */ cairo_array_t doc_surfaces; /* cairo_pdf_source_surface_t */ - cairo_hash_table_t *all_surfaces; + cairo_hash_table_t *all_surfaces; /* cairo_pdf_source_surface_entry_t* */ + int duplicate_surface_number; cairo_array_t smask_groups; cairo_array_t knockout_group; cairo_array_t jbig2_global; - cairo_array_t page_heights; cairo_hash_table_t *color_glyphs; cairo_scaled_font_subsets_t *font_subsets; @@ -374,6 +482,8 @@ struct _cairo_pdf_surface { cairo_image_surface_t *thumbnail_image; cairo_surface_t *paginated_surface; + + cairo_bool_t debug; }; cairo_private cairo_pdf_resource_t @@ -410,15 +520,56 @@ _cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface, cairo_private void _cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface); +cairo_private cairo_bool_t +_cairo_pdf_interchange_struct_tree_requires_recording_surface ( + cairo_pdf_surface_t *surface, + const cairo_surface_pattern_t *recording_surface, + cairo_analysis_source_t source_type); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_recording_source_surface_begin ( + cairo_pdf_surface_t *surface, + const cairo_surface_pattern_t *recording_surface_pattern, + unsigned int region_id, + cairo_analysis_source_t source_type); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_recording_source_surface_end ( + cairo_pdf_surface_t *surface, + const cairo_surface_pattern_t *recording_surface_pattern, + unsigned int region_id, + cairo_analysis_source_t source_type); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_emit_recording_surface_begin ( + cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface, + int region_id, + cairo_pdf_resource_t surface_resource, + int *struct_parents); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_emit_recording_surface_end ( + cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface); + 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_command_id (cairo_pdf_surface_t *surface, + unsigned int recording_id, + unsigned int command_id); + +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_add_content (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t _cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface); cairo_private cairo_int_status_t diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 2b1bf72e4..ca83e61ed 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -471,7 +471,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->surface_bounded = TRUE; _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t)); - _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&surface->pages, sizeof (cairo_pdf_page_info_t)); _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t)); _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t)); _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); @@ -482,7 +482,6 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); _cairo_array_init (&surface->doc_surfaces, sizeof (cairo_pdf_source_surface_t)); _cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_t)); - _cairo_array_init (&surface->page_heights, sizeof (double)); surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal); if (unlikely (surface->all_surfaces == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -495,6 +494,8 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL1; } + surface->duplicate_surface_number = 0; + _cairo_pdf_group_resources_init (&surface->resources); surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); @@ -565,8 +566,11 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->thumbnail_height = 0; surface->thumbnail_image = NULL; - if (getenv ("CAIRO_DEBUG_PDF") != NULL) + surface->debug = FALSE; + if (getenv ("CAIRO_DEBUG_PDF") != NULL) { + surface->debug = TRUE; surface->compress_streams = FALSE; + } surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, @@ -1457,6 +1461,9 @@ _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b) if (a->interpolate != b->interpolate) return FALSE; + if (a->region_id != b->region_id) + return FALSE; + if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length) return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0); @@ -1472,6 +1479,9 @@ _cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) } else { key->base.hash = key->id; } + key->base.hash = _cairo_hash_bytes (key->base.hash, + &key->region_id, + sizeof(key->region_id)); } static cairo_bool_t @@ -1601,6 +1611,11 @@ _get_source_surface_extents (cairo_surface_t *source, * @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 + * @region_id: [in] Surfaces containing tags set this to the recording + * surface region id. When tags are used in a XObject, PDF requires a + * separate object for each use (section 14.7.4.2) @region_id is used + * as a key to ensure a separate object is emitted for each use. Set + * to 0 for surfaces without tags. * @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 @@ -1628,6 +1643,7 @@ 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, + int region_id, cairo_operator_t op, cairo_filter_t filter, cairo_bool_t stencil_mask, @@ -1653,6 +1669,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, cairo_rectangle_int_t op_extents; double x, y; cairo_bool_t subsurface; + cairo_bool_t emit_image; switch (filter) { default: @@ -1700,10 +1717,21 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, *source_extents = op_extents; surface_key.id = source_surface->unique_id; - surface_key.interpolate = interpolate; + + /* Recording surfaces do not use interpolate. Ensure it is always + * false for recording surfaces. This is because pdf-interchange + * needs to lookup recording surfaces in the hash table using + * interpolate = FALSE in the key since it does not know the + * interpolate value passed to this function. + */ + emit_image = source_surface->type != CAIRO_SURFACE_TYPE_RECORDING; + surface_key.interpolate = emit_image ? interpolate : FALSE; + cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID, (const unsigned char **) &surface_key.unique_id, &surface_key.unique_id_length); + + surface_key.region_id = region_id; _cairo_pdf_source_surface_init_key (&surface_key); surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); if (surface_entry) { @@ -1746,9 +1774,12 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, if (pdf_source) *pdf_source = surface_entry; + surface_entry->id = surface_key.id; + surface_entry->region_id = region_id; surface_entry->operator = op; - surface_entry->interpolate = interpolate; + surface_entry->interpolate = emit_image ? interpolate : FALSE; + surface_entry->emit_image = emit_image; surface_entry->stencil_mask = stencil_mask; surface_entry->smask = smask; surface_entry->need_transp_group = need_transp_group; @@ -1810,11 +1841,6 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, goto fail3; } - /* Test if surface will be emitted as image or recording */ - status = _cairo_pdf_surface_emit_surface (surface, &src_surface, TRUE, &surface_entry->emit_image); - if (unlikely (status)) - goto fail3; - if (surface_entry->bounded) { status = _cairo_array_append (&surface->page_surfaces, &src_surface); if (unlikely (status)) @@ -1858,6 +1884,7 @@ static cairo_int_status_t _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_operator_t op, + cairo_analysis_source_t source_type, const cairo_rectangle_int_t *extents, cairo_bool_t is_shading, cairo_pdf_resource_t *pattern_res, @@ -1865,6 +1892,7 @@ _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, { cairo_pdf_pattern_t pdf_pattern; cairo_int_status_t status; + int region_id = 0; pdf_pattern.is_shading = is_shading; pdf_pattern.operator = op; @@ -1923,6 +1951,17 @@ _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, * Y-axis. */ pdf_pattern.inverted_y_axis = pdf_pattern.gstate_res.id ? TRUE : surface->in_xobject; + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface, + surface_pattern, + source_type)) + { + region_id = surface_pattern->region_array_id; + } + } + pdf_pattern.region_id = region_id; + status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); if (unlikely (status)) { cairo_pattern_destroy (pdf_pattern.pattern); @@ -1954,6 +1993,7 @@ _cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, pattern, op, + CAIRO_ANALYSIS_SOURCE_NONE, extents, TRUE, shading_res, @@ -1964,6 +2004,7 @@ static cairo_int_status_t _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_operator_t op, + cairo_analysis_source_t source_type, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) @@ -1971,6 +2012,7 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, pattern, op, + source_type, extents, FALSE, pattern_res, @@ -2242,6 +2284,7 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, surface->group_stream.mem_stream = NULL; surface->group_stream.stream = NULL; + surface->reset_gs_required = FALSE; return status; } @@ -2249,7 +2292,7 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, static cairo_int_status_t _cairo_pdf_surface_open_object_stream (cairo_pdf_surface_t *surface) { - if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) { + if (surface->debug || surface->pdf_version < CAIRO_PDF_VERSION_1_5) { /* Object streams not supported. All objects will be written * directly to the file. */ assert (surface->pdf_stream.active == FALSE); @@ -2305,7 +2348,9 @@ _cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface) } } -static int _cairo_xref_stream_object_compare (const void *a, const void *b) +static int +_cairo_xref_stream_object_compare (const void *a, + const void *b) { const cairo_xref_stream_object_t *a_obj = a; const cairo_xref_stream_object_t *b_obj = b; @@ -2427,9 +2472,11 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox, cairo_pdf_resource_t *resource, cairo_bool_t is_form, - cairo_bool_t is_group) + cairo_bool_t is_group, + int struct_parents) { cairo_int_status_t status; + char buf[1000]; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); @@ -2442,40 +2489,46 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, assert (bbox != NULL); if (is_group) { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_streams, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ %f %f %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - bbox->p1.x, - bbox->p1.y, - bbox->p2.x, - bbox->p2.y, - surface->content_resources.id); + snprintf(buf, + sizeof(buf), + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /I true\n" + " /CS /DeviceRGB\n" + " >>\n" + " /Resources %d 0 R\n", + bbox->p1.x, + bbox->p1.y, + bbox->p2.x, + bbox->p2.y, + surface->content_resources.id); } else { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_streams, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ %f %f %f %f ]\n" - " /Resources %d 0 R\n", - bbox->p1.x, - bbox->p1.y, - bbox->p2.x, - bbox->p2.y, - surface->content_resources.id); + snprintf(buf, + sizeof(buf), + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Resources %d 0 R\n", + bbox->p1.x, + bbox->p1.y, + bbox->p2.x, + bbox->p2.y, + surface->content_resources.id); } + if (struct_parents >= 0) { + snprintf(buf + strlen(buf), + sizeof(buf) - strlen(buf), + " /StructParents %d\n", struct_parents); + } + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_streams, + buf); } else { status = _cairo_pdf_surface_open_stream (surface, @@ -2550,6 +2603,73 @@ _cairo_pdf_color_glyph_pluck (void *entry, void *closure) free (glyph_entry); } +static cairo_int_status_t +_cairo_pdf_surface_write_page_dicts (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + cairo_pdf_page_info_t *page_info; + int num_annots; + cairo_pdf_resource_t res; + + for (unsigned i = 0; i < _cairo_array_num_elements (&surface->pages); i++) { + page_info = _cairo_array_index (&surface->pages, i); + + status = _cairo_pdf_surface_object_begin (surface, page_info->page_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /Page %% %d\n" + " /Parent %d 0 R\n" + " /MediaBox [ 0 0 %f %f ]\n" + " /Contents %d 0 R\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /I true\n" + " /CS /DeviceRGB\n" + " >>\n" + " /Resources %d 0 R\n", + i + 1, + surface->pages_resource.id, + page_info->width, + page_info->height, + page_info->content.id, + page_info->resources.id); + + if (page_info->struct_parents >= 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /StructParents %d\n", + page_info->struct_parents); + } + + num_annots = _cairo_array_num_elements (&page_info->annots); + if (num_annots > 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Annots [ "); + for (int j = 0; j < num_annots; j++) { + _cairo_array_copy_element (&page_info->annots, j, &res); + _cairo_output_stream_printf (surface->object_stream.stream, + "%d 0 R ", + res.id); + } + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); + } + + if (page_info->thumbnail.id) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Thumb %d 0 R\n", + page_info->thumbnail.id); + } + + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + } + + return status; +} + static cairo_status_t _cairo_pdf_surface_finish (void *abstract_surface) { @@ -2594,6 +2714,10 @@ _cairo_pdf_surface_finish (void *abstract_surface) if (unlikely (status)) return status; + status = _cairo_pdf_surface_write_page_dicts (surface); + if (unlikely (status)) + return status; + catalog = _cairo_pdf_surface_new_object (surface); if (catalog.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2606,7 +2730,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) if (unlikely (status)) return status; - if (surface->pdf_version >= CAIRO_PDF_VERSION_1_5) + if (!surface->debug && surface->pdf_version >= CAIRO_PDF_VERSION_1_5) { xref_res = _cairo_pdf_surface_new_object (surface); status = _cairo_pdf_surface_write_xref_stream (surface, @@ -2669,6 +2793,11 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_pdf_group_resources_fini (&surface->resources); _cairo_array_fini (&surface->objects); + size = _cairo_array_num_elements (&surface->pages); + for (i = 0; i < size; i++) { + cairo_pdf_page_info_t *page_info = _cairo_array_index (&surface->pages, i); + _cairo_array_fini (&page_info->annots); + } _cairo_array_fini (&surface->pages); _cairo_array_fini (&surface->rgb_linear_functions); _cairo_array_fini (&surface->alpha_linear_functions); @@ -2705,7 +2834,6 @@ _cairo_pdf_surface_finish (void *abstract_surface) return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING); } _cairo_array_fini (&surface->jbig2_global); - _cairo_array_fini (&surface->page_heights); size = _cairo_array_num_elements (&surface->page_labels); for (i = 0; i < size; i++) { @@ -2725,7 +2853,7 @@ 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_pdf_page_info_t page_info; cairo_int_status_t status; /* Document header */ @@ -2758,11 +2886,19 @@ _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) + page_info.page_res = _cairo_pdf_surface_new_object (surface); + if (page_info.page_res.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_array_append (&surface->pages, &page); + page_info.width = surface->width; + page_info.height = surface->height; + page_info.content.id = 0; + page_info.resources.id = 0; + page_info.thumbnail.id = 0; + page_info.struct_parents = -1; + _cairo_array_init (&page_info.annots, sizeof (cairo_pdf_resource_t)); + + status = _cairo_array_append (&surface->pages, &page_info); if (unlikely (status)) return status; @@ -2777,13 +2913,17 @@ _cairo_pdf_surface_has_fallback_images (void *abstract_surface, cairo_pdf_surface_t *surface = abstract_surface; cairo_box_double_t bbox; + status = _cairo_pdf_interchange_end_page_content (surface); + if (unlikely (status)) + return status; + surface->has_fallback_images = has_fallbacks; surface->in_xobject = has_fallbacks; bbox.p1.x = 0; bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; - status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks, has_fallbacks); + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks, has_fallbacks, -1); if (unlikely (status)) return status; @@ -2883,6 +3023,7 @@ _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surfa status = _cairo_pdf_surface_add_source_surface (surface, pad_image, NULL, + -1 , /* node_surface_index */ CAIRO_OPERATOR_OVER, /* not used for images */ source->filter, FALSE, /* stencil mask */ @@ -3722,6 +3863,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, cairo_bool_t is_subsurface; cairo_bool_t transparency_group; cairo_recording_surface_t *recording; + int struct_parents = -1; assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE); @@ -3780,11 +3922,20 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, _cairo_recording_surface_has_only_bilevel_alpha (recording) && _cairo_recording_surface_has_only_op_over (recording)); + status = _cairo_pdf_interchange_emit_recording_surface_begin (surface, + pdf_source->surface, + pdf_source->hash_entry->region_id, + pdf_source->hash_entry->surface_res, + &struct_parents); + if (unlikely (status)) + goto err; + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, TRUE, - transparency_group); + transparency_group, + struct_parents); if (unlikely (status)) goto err; @@ -3823,6 +3974,10 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, surface->paginated_mode = old_paginated_mode; surface->surface_extents = old_surface_extents; surface->surface_bounded = old_surface_bounded; + surface->reset_gs_required = FALSE; + + if (pdf_source->hash_entry->region_id > 0) + status = _cairo_pdf_interchange_emit_recording_surface_end (surface, pdf_source->surface); err: cairo_surface_destroy (free_me); @@ -3964,6 +4119,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_source_surface (surface, NULL, pattern, + pdf_pattern->region_id, pdf_pattern->operator, pattern->filter, FALSE, /* stencil mask */ @@ -5108,6 +5264,7 @@ static cairo_int_status_t _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, + cairo_analysis_source_t source_type, const cairo_rectangle_int_t *extents, double alpha, cairo_pdf_resource_t *smask_res, @@ -5119,6 +5276,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, double x_offset; double y_offset; cairo_pdf_source_surface_entry_t *pdf_source; + int region_id = 0; if (source->extend == CAIRO_EXTEND_PAD && !(source->type == CAIRO_PATTERN_TYPE_SURFACE && @@ -5132,9 +5290,19 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, &y_offset, NULL); } else { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface, + surface_pattern, + source_type)) + { + region_id = surface_pattern->region_array_id; + } + } status = _cairo_pdf_surface_add_source_surface (surface, NULL, source, + region_id, op, source->filter, stencil_mask, @@ -5269,6 +5437,7 @@ static cairo_int_status_t _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, + cairo_analysis_source_t source_type, const cairo_rectangle_int_t *extents, double alpha, cairo_bool_t mask) @@ -5279,6 +5448,7 @@ _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_paint_surface_pattern (surface, op, source, + source_type, extents, alpha, NULL, @@ -5474,10 +5644,6 @@ _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_heights, &surface->height); - if (unlikely (status)) - return status; - status = _cairo_array_append (&surface->page_labels, &surface->current_page_label); if (unlikely (status)) return status; @@ -5530,7 +5696,7 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface, static cairo_int_status_t _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) { - cairo_pdf_resource_t page; + cairo_pdf_page_info_t *page_info; int num_pages, i; cairo_int_status_t status; @@ -5544,8 +5710,8 @@ _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) num_pages = _cairo_array_num_elements (&surface->pages); for (i = 0; i < num_pages; i++) { - _cairo_array_copy_element (&surface->pages, i, &page); - _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", page.id); + page_info = _cairo_array_index (&surface->pages, i); + _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", page_info->page_res.id); } _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); @@ -6605,7 +6771,7 @@ cairo_pdf_surface_emit_color_glyph (cairo_pdf_surface_t *surface, goto cleanup; status = _cairo_recording_surface_replay_and_create_regions (glyph_surface, regions_id, - NULL, analysis, TRUE); + NULL, analysis, TRUE, FALSE); if (status) goto cleanup; @@ -6667,6 +6833,7 @@ cairo_pdf_surface_emit_color_glyph (cairo_pdf_surface_t *surface, _cairo_pdf_surface_paint_surface_pattern (surface, CAIRO_OPERATOR_OVER, &surface_pattern.base, + CAIRO_ANALYSIS_SOURCE_NONE, &extents, 1.0, /* alpha */ NULL, /* smask_res */ @@ -6738,6 +6905,7 @@ cairo_pdf_surface_emit_color_glyph_image (cairo_pdf_surface_t *surface, _cairo_pdf_surface_paint_surface_pattern (surface, CAIRO_OPERATOR_OVER, image_pattern, + CAIRO_ANALYSIS_SOURCE_NONE, &extents, 1.0, /* alpha */ NULL, /* smask_res */ @@ -7267,6 +7435,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_paint_pattern (surface, CAIRO_OPERATOR_OVER, group->mask, + CAIRO_ANALYSIS_MASK_MASK, &group->extents, 1.0, /* alpha */ FALSE); /* mask */ @@ -7279,6 +7448,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, CAIRO_OPERATOR_OVER, + CAIRO_ANALYSIS_MASK_MASK, NULL, &pattern_res, &gstate_res); if (unlikely (status)) @@ -7344,6 +7514,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_paint_pattern (surface, CAIRO_OPERATOR_OVER, group->source, + CAIRO_ANALYSIS_MASK_MASK, &group->extents, 1.0, /* alpha */ FALSE); /* mask */ @@ -7356,6 +7527,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, CAIRO_OPERATOR_OVER, + CAIRO_ANALYSIS_MASK_MASK, NULL, &pattern_res, &gstate_res); if (unlikely (status)) @@ -7593,10 +7765,12 @@ _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 knockout, res, thumbnail_res; - cairo_pdf_resource_t *page; + cairo_pdf_resource_t knockout, res; cairo_int_status_t status; - unsigned int i, len, page_num, num_annots; + unsigned int i, len; + cairo_pdf_page_info_t *page_info; + + page_info = _cairo_array_last_element (&surface->pages); status = _cairo_pdf_surface_open_object_stream (surface); if (unlikely (status)) @@ -7642,7 +7816,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) return status; _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE, -1); if (unlikely (status)) return status; @@ -7658,70 +7832,18 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) return status; } - 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; + page_info->thumbnail = _cairo_pdf_surface_new_object (surface); + entry.surface_res = page_info->thumbnail; _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); - - status = _cairo_pdf_surface_object_begin (surface, *page); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->object_stream.stream, - "<< /Type /Page %% %d\n" - " /Parent %d 0 R\n" - " /MediaBox [ 0 0 %f %f ]\n" - " /Contents %d 0 R\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - page_num, - surface->pages_resource.id, - surface->width, - surface->height, - surface->content.id, - surface->content_resources.id); - - if (surface->page_parent_tree >= 0) { - _cairo_output_stream_printf (surface->object_stream.stream, - " /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->object_stream.stream, - " /Annots [ "); - for (i = 0; i < num_annots; i++) { - _cairo_array_copy_element (&surface->page_annots, i, &res); - _cairo_output_stream_printf (surface->object_stream.stream, - "%d 0 R ", - res.id); - } - _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); - } - - if (thumbnail_res.id) { - _cairo_output_stream_printf (surface->object_stream.stream, - " /Thumb %d 0 R\n", - thumbnail_res.id); - } - - _cairo_output_stream_printf (surface->object_stream.stream, - ">>\n"); - _cairo_pdf_surface_object_end (surface); + page_info->content = surface->content; + page_info->resources = surface->content_resources; + page_info->struct_parents = surface->page_parent_tree; status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, FALSE); if (unlikely (status)) @@ -7968,7 +8090,7 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; - status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE); + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE, -1); if (unlikely (status)) return status; @@ -8115,6 +8237,7 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_source_surface (surface, NULL, mask, + -1, /* node_surface_index */ op, source->filter, FALSE, /* stencil mask */ @@ -8135,7 +8258,9 @@ _cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, extents, + status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, + CAIRO_ANALYSIS_SOURCE_NONE, + extents, 1.0, /* alpha */ need_smask ? &pdf_source->surface_res : NULL, FALSE); @@ -8200,7 +8325,9 @@ _cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, 1.0, NULL, TRUE); + status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, + CAIRO_ANALYSIS_SOURCE_NONE, + extents, 1.0, NULL, TRUE); if (unlikely (status)) return status; @@ -8282,6 +8409,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, status = _cairo_pdf_surface_paint_pattern (surface, op, source, + CAIRO_ANALYSIS_SOURCE_PAINT, &extents.bounded, 1.0, /* alpha */ FALSE); /* mask */ @@ -8296,6 +8424,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, + CAIRO_ANALYSIS_SOURCE_PAINT, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -8467,6 +8596,7 @@ _cairo_pdf_surface_mask (void *abstract_surface, status = _cairo_pdf_surface_paint_pattern (surface, op, source, + CAIRO_ANALYSIS_SOURCE_MASK, &extents.bounded, alpha, FALSE); /* mask */ @@ -8596,6 +8726,7 @@ _cairo_pdf_surface_stroke (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, + CAIRO_ANALYSIS_SOURCE_STROKE, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -8754,6 +8885,7 @@ _cairo_pdf_surface_fill (void *abstract_surface, status = _cairo_pdf_surface_paint_pattern (surface, op, source, + CAIRO_ANALYSIS_SOURCE_FILL, &extents.bounded, 1.0, /* alpha */ FALSE); /* mask */ @@ -8768,6 +8900,7 @@ _cairo_pdf_surface_fill (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, + CAIRO_ANALYSIS_SOURCE_FILL, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -8947,6 +9080,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, fill_op, + CAIRO_ANALYSIS_SOURCE_FILL, &extents.bounded, &fill_pattern_res, &gstate_res); @@ -8960,6 +9094,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, stroke_op, + CAIRO_ANALYSIS_SOURCE_STROKE, &extents.bounded, &stroke_pattern_res, &gstate_res); @@ -9038,6 +9173,10 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, if (unlikely (status)) return status; + status = _cairo_pdf_interchange_add_content (surface); + if (unlikely (status)) + return status; + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); if (unlikely (status)) return status; @@ -9063,6 +9202,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, + CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -9191,9 +9331,10 @@ _cairo_pdf_surface_tag (void *abstract_surface, cairo_bool_t begin, const char *tag_name, const char *attributes) + { cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status = 0; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; if (begin) status = _cairo_pdf_interchange_tag_begin (surface, tag_name, attributes); @@ -9203,6 +9344,16 @@ _cairo_pdf_surface_tag (void *abstract_surface, return status; } +static cairo_int_status_t +_cairo_pdf_surface_command_id (void *abstract_surface, + unsigned int recording_id, + unsigned int command_id) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + return _cairo_pdf_interchange_command_id (surface, recording_id, command_id); +} + /* The Type 3 font subset support will the embed the * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE image if vector operations * are not supported. The only case we don't currently handle is if a @@ -9266,6 +9417,33 @@ _cairo_pdf_surface_supports_color_glyph (void *abstract_surface } static cairo_int_status_t +_cairo_pdf_surface_analyze_recording_surface(void *abstract_surface, + const cairo_surface_pattern_t *recording_surface_pattern, + unsigned int region_id, + cairo_analysis_source_t source_type, + cairo_bool_t begin) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + if (begin) { + status = _cairo_pdf_interchange_recording_source_surface_begin ( + surface, + recording_surface_pattern, + region_id, + source_type); + } else { + status = _cairo_pdf_interchange_recording_source_surface_end ( + surface, + recording_surface_pattern, + region_id, + source_type); + } + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { @@ -9273,6 +9451,7 @@ _cairo_pdf_surface_set_paginated_mode (void *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; @@ -9324,6 +9503,8 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_get_supported_mime_types, _cairo_pdf_surface_tag, _cairo_pdf_surface_supports_color_glyph, + _cairo_pdf_surface_analyze_recording_surface, + _cairo_pdf_surface_command_id, }; static const cairo_paginated_surface_backend_t diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index d8a8065d0..6da1b6ffa 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -722,8 +722,7 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, status = _cairo_recording_surface_replay_with_clip (source, matrix, &surface->base, - NULL, - FALSE); + NULL); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return status; diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h index 7d4de1ed9..acace4ec8 100644 --- a/src/cairo-recording-surface-private.h +++ b/src/cairo-recording-surface-private.h @@ -155,6 +155,7 @@ typedef struct _cairo_recording_surface { cairo_bool_t optimize_clears; cairo_bool_t has_bilevel_alpha; cairo_bool_t has_only_op_over; + cairo_bool_t has_tags; struct bbtree { cairo_box_t extents; @@ -204,18 +205,26 @@ _cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surf cairo_bool_t *foreground_used); cairo_private cairo_status_t -_cairo_recording_surface_replay_with_clip (cairo_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_surface_replay_with_transform (cairo_surface_t *surface, + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded, + cairo_bool_t replay_all); + +cairo_private cairo_status_t +_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + const cairo_clip_t *target_clip); cairo_private cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, unsigned int regions_id, const cairo_matrix_t *surface_transform, cairo_surface_t *target, - cairo_bool_t surface_is_unbounded); + cairo_bool_t surface_is_unbounded, + cairo_bool_t replay_all); + cairo_private cairo_status_t _cairo_recording_surface_replay_region (cairo_surface_t *surface, unsigned int regions_id, @@ -239,6 +248,9 @@ _cairo_recording_surface_has_only_bilevel_alpha (cairo_recording_surface_t *surf cairo_private cairo_bool_t _cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface); +cairo_private cairo_bool_t +_cairo_recording_surface_has_tags (cairo_surface_t *surface); + cairo_private cairo_status_t _cairo_recording_surface_region_array_attach (cairo_surface_t *surface, unsigned int *id); diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index 2912f5ede..5735c7ca3 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -103,6 +103,7 @@ typedef struct _cairo_recording_surface_replay_params { unsigned int regions_id; const cairo_color_t *foreground_color; cairo_bool_t foreground_used; + cairo_bool_t replay_all; } cairo_recording_surface_replay_params_t; static const cairo_surface_backend_t cairo_recording_surface_backend; @@ -436,6 +437,7 @@ cairo_recording_surface_create (cairo_content_t content, surface->optimize_clears = TRUE; surface->has_bilevel_alpha = FALSE; surface->has_only_op_over = FALSE; + surface->has_tags = FALSE; CAIRO_MUTEX_INIT (surface->mutex); @@ -1190,6 +1192,8 @@ _cairo_recording_surface_tag (void *abstract_surface, TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + surface->has_tags = TRUE; + command = calloc (1, sizeof (cairo_command_tag_t)); if (unlikely (command == NULL)) { return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1635,6 +1639,7 @@ _cairo_recording_surface_snapshot (void *abstract_other) surface->unbounded = other->unbounded; surface->has_bilevel_alpha = other->has_bilevel_alpha; surface->has_only_op_over = other->has_only_op_over; + surface->has_tags = other->has_tags; surface->base.is_clear = other->base.is_clear; @@ -1644,8 +1649,6 @@ _cairo_recording_surface_snapshot (void *abstract_other) surface->indices = NULL; surface->num_indices = 0; surface->optimize_clears = TRUE; - surface->has_bilevel_alpha = other->has_bilevel_alpha; - surface->has_only_op_over = other->has_only_op_over; CAIRO_MUTEX_INIT (surface->mutex); @@ -1715,6 +1718,8 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { NULL, /* get_supported_mime_types */ _cairo_recording_surface_tag, _cairo_recording_surface_supports_color_glyph, + NULL, /* analyze_recording_surface */ + NULL, /* command_id */ }; static unsigned int @@ -1918,7 +1923,7 @@ _cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surfac cairo_box_t box; if (surface->commands.num_elements == 0) - return 0; + return 0; _cairo_box_from_rectangle (&box, extents); @@ -2078,7 +2083,7 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, if (regions_array) region_elements = _cairo_array_index (®ions_array->regions, 0); - if (extents.width < r->width || extents.height < r->height) { + if (!params->replay_all && (extents.width < r->width || extents.height < r->height)) { num_elements = _cairo_recording_surface_get_visible_commands (surface, &extents); use_indices = num_elements != surface->commands.num_elements; @@ -2106,6 +2111,13 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, continue; } + + if (params->target->backend->command_id) { + status = params->target->backend->command_id (params->target, params->regions_id, i); + if (unlikely (status)) + return status; + } + switch (command->header.type) { case CAIRO_COMMAND_PAINT: if (region_element) @@ -2457,6 +2469,7 @@ _cairo_recording_surface_replay (cairo_surface_t *surface, params.region = CAIRO_RECORDING_REGION_ALL; params.regions_id = 0; params.foreground_color = NULL; + params.replay_all = FALSE; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); } @@ -2480,6 +2493,7 @@ _cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surf params.regions_id = 0; params.foreground_color = foreground_color; params.foreground_used = FALSE; + params.replay_all = FALSE; status = _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); *foreground_used = params.foreground_used; @@ -2488,11 +2502,33 @@ _cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surf } cairo_status_t +_cairo_recording_surface_replay_with_transform (cairo_surface_t *surface, + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded, + cairo_bool_t replay_all) +{ + cairo_recording_surface_replay_params_t params; + + params.surface_extents = NULL; + params.surface_transform = surface_transform; + params.target = target; + params.target_clip = NULL; + params.surface_is_unbounded = surface_is_unbounded; + params.type = CAIRO_RECORDING_REPLAY; + params.region = CAIRO_RECORDING_REGION_ALL; + params.regions_id = 0; + params.foreground_color = NULL; + params.replay_all = replay_all; + + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); +} + +cairo_status_t _cairo_recording_surface_replay_with_clip (cairo_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) + const cairo_clip_t *target_clip) { cairo_recording_surface_replay_params_t params; @@ -2500,11 +2536,12 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, params.surface_transform = surface_transform; params.target = target; params.target_clip = target_clip; - params.surface_is_unbounded = surface_is_unbounded; + params.surface_is_unbounded = FALSE; params.type = CAIRO_RECORDING_REPLAY; params.region = CAIRO_RECORDING_REGION_ALL; params.regions_id = 0; params.foreground_color = NULL; + params.replay_all = FALSE; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); } @@ -2520,7 +2557,8 @@ _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, unsigned int regions_id, const cairo_matrix_t *surface_transform, cairo_surface_t *target, - cairo_bool_t surface_is_unbounded) + cairo_bool_t surface_is_unbounded, + cairo_bool_t replay_all) { cairo_recording_surface_replay_params_t params; @@ -2533,6 +2571,7 @@ _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, params.region = CAIRO_RECORDING_REGION_ALL; params.regions_id = regions_id; params.foreground_color = NULL; + params.replay_all = replay_all; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); } @@ -2555,6 +2594,7 @@ _cairo_recording_surface_replay_region (cairo_surface_t *surface, params.region = region; params.regions_id = regions_id; params.foreground_color = NULL; + params.replay_all = FALSE; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); } @@ -2702,6 +2742,21 @@ _cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface) return surface->has_only_op_over; } +cairo_bool_t +_cairo_recording_surface_has_tags (cairo_surface_t *surface) +{ + cairo_recording_surface_t *record; + + if (surface->status || ! _cairo_surface_is_recording (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return FALSE; + } + + record = (cairo_recording_surface_t *)surface; + + return record->has_tags; +} + static void print_indent (FILE *file, int indent) { @@ -2875,7 +2930,10 @@ _cairo_debug_print_recording_surface (FILE *file, case CAIRO_COMMAND_TAG: print_indent (file, indent); - fprintf(file, "%d TAG\n", i); + fprintf(file, "%d %s %s '%s'\n", + i, + command->tag.begin ? "BEGIN TAG" : "END TAG", + command->tag.tag_name, command->tag.attributes); break; default: diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c index 49c5999d2..50c92b25c 100644 --- a/src/cairo-spans-compositor.c +++ b/src/cairo-spans-compositor.c @@ -612,7 +612,7 @@ composite_aligned_boxes (const cairo_spans_compositor_t *compositor, recording_clip = _cairo_clip_from_boxes (boxes); status = _cairo_recording_surface_replay_with_clip (unwrap_source (source), - m, dst, recording_clip, FALSE); + m, dst, recording_clip); _cairo_clip_destroy (recording_clip); return status; diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h index 15032de20..856c596c8 100644 --- a/src/cairo-surface-backend-private.h +++ b/src/cairo-surface-backend-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California @@ -40,6 +41,7 @@ #include "cairo-compiler-private.h" #include "cairo-error-private.h" +#include "cairo-pattern-private.h" CAIRO_BEGIN_DECLS @@ -211,6 +213,29 @@ struct _cairo_surface_backend { (*supports_color_glyph) (void *surface, cairo_scaled_font_t *scaled_font, unsigned long glyph_index); + + /* Paginated surfaces only. During the analysis stage, if any + * recording surfaces used as a source are to be replayed, this + * function will be called at the begining and end of the replay. + * + * @recording_surface - the recording surface used as a source + * @source_type - a type indicating the combination of drawing + * operation and source type + * @begin - TRUE when called before the replay, FALSE when called + * after the replay has finished. + */ + cairo_warn cairo_int_status_t + (*analyze_recording_surface)(void *surface, + const cairo_surface_pattern_t *recording_surface_pattern, + unsigned int region_id, + cairo_analysis_source_t source_type, + cairo_bool_t begin); + + cairo_warn cairo_int_status_t + (*command_id) (void *surface, + unsigned int recording_id, + unsigned int command_id); + }; cairo_private cairo_status_t diff --git a/src/cairo-tag-attributes-private.h b/src/cairo-tag-attributes-private.h index 3f5fa5b64..1b770aef9 100644 --- a/src/cairo-tag-attributes-private.h +++ b/src/cairo-tag-attributes-private.h @@ -49,6 +49,15 @@ typedef enum { TAG_LINK_FILE, } cairo_tag_link_type_t; +typedef struct _cairo_content_attrs { + char *id; + char *tag_name; +} cairo_content_attrs_t; + +typedef struct _cairo_content_ref_attrs { + char *ref; +} cairo_content_ref_attrs_t; + typedef struct _cairo_link_attrs { cairo_tag_link_type_t link_type; cairo_array_t rects; @@ -58,6 +67,9 @@ typedef struct _cairo_link_attrs { int page; cairo_bool_t has_pos; cairo_point_double_t pos; + char *id; + char *ref; + int link_page; } cairo_link_attrs_t; typedef struct _cairo_dest_attrs { @@ -91,9 +103,27 @@ cairo_private cairo_int_status_t _cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs); cairo_private cairo_int_status_t +_cairo_tag_parse_content_attributes (const char *attributes, cairo_content_attrs_t *content_attrs); + +cairo_private cairo_int_status_t +_cairo_tag_parse_content_ref_attributes (const char *attributes, cairo_content_ref_attrs_t *content_ref_attrs); + +cairo_private cairo_int_status_t _cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *dest_attrs); cairo_private cairo_int_status_t _cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *dest_attrs); +cairo_private void +_cairo_tag_free_link_attributes (cairo_link_attrs_t *link_attrs); + +cairo_private void +_cairo_tag_free_dest_attributes (cairo_dest_attrs_t *link_attrs); + +cairo_private void +_cairo_tag_free_content_attributes (cairo_content_attrs_t *link_attrs); + +cairo_private void +_cairo_tag_free_content_ref_attributes (cairo_content_ref_attrs_t *link_attrs); + #endif /* CAIRO_TAG_ATTRIBUTES_PRIVATE_H */ diff --git a/src/cairo-tag-attributes.c b/src/cairo-tag-attributes.c index 85467ad73..4fde1b5b8 100644 --- a/src/cairo-tag-attributes.c +++ b/src/cairo-tag-attributes.c @@ -60,6 +60,25 @@ typedef struct _attribute_spec { } attribute_spec_t; /* + * id [optional] content id + * content [optional] One or more content ids + */ +static const attribute_spec_t _content_attrib_spec[] = { + { "tag_name", ATTRIBUTE_STRING }, + { "id", ATTRIBUTE_STRING }, + { NULL } +}; + +/* + * id [optional] content id + * content [optional] One or more content ids + */ +static const attribute_spec_t _content_ref_attrib_spec[] = { + { "ref", ATTRIBUTE_STRING }, + { NULL } +}; + +/* * 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. @@ -68,7 +87,7 @@ typedef struct _attribute_spec { * internal [optional] If true, the name may be optimized out of the PDF where * possible. Default false. */ -static attribute_spec_t _dest_attrib_spec[] = { +static const attribute_spec_t _dest_attrib_spec[] = { { "name", ATTRIBUTE_STRING }, { "x", ATTRIBUTE_FLOAT }, { "y", ATTRIBUTE_FLOAT }, @@ -103,7 +122,7 @@ static attribute_spec_t _dest_attrib_spec[] = { * 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[] = +static const attribute_spec_t _link_attrib_spec[] = { { "rect", ATTRIBUTE_FLOAT, -1 }, { "dest", ATTRIBUTE_STRING }, @@ -111,6 +130,9 @@ static attribute_spec_t _link_attrib_spec[] = { "file", ATTRIBUTE_STRING }, { "page", ATTRIBUTE_INT }, { "pos", ATTRIBUTE_FLOAT, 2 }, + { "id", ATTRIBUTE_STRING }, + { "ref", ATTRIBUTE_STRING }, + { "link_page", ATTRIBUTE_INT }, { NULL } }; @@ -131,7 +153,7 @@ static attribute_spec_t _link_attrib_spec[] = * DamagedRowsBeforeError - Number of damages rows tolerated before an error * occurs. Default: 0. */ -static attribute_spec_t _ccitt_params_spec[] = +static const attribute_spec_t _ccitt_params_spec[] = { { "Columns", ATTRIBUTE_INT }, { "Rows", ATTRIBUTE_INT }, @@ -152,7 +174,7 @@ static attribute_spec_t _ccitt_params_spec[] = * ury - upper right y xoordinate * all coordinates are in PostScript coordinates. */ -static attribute_spec_t _eps_params_spec[] = +static const attribute_spec_t _eps_params_spec[] = { { "bbox", ATTRIBUTE_FLOAT, 4 }, { NULL } @@ -362,7 +384,7 @@ parse_name (const char *attributes, const char *p, const char **end, char **s) attributes, p); p2 = p; - while (_cairo_isalpha (*p2) || _cairo_isdigit (*p2)) + while (_cairo_isalpha (*p2) || _cairo_isdigit (*p2) || *p2 == '_') p2++; len = p2 - p; @@ -376,9 +398,9 @@ parse_name (const char *attributes, const char *p, const char **end, char **s) } static cairo_int_status_t -parse_attributes (const char *attributes, attribute_spec_t *attrib_def, cairo_list_t *list) +parse_attributes (const char *attributes, const attribute_spec_t *attrib_def, cairo_list_t *list) { - attribute_spec_t *def; + const attribute_spec_t *def; attribute_t *attrib; char *name = NULL; cairo_int_status_t status; @@ -490,13 +512,79 @@ free_attributes_list (cairo_list_t *list) } cairo_int_status_t +_cairo_tag_parse_content_attributes (const char *attributes, cairo_content_attrs_t *content_attrs) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + + cairo_list_init (&list); + status = parse_attributes (attributes, _content_attrib_spec, &list); + if (unlikely (status)) + goto cleanup; + + memset (content_attrs, 0, sizeof (cairo_content_attrs_t)); + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "tag_name") == 0) { + content_attrs->tag_name = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "id") == 0) { + content_attrs->id = strdup (attr->scalar.s); + } + } + + if (! content_attrs->tag_name) { + status = _cairo_tag_error ("CONTENT attributes: \"%s\" missing tag_name attribute", + attributes); + } else if (! content_attrs->tag_name) { + status = _cairo_tag_error ("CONTENT attributes: \"%s\" missing id attribute", + attributes); + } + + cleanup: + free_attributes_list (&list); + + return status; +} + +cairo_int_status_t +_cairo_tag_parse_content_ref_attributes (const char *attributes, cairo_content_ref_attrs_t *content_ref_attrs) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + + cairo_list_init (&list); + status = parse_attributes (attributes, _content_ref_attrib_spec, &list); + if (unlikely (status)) + goto cleanup; + + memset (content_ref_attrs, 0, sizeof (cairo_content_ref_attrs_t)); + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "ref") == 0) { + content_ref_attrs->ref = strdup (attr->scalar.s); + } + } + + if (! content_ref_attrs->ref) { + status = _cairo_tag_error ("CONTENT_REF attributes: \"%s\" missing ref attribute", + attributes); + } + + cleanup: + free_attributes_list (&list); + + return status; +} + +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_bool_t has_rect = FALSE; cairo_bool_t invalid_combination = FALSE; cairo_list_init (&list); @@ -554,7 +642,16 @@ _cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *li if (unlikely (status)) goto cleanup; } - has_rect = TRUE; + } else if (strcmp (attr->name, "id") == 0) { + link_attrs->id = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "ref") == 0) { + link_attrs->ref = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "link_page") == 0) { + link_attrs->link_page = attr->scalar.i; + if (link_attrs->link_page < 1) { + status = _cairo_tag_error ("Link attributes: \"%s\" page must be >= 1", attributes); + goto cleanup; + } } } @@ -723,3 +820,33 @@ _cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *eps_par return status; } + +void +_cairo_tag_free_link_attributes (cairo_link_attrs_t *link_attrs) +{ + _cairo_array_fini (&link_attrs->rects); + free (link_attrs->dest); + free (link_attrs->uri); + free (link_attrs->file); + free (link_attrs->id); + free (link_attrs->ref); +} + +void +_cairo_tag_free_dest_attributes (cairo_dest_attrs_t *dest_attrs) +{ + free (dest_attrs->name); +} + +void +_cairo_tag_free_content_attributes (cairo_content_attrs_t *content_attrs) +{ + free (content_attrs->id); + free (content_attrs->tag_name); +} + +void +_cairo_tag_free_content_ref_attributes (cairo_content_ref_attrs_t *content_ref_attrs) +{ + free (content_ref_attrs->ref); +} diff --git a/src/cairo-tag-stack-private.h b/src/cairo-tag-stack-private.h index fbb2c0e25..49145bf1d 100644 --- a/src/cairo-tag-stack-private.h +++ b/src/cairo-tag-stack-private.h @@ -46,6 +46,9 @@ typedef enum { TAG_TYPE_STRUCTURE = 1, TAG_TYPE_LINK = 2, TAG_TYPE_DEST = 4, + TAG_TYPE_CONTENT = 8, + TAG_TYPE_CONTENT_REF = 16, + TAG_TYPE_ARTIFACT = 16, } cairo_tag_type_t; /* The type of the structure tree. */ @@ -99,6 +102,12 @@ cairo_private cairo_tag_stack_elem_t * _cairo_tag_stack_top_elem (cairo_tag_stack_t *stack); cairo_private void +_cairo_tag_stack_foreach (cairo_tag_stack_t *stack, + void (*func)(cairo_tag_stack_elem_t *elem, + void *closure), + void *closure); + +cairo_private void _cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem); cairo_private cairo_tag_type_t diff --git a/src/cairo-tag-stack.c b/src/cairo-tag-stack.c index 7341aa41a..65444c907 100644 --- a/src/cairo-tag-stack.c +++ b/src/cairo-tag-stack.c @@ -99,6 +99,9 @@ static const char * _cairo_tag_stack_struct_pdf_list[] = "Formula", "Form", + /* Section 14.8.2.2.2 - Artifacts */ + "Artifact", + NULL }; @@ -106,6 +109,8 @@ static const char * _cairo_tag_stack_struct_pdf_list[] = static const char * _cairo_tag_stack_cairo_tag_list[] = { CAIRO_TAG_DEST, + CAIRO_TAG_CONTENT, + CAIRO_TAG_CONTENT_REF, NULL }; @@ -166,6 +171,17 @@ _cairo_tag_stack_push (cairo_tag_stack_t *stack, return _cairo_tag_error ("Invalid tag: %s", name); } + cairo_tag_stack_elem_t *top = _cairo_tag_stack_top_elem (stack); + if (top && + (strcmp (top->name, CAIRO_TAG_CONTENT) == 0 || + strcmp (top->name, CAIRO_TAG_CONTENT_REF) == 0 || + strcmp (top->name, "Artifact") == 0)) + { + return _cairo_tag_error ("%s tag can not contain nested tags", + (strcmp (top->name, CAIRO_TAG_CONTENT) == 0) ? "CAIRO_TAG_CONTENT" : + ((strcmp (top->name, CAIRO_TAG_CONTENT_REF) == 0) ? "CAIRO_TAG_CONTENT_REF" : top->name)); + } + 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; @@ -257,6 +273,19 @@ _cairo_tag_stack_top_elem (cairo_tag_stack_t *stack) } void +_cairo_tag_stack_foreach (cairo_tag_stack_t *stack, + void (*func)(cairo_tag_stack_elem_t *elem, + void *closure), + void *closure) +{ + cairo_tag_stack_elem_t *elem; + + cairo_list_foreach_entry (elem, cairo_tag_stack_elem_t, &stack->list, link) { + func (elem, closure); + } +} + +void _cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem) { free (elem->name); @@ -274,9 +303,15 @@ _cairo_tag_get_type (const char *name) if (strcmp(name, "Link") == 0) return (TAG_TYPE_LINK | TAG_TYPE_STRUCTURE); - if (strcmp(name, "cairo.dest") == 0) + if (strcmp(name, CAIRO_TAG_DEST) == 0) return TAG_TYPE_DEST; + if (strcmp(name, CAIRO_TAG_CONTENT) == 0) + return TAG_TYPE_CONTENT; + + if (strcmp(name, CAIRO_TAG_CONTENT_REF) == 0) + return TAG_TYPE_CONTENT_REF; + return TAG_TYPE_STRUCTURE; } diff --git a/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c index d1402d2a3..3414fc268 100644 --- a/src/cairo-traps-compositor.c +++ b/src/cairo-traps-compositor.c @@ -1240,7 +1240,7 @@ composite_aligned_boxes (const cairo_traps_compositor_t *compositor, recording_clip = _cairo_clip_from_boxes (boxes); status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source), - m, dst, recording_clip, FALSE); + m, dst, recording_clip); _cairo_clip_destroy (recording_clip); return status; diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 736a1bcb3..74a366c23 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -437,6 +437,17 @@ typedef struct _cairo_unscaled_font { cairo_reference_count_t ref_count; const cairo_unscaled_font_backend_t *backend; } cairo_unscaled_font_t; + +typedef enum _cairo_analysis_source { + CAIRO_ANALYSIS_SOURCE_PAINT, + CAIRO_ANALYSIS_SOURCE_MASK, + CAIRO_ANALYSIS_MASK_MASK, + CAIRO_ANALYSIS_SOURCE_FILL, + CAIRO_ANALYSIS_SOURCE_STROKE, + CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS, + CAIRO_ANALYSIS_SOURCE_NONE /* Used when analysis_source is not applicable. */ +} cairo_analysis_source_t; + CAIRO_END_DECLS #endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c index a4441dc46..ab3d6881f 100644 --- a/src/cairo-xcb-surface-render.c +++ b/src/cairo-xcb-surface-render.c @@ -1112,8 +1112,7 @@ record_to_picture (cairo_surface_t *target, status = _cairo_recording_surface_replay_with_clip (source, &matrix, tmp, - NULL, - FALSE); + NULL); if (unlikely (status)) { cairo_surface_destroy (tmp); return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c index 63e155e00..4c3b99d9e 100644 --- a/src/cairo-xlib-source.c +++ b/src/cairo-xlib-source.c @@ -920,8 +920,7 @@ record_source (cairo_xlib_surface_t *dst, recording = recording_pattern_get_surface (&pattern->base), status = _cairo_recording_surface_replay_with_clip (recording, &matrix, &src->base, - NULL, - FALSE); + NULL); cairo_surface_destroy (recording); if (unlikely (status)) { cairo_surface_destroy (&src->base); diff --git a/src/cairo.h b/src/cairo.h index eef4c442b..a2c955281 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1039,6 +1039,8 @@ cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); #define CAIRO_TAG_DEST "cairo.dest" #define CAIRO_TAG_LINK "Link" +#define CAIRO_TAG_CONTENT "cairo.content" +#define CAIRO_TAG_CONTENT_REF "cairo.content_ref" cairo_public void cairo_tag_begin (cairo_t *cr, const char *tag_name, const char *attributes); diff --git a/src/cairoint.h b/src/cairoint.h index e5c281842..ddbbe7baa 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California |