Bug Summary

File:root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c
Warning:line 1262, column 22
Access to field 'extents' results in a dereference of an undefined pointer value (loaded from variable 'content_node')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cairo-pdf-interchange.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/cairo/cairo/src -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/cairo/cairo/src -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D HAVE_FT_LOAD_SFNT_TABLE -D PACKAGE_VERSION="moz" -D PACKAGE_BUGREPORT="http://bugzilla.mozilla.org/" -D CAIRO_HAS_PTHREAD -D _GNU_SOURCE -D MOZ_TREE_PIXMAN -D SIZEOF_VOID_P=__SIZEOF_POINTER__ -D SIZEOF_INT=__SIZEOF_INT__ -D SIZEOF_LONG=__SIZEOF_LONG__ -D SIZEOF_LONG_LONG=__SIZEOF_LONG_LONG__ -D HAVE_UINT64_T -D HAVE_CXX11_ATOMIC_PRIMITIVES -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/cairo/cairo/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/cairo/cairo/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/freetype2 -I /usr/include/libpng16 -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-range-loop-analysis -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-unknown-warning-option -Wno-enum-compare -Wno-int-to-pointer-cast -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-sign-compare -Wno-type-limits -Wno-missing-field-initializers -Wno-conversion -Wno-narrowing -Wno-switch -Wno-unused -Wno-unused-variable -Wno-error=uninitialized -Wno-absolute-value -Wno-deprecated-register -Wno-incompatible-pointer-types -Wno-macro-redefined -Wno-shift-negative-value -Wno-tautological-compare -Wno-tautological-constant-out-of-range-compare -Wno-unreachable-code -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-26-231904-1820671-1 -x c /root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c
1/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2/* cairo - a vector graphics library with display and print output
3 *
4 * Copyright © 2016 Adrian Johnson
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
13 *
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
19 *
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
24 *
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
28 *
29 * The Original Code is the cairo graphics library.
30 *
31 * The Initial Developer of the Original Code is Adrian Johnson.
32 *
33 * Contributor(s):
34 * Adrian Johnson <ajohnson@redneon.com>
35 */
36
37
38/* PDF Document Interchange features:
39 * - metadata
40 * - document outline
41 * - tagged pdf
42 * - hyperlinks
43 * - page labels
44 */
45
46#define _DEFAULT_SOURCE1 /* for localtime_r(), gmtime_r(), snprintf(), strdup() */
47#include "cairoint.h"
48
49#include "cairo-pdf.h"
50#include "cairo-pdf-surface-private.h"
51
52#include "cairo-array-private.h"
53#include "cairo-error-private.h"
54#include "cairo-output-stream-private.h"
55#include "cairo-recording-surface-inline.h"
56#include "cairo-recording-surface-private.h"
57#include "cairo-surface-snapshot-inline.h"
58
59#include <time.h>
60
61#ifndef HAVE_LOCALTIME_R1
62#define localtime_r(T, BUF) (*(BUF) = *localtime (T))
63#endif
64#ifndef HAVE_GMTIME_R1
65#define gmtime_r(T, BUF) (*(BUF) = *gmtime (T))
66#endif
67
68/* #define DEBUG_PDF_INTERCHANGE 1 */
69
70#if DEBUG_PDF_INTERCHANGE
71static void
72print_tree (cairo_pdf_surface_t *surface, cairo_pdf_struct_tree_node_t *node);
73
74static void
75print_command (cairo_pdf_command_t *command, int indent);
76
77static void
78print_command_list(cairo_pdf_command_list_t *command_list);
79#endif
80
81static void
82_cairo_pdf_command_init_key (cairo_pdf_command_entry_t *key)
83{
84 key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE5381, (uintptr_t)key->recording_id);
85 key->base.hash = _cairo_hash_uintptr (key->base.hash, (uintptr_t)key->command_id);
86}
87
88static cairo_bool_t
89_cairo_pdf_command_equal (const void *key_a, const void *key_b)
90{
91 const cairo_pdf_command_entry_t *a = key_a;
92 const cairo_pdf_command_entry_t *b = key_b;
93
94 return a->recording_id == b->recording_id && a->command_id == b->command_id;
95}
96
97static void
98_cairo_pdf_command_pluck (void *entry, void *closure)
99{
100 cairo_pdf_command_entry_t *dest = entry;
101 cairo_hash_table_t *table = closure;
102
103 _cairo_hash_table_remove (table, &dest->base);
104 free (dest);
105}
106
107static cairo_pdf_struct_tree_node_t *
108lookup_node_for_command (cairo_pdf_surface_t *surface,
109 unsigned int recording_id,
110 unsigned int command_id)
111{
112 cairo_pdf_command_entry_t entry_key;
113 cairo_pdf_command_entry_t *entry;
114 cairo_pdf_interchange_t *ic = &surface->interchange;
115
116 entry_key.recording_id = recording_id;
117 entry_key.command_id = command_id;
118 _cairo_pdf_command_init_key (&entry_key);
119 entry = _cairo_hash_table_lookup (ic->command_to_node_map, &entry_key.base);
120 assert (entry != NULL)((void) sizeof ((entry != ((void*)0)) ? 1 : 0), __extension__
({ if (entry != ((void*)0)) ; else __assert_fail ("entry != NULL"
, "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 120, __extension__ __PRETTY_FUNCTION__); }))
;
121 return entry->node;
122}
123
124static cairo_int_status_t
125command_list_add (cairo_pdf_surface_t *surface,
126 unsigned int command_id,
127 cairo_pdf_operation_t flags)
128{
129 cairo_pdf_interchange_t *ic = &surface->interchange;
130 cairo_pdf_command_t command;
131 cairo_int_status_t status;
132
133 unsigned num_elements = _cairo_array_num_elements (&ic->current_commands->commands);
134 if (command_id > num_elements) {
135 void *elements;
136 unsigned additional_elements = command_id - num_elements;
137 status = _cairo_array_allocate (&ic->current_commands->commands, additional_elements, &elements);
138 if (unlikely (status)(__builtin_expect (!!(status), 0)))
139 return status;
140 memset (elements, 0, additional_elements * sizeof(cairo_pdf_command_t));
141 }
142
143 command.group = NULL((void*)0);
144 command.node = NULL((void*)0);
145 command.command_id = command_id;
146 command.mcid_index = 0;
147 command.flags = flags;
148 return _cairo_array_append (&ic->current_commands->commands, &command);
149}
150
151static cairo_int_status_t
152command_list_push_group (cairo_pdf_surface_t *surface,
153 unsigned int command_id,
154 cairo_surface_t *recording_surface,
155 unsigned int region_id)
156{
157 cairo_pdf_interchange_t *ic = &surface->interchange;
158 cairo_pdf_command_t *command;
159 cairo_pdf_command_list_t *group;
160 cairo_pdf_recording_surface_commands_t recording_commands;
161 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
162
163 group = _cairo_malloc (sizeof(cairo_pdf_command_list_t))((sizeof(cairo_pdf_command_list_t)) != 0 ? malloc(sizeof(cairo_pdf_command_list_t
)) : ((void*)0))
;
164 _cairo_array_init (&group->commands, sizeof(cairo_pdf_command_t));
165 group->parent = ic->current_commands;
166
167 command_list_add (surface, command_id, PDF_GROUP);
168 command = _cairo_array_index (&ic->current_commands->commands, command_id);
169 command->group = group;
170 ic->current_commands = group;
171
172 recording_commands.recording_surface = recording_surface;
173 recording_commands.command_list = group;
174 recording_commands.region_id = region_id;
175 status = _cairo_array_append (&ic->recording_surface_commands, &recording_commands);
176
177 return status;
178}
179
180static void
181command_list_pop_group (cairo_pdf_surface_t *surface)
182{
183 cairo_pdf_interchange_t *ic = &surface->interchange;
184
185 ic->current_commands = ic->current_commands->parent;
186}
187
188static cairo_bool_t
189command_list_is_group (cairo_pdf_surface_t *surface,
190 unsigned int command_id)
191{
192 cairo_pdf_interchange_t *ic = &surface->interchange;
193 cairo_pdf_command_t *command;
194 unsigned num_elements = _cairo_array_num_elements (&ic->current_commands->commands);
195
196 if (command_id >= num_elements)
197 return FALSE0;
198
199 command = _cairo_array_index (&ic->current_commands->commands, command_id);
200 return command->flags == PDF_GROUP;
201}
202
203
204/* Is there any content between current command and next
205 * begin/end/group? */
206static cairo_bool_t
207command_list_has_content (cairo_pdf_surface_t *surface,
208 unsigned int command_id,
209 unsigned int *content_command_id)
210{
211 cairo_pdf_interchange_t *ic = &surface->interchange;
212 cairo_pdf_command_t *command;
213 unsigned i;
214 unsigned num_elements = _cairo_array_num_elements (&ic->current_commands->commands);
215
216 for (i = command_id + 1; i < num_elements; i++) {
217 command = _cairo_array_index (&ic->current_commands->commands, i);
218 switch (command->flags) {
219 case PDF_CONTENT:
220 if (content_command_id)
221 *content_command_id = i;
222 return TRUE1;
223 break;
224 case PDF_BEGIN:
225 case PDF_END:
226 case PDF_GROUP:
227 return FALSE0;
228 case PDF_NONE:
229 break;
230 }
231 }
232 return FALSE0;
233}
234
235static void
236command_list_set_mcid (cairo_pdf_surface_t *surface,
237 unsigned int command_id,
238 cairo_pdf_struct_tree_node_t *node,
239 int mcid_index)
240{
241 cairo_pdf_interchange_t *ic = &surface->interchange;
242 cairo_pdf_command_t *command;
243
244 command = _cairo_array_index (&ic->current_commands->commands, command_id);
245 command->node = node;
246 command->mcid_index = mcid_index;
247}
248
249static void
250command_list_set_current_recording_commands (cairo_pdf_surface_t *surface,
251 cairo_surface_t *recording_surface,
252 unsigned int region_id)
253{
254 cairo_pdf_interchange_t *ic = &surface->interchange;
255 unsigned i;
256 cairo_pdf_recording_surface_commands_t *commands;
257 unsigned num_elements = _cairo_array_num_elements (&ic->recording_surface_commands);
258
259 for (i = 0; i < num_elements; i++) {
260 commands = _cairo_array_index (&ic->recording_surface_commands, i);
261 if (commands->region_id == region_id) {
262 ic->current_commands = commands->command_list;
263 return;
264 }
265 }
266 ASSERT_NOT_REACHEDdo { ((void) sizeof ((!"reached") ? 1 : 0), __extension__ ({ if
(!"reached") ; else __assert_fail ("!\"reached\"", "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 266, __extension__ __PRETTY_FUNCTION__); })); } while (0)
; /* recording_surface not found */
267}
268
269static void
270update_mcid_order (cairo_pdf_surface_t *surface,
271 cairo_pdf_command_list_t *command_list)
272{
273 cairo_pdf_interchange_t *ic = &surface->interchange;
274 cairo_pdf_command_t *command;
275 cairo_pdf_page_mcid_t *mcid_elem;
276 unsigned i;
277 unsigned num_elements = _cairo_array_num_elements (&command_list->commands);
278
279 for (i = 0; i < num_elements; i++) {
280 command = _cairo_array_index (&command_list->commands, i);
281 if (command->node) {
282 mcid_elem = _cairo_array_index (&command->node->mcid, command->mcid_index);
283 mcid_elem->order = ic->mcid_order++;
284 }
285
286 if (command->group)
287 update_mcid_order (surface, command->group);
288 }
289}
290
291static void
292_cairo_pdf_content_tag_init_key (cairo_pdf_content_tag_t *key)
293{
294 key->base.hash = _cairo_hash_string (key->node->attributes.content.id);
295}
296
297static cairo_bool_t
298_cairo_pdf_content_tag_equal (const void *key_a, const void *key_b)
299{
300 const cairo_pdf_content_tag_t *a = key_a;
301 const cairo_pdf_content_tag_t *b = key_b;
302
303 return strcmp (a->node->attributes.content.id, b->node->attributes.content.id) == 0;
304}
305
306static void
307_cairo_pdf_content_tag_pluck (void *entry, void *closure)
308{
309 cairo_pdf_content_tag_t *content_tag = entry;
310 cairo_hash_table_t *table = closure;
311
312 _cairo_hash_table_remove (table, &content_tag->base);
313 free (content_tag);
314}
315
316static cairo_status_t
317lookup_content_node_for_ref_node (cairo_pdf_surface_t *surface,
318 cairo_pdf_struct_tree_node_t *ref_node,
319 cairo_pdf_struct_tree_node_t **node)
320{
321 cairo_pdf_content_tag_t entry_key;
322 cairo_pdf_content_tag_t *entry;
323 cairo_pdf_interchange_t *ic = &surface->interchange;
324
325 entry_key.node = ref_node;
326 _cairo_pdf_content_tag_init_key (&entry_key);
327 entry = _cairo_hash_table_lookup (ic->content_tag_map, &entry_key.base);
8
Value assigned to field 'parent', which participates in a condition later
328 if (!entry) {
9
Assuming 'entry' is null, which participates in a condition later
10
Taking true branch
329 return _cairo_tag_error ("CONTENT_REF ref='%s' not found",
11
Returning without writing to '*node'
12
Returning value, which participates in a condition later
330 ref_node->attributes.content_ref.ref);
331 }
332
333 *node = entry->node;
334 return CAIRO_STATUS_SUCCESS;
335}
336
337static void
338write_rect_to_pdf_quad_points (cairo_output_stream_t *stream,
339 const cairo_rectangle_t *rect,
340 double surface_height)
341{
342 _cairo_output_stream_printf (stream,
343 "%f %f %f %f %f %f %f %f",
344 rect->x,
345 surface_height - rect->y,
346 rect->x + rect->width,
347 surface_height - rect->y,
348 rect->x + rect->width,
349 surface_height - (rect->y + rect->height),
350 rect->x,
351 surface_height - (rect->y + rect->height));
352}
353
354static void
355write_rect_int_to_pdf_bbox (cairo_output_stream_t *stream,
356 const cairo_rectangle_int_t *rect,
357 double surface_height)
358{
359 _cairo_output_stream_printf (stream,
360 "%d %f %d %f",
361 rect->x,
362 surface_height - (rect->y + rect->height),
363 rect->x + rect->width,
364 surface_height - rect->y);
365}
366
367static cairo_int_status_t
368add_tree_node (cairo_pdf_surface_t *surface,
369 cairo_pdf_struct_tree_node_t *parent,
370 const char *name,
371 const char *attributes,
372 cairo_pdf_struct_tree_node_t **new_node)
373{
374 cairo_pdf_struct_tree_node_t *node;
375 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
376
377 node = _cairo_malloc (sizeof(cairo_pdf_struct_tree_node_t))((sizeof(cairo_pdf_struct_tree_node_t)) != 0 ? malloc(sizeof(
cairo_pdf_struct_tree_node_t)) : ((void*)0))
;
378 if (unlikely (node == NULL)(__builtin_expect (!!(node == ((void*)0)), 0)))
379 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
380
381 node->name = strdup (name);
382 node->res = _cairo_pdf_surface_new_object (surface);
383 if (node->res.id == 0)
384 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
385
386 node->parent = parent;
387 cairo_list_init (&node->children);
388 _cairo_array_init (&node->mcid, sizeof (cairo_pdf_page_mcid_t));
389 node->annot = NULL((void*)0);
390 node->extents.valid = FALSE0;
391
392 cairo_list_add_tail (&node->link, &parent->children);
393
394 if (strcmp (node->name, CAIRO_TAG_CONTENT"cairo.content") == 0) {
395 node->type = PDF_NODE_CONTENT;
396 status = _cairo_tag_parse_content_attributes (attributes, &node->attributes.content);
397 } else if (strcmp (node->name, CAIRO_TAG_CONTENT_REF"cairo.content_ref") == 0) {
398 node->type = PDF_NODE_CONTENT_REF;
399 status = _cairo_tag_parse_content_ref_attributes (attributes, &node->attributes.content_ref);
400 } else if (strcmp (node->name, "Artifact") == 0) {
401 node->type = PDF_NODE_ARTIFACT;
402 } else {
403 node->type = PDF_NODE_STRUCT;
404 }
405
406 *new_node = node;
407 return status;
408}
409
410static void
411free_node (cairo_pdf_struct_tree_node_t *node)
412{
413 cairo_pdf_struct_tree_node_t *child, *next;
414
415 if (!node)
416 return;
417
418 cairo_list_foreach_entry_safe (child, next, cairo_pdf_struct_tree_node_t,for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&node->children)->next
); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__ - __builtin_offsetof
(cairo_pdf_struct_tree_node_t, link)); }), next = ({ const __typeof__
(((cairo_pdf_struct_tree_node_t *) 0)->link) *mptr__ = (child
->link.next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
); &child->link != (&node->children); child = next
, next = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t *
) 0)->link) *mptr__ = (next->link.next); (cairo_pdf_struct_tree_node_t
*) ((char *) mptr__ - __builtin_offsetof(cairo_pdf_struct_tree_node_t
, link)); }))
419 &node->children,for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&node->children)->next
); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__ - __builtin_offsetof
(cairo_pdf_struct_tree_node_t, link)); }), next = ({ const __typeof__
(((cairo_pdf_struct_tree_node_t *) 0)->link) *mptr__ = (child
->link.next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
); &child->link != (&node->children); child = next
, next = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t *
) 0)->link) *mptr__ = (next->link.next); (cairo_pdf_struct_tree_node_t
*) ((char *) mptr__ - __builtin_offsetof(cairo_pdf_struct_tree_node_t
, link)); }))
420 link)for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&node->children)->next
); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__ - __builtin_offsetof
(cairo_pdf_struct_tree_node_t, link)); }), next = ({ const __typeof__
(((cairo_pdf_struct_tree_node_t *) 0)->link) *mptr__ = (child
->link.next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
); &child->link != (&node->children); child = next
, next = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t *
) 0)->link) *mptr__ = (next->link.next); (cairo_pdf_struct_tree_node_t
*) ((char *) mptr__ - __builtin_offsetof(cairo_pdf_struct_tree_node_t
, link)); }))
421 {
422 cairo_list_del (&child->link);
423 free_node (child);
424 }
425 free (node->name);
426 _cairo_array_fini (&node->mcid);
427 if (node->type == PDF_NODE_CONTENT)
428 _cairo_tag_free_content_attributes (&node->attributes.content);
429
430 if (node->type == PDF_NODE_CONTENT_REF)
431 _cairo_tag_free_content_ref_attributes (&node->attributes.content_ref);
432
433 free (node);
434}
435
436static cairo_status_t
437add_mcid_to_node (cairo_pdf_surface_t *surface,
438 cairo_pdf_struct_tree_node_t *node,
439 unsigned int command_id,
440 int *mcid)
441{
442 cairo_pdf_page_mcid_t mcid_elem;
443 cairo_int_status_t status;
444 cairo_pdf_interchange_t *ic = &surface->interchange;
445
446 status = _cairo_array_append (&ic->mcid_to_tree, &node);
447 if (unlikely (status)(__builtin_expect (!!(status), 0)))
448 return status;
449
450 mcid_elem.order = -1;
451 mcid_elem.page = _cairo_array_num_elements (&surface->pages);
452 mcid_elem.xobject_res = ic->current_recording_surface_res;
453 mcid_elem.mcid = _cairo_array_num_elements (&ic->mcid_to_tree) - 1;
454 mcid_elem.child_node = NULL((void*)0);
455 command_list_set_mcid (surface, command_id, node, _cairo_array_num_elements (&node->mcid));
456 *mcid = mcid_elem.mcid;
457 return _cairo_array_append (&node->mcid, &mcid_elem);
458}
459
460static cairo_status_t
461add_child_to_mcid_array (cairo_pdf_surface_t *surface,
462 cairo_pdf_struct_tree_node_t *node,
463 unsigned int command_id,
464 cairo_pdf_struct_tree_node_t *child)
465{
466 cairo_pdf_page_mcid_t mcid_elem;
467
468 mcid_elem.order = -1;
469 mcid_elem.page = 0;
470 mcid_elem.xobject_res.id = 0;
471 mcid_elem.mcid = 0;
472 mcid_elem.child_node = child;
473 command_list_set_mcid (surface, command_id, node, _cairo_array_num_elements (&node->mcid));
474 return _cairo_array_append (&node->mcid, &mcid_elem);
475}
476
477static cairo_int_status_t
478add_annotation (cairo_pdf_surface_t *surface,
479 cairo_pdf_struct_tree_node_t *node,
480 const char *name,
481 const char *attributes)
482{
483 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
484 cairo_pdf_interchange_t *ic = &surface->interchange;
485 cairo_pdf_annotation_t *annot;
486
487 annot = _cairo_malloc (sizeof (cairo_pdf_annotation_t))((sizeof (cairo_pdf_annotation_t)) != 0 ? malloc(sizeof (cairo_pdf_annotation_t
)) : ((void*)0))
;
488 if (unlikely (annot == NULL)(__builtin_expect (!!(annot == ((void*)0)), 0)))
489 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
490
491 status = _cairo_tag_parse_link_attributes (attributes, &annot->link_attrs);
492 if (unlikely (status)(__builtin_expect (!!(status), 0))) {
493 free (annot);
494 return status;
495 }
496
497 if (annot->link_attrs.link_page == 0)
498 annot->link_attrs.link_page = _cairo_array_num_elements (&surface->pages);
499
500 annot->node = node;
501
502 annot->res = _cairo_pdf_surface_new_object (surface);
503 if (annot->res.id == 0)
504 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
505
506 node->annot = annot;
507 status = _cairo_array_append (&ic->annots, &annot);
508
509 return status;
510}
511
512static void
513free_annotation (cairo_pdf_annotation_t *annot)
514{
515 _cairo_tag_free_link_attributes (&annot->link_attrs);
516 free (annot);
517}
518
519static void
520cairo_pdf_interchange_clear_annotations (cairo_pdf_surface_t *surface)
521{
522 cairo_pdf_interchange_t *ic = &surface->interchange;
523 int num_elems, i;
524
525 num_elems = _cairo_array_num_elements (&ic->annots);
526 for (i = 0; i < num_elems; i++) {
527 cairo_pdf_annotation_t * annot;
528
529 _cairo_array_copy_element (&ic->annots, i, &annot);
530 free_annotation (annot);
531 }
532 _cairo_array_truncate (&ic->annots, 0);
533}
534
535static void
536cairo_pdf_interchange_write_node_mcid (cairo_pdf_surface_t *surface,
537 cairo_pdf_page_mcid_t *mcid_elem,
538 int page)
539{
540 cairo_pdf_page_info_t *page_info;
541
542 page_info = _cairo_array_index (&surface->pages, mcid_elem->page - 1);
543 if (mcid_elem->page == page && mcid_elem->xobject_res.id == 0) {
544 _cairo_output_stream_printf (surface->object_stream.stream, "%d ", mcid_elem->mcid);
545 } else {
546 _cairo_output_stream_printf (surface->object_stream.stream,
547 "\n << /Type /MCR ");
548 if (mcid_elem->page != page) {
549 _cairo_output_stream_printf (surface->object_stream.stream,
550 "/Pg %d 0 R ",
551 page_info->page_res.id);
552 }
553 if (mcid_elem->xobject_res.id != 0) {
554 _cairo_output_stream_printf (surface->object_stream.stream,
555 "/Stm %d 0 R ",
556 mcid_elem->xobject_res.id);
557 }
558 _cairo_output_stream_printf (surface->object_stream.stream,
559 "/MCID %d >> ",
560 mcid_elem->mcid);
561 }
562}
563
564static int
565_mcid_order_compare (const void *a,
566 const void *b)
567{
568 const cairo_pdf_page_mcid_t *mcid_a = a;
569 const cairo_pdf_page_mcid_t *mcid_b = b;
570
571 if (mcid_a->order < mcid_b->order)
572 return -1;
573 else if (mcid_a->order > mcid_b->order)
574 return 1;
575 else
576 return 0;
577}
578
579static cairo_int_status_t
580cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface,
581 cairo_pdf_struct_tree_node_t *node,
582 int depth)
583{
584 cairo_pdf_page_mcid_t *mcid_elem, *child_mcid_elem;
585 unsigned i, j, num_mcid;
586 int first_page = 0;
587 cairo_pdf_page_info_t *page_info;
588 cairo_int_status_t status;
589 cairo_bool_t has_children = FALSE0;
590
591 /* The Root node is written in cairo_pdf_interchange_write_struct_tree(). */
592 if (!node->parent)
593 return CAIRO_STATUS_SUCCESS;
594
595 if (node->type == PDF_NODE_CONTENT ||
596 node->type == PDF_NODE_CONTENT_REF ||
597 node->type == PDF_NODE_ARTIFACT)
598 {
599 return CAIRO_STATUS_SUCCESS;
600 }
601
602 status = _cairo_pdf_surface_object_begin (surface, node->res);
603 if (unlikely (status)(__builtin_expect (!!(status), 0)))
604 return status;
605
606 _cairo_output_stream_printf (surface->object_stream.stream,
607 "<< /Type /StructElem\n"
608 " /S /%s\n"
609 " /P %d 0 R\n",
610 node->name,
611 node->parent->res.id);
612
613 /* Write /K entry (children of this StructElem) */
614 num_mcid = _cairo_array_num_elements (&node->mcid);
615 if (num_mcid > 0 ) {
616 _cairo_array_sort (&node->mcid, _mcid_order_compare);
617 /* Find the first MCID element and use the page number to set /Pg */
618 for (i = 0; i < num_mcid; i++) {
619 mcid_elem = _cairo_array_index (&node->mcid, i);
620 assert (mcid_elem->order != -1)((void) sizeof ((mcid_elem->order != -1) ? 1 : 0), __extension__
({ if (mcid_elem->order != -1) ; else __assert_fail ("mcid_elem->order != -1"
, "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 620, __extension__ __PRETTY_FUNCTION__); }))
;
621 if (mcid_elem->child_node) {
622 if (mcid_elem->child_node->type == PDF_NODE_CONTENT_REF) {
623 cairo_pdf_struct_tree_node_t *content_node;
624 status = lookup_content_node_for_ref_node (surface, mcid_elem->child_node, &content_node);
625 if (status)
626 return status;
627
628 /* CONTENT_REF will not have child nodes */
629 if (_cairo_array_num_elements (&content_node->mcid) > 0) {
630 child_mcid_elem = _cairo_array_index (&content_node->mcid, 0);
631 first_page = child_mcid_elem->page;
632 page_info = _cairo_array_index (&surface->pages, first_page - 1);
633 _cairo_output_stream_printf (surface->object_stream.stream,
634 " /Pg %d 0 R\n",
635 page_info->page_res.id);
636 has_children = TRUE1;
637 break;
638 }
639 } else {
640 has_children = TRUE1;
641 }
642 } else {
643 first_page = mcid_elem->page;
644 page_info = _cairo_array_index (&surface->pages, first_page - 1);
645 _cairo_output_stream_printf (surface->object_stream.stream,
646 " /Pg %d 0 R\n",
647 page_info->page_res.id);
648 has_children = TRUE1;
649 break;
650 }
651 }
652
653 if (has_children || node->annot) {
654 _cairo_output_stream_printf (surface->object_stream.stream, " /K ");
655
656 if (num_mcid > 1 || node->annot)
657 _cairo_output_stream_printf (surface->object_stream.stream, "[ ");
658
659 for (i = 0; i < num_mcid; i++) {
660 if (node->annot) {
661 if (node->annot->link_attrs.link_page != first_page) {
662 page_info = _cairo_array_index (&surface->pages, node->annot->link_attrs.link_page - 1);
663 _cairo_output_stream_printf (surface->object_stream.stream,
664 "<< /Type /OBJR /Pg %d 0 R /Obj %d 0 R >> ",
665 page_info->page_res.id,
666 node->annot->res.id);
667 } else {
668 _cairo_output_stream_printf (surface->object_stream.stream,
669 "<< /Type /OBJR /Obj %d 0 R >> ",
670 node->annot->res.id);
671 }
672 }
673 mcid_elem = _cairo_array_index (&node->mcid, i);
674 if (mcid_elem->child_node) {
675 if (mcid_elem->child_node->type == PDF_NODE_CONTENT_REF) {
676 cairo_pdf_struct_tree_node_t *content_node;
677 status = lookup_content_node_for_ref_node (surface, mcid_elem->child_node, &content_node);
678 if (status)
679 return status;
680
681 assert (content_node->type == PDF_NODE_CONTENT)((void) sizeof ((content_node->type == PDF_NODE_CONTENT) ?
1 : 0), __extension__ ({ if (content_node->type == PDF_NODE_CONTENT
) ; else __assert_fail ("content_node->type == PDF_NODE_CONTENT"
, "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 681, __extension__ __PRETTY_FUNCTION__); }))
;
682
683 /* CONTENT_REF will not have child nodes */
684 for (j = 0; j < _cairo_array_num_elements (&content_node->mcid); j++) {
685 child_mcid_elem = _cairo_array_index (&content_node->mcid, j);
686 cairo_pdf_interchange_write_node_mcid (surface, child_mcid_elem, first_page);
687 }
688 } else if (mcid_elem->child_node->type != PDF_NODE_CONTENT) {
689 _cairo_output_stream_printf (surface->object_stream.stream,
690 " %d 0 R ",
691 mcid_elem->child_node->res.id);
692 }
693 } else {
694 cairo_pdf_interchange_write_node_mcid (surface, mcid_elem, first_page);
695 }
696 }
697
698 if (num_mcid > 1 || node->annot)
699 _cairo_output_stream_printf (surface->object_stream.stream, "]");
700 }
701
702 _cairo_output_stream_printf (surface->object_stream.stream, "\n");
703 }
704
705 _cairo_output_stream_printf (surface->object_stream.stream,
706 ">>\n");
707
708 _cairo_pdf_surface_object_end (surface);
709
710 return _cairo_output_stream_get_status (surface->object_stream.stream);
711}
712
713static void
714init_named_dest_key (cairo_pdf_named_dest_t *dest)
715{
716 dest->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE5381,
717 dest->attrs.name,
718 strlen (dest->attrs.name));
719}
720
721static cairo_bool_t
722_named_dest_equal (const void *key_a, const void *key_b)
723{
724 const cairo_pdf_named_dest_t *a = key_a;
725 const cairo_pdf_named_dest_t *b = key_b;
726
727 return strcmp (a->attrs.name, b->attrs.name) == 0;
728}
729
730static void
731_named_dest_pluck (void *entry, void *closure)
732{
733 cairo_pdf_named_dest_t *dest = entry;
734 cairo_hash_table_t *table = closure;
735
736 _cairo_hash_table_remove (table, &dest->base);
737 _cairo_tag_free_dest_attributes (&dest->attrs);
738 free (dest);
739}
740
741static cairo_int_status_t
742cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface,
743 int page,
744 cairo_bool_t has_pos,
745 double x,
746 double y)
747{
748 cairo_pdf_page_info_t *page_info;
749
750 page_info = _cairo_array_index (&surface->pages, page - 1);
751
752 if (has_pos) {
753 _cairo_output_stream_printf (surface->object_stream.stream,
754 "[%d 0 R /XYZ %f %f 0]\n",
755 page_info->page_res.id,
756 x,
757 page_info->height - y);
758 } else {
759 _cairo_output_stream_printf (surface->object_stream.stream,
760 "[%d 0 R /XYZ null null 0]\n",
761 page_info->page_res.id);
762 }
763
764 return CAIRO_STATUS_SUCCESS;
765}
766
767static cairo_int_status_t
768cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
769 cairo_link_attrs_t *link_attrs)
770{
771 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
772 cairo_pdf_interchange_t *ic = &surface->interchange;
773
774 /* If the dest is known, emit an explicit dest */
775 if (link_attrs->link_type == TAG_LINK_DEST_AND_URI || link_attrs->link_type == TAG_LINK_DEST) {
776 cairo_pdf_named_dest_t key;
777 cairo_pdf_named_dest_t *named_dest;
778
779 /* check if we already have this dest */
780 key.attrs.name = link_attrs->dest;
781 init_named_dest_key (&key);
782 named_dest = _cairo_hash_table_lookup (ic->named_dests, &key.base);
783 if (named_dest) {
784 double x = 0;
785 double y = 0;
786
787 if (named_dest->extents.valid) {
788 x = named_dest->extents.extents.x;
789 y = named_dest->extents.extents.y;
790 }
791
792 if (named_dest->attrs.x_valid)
793 x = named_dest->attrs.x;
794
795 if (named_dest->attrs.y_valid)
796 y = named_dest->attrs.y;
797
798 if (named_dest->attrs.internal) {
799 _cairo_output_stream_printf (surface->object_stream.stream, " /Dest ");
800 status = cairo_pdf_interchange_write_explicit_dest (surface,
801 named_dest->page,
802 TRUE1,
803 x, y);
804 } else {
805 char *name = NULL((void*)0);
806
807 status = _cairo_utf8_to_pdf_string (named_dest->attrs.name, &name);
808 if (unlikely (status)(__builtin_expect (!!(status), 0)))
809 return status;
810
811 _cairo_output_stream_printf (surface->object_stream.stream, " /Dest %s\n",
812 name);
813 free (name);
814 }
815 return status;
816 }
817 /* name does not exist */
818 if (link_attrs->link_type == TAG_LINK_DEST_AND_URI) {
819 /* Don't emit anything. The caller will fallback to emitting a URI destination. */
820 return CAIRO_INT_STATUS_NOTHING_TO_DO;
821 }
822
823 /* Mising destination. Emit a "do nothing" dest that points to the same page and position. */
824 _cairo_tag_warning ("Link to dest=\"%s\" not found", link_attrs->dest);
825 _cairo_output_stream_printf (surface->object_stream.stream, " /Dest ");
826 status = cairo_pdf_interchange_write_explicit_dest (surface,
827 link_attrs->link_page,
828 FALSE0,
829 0, 0);
830 return status;
831 }
832
833 /* link_attrs->link_type == TAG_LINK_PAGE */
834
835 if (link_attrs->page < 1)
836 return _cairo_tag_error ("Link attribute: \"page=%d\" page must be >= 1", link_attrs->page);
837
838 if (link_attrs->page > (int)_cairo_array_num_elements (&surface->pages))
839 return _cairo_tag_error ("Link attribute: \"page=%d\" page exceeds page count (%d)",
840 link_attrs->page, _cairo_array_num_elements (&surface->pages));
841
842 _cairo_output_stream_printf (surface->object_stream.stream, " /Dest ");
843 return cairo_pdf_interchange_write_explicit_dest (surface,
844 link_attrs->page,
845 link_attrs->has_pos,
846 link_attrs->pos.x,
847 link_attrs->pos.y);
848}
849
850static cairo_int_status_t
851_cairo_utf8_to_pdf_utf8_hexstring (const char *utf8, char **str_out)
852{
853 int i;
854 int len;
855 unsigned char *p;
856 cairo_bool_t ascii;
857 char *str;
858 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
859
860 ascii = TRUE1;
861 p = (unsigned char *)utf8;
862 len = 0;
863 while (*p) {
864 if (*p < 32 || *p > 126) {
865 ascii = FALSE0;
866 }
867 if (*p == '(' || *p == ')' || *p == '\\')
868 len += 2;
869 else
870 len++;
871 p++;
872 }
873
874 if (ascii) {
875 str = _cairo_malloc (len + 3)((len + 3) != 0 ? malloc(len + 3) : ((void*)0));
876 if (str == NULL((void*)0))
877 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
878
879 str[0] = '(';
880 p = (unsigned char *)utf8;
881 i = 1;
882 while (*p) {
883 if (*p == '(' || *p == ')' || *p == '\\')
884 str[i++] = '\\';
885 str[i++] = *p;
886 p++;
887 }
888 str[i++] = ')';
889 str[i++] = 0;
890 } else {
891 str = _cairo_malloc (len*2 + 3)((len*2 + 3) != 0 ? malloc(len*2 + 3) : ((void*)0));
892 if (str == NULL((void*)0))
893 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
894
895 str[0] = '<';
896 p = (unsigned char *)utf8;
897 i = 1;
898 while (*p) {
899 if (*p == '\\') {
900 snprintf(str + i, 3, "%02x", '\\');
901 i += 2;
902 }
903 snprintf(str + i, 3, "%02x", *p);
904 i += 2;
905 p++;
906 }
907 str[i++] = '>';
908 str[i++] = 0;
909 }
910 *str_out = str;
911
912 return status;
913}
914
915static cairo_int_status_t
916cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface,
917 cairo_link_attrs_t *link_attrs)
918{
919 cairo_int_status_t status;
920 char *dest = NULL((void*)0);
921
922 if (link_attrs->link_type == TAG_LINK_DEST_AND_URI ||
923 link_attrs->link_type == TAG_LINK_DEST ||
924 link_attrs->link_type == TAG_LINK_PAGE)
925 {
926 status = cairo_pdf_interchange_write_dest (surface, link_attrs);
927 if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
928 return status;
929
930 /* CAIRO_INT_STATUS_NOTHING_TO_DO means that the link type is TAG_LINK_DEST_AND_URI
931 * and the DEST is missing. Fall through to writing a URI link below.
932 */
933 }
934
935 if (link_attrs->link_type == TAG_LINK_URI || link_attrs->link_type == TAG_LINK_DEST_AND_URI) {
936 status = _cairo_utf8_to_pdf_string (link_attrs->uri, &dest);
937 if (unlikely (status)(__builtin_expect (!!(status), 0)))
938 return status;
939
940 if (dest[0] != '(') {
941 free (dest);
942 return _cairo_tag_error ("Link attribute: \"url=%s\" URI may only contain ASCII characters",
943 link_attrs->uri);
944 }
945
946 _cairo_output_stream_printf (surface->object_stream.stream,
947 " /A <<\n"
948 " /Type /Action\n"
949 " /S /URI\n"
950 " /URI %s\n"
951 " >>\n",
952 dest);
953 free (dest);
954 } else if (link_attrs->link_type == TAG_LINK_FILE) {
955 /* According to "Developing with PDF", Leonard Rosenthol, 2013,
956 * The F key is encoded in the "standard encoding for the
957 * platform on which the document is being viewed. For most
958 * modern operating systems, that's UTF-8"
959 *
960 * As we don't know the target platform, we assume UTF-8. The
961 * F key may contain multi-byte encodings using the hex
962 * encoding.
963 *
964 * For PDF 1.7 we also include the UF key which uses the
965 * standard PDF UTF-16BE strings.
966 */
967 status = _cairo_utf8_to_pdf_utf8_hexstring (link_attrs->file, &dest);
968 if (unlikely (status)(__builtin_expect (!!(status), 0)))
969 return status;
970
971 _cairo_output_stream_printf (surface->object_stream.stream,
972 " /A <<\n"
973 " /Type /Action\n"
974 " /S /GoToR\n"
975 " /F %s\n",
976 dest);
977 free (dest);
978
979 if (surface->pdf_version >= CAIRO_PDF_VERSION_1_7)
980 {
981 status = _cairo_utf8_to_pdf_string (link_attrs->file, &dest);
982 if (unlikely (status)(__builtin_expect (!!(status), 0)))
983 return status;
984
985 _cairo_output_stream_printf (surface->object_stream.stream,
986 " /UF %s\n",
987 dest);
988 free (dest);
989 }
990
991 if (link_attrs->dest) {
992 status = _cairo_utf8_to_pdf_string (link_attrs->dest, &dest);
993 if (unlikely (status)(__builtin_expect (!!(status), 0)))
994 return status;
995
996 _cairo_output_stream_printf (surface->object_stream.stream,
997 " /D %s\n",
998 dest);
999 free (dest);
1000 } else {
1001 if (link_attrs->has_pos) {
1002 _cairo_output_stream_printf (surface->object_stream.stream,
1003 " /D [%d /XYZ %f %f 0]\n",
1004 link_attrs->page,
1005 link_attrs->pos.x,
1006 link_attrs->pos.y);
1007 } else {
1008 _cairo_output_stream_printf (surface->object_stream.stream,
1009 " /D [%d /XYZ null null 0]\n",
1010 link_attrs->page);
1011 }
1012 }
1013 _cairo_output_stream_printf (surface->object_stream.stream,
1014 " >>\n");
1015 }
1016
1017 return CAIRO_STATUS_SUCCESS;
1018}
1019
1020static cairo_int_status_t
1021cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
1022 cairo_pdf_annotation_t *annot,
1023 cairo_bool_t struct_parents)
1024{
1025 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1026 cairo_pdf_interchange_t *ic = &surface->interchange;
1027 cairo_pdf_struct_tree_node_t *node = annot->node;
1028 int sp;
1029 int i, num_rects;
1030 double height;
1031
1032 num_rects = _cairo_array_num_elements (&annot->link_attrs.rects);
1033 if (strcmp (node->name, CAIRO_TAG_LINK"Link") == 0 &&
1034 annot->link_attrs.link_type != TAG_LINK_EMPTY &&
1035 (node->extents.valid || num_rects > 0))
1036 {
1037 status = _cairo_array_append (&ic->parent_tree, &node->res);
1038 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1039 return status;
1040
1041 sp = _cairo_array_num_elements (&ic->parent_tree) - 1;
1042
1043 status = _cairo_pdf_surface_object_begin (surface, annot->res);
1044 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1045 return status;
1046
1047 _cairo_output_stream_printf (surface->object_stream.stream,
1048 "<< /Type /Annot\n"
1049 " /Subtype /Link\n");
1050
1051 if (struct_parents) {
1052 _cairo_output_stream_printf (surface->object_stream.stream,
1053 " /StructParent %d\n",
1054 sp);
1055 }
1056
1057 height = surface->height;
1058 if (num_rects > 0) {
1059 cairo_rectangle_int_t bbox_rect;
1060
1061 _cairo_output_stream_printf (surface->object_stream.stream,
1062 " /QuadPoints [ ");
1063 for (i = 0; i < num_rects; i++) {
1064 cairo_rectangle_t rectf;
1065 cairo_rectangle_int_t recti;
1066
1067 _cairo_array_copy_element (&annot->link_attrs.rects, i, &rectf);
1068 _cairo_rectangle_int_from_double (&recti, &rectf);
1069 if (i == 0)
1070 bbox_rect = recti;
1071 else
1072 _cairo_rectangle_union (&bbox_rect, &recti);
1073
1074 write_rect_to_pdf_quad_points (surface->object_stream.stream, &rectf, height);
1075 _cairo_output_stream_printf (surface->object_stream.stream, " ");
1076 }
1077 _cairo_output_stream_printf (surface->object_stream.stream,
1078 "]\n"
1079 " /Rect [ ");
1080 write_rect_int_to_pdf_bbox (surface->object_stream.stream, &bbox_rect, height);
1081 _cairo_output_stream_printf (surface->object_stream.stream, " ]\n");
1082 } else {
1083 _cairo_output_stream_printf (surface->object_stream.stream,
1084 " /Rect [ ");
1085 write_rect_int_to_pdf_bbox (surface->object_stream.stream, &node->extents.extents, height);
1086 _cairo_output_stream_printf (surface->object_stream.stream, " ]\n");
1087 }
1088
1089 status = cairo_pdf_interchange_write_link_action (surface, &annot->link_attrs);
1090 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1091 return status;
1092
1093 _cairo_output_stream_printf (surface->object_stream.stream,
1094 " /BS << /W 0 >>\n"
1095 ">>\n");
1096
1097 _cairo_pdf_surface_object_end (surface);
1098 status = _cairo_output_stream_get_status (surface->object_stream.stream);
1099 }
1100
1101 return status;
1102}
1103
1104static cairo_int_status_t
1105cairo_pdf_interchange_walk_struct_tree (cairo_pdf_surface_t *surface,
1106 cairo_pdf_struct_tree_node_t *node,
1107 int depth,
1108 cairo_int_status_t (*func) (cairo_pdf_surface_t *surface,
1109 cairo_pdf_struct_tree_node_t *node,
1110 int depth))
1111{
1112 cairo_int_status_t status;
1113 cairo_pdf_struct_tree_node_t *child;
1114
1115 status = func (surface, node, depth);
3
Calling 'cairo_pdf_interchange_apply_extents_from_content_ref'
1116 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1117 return status;
1118
1119 depth++;
1120 cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&node->children)->next
); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__ - __builtin_offsetof
(cairo_pdf_struct_tree_node_t, link)); }); &child->link
!= (&node->children); child = ({ const __typeof__ (((
cairo_pdf_struct_tree_node_t *) 0)->link) *mptr__ = (child
->link.next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
))
1121 &node->children, link)for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&node->children)->next
); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__ - __builtin_offsetof
(cairo_pdf_struct_tree_node_t, link)); }); &child->link
!= (&node->children); child = ({ const __typeof__ (((
cairo_pdf_struct_tree_node_t *) 0)->link) *mptr__ = (child
->link.next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
))
1122 {
1123 status = cairo_pdf_interchange_walk_struct_tree (surface, child, depth, func);
1124 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1125 return status;
1126 }
1127 depth--;
1128
1129 return CAIRO_STATUS_SUCCESS;
1130}
1131
1132static cairo_int_status_t
1133cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface)
1134{
1135 cairo_pdf_interchange_t *ic = &surface->interchange;
1136 cairo_pdf_struct_tree_node_t *child;
1137 cairo_int_status_t status;
1138
1139 if (cairo_list_is_empty (&ic->struct_root->children))
1140 return CAIRO_STATUS_SUCCESS;
1141
1142 status = cairo_pdf_interchange_walk_struct_tree (surface,
1143 ic->struct_root,
1144 0,
1145 cairo_pdf_interchange_write_node_object);
1146 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1147 return status;
1148
1149 status = _cairo_pdf_surface_object_begin (surface, surface->struct_tree_root);
1150 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1151 return status;
1152
1153 _cairo_output_stream_printf (surface->object_stream.stream,
1154 "<< /Type /StructTreeRoot\n"
1155 " /ParentTree %d 0 R\n",
1156 ic->parent_tree_res.id);
1157
1158 if (cairo_list_is_singular (&ic->struct_root->children)) {
1159 child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link)({ const __typeof__ (((cairo_pdf_struct_tree_node_t *) 0)->
link) *mptr__ = ((&ic->struct_root->children)->next
); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__ - __builtin_offsetof
(cairo_pdf_struct_tree_node_t, link)); })
;
1160 _cairo_output_stream_printf (surface->object_stream.stream, " /K [ %d 0 R ]\n", child->res.id);
1161 } else {
1162 _cairo_output_stream_printf (surface->object_stream.stream, " /K [ ");
1163
1164 cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&ic->struct_root->children
)->next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
); &child->link != (&ic->struct_root->children
); child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = (child->link.next); (cairo_pdf_struct_tree_node_t
*) ((char *) mptr__ - __builtin_offsetof(cairo_pdf_struct_tree_node_t
, link)); }))
1165 &ic->struct_root->children, link)for (child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = ((&ic->struct_root->children
)->next); (cairo_pdf_struct_tree_node_t *) ((char *) mptr__
- __builtin_offsetof(cairo_pdf_struct_tree_node_t, link)); }
); &child->link != (&ic->struct_root->children
); child = ({ const __typeof__ (((cairo_pdf_struct_tree_node_t
*) 0)->link) *mptr__ = (child->link.next); (cairo_pdf_struct_tree_node_t
*) ((char *) mptr__ - __builtin_offsetof(cairo_pdf_struct_tree_node_t
, link)); }))
1166 {
1167 if (child->type == PDF_NODE_CONTENT || child->type == PDF_NODE_ARTIFACT)
1168 continue;
1169
1170 _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", child->res.id);
1171 }
1172 _cairo_output_stream_printf (surface->object_stream.stream, "]\n");
1173 }
1174
1175 _cairo_output_stream_printf (surface->object_stream.stream,
1176 ">>\n");
1177 _cairo_pdf_surface_object_end (surface);
1178
1179 return CAIRO_STATUS_SUCCESS;
1180}
1181
1182static cairo_int_status_t
1183cairo_pdf_interchange_write_annots (cairo_pdf_surface_t *surface,
1184 cairo_bool_t struct_parents)
1185{
1186 cairo_pdf_interchange_t *ic = &surface->interchange;
1187 int num_elems, i, page_num;
1188 cairo_pdf_page_info_t *page_info;
1189 cairo_pdf_annotation_t *annot;
1190 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1191
1192 num_elems = _cairo_array_num_elements (&ic->annots);
1193 for (i = 0; i < num_elems; i++) {
1194 _cairo_array_copy_element (&ic->annots, i, &annot);
1195 page_num = annot->link_attrs.link_page;
1196 if (page_num > (int)_cairo_array_num_elements (&surface->pages)) {
1197 return _cairo_tag_error ("Link attribute: \"link_page=%d\" page exceeds page count (%d)",
1198 page_num,
1199 _cairo_array_num_elements (&surface->pages));
1200 }
1201
1202 page_info = _cairo_array_index (&surface->pages, page_num - 1);
1203 status = _cairo_array_append (&page_info->annots, &annot->res);
1204 if (status)
1205 return status;
1206
1207 status = cairo_pdf_interchange_write_annot (surface, annot, struct_parents);
1208 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1209 return status;
1210 }
1211
1212 return status;
1213}
1214
1215static cairo_int_status_t
1216cairo_pdf_interchange_write_content_parent_elems (cairo_pdf_surface_t *surface)
1217{
1218 int num_elems, i;
1219 cairo_pdf_struct_tree_node_t *node;
1220 cairo_pdf_interchange_t *ic = &surface->interchange;
1221 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1222
1223 num_elems = _cairo_array_num_elements (&ic->mcid_to_tree);
1224 status = _cairo_pdf_surface_object_begin (surface, ic->content_parent_res);
1225 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1226 return status;
1227
1228 _cairo_output_stream_printf (surface->object_stream.stream,
1229 "[\n");
1230 for (i = 0; i < num_elems; i++) {
1231 _cairo_array_copy_element (&ic->mcid_to_tree, i, &node);
1232 _cairo_output_stream_printf (surface->object_stream.stream, " %d 0 R\n", node->res.id);
1233 }
1234 _cairo_output_stream_printf (surface->object_stream.stream,
1235 "]\n");
1236 _cairo_pdf_surface_object_end (surface);
1237
1238 return status;
1239}
1240
1241static cairo_int_status_t
1242cairo_pdf_interchange_apply_extents_from_content_ref (cairo_pdf_surface_t *surface,
1243 cairo_pdf_struct_tree_node_t *node,
1244 int depth)
1245{
1246 cairo_int_status_t status;
1247
1248 if (node->type != PDF_NODE_CONTENT_REF)
4
Assuming field 'type' is equal to PDF_NODE_CONTENT_REF
5
Taking false branch
1249 return CAIRO_STATUS_SUCCESS;
1250
1251 cairo_pdf_struct_tree_node_t *content_node;
6
'content_node' declared without an initial value
1252 status = lookup_content_node_for_ref_node (surface, node, &content_node);
7
Calling 'lookup_content_node_for_ref_node'
13
Returning from 'lookup_content_node_for_ref_node'
1253 if (status)
14
Assuming 'status' is 0
15
Taking false branch
1254 return status;
1255
1256 /* Merge extents with all parent nodes */
1257 node = node->parent;
1258 while (node) {
16
Loop condition is true. Entering loop body
1259 if (node->extents.valid) {
17
Assuming field 'valid' is 0
18
Taking false branch
1260 _cairo_rectangle_union (&node->extents.extents, &content_node->extents.extents);
1261 } else {
1262 node->extents = content_node->extents;
19
Access to field 'extents' results in a dereference of an undefined pointer value (loaded from variable 'content_node')
1263 }
1264 node = node->parent;
1265 }
1266
1267 return CAIRO_STATUS_SUCCESS;
1268}
1269
1270static cairo_int_status_t
1271cairo_pdf_interchange_update_extents (cairo_pdf_surface_t *surface)
1272{
1273 cairo_pdf_interchange_t *ic = &surface->interchange;
1274
1275 return cairo_pdf_interchange_walk_struct_tree (surface,
2
Calling 'cairo_pdf_interchange_walk_struct_tree'
1276 ic->struct_root,
1277 0,
1278 cairo_pdf_interchange_apply_extents_from_content_ref);
1279}
1280
1281static cairo_int_status_t
1282cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface)
1283{
1284 int num_elems, i;
1285 cairo_pdf_resource_t *res;
1286 cairo_pdf_interchange_t *ic = &surface->interchange;
1287 cairo_int_status_t status;
1288
1289 num_elems = _cairo_array_num_elements (&ic->parent_tree);
1290 if (num_elems > 0) {
1291 ic->parent_tree_res = _cairo_pdf_surface_new_object (surface);
1292 if (ic->parent_tree_res.id == 0)
1293 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1294
1295 status = _cairo_pdf_surface_object_begin (surface, ic->parent_tree_res);
1296 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1297 return status;
1298
1299 _cairo_output_stream_printf (surface->object_stream.stream,
1300 "<< /Nums [\n");
1301 for (i = 0; i < num_elems; i++) {
1302 res = _cairo_array_index (&ic->parent_tree, i);
1303 if (res->id) {
1304 _cairo_output_stream_printf (surface->object_stream.stream,
1305 " %d %d 0 R\n",
1306 i,
1307 res->id);
1308 }
1309 }
1310 _cairo_output_stream_printf (surface->object_stream.stream,
1311 " ]\n"
1312 ">>\n");
1313 _cairo_pdf_surface_object_end (surface);
1314 }
1315
1316 return CAIRO_STATUS_SUCCESS;
1317}
1318
1319static cairo_int_status_t
1320cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
1321{
1322 int num_elems, i;
1323 cairo_pdf_outline_entry_t *outline;
1324 cairo_pdf_interchange_t *ic = &surface->interchange;
1325 cairo_int_status_t status;
1326 char *name = NULL((void*)0);
1327
1328 num_elems = _cairo_array_num_elements (&ic->outline);
1329 if (num_elems < 2)
1330 return CAIRO_INT_STATUS_SUCCESS;
1331
1332 _cairo_array_copy_element (&ic->outline, 0, &outline);
1333 outline->res = _cairo_pdf_surface_new_object (surface);
1334 if (outline->res.id == 0)
1335 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1336
1337 surface->outlines_dict_res = outline->res;
1338 status = _cairo_pdf_surface_object_begin (surface, outline->res);
1339 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1340 return status;
1341
1342 _cairo_output_stream_printf (surface->object_stream.stream,
1343 "<< /Type /Outlines\n"
1344 " /First %d 0 R\n"
1345 " /Last %d 0 R\n"
1346 " /Count %d\n"
1347 ">>\n",
1348 outline->first_child->res.id,
1349 outline->last_child->res.id,
1350 outline->count);
1351 _cairo_pdf_surface_object_end (surface);
1352
1353 for (i = 1; i < num_elems; i++) {
1354 _cairo_array_copy_element (&ic->outline, i, &outline);
1355 _cairo_pdf_surface_update_object (surface, outline->res);
1356
1357 status = _cairo_utf8_to_pdf_string (outline->name, &name);
1358 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1359 return status;
1360
1361 status = _cairo_pdf_surface_object_begin (surface, outline->res);
1362 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1363 return status;
1364
1365 _cairo_output_stream_printf (surface->object_stream.stream,
1366 "<< /Title %s\n"
1367 " /Parent %d 0 R\n",
1368 name,
1369 outline->parent->res.id);
1370 free (name);
1371
1372 if (outline->prev) {
1373 _cairo_output_stream_printf (surface->object_stream.stream,
1374 " /Prev %d 0 R\n",
1375 outline->prev->res.id);
1376 }
1377
1378 if (outline->next) {
1379 _cairo_output_stream_printf (surface->object_stream.stream,
1380 " /Next %d 0 R\n",
1381 outline->next->res.id);
1382 }
1383
1384 if (outline->first_child) {
1385 _cairo_output_stream_printf (surface->object_stream.stream,
1386 " /First %d 0 R\n"
1387 " /Last %d 0 R\n"
1388 " /Count %d\n",
1389 outline->first_child->res.id,
1390 outline->last_child->res.id,
1391 outline->count);
1392 }
1393
1394 if (outline->flags) {
1395 int flags = 0;
1396 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_ITALIC)
1397 flags |= 1;
1398 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_BOLD)
1399 flags |= 2;
1400 _cairo_output_stream_printf (surface->object_stream.stream,
1401 " /F %d\n",
1402 flags);
1403 }
1404
1405 status = cairo_pdf_interchange_write_link_action (surface, &outline->link_attrs);
1406 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1407 return status;
1408
1409 _cairo_output_stream_printf (surface->object_stream.stream,
1410 ">>\n");
1411 _cairo_pdf_surface_object_end (surface);
1412 }
1413
1414 return status;
1415}
1416
1417/*
1418 * Split a page label into a text prefix and numeric suffix. Leading '0's are
1419 * included in the prefix. eg
1420 * "3" => NULL, 3
1421 * "cover" => "cover", 0
1422 * "A-2" => "A-", 2
1423 * "A-002" => "A-00", 2
1424 */
1425static char *
1426split_label (const char* label, int *num)
1427{
1428 int len, i;
1429
1430 *num = 0;
1431 len = strlen (label);
1432 if (len == 0)
1433 return NULL((void*)0);
1434
1435 i = len;
1436 while (i > 0 && _cairo_isdigit (label[i-1]))
1437 i--;
1438
1439 while (i < len && label[i] == '0')
1440 i++;
1441
1442 if (i < len)
1443 sscanf (label + i, "%d", num);
1444
1445 if (i > 0) {
1446 char *s;
1447 s = _cairo_malloc (i + 1)((i + 1) != 0 ? malloc(i + 1) : ((void*)0));
1448 if (!s)
1449 return NULL((void*)0);
1450
1451 memcpy (s, label, i);
1452 s[i] = 0;
1453 return s;
1454 }
1455
1456 return NULL((void*)0);
1457}
1458
1459/* strcmp that handles NULL arguments */
1460static cairo_bool_t
1461strcmp_null (const char *s1, const char *s2)
1462{
1463 if (s1 && s2)
1464 return strcmp (s1, s2) == 0;
1465
1466 if (!s1 && !s2)
1467 return TRUE1;
1468
1469 return FALSE0;
1470}
1471
1472static cairo_int_status_t
1473cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
1474{
1475 int num_elems, i;
1476 char *label;
1477 char *prefix;
1478 char *prev_prefix;
1479 int num, prev_num;
1480 cairo_int_status_t status;
1481 cairo_bool_t has_labels;
1482
1483 /* Check if any labels defined */
1484 num_elems = _cairo_array_num_elements (&surface->page_labels);
1485 has_labels = FALSE0;
1486 for (i = 0; i < num_elems; i++) {
1487 _cairo_array_copy_element (&surface->page_labels, i, &label);
1488 if (label) {
1489 has_labels = TRUE1;
1490 break;
1491 }
1492 }
1493
1494 if (!has_labels)
1495 return CAIRO_STATUS_SUCCESS;
1496
1497 surface->page_labels_res = _cairo_pdf_surface_new_object (surface);
1498 if (surface->page_labels_res.id == 0)
1499 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1500
1501 status = _cairo_pdf_surface_object_begin (surface, surface->page_labels_res);
1502 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1503 return status;
1504
1505 _cairo_output_stream_printf (surface->object_stream.stream,
1506 "<< /Nums [\n");
1507 prefix = NULL((void*)0);
1508 prev_prefix = NULL((void*)0);
1509 num = 0;
1510 prev_num = 0;
1511 for (i = 0; i < num_elems; i++) {
1512 _cairo_array_copy_element (&surface->page_labels, i, &label);
1513 if (label) {
1514 prefix = split_label (label, &num);
1515 } else {
1516 prefix = NULL((void*)0);
1517 num = i + 1;
1518 }
1519
1520 if (!strcmp_null (prefix, prev_prefix) || num != prev_num + 1) {
1521 _cairo_output_stream_printf (surface->object_stream.stream, " %d << ", i);
1522
1523 if (num)
1524 _cairo_output_stream_printf (surface->object_stream.stream, "/S /D /St %d ", num);
1525
1526 if (prefix) {
1527 char *s;
1528 status = _cairo_utf8_to_pdf_string (prefix, &s);
1529 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1530 return status;
1531
1532 _cairo_output_stream_printf (surface->object_stream.stream, "/P %s ", s);
1533 free (s);
1534 }
1535
1536 _cairo_output_stream_printf (surface->object_stream.stream, ">>\n");
1537 }
1538 free (prev_prefix);
1539 prev_prefix = prefix;
1540 prefix = NULL((void*)0);
1541 prev_num = num;
1542 }
1543 free (prefix);
1544 free (prev_prefix);
1545 _cairo_output_stream_printf (surface->object_stream.stream,
1546 " ]\n"
1547 ">>\n");
1548 _cairo_pdf_surface_object_end (surface);
1549
1550 return CAIRO_STATUS_SUCCESS;
1551}
1552
1553static void
1554_collect_external_dest (void *entry, void *closure)
1555{
1556 cairo_pdf_named_dest_t *dest = entry;
1557 cairo_pdf_surface_t *surface = closure;
1558 cairo_pdf_interchange_t *ic = &surface->interchange;
1559
1560 if (!dest->attrs.internal)
1561 ic->sorted_dests[ic->num_dests++] = dest;
1562}
1563
1564static int
1565_dest_compare (const void *a, const void *b)
1566{
1567 const cairo_pdf_named_dest_t * const *dest_a = a;
1568 const cairo_pdf_named_dest_t * const *dest_b = b;
1569
1570 return strcmp ((*dest_a)->attrs.name, (*dest_b)->attrs.name);
1571}
1572
1573static cairo_int_status_t
1574_cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
1575{
1576 int i;
1577 cairo_pdf_interchange_t *ic = &surface->interchange;
1578 cairo_int_status_t status;
1579 cairo_pdf_page_info_t *page_info;
1580
1581 if (ic->num_dests == 0) {
1582 ic->dests_res.id = 0;
1583 return CAIRO_STATUS_SUCCESS;
1584 }
1585
1586 ic->sorted_dests = calloc (ic->num_dests, sizeof (cairo_pdf_named_dest_t *));
1587 if (unlikely (ic->sorted_dests == NULL)(__builtin_expect (!!(ic->sorted_dests == ((void*)0)), 0)))
1588 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1589
1590 ic->num_dests = 0;
1591 _cairo_hash_table_foreach (ic->named_dests, _collect_external_dest, surface);
1592
1593 qsort (ic->sorted_dests, ic->num_dests, sizeof (cairo_pdf_named_dest_t *), _dest_compare);
1594
1595 ic->dests_res = _cairo_pdf_surface_new_object (surface);
1596 if (ic->dests_res.id == 0)
1597 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1598
1599 status = _cairo_pdf_surface_object_begin (surface, ic->dests_res);
1600 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1601 return status;
1602
1603 _cairo_output_stream_printf (surface->object_stream.stream,
1604 "<< /Names [\n");
1605 for (i = 0; i < ic->num_dests; i++) {
1606 cairo_pdf_named_dest_t *dest = ic->sorted_dests[i];
1607 double x = 0;
1608 double y = 0;
1609 char *name = NULL((void*)0);
1610
1611 if (dest->attrs.internal)
1612 continue;
1613
1614 if (dest->extents.valid) {
1615 x = dest->extents.extents.x;
1616 y = dest->extents.extents.y;
1617 }
1618
1619 if (dest->attrs.x_valid)
1620 x = dest->attrs.x;
1621
1622 if (dest->attrs.y_valid)
1623 y = dest->attrs.y;
1624
1625 status = _cairo_utf8_to_pdf_string (dest->attrs.name, &name);
1626 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1627 return status;
1628
1629 page_info = _cairo_array_index (&surface->pages, dest->page - 1);
1630 _cairo_output_stream_printf (surface->object_stream.stream,
1631 " %s [%d 0 R /XYZ %f %f 0]\n",
1632 name,
1633 page_info->page_res.id,
1634 x,
1635 page_info->height - y);
1636 free (name);
1637
1638 }
1639 _cairo_output_stream_printf (surface->object_stream.stream,
1640 " ]\n"
1641 ">>\n");
1642 _cairo_pdf_surface_object_end (surface);
1643
1644 return CAIRO_STATUS_SUCCESS;
1645}
1646
1647static cairo_int_status_t
1648cairo_pdf_interchange_write_names_dict (cairo_pdf_surface_t *surface)
1649{
1650 cairo_pdf_interchange_t *ic = &surface->interchange;
1651 cairo_int_status_t status;
1652
1653 status = _cairo_pdf_interchange_write_document_dests (surface);
1654 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1655 return status;
1656
1657 surface->names_dict_res.id = 0;
1658 if (ic->dests_res.id != 0) {
1659 surface->names_dict_res = _cairo_pdf_surface_new_object (surface);
1660 if (surface->names_dict_res.id == 0)
1661 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1662
1663 status = _cairo_pdf_surface_object_begin (surface, surface->names_dict_res);
1664 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1665 return status;
1666
1667 _cairo_output_stream_printf (surface->object_stream.stream,
1668 "<< /Dests %d 0 R >>\n",
1669 ic->dests_res.id);
1670 _cairo_pdf_surface_object_end (surface);
1671 }
1672
1673 return CAIRO_STATUS_SUCCESS;
1674}
1675
1676static cairo_int_status_t
1677cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface)
1678{
1679 cairo_pdf_interchange_t *ic = &surface->interchange;
1680 cairo_int_status_t status;
1681 unsigned int i, num_elems;
1682 struct metadata *data;
1683 unsigned char *p;
1684
1685 surface->docinfo_res = _cairo_pdf_surface_new_object (surface);
1686 if (surface->docinfo_res.id == 0)
1687 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1688
1689 status = _cairo_pdf_surface_object_begin (surface, surface->docinfo_res);
1690 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1691 return status;
1692
1693 _cairo_output_stream_printf (surface->object_stream.stream,
1694 "<< /Producer (cairo %s (https://cairographics.org))\n",
1695 cairo_version_string_moz_cairo_version_string ());
1696
1697 if (ic->docinfo.title)
1698 _cairo_output_stream_printf (surface->object_stream.stream, " /Title %s\n", ic->docinfo.title);
1699
1700 if (ic->docinfo.author)
1701 _cairo_output_stream_printf (surface->object_stream.stream, " /Author %s\n", ic->docinfo.author);
1702
1703 if (ic->docinfo.subject)
1704 _cairo_output_stream_printf (surface->object_stream.stream, " /Subject %s\n", ic->docinfo.subject);
1705
1706 if (ic->docinfo.keywords)
1707 _cairo_output_stream_printf (surface->object_stream.stream, " /Keywords %s\n", ic->docinfo.keywords);
1708
1709 if (ic->docinfo.creator)
1710 _cairo_output_stream_printf (surface->object_stream.stream, " /Creator %s\n", ic->docinfo.creator);
1711
1712 if (ic->docinfo.create_date)
1713 _cairo_output_stream_printf (surface->object_stream.stream, " /CreationDate %s\n", ic->docinfo.create_date);
1714
1715 if (ic->docinfo.mod_date)
1716 _cairo_output_stream_printf (surface->object_stream.stream, " /ModDate %s\n", ic->docinfo.mod_date);
1717
1718 num_elems = _cairo_array_num_elements (&ic->custom_metadata);
1719 for (i = 0; i < num_elems; i++) {
1720 data = _cairo_array_index (&ic->custom_metadata, i);
1721 if (data->value) {
1722 _cairo_output_stream_printf (surface->object_stream.stream, " /");
1723 /* The name can be any utf8 string. Use hex codes as
1724 * specified in section 7.3.5 of PDF reference
1725 */
1726 p = (unsigned char *)data->name;
1727 while (*p) {
1728 if (*p < 0x21 || *p > 0x7e || *p == '#' || *p == '/')
1729 _cairo_output_stream_printf (surface->object_stream.stream, "#%02x", *p);
1730 else
1731 _cairo_output_stream_printf (surface->object_stream.stream, "%c", *p);
1732 p++;
1733 }
1734 _cairo_output_stream_printf (surface->object_stream.stream, " %s\n", data->value);
1735 }
1736 }
1737
1738 _cairo_output_stream_printf (surface->object_stream.stream,
1739 ">>\n");
1740 _cairo_pdf_surface_object_end (surface);
1741
1742 return CAIRO_STATUS_SUCCESS;
1743}
1744
1745static cairo_int_status_t
1746_cairo_pdf_interchange_begin_structure_tag (cairo_pdf_surface_t *surface,
1747 cairo_tag_type_t tag_type,
1748 const char *name,
1749 const char *attributes)
1750{
1751 int mcid;
1752 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1753 cairo_pdf_interchange_t *ic = &surface->interchange;
1754 cairo_pdf_command_entry_t *command_entry;
1755 cairo_pdf_struct_tree_node_t *parent_node;
1756 unsigned int content_command_id;
1757
1758 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1759 ic->content_emitted = FALSE0;
1760 status = add_tree_node (surface, ic->current_analyze_node, name, attributes, &ic->current_analyze_node);
1761 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1762 return status;
1763
1764 status = command_list_add (surface, ic->command_id, PDF_BEGIN);
1765 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1766 return status;
1767
1768 /* Add to command_id to node map. */
1769 command_entry = _cairo_malloc (sizeof(cairo_pdf_command_entry_t))((sizeof(cairo_pdf_command_entry_t)) != 0 ? malloc(sizeof(cairo_pdf_command_entry_t
)) : ((void*)0))
;
1770 command_entry->recording_id = ic->recording_id;
1771 command_entry->command_id = ic->command_id;
1772 command_entry->node = ic->current_analyze_node;
1773 _cairo_pdf_command_init_key (command_entry);
1774 status = _cairo_hash_table_insert (ic->command_to_node_map, &command_entry->base);
1775 if (unlikely(status)(__builtin_expect (!!(status), 0)))
1776 return status;
1777
1778 if (tag_type & TAG_TYPE_LINK) {
1779 status = add_annotation (surface, ic->current_analyze_node, name, attributes);
1780 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1781 return status;
1782 }
1783
1784 if (ic->current_analyze_node->type == PDF_NODE_CONTENT) {
1785 cairo_pdf_content_tag_t *content = _cairo_malloc (sizeof(cairo_pdf_content_tag_t))((sizeof(cairo_pdf_content_tag_t)) != 0 ? malloc(sizeof(cairo_pdf_content_tag_t
)) : ((void*)0))
;
1786 content->node = ic->current_analyze_node;
1787 _cairo_pdf_content_tag_init_key (content);
1788 status = _cairo_hash_table_insert (ic->content_tag_map, &content->base);
1789 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1790 return status;
1791 }
1792
1793 ic->content_emitted = FALSE0;
1794
1795 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1796 if (ic->marked_content_open) {
1797 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
1798 ic->marked_content_open = FALSE0;
1799 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1800 return status;
1801 }
1802
1803 ic->current_render_node = lookup_node_for_command (surface, ic->recording_id, ic->command_id);
1804 if (ic->current_render_node->type == PDF_NODE_ARTIFACT) {
1805 if (command_list_has_content (surface, ic->command_id, NULL((void*)0))) {
1806 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, name, -1);
1807 ic->marked_content_open = TRUE1;
1808 }
1809 } else if (ic->current_render_node->type == PDF_NODE_CONTENT_REF) {
1810 parent_node = ic->current_render_node->parent;
1811 add_child_to_mcid_array (surface, parent_node, ic->command_id, ic->current_render_node);
1812 } else {
1813 parent_node = ic->current_render_node->parent;
1814 add_child_to_mcid_array (surface, parent_node, ic->command_id, ic->current_render_node);
1815 if (command_list_has_content (surface, ic->command_id, &content_command_id)) {
1816 add_mcid_to_node (surface, ic->current_render_node, content_command_id, &mcid);
1817 const char *tag_name = name;
1818 if (ic->current_render_node->type == PDF_NODE_CONTENT)
1819 tag_name = ic->current_render_node->attributes.content.tag_name;
1820
1821 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, tag_name, mcid);
1822 ic->marked_content_open = TRUE1;
1823 }
1824 }
1825 }
1826
1827 return status;
1828}
1829
1830static cairo_int_status_t
1831_cairo_pdf_interchange_begin_dest_tag (cairo_pdf_surface_t *surface,
1832 cairo_tag_type_t tag_type,
1833 const char *name,
1834 const char *attributes)
1835{
1836 cairo_pdf_named_dest_t *dest;
1837 cairo_pdf_interchange_t *ic = &surface->interchange;
1838 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1839
1840 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1841 dest = calloc (1, sizeof (cairo_pdf_named_dest_t));
1842 if (unlikely (dest == NULL)(__builtin_expect (!!(dest == ((void*)0)), 0)))
1843 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1844
1845 status = _cairo_tag_parse_dest_attributes (attributes, &dest->attrs);
1846 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1847 {
1848 free (dest);
1849 return status;
1850 }
1851
1852 dest->page = _cairo_array_num_elements (&surface->pages);
1853 init_named_dest_key (dest);
1854 status = _cairo_hash_table_insert (ic->named_dests, &dest->base);
1855 if (unlikely (status)(__builtin_expect (!!(status), 0))) {
1856 free (dest->attrs.name);
1857 free (dest);
1858 return status;
1859 }
1860
1861 _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, dest);
1862 ic->num_dests++;
1863 }
1864
1865 return status;
1866}
1867
1868cairo_int_status_t
1869_cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface,
1870 const char *name,
1871 const char *attributes)
1872{
1873 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1874 cairo_tag_type_t tag_type;
1875 cairo_pdf_interchange_t *ic = &surface->interchange;
1876
1877 if (ic->ignore_current_surface)
1878 return CAIRO_STATUS_SUCCESS;
1879
1880 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1881 status = _cairo_tag_stack_push (&ic->analysis_tag_stack, name, attributes);
1882
1883 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1884 status = _cairo_tag_stack_push (&ic->render_tag_stack, name, attributes);
1885 }
1886 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1887 return status;
1888
1889 tag_type = _cairo_tag_get_type (name);
1890 if (tag_type & (TAG_TYPE_STRUCTURE|TAG_TYPE_CONTENT|TAG_TYPE_CONTENT_REF|TAG_TYPE_ARTIFACT)) {
1891 status = _cairo_pdf_interchange_begin_structure_tag (surface, tag_type, name, attributes);
1892 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1893 return status;
1894 }
1895
1896 if (tag_type & TAG_TYPE_DEST) {
1897 status = _cairo_pdf_interchange_begin_dest_tag (surface, tag_type, name, attributes);
1898 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1899 return status;
1900 }
1901
1902 return status;
1903}
1904
1905static cairo_int_status_t
1906_cairo_pdf_interchange_end_structure_tag (cairo_pdf_surface_t *surface,
1907 cairo_tag_type_t tag_type,
1908 cairo_tag_stack_elem_t *elem)
1909{
1910 cairo_pdf_interchange_t *ic = &surface->interchange;
1911 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1912 int mcid;
1913 unsigned int content_command_id;
1914
1915 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1916 assert (ic->current_analyze_node->parent != NULL)((void) sizeof ((ic->current_analyze_node->parent != ((
void*)0)) ? 1 : 0), __extension__ ({ if (ic->current_analyze_node
->parent != ((void*)0)) ; else __assert_fail ("ic->current_analyze_node->parent != NULL"
, "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 1916, __extension__ __PRETTY_FUNCTION__); }))
;
1917 status = command_list_add (surface, ic->command_id, PDF_END);
1918 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1919 return status;
1920
1921 ic->content_emitted = FALSE0;
1922 ic->current_analyze_node = ic->current_analyze_node->parent;
1923
1924 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1925 if (ic->marked_content_open) {
1926 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
1927 ic->marked_content_open = FALSE0;
1928 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1929 return status;
1930 }
1931 ic->current_render_node = ic->current_render_node->parent;
1932 if (ic->current_render_node->parent &&
1933 command_list_has_content (surface, ic->command_id, &content_command_id))
1934 {
1935 add_mcid_to_node (surface, ic->current_render_node, content_command_id, &mcid);
1936 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators,
1937 ic->current_render_node->name, mcid);
1938 ic->marked_content_open = TRUE1;
1939 }
1940 }
1941
1942 return status;
1943}
1944
1945cairo_int_status_t
1946_cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface,
1947 const char *name)
1948{
1949 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1950 cairo_pdf_interchange_t *ic = &surface->interchange;
1951 cairo_tag_type_t tag_type;
1952 cairo_tag_stack_elem_t *elem;
1953
1954 if (ic->ignore_current_surface)
1955 return CAIRO_STATUS_SUCCESS;
1956
1957 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1958 status = _cairo_tag_stack_pop (&ic->analysis_tag_stack, name, &elem);
1959
1960 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1961 status = _cairo_tag_stack_pop (&ic->render_tag_stack, name, &elem);
1962 }
1963 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1964 return status;
1965
1966 tag_type = _cairo_tag_get_type (name);
1967 if (tag_type & (TAG_TYPE_STRUCTURE|TAG_TYPE_CONTENT|TAG_TYPE_CONTENT_REF|TAG_TYPE_ARTIFACT)) {
1968 status = _cairo_pdf_interchange_end_structure_tag (surface, tag_type, elem);
1969 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1970 goto cleanup;
1971 }
1972
1973 cleanup:
1974 _cairo_tag_stack_free_elem (elem);
1975
1976 return status;
1977}
1978
1979cairo_int_status_t
1980_cairo_pdf_interchange_command_id (cairo_pdf_surface_t *surface,
1981 unsigned int recording_id,
1982 unsigned int command_id)
1983{
1984 cairo_pdf_interchange_t *ic = &surface->interchange;
1985 int mcid;
1986 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1987
1988 ic->recording_id = recording_id;
1989 ic->command_id = command_id;
1990
1991 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER && ic->current_render_node) {
1992 /* TODO If the group does not have tags we don't need to close the current tag. */
1993 if (command_list_is_group (surface, command_id)) {
1994 /* A "Do /xnnn" can not be inside a tag (since the
1995 * XObject may also contain tags). Close the tag.
1996 */
1997 if (ic->marked_content_open) {
1998 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
1999 ic->marked_content_open = FALSE0;
2000 }
2001 /* If there is any more content after this and we are
2002 * inside a tag (current node is not the root node),
2003 * ensure that the next command will open the tag.
2004 */
2005 if (command_list_has_content (surface, command_id, NULL((void*)0)) && ic->current_render_node->parent) {
2006 ic->render_next_command_has_content = TRUE1;
2007 }
2008 } else if (ic->render_next_command_has_content) {
2009 /* After a "Do /xnnn" operation, if there is more content, open the tag. */
2010 add_mcid_to_node (surface, ic->current_render_node, ic->command_id, &mcid);
2011 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators,
2012 ic->current_render_node->name, mcid);
2013 ic->marked_content_open = TRUE1;
2014 ic->render_next_command_has_content = FALSE0;
2015 }
2016 }
2017
2018 return status;
2019}
2020
2021/* Check if this use of recording surface is or will need to be part of the the struct tree */
2022cairo_bool_t
2023_cairo_pdf_interchange_struct_tree_requires_recording_surface (
2024 cairo_pdf_surface_t *surface,
2025 const cairo_surface_pattern_t *recording_surface_pattern,
2026 cairo_analysis_source_t source_type)
2027{
2028 cairo_surface_t *recording_surface = recording_surface_pattern->surface;
2029 cairo_surface_t *free_me = NULL((void*)0);
2030 cairo_bool_t requires_recording = FALSE0;
2031
2032 if (recording_surface_pattern->base.extend != CAIRO_EXTEND_NONE)
2033 return FALSE0;
2034
2035 if (_cairo_surface_is_snapshot (recording_surface))
2036 free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
2037
2038 if (_cairo_surface_is_recording(recording_surface) &&
2039 _cairo_recording_surface_has_tags(recording_surface))
2040 {
2041 /* Check if tags are to be ignored in this source */
2042 switch (source_type) {
2043 case CAIRO_ANALYSIS_SOURCE_PAINT:
2044 case CAIRO_ANALYSIS_SOURCE_FILL:
2045 requires_recording = TRUE1;
2046 break;
2047 case CAIRO_ANALYSIS_SOURCE_MASK: /* TODO: allow SOURCE_MASK with solid MASK_MASK */
2048 case CAIRO_ANALYSIS_MASK_MASK:
2049 case CAIRO_ANALYSIS_SOURCE_STROKE:
2050 case CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS:
2051 case CAIRO_ANALYSIS_SOURCE_NONE:
2052 break;
2053 }
2054 }
2055
2056 cairo_surface_destroy_moz_cairo_surface_destroy (free_me);
2057 return requires_recording;
2058}
2059
2060/* Called at the start of a recording group during analyze. This will
2061 * be called during the analysis of the drawing operation. */
2062cairo_int_status_t
2063_cairo_pdf_interchange_recording_source_surface_begin (
2064 cairo_pdf_surface_t *surface,
2065 const cairo_surface_pattern_t *recording_surface_pattern,
2066 unsigned int region_id,
2067 cairo_analysis_source_t source_type)
2068{
2069 cairo_pdf_interchange_t *ic = &surface->interchange;
2070 cairo_recording_surface_stack_entry_t element;
2071 cairo_int_status_t status;
2072
2073 /* A new recording surface is being replayed */
2074 ic->ignore_current_surface = TRUE1;
2075 if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface,
2076 recording_surface_pattern,
2077 source_type))
2078 {
2079 ic->ignore_current_surface = FALSE0;
2080 }
2081
2082 element.ignore_surface = ic->ignore_current_surface;
2083 element.current_node = ic->current_analyze_node;
2084 ic->content_emitted = FALSE0;
2085
2086 /* Push to stack so that the current source identifiers can be
2087 * restored after this recording surface has ended. */
2088 status = _cairo_array_append (&ic->recording_surface_stack, &element);
2089 if (status)
2090 return status;
2091
2092 if (ic->ignore_current_surface)
2093 return CAIRO_STATUS_SUCCESS;
2094
2095 status = command_list_push_group (surface, ic->command_id, recording_surface_pattern->surface, region_id);
2096 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2097 return status;
2098
2099 return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
2100}
2101
2102/* Called at the end of a recording group during analyze. */
2103cairo_int_status_t
2104_cairo_pdf_interchange_recording_source_surface_end (
2105 cairo_pdf_surface_t *surface,
2106 const cairo_surface_pattern_t *recording_surface_pattern,
2107 unsigned int region_id,
2108 cairo_analysis_source_t source_type)
2109{
2110 cairo_pdf_interchange_t *ic = &surface->interchange;
2111 cairo_recording_surface_stack_entry_t element;
2112 cairo_recording_surface_stack_entry_t *element_ptr;
2113
2114 if (!ic->ignore_current_surface)
2115 command_list_pop_group (surface);
2116
2117 if (_cairo_array_pop_element (&ic->recording_surface_stack, &element)) {
2118 element_ptr = _cairo_array_last_element (&ic->recording_surface_stack);
2119 if (element_ptr) {
2120 ic->ignore_current_surface = element_ptr->ignore_surface;
2121 assert (ic->current_analyze_node == element_ptr->current_node)((void) sizeof ((ic->current_analyze_node == element_ptr->
current_node) ? 1 : 0), __extension__ ({ if (ic->current_analyze_node
== element_ptr->current_node) ; else __assert_fail ("ic->current_analyze_node == element_ptr->current_node"
, "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 2121, __extension__ __PRETTY_FUNCTION__); }))
;
2122 } else {
2123 /* Back at the page content. */
2124 ic->ignore_current_surface = FALSE0;
2125 }
2126 ic->content_emitted = FALSE0;
2127 return CAIRO_STATUS_SUCCESS;
2128 }
2129 ASSERT_NOT_REACHEDdo { ((void) sizeof ((!"reached") ? 1 : 0), __extension__ ({ if
(!"reached") ; else __assert_fail ("!\"reached\"", "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 2129, __extension__ __PRETTY_FUNCTION__); })); } while (0)
; /* _recording_source_surface_begin/end mismatch */
2130
2131 return CAIRO_STATUS_SUCCESS;
2132}
2133
2134/* Called at the start of a recording group during render. This will
2135 * be called after the end of page content. */
2136cairo_int_status_t
2137_cairo_pdf_interchange_emit_recording_surface_begin (cairo_pdf_surface_t *surface,
2138 cairo_surface_t *recording_surface,
2139 int region_id,
2140 cairo_pdf_resource_t surface_resource,
2141 int *struct_parents)
2142{
2143 cairo_pdf_interchange_t *ic = &surface->interchange;
2144 cairo_int_status_t status;
2145
2146 /* When
2147 * _cairo_pdf_interchange_struct_tree_requires_recording_surface()
2148 * is false, the region_id of the recording surface is set to 0.
2149 */
2150 if (region_id == 0) {
2151 ic->ignore_current_surface = TRUE1;
2152 return CAIRO_STATUS_SUCCESS;
2153 }
2154
2155 command_list_set_current_recording_commands (surface, recording_surface, region_id);
2156
2157 ic->ignore_current_surface = FALSE0;
2158 _cairo_array_truncate (&ic->mcid_to_tree, 0);
2159 ic->current_recording_surface_res = surface_resource;
2160
2161 ic->content_parent_res = _cairo_pdf_surface_new_object (surface);
2162 if (ic->content_parent_res.id == 0)
2163 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2164
2165 status = _cairo_array_append (&ic->parent_tree, &ic->content_parent_res);
2166 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2167 return status;
2168
2169 *struct_parents = _cairo_array_num_elements (&ic->parent_tree) - 1;
2170
2171 ic->render_next_command_has_content = FALSE0;
2172
2173 return CAIRO_STATUS_SUCCESS;
2174}
2175
2176/* Called at the end of a recording group during render. */
2177cairo_int_status_t
2178_cairo_pdf_interchange_emit_recording_surface_end (cairo_pdf_surface_t *surface,
2179 cairo_surface_t *recording_surface)
2180{
2181 cairo_pdf_interchange_t *ic = &surface->interchange;
2182
2183 if (ic->ignore_current_surface)
2184 return CAIRO_STATUS_SUCCESS;
2185
2186 ic->current_recording_surface_res.id = 0;
2187 return cairo_pdf_interchange_write_content_parent_elems (surface);
2188}
2189
2190static void _add_operation_extents_to_dest_tag (cairo_tag_stack_elem_t *elem,
2191 void *closure)
2192{
2193 const cairo_rectangle_int_t *extents = (const cairo_rectangle_int_t *) closure;
2194 cairo_pdf_named_dest_t *dest;
2195
2196 if (_cairo_tag_get_type (elem->name) & TAG_TYPE_DEST) {
2197 if (elem->data) {
2198 dest = (cairo_pdf_named_dest_t *) elem->data;
2199 if (dest->extents.valid) {
2200 _cairo_rectangle_union (&dest->extents.extents, extents);
2201 } else {
2202 dest->extents.extents = *extents;
2203 dest->extents.valid = TRUE1;
2204 }
2205 }
2206 }
2207}
2208
2209cairo_int_status_t
2210_cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface,
2211 const cairo_rectangle_int_t *extents)
2212{
2213 cairo_pdf_interchange_t *ic = &surface->interchange;
2214
2215 /* Add extents to current node and all DEST tags on the stack */
2216 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2217 if (ic->current_analyze_node) {
2218 if (ic->current_analyze_node->extents.valid) {
2219 _cairo_rectangle_union (&ic->current_analyze_node->extents.extents, extents);
2220 } else {
2221 ic->current_analyze_node->extents.extents = *extents;
2222 ic->current_analyze_node->extents.valid = TRUE1;
2223 }
2224 }
2225
2226 _cairo_tag_stack_foreach (&ic->analysis_tag_stack,
2227 _add_operation_extents_to_dest_tag,
2228 (void*)extents);
2229 }
2230
2231 return CAIRO_STATUS_SUCCESS;
2232}
2233
2234cairo_int_status_t
2235_cairo_pdf_interchange_add_content (cairo_pdf_surface_t *surface)
2236{
2237 cairo_pdf_interchange_t *ic = &surface->interchange;
2238 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2239
2240 if (ic->ignore_current_surface)
2241 return CAIRO_STATUS_SUCCESS;
2242
2243 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2244 status = command_list_add (surface, ic->command_id, PDF_CONTENT);
2245 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2246 return status;
2247 }
2248
2249 return status;
2250}
2251
2252/* Called at the start of 1emiting the page content during analyze or render */
2253cairo_int_status_t
2254_cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface)
2255{
2256 cairo_pdf_interchange_t *ic = &surface->interchange;
2257 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2258 int mcid;
2259 unsigned int content_command_id;
2260 cairo_pdf_command_list_t *page_commands;
2261
2262 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2263 status = _cairo_array_allocate (&ic->page_commands, 1, (void**)&page_commands);
2264 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2265 return status;
2266
2267 _cairo_array_init (&page_commands->commands, sizeof(cairo_pdf_command_t));
2268 page_commands->parent = NULL((void*)0);
2269 ic->current_commands = page_commands;
2270 ic->ignore_current_surface = FALSE0;
2271 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
2272 ic->current_commands = _cairo_array_last_element (&ic->page_commands);
2273 /* Each page has its own parent tree to map MCID to nodes. */
2274 _cairo_array_truncate (&ic->mcid_to_tree, 0);
2275 ic->ignore_current_surface = FALSE0;
2276 ic->content_parent_res = _cairo_pdf_surface_new_object (surface);
2277 if (ic->content_parent_res.id == 0)
2278 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2279
2280 status = _cairo_array_append (&ic->parent_tree, &ic->content_parent_res);
2281 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2282 return status;
2283
2284 surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1;
2285
2286 if (ic->next_page_render_node && ic->next_page_render_node->parent &&
2287 command_list_has_content (surface, -1, &content_command_id))
2288 {
2289 add_mcid_to_node (surface, ic->next_page_render_node, content_command_id, &mcid);
2290 const char *tag_name = ic->next_page_render_node->name;
2291 if (ic->next_page_render_node->type == PDF_NODE_CONTENT)
2292 tag_name = ic->next_page_render_node->attributes.content.tag_name;
2293
2294 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, tag_name, mcid);
2295 ic->marked_content_open = TRUE1;
2296 }
2297 ic->render_next_command_has_content = FALSE0;
2298 }
2299
2300 return status;
2301}
2302
2303/* Called at the end of emiting the page content during analyze or render */
2304cairo_int_status_t
2305_cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface)
2306{
2307 cairo_pdf_interchange_t *ic = &surface->interchange;
2308 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2309
2310 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
2311 /* If a content tag is open across pages, the old page needs an EMC emitted. */
2312 if (ic->marked_content_open) {
2313 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
2314 ic->marked_content_open = FALSE0;
2315 }
2316 ic->next_page_render_node = ic->current_render_node;
2317 }
2318
2319 return status;
2320}
2321
2322cairo_int_status_t
2323_cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface)
2324{
2325 return cairo_pdf_interchange_write_content_parent_elems (surface);
2326}
2327
2328cairo_int_status_t
2329_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
2330{
2331 cairo_pdf_interchange_t *ic = &surface->interchange;
2332 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2333 cairo_tag_stack_structure_type_t tag_type;
2334 cairo_bool_t write_struct_tree = FALSE0;
2335
2336 status = cairo_pdf_interchange_update_extents (surface);
1
Calling 'cairo_pdf_interchange_update_extents'
2337 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2338 return status;
2339
2340 tag_type = _cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack);
2341 if (tag_type == TAG_TREE_TYPE_TAGGED || tag_type == TAG_TREE_TYPE_STRUCTURE ||
2342 tag_type == TAG_TREE_TYPE_LINK_ONLY)
2343 {
2344 write_struct_tree = TRUE1;
2345 }
2346
2347 status = cairo_pdf_interchange_write_annots (surface, write_struct_tree);
2348 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2349 return status;
2350
2351 if (write_struct_tree) {
2352 surface->struct_tree_root = _cairo_pdf_surface_new_object (surface);
2353 if (surface->struct_tree_root.id == 0)
2354 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2355
2356 ic->struct_root->res = surface->struct_tree_root;
2357
2358 status = cairo_pdf_interchange_write_parent_tree (surface);
2359 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2360 return status;
2361
2362 unsigned num_pages = _cairo_array_num_elements (&ic->page_commands);
2363 for (unsigned i = 0; i < num_pages; i++) {
2364 cairo_pdf_command_list_t *command_list;
2365 command_list = _cairo_array_index (&ic->page_commands, i);
2366 update_mcid_order (surface, command_list);
2367 }
2368
2369 status = cairo_pdf_interchange_write_struct_tree (surface);
2370 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2371 return status;
2372
2373 if (tag_type == TAG_TREE_TYPE_TAGGED)
2374 surface->tagged = TRUE1;
2375 }
2376
2377 status = cairo_pdf_interchange_write_outline (surface);
2378 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2379 return status;
2380
2381 status = cairo_pdf_interchange_write_page_labels (surface);
2382 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2383 return status;
2384
2385 status = cairo_pdf_interchange_write_names_dict (surface);
2386 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2387 return status;
2388
2389 status = cairo_pdf_interchange_write_docinfo (surface);
2390
2391 return status;
2392}
2393
2394static void
2395_cairo_pdf_interchange_set_create_date (cairo_pdf_surface_t *surface)
2396{
2397 time_t utc, local, offset;
2398 struct tm tm_local, tm_utc;
2399 char buf[50];
2400 int buf_size;
2401 char *p;
2402 cairo_pdf_interchange_t *ic = &surface->interchange;
2403
2404 utc = time (NULL((void*)0));
2405 localtime_r (&utc, &tm_local);
2406 strftime (buf, sizeof(buf), "(D:%Y%m%d%H%M%S", &tm_local);
2407
2408 /* strftime "%z" is non standard and does not work on windows (it prints zone name, not offset).
2409 * Calculate time zone offset by comparing local and utc time_t values for the same time.
2410 */
2411 gmtime_r (&utc, &tm_utc);
2412 tm_utc.tm_isdst = tm_local.tm_isdst;
2413 local = mktime (&tm_utc);
2414 offset = difftime (utc, local);
2415
2416 if (offset == 0) {
2417 strcat (buf, "Z");
2418 } else {
2419 if (offset > 0) {
2420 strcat (buf, "+");
2421 } else {
2422 strcat (buf, "-");
2423 offset = -offset;
2424 }
2425 p = buf + strlen (buf);
2426 buf_size = sizeof (buf) - strlen (buf);
2427 snprintf (p, buf_size, "%02d'%02d", (int)(offset/3600), (int)(offset%3600)/60);
2428 }
2429 strcat (buf, ")");
2430 ic->docinfo.create_date = strdup (buf);
2431}
2432
2433cairo_int_status_t
2434_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface)
2435{
2436 cairo_pdf_interchange_t *ic = &surface->interchange;
2437 cairo_pdf_outline_entry_t *outline_root;
2438 cairo_int_status_t status;
2439
2440 _cairo_tag_stack_init (&ic->analysis_tag_stack);
2441 _cairo_tag_stack_init (&ic->render_tag_stack);
2442 ic->struct_root = calloc (1, sizeof(cairo_pdf_struct_tree_node_t));
2443 if (unlikely (ic->struct_root == NULL)(__builtin_expect (!!(ic->struct_root == ((void*)0)), 0)))
2444 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2445
2446 ic->struct_root->res.id = 0;
2447 cairo_list_init (&ic->struct_root->children);
2448 _cairo_array_init (&ic->struct_root->mcid, sizeof(cairo_pdf_page_mcid_t));
2449
2450 ic->current_analyze_node = ic->struct_root;
2451 ic->current_render_node = NULL((void*)0);
2452 ic->next_page_render_node = ic->struct_root;
2453 _cairo_array_init (&ic->recording_surface_stack, sizeof(cairo_recording_surface_stack_entry_t));
2454 ic->current_recording_surface_res.id = 0;
2455 ic->command_to_node_map = _cairo_hash_table_create (_cairo_pdf_command_equal);
2456 if (unlikely (ic->command_to_node_map == NULL)(__builtin_expect (!!(ic->command_to_node_map == ((void*)0
)), 0))
)
2457 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2458
2459 ic->content_tag_map = _cairo_hash_table_create (_cairo_pdf_content_tag_equal);
2460 if (unlikely (ic->content_tag_map == NULL)(__builtin_expect (!!(ic->content_tag_map == ((void*)0)), 0
))
)
2461 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2462
2463 _cairo_array_init (&ic->parent_tree, sizeof(cairo_pdf_resource_t));
2464 _cairo_array_init (&ic->mcid_to_tree, sizeof(cairo_pdf_struct_tree_node_t *));
2465 _cairo_array_init (&ic->annots, sizeof(cairo_pdf_annotation_t *));
2466 ic->parent_tree_res.id = 0;
2467 cairo_list_init (&ic->extents_list);
2468 ic->named_dests = _cairo_hash_table_create (_named_dest_equal);
2469 if (unlikely (ic->named_dests == NULL)(__builtin_expect (!!(ic->named_dests == ((void*)0)), 0)))
2470 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2471
2472 _cairo_array_init (&ic->page_commands, sizeof(cairo_pdf_command_list_t));
2473 ic->current_commands = NULL((void*)0);
2474 _cairo_array_init (&ic->recording_surface_commands, sizeof(cairo_pdf_recording_surface_commands_t));
2475
2476 ic->num_dests = 0;
2477 ic->sorted_dests = NULL((void*)0);
2478 ic->dests_res.id = 0;
2479 ic->ignore_current_surface = FALSE0;
2480 ic->content_emitted = FALSE0;
2481 ic->marked_content_open = FALSE0;
2482 ic->render_next_command_has_content = FALSE0;
2483 ic->mcid_order = 0;
2484
2485 _cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *));
2486 outline_root = calloc (1, sizeof(cairo_pdf_outline_entry_t));
2487 if (unlikely (outline_root == NULL)(__builtin_expect (!!(outline_root == ((void*)0)), 0)))
2488 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2489
2490 memset (&ic->docinfo, 0, sizeof (ic->docinfo));
2491 _cairo_array_init (&ic->custom_metadata, sizeof(struct metadata));
2492 _cairo_pdf_interchange_set_create_date (surface);
2493 status = _cairo_array_append (&ic->outline, &outline_root);
2494
2495 return status;
2496}
2497
2498static void
2499_cairo_pdf_interchange_free_outlines (cairo_pdf_surface_t *surface)
2500{
2501 cairo_pdf_interchange_t *ic = &surface->interchange;
2502 int num_elems, i;
2503
2504 num_elems = _cairo_array_num_elements (&ic->outline);
2505 for (i = 0; i < num_elems; i++) {
2506 cairo_pdf_outline_entry_t *outline;
2507
2508 _cairo_array_copy_element (&ic->outline, i, &outline);
2509 free (outline->name);
2510 _cairo_tag_free_link_attributes (&outline->link_attrs);
2511 free (outline);
2512 }
2513 _cairo_array_fini (&ic->outline);
2514}
2515
2516void
2517_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface)
2518{
2519 cairo_pdf_interchange_t *ic = &surface->interchange;
2520 unsigned int i, num_elems;
2521 struct metadata *data;
2522
2523 _cairo_tag_stack_fini (&ic->analysis_tag_stack);
2524 _cairo_tag_stack_fini (&ic->render_tag_stack);
2525 _cairo_array_fini (&ic->mcid_to_tree);
2526 cairo_pdf_interchange_clear_annotations (surface);
2527 _cairo_array_fini (&ic->annots);
2528
2529 _cairo_array_fini (&ic->recording_surface_stack);
2530 _cairo_array_fini (&ic->parent_tree);
2531
2532 _cairo_hash_table_foreach (ic->command_to_node_map,
2533 _cairo_pdf_command_pluck,
2534 ic->command_to_node_map);
2535 _cairo_hash_table_destroy (ic->command_to_node_map);
2536
2537 _cairo_hash_table_foreach (ic->named_dests, _named_dest_pluck, ic->named_dests);
2538 _cairo_hash_table_destroy (ic->named_dests);
2539
2540 _cairo_hash_table_foreach (ic->content_tag_map, _cairo_pdf_content_tag_pluck, ic->content_tag_map);
2541 _cairo_hash_table_destroy(ic->content_tag_map);
2542
2543 free_node (ic->struct_root);
2544
2545 num_elems = _cairo_array_num_elements (&ic->recording_surface_commands);
2546 for (i = 0; i < num_elems; i++) {
2547 cairo_pdf_recording_surface_commands_t *recording_command;
2548 cairo_pdf_command_list_t *command_list;
2549
2550 recording_command = _cairo_array_index (&ic->recording_surface_commands, i);
2551 command_list = recording_command->command_list;
2552 _cairo_array_fini (&command_list->commands);
2553 free (command_list);
2554 }
2555 _cairo_array_fini (&ic->recording_surface_commands);
2556
2557 num_elems = _cairo_array_num_elements (&ic->page_commands);
2558 for (i = 0; i < num_elems; i++) {
2559 cairo_pdf_command_list_t *command_list;
2560 command_list = _cairo_array_index (&ic->page_commands, i);
2561 _cairo_array_fini (&command_list->commands);
2562 }
2563 _cairo_array_fini (&ic->page_commands);
2564
2565 free (ic->sorted_dests);
2566 _cairo_pdf_interchange_free_outlines (surface);
2567 free (ic->docinfo.title);
2568 free (ic->docinfo.author);
2569 free (ic->docinfo.subject);
2570 free (ic->docinfo.keywords);
2571 free (ic->docinfo.creator);
2572 free (ic->docinfo.create_date);
2573 free (ic->docinfo.mod_date);
2574
2575 num_elems = _cairo_array_num_elements (&ic->custom_metadata);
2576 for (i = 0; i < num_elems; i++) {
2577 data = _cairo_array_index (&ic->custom_metadata, i);
2578 free (data->name);
2579 free (data->value);
2580 }
2581 _cairo_array_fini (&ic->custom_metadata);
2582}
2583
2584cairo_int_status_t
2585_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
2586 int parent_id,
2587 const char *name,
2588 const char *link_attribs,
2589 cairo_pdf_outline_flags_t flags,
2590 int *id)
2591{
2592 cairo_pdf_interchange_t *ic = &surface->interchange;
2593 cairo_pdf_outline_entry_t *outline;
2594 cairo_pdf_outline_entry_t *parent;
2595 cairo_int_status_t status;
2596
2597 if (parent_id < 0 || parent_id >= (int)_cairo_array_num_elements (&ic->outline))
2598 return CAIRO_STATUS_SUCCESS;
2599
2600 outline = _cairo_malloc (sizeof(cairo_pdf_outline_entry_t))((sizeof(cairo_pdf_outline_entry_t)) != 0 ? malloc(sizeof(cairo_pdf_outline_entry_t
)) : ((void*)0))
;
2601 if (unlikely (outline == NULL)(__builtin_expect (!!(outline == ((void*)0)), 0)))
2602 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2603
2604 status = _cairo_tag_parse_link_attributes (link_attribs, &outline->link_attrs);
2605 if (unlikely (status)(__builtin_expect (!!(status), 0))) {
2606 free (outline);
2607 return status;
2608 }
2609
2610 outline->res = _cairo_pdf_surface_new_object (surface);
2611 if (outline->res.id == 0)
2612 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2613
2614 outline->name = strdup (name);
2615 outline->flags = flags;
2616 outline->count = 0;
2617
2618 _cairo_array_copy_element (&ic->outline, parent_id, &parent);
2619
2620 outline->parent = parent;
2621 outline->first_child = NULL((void*)0);
2622 outline->last_child = NULL((void*)0);
2623 outline->next = NULL((void*)0);
2624 if (parent->last_child) {
2625 parent->last_child->next = outline;
2626 outline->prev = parent->last_child;
2627 parent->last_child = outline;
2628 } else {
2629 parent->first_child = outline;
2630 parent->last_child = outline;
2631 outline->prev = NULL((void*)0);
2632 }
2633
2634 *id = _cairo_array_num_elements (&ic->outline);
2635 status = _cairo_array_append (&ic->outline, &outline);
2636 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2637 return status;
2638
2639 /* Update Count */
2640 outline = outline->parent;
2641 while (outline) {
2642 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_OPEN) {
2643 outline->count++;
2644 } else {
2645 outline->count--;
2646 break;
2647 }
2648 outline = outline->parent;
2649 }
2650
2651 return CAIRO_STATUS_SUCCESS;
2652}
2653
2654/*
2655 * Date must be in the following format:
2656 *
2657 * YYYY-MM-DDThh:mm:ss[Z+-]hh:mm
2658 *
2659 * Only the year is required. If a field is included all preceding
2660 * fields must be included.
2661 */
2662static char *
2663iso8601_to_pdf_date_string (const char *iso)
2664{
2665 char buf[40];
2666 const char *p;
2667 int i;
2668
2669 /* Check that utf8 contains only the characters "0123456789-T:Z+" */
2670 p = iso;
2671 while (*p) {
2672 if (!_cairo_isdigit (*p) && *p != '-' && *p != 'T' &&
2673 *p != ':' && *p != 'Z' && *p != '+')
2674 return NULL((void*)0);
2675 p++;
2676 }
2677
2678 p = iso;
2679 strcpy (buf, "(");
2680
2681 /* YYYY (required) */
2682 if (strlen (p) < 4)
2683 return NULL((void*)0);
2684
2685 strncat (buf, p, 4);
2686 p += 4;
2687
2688 /* -MM, -DD, Thh, :mm, :ss */
2689 for (i = 0; i < 5; i++) {
2690 if (strlen (p) < 3)
2691 goto finish;
2692
2693 strncat (buf, p + 1, 2);
2694 p += 3;
2695 }
2696
2697 /* Z, +, - */
2698 if (strlen (p) < 1)
2699 goto finish;
2700 strncat (buf, p, 1);
2701 p += 1;
2702
2703 /* hh */
2704 if (strlen (p) < 2)
2705 goto finish;
2706
2707 strncat (buf, p, 2);
2708 strcat (buf, "'");
2709 p += 2;
2710
2711 /* :mm */
2712 if (strlen (p) < 3)
2713 goto finish;
2714
2715 strncat (buf, p + 1, 2);
2716 strcat (buf, "'");
2717
2718 finish:
2719 strcat (buf, ")");
2720 return strdup (buf);
2721}
2722
2723cairo_int_status_t
2724_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface,
2725 cairo_pdf_metadata_t metadata,
2726 const char *utf8)
2727{
2728 cairo_pdf_interchange_t *ic = &surface->interchange;
2729 cairo_status_t status;
2730 char *s = NULL((void*)0);
2731
2732 if (utf8) {
2733 if (metadata == CAIRO_PDF_METADATA_CREATE_DATE ||
2734 metadata == CAIRO_PDF_METADATA_MOD_DATE) {
2735 s = iso8601_to_pdf_date_string (utf8);
2736 } else {
2737 status = _cairo_utf8_to_pdf_string (utf8, &s);
2738 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2739 return status;
2740 }
2741 }
2742
2743 switch (metadata) {
2744 case CAIRO_PDF_METADATA_TITLE:
2745 free (ic->docinfo.title);
2746 ic->docinfo.title = s;
2747 break;
2748 case CAIRO_PDF_METADATA_AUTHOR:
2749 free (ic->docinfo.author);
2750 ic->docinfo.author = s;
2751 break;
2752 case CAIRO_PDF_METADATA_SUBJECT:
2753 free (ic->docinfo.subject);
2754 ic->docinfo.subject = s;
2755 break;
2756 case CAIRO_PDF_METADATA_KEYWORDS:
2757 free (ic->docinfo.keywords);
2758 ic->docinfo.keywords = s;
2759 break;
2760 case CAIRO_PDF_METADATA_CREATOR:
2761 free (ic->docinfo.creator);
2762 ic->docinfo.creator = s;
2763 break;
2764 case CAIRO_PDF_METADATA_CREATE_DATE:
2765 free (ic->docinfo.create_date);
2766 ic->docinfo.create_date = s;
2767 break;
2768 case CAIRO_PDF_METADATA_MOD_DATE:
2769 free (ic->docinfo.mod_date);
2770 ic->docinfo.mod_date = s;
2771 break;
2772 }
2773
2774 return CAIRO_STATUS_SUCCESS;
2775}
2776
2777static const char *reserved_metadata_names[] = {
2778 "",
2779 "Title",
2780 "Author",
2781 "Subject",
2782 "Keywords",
2783 "Creator",
2784 "Producer",
2785 "CreationDate",
2786 "ModDate",
2787 "Trapped",
2788};
2789
2790cairo_int_status_t
2791_cairo_pdf_interchange_set_custom_metadata (cairo_pdf_surface_t *surface,
2792 const char *name,
2793 const char *value)
2794{
2795 cairo_pdf_interchange_t *ic = &surface->interchange;
2796 struct metadata *data;
2797 struct metadata new_data;
2798 int i, num_elems;
2799 cairo_int_status_t status;
2800 char *s = NULL((void*)0);
2801
2802 if (name == NULL((void*)0))
2803 return CAIRO_STATUS_NULL_POINTER;
2804
2805 for (i = 0; i < ARRAY_LENGTH (reserved_metadata_names)((int) (sizeof (reserved_metadata_names) / sizeof (reserved_metadata_names
[0])))
; i++) {
2806 if (strcmp(name, reserved_metadata_names[i]) == 0)
2807 return CAIRO_STATUS_INVALID_STRING;
2808 }
2809
2810 /* First check if we already have an entry for this name. If so,
2811 * update the value. A NULL value means the entry has been removed
2812 * and will not be emitted. */
2813 num_elems = _cairo_array_num_elements (&ic->custom_metadata);
2814 for (i = 0; i < num_elems; i++) {
2815 data = _cairo_array_index (&ic->custom_metadata, i);
2816 if (strcmp(name, data->name) == 0) {
2817 free (data->value);
2818 data->value = NULL((void*)0);
2819 if (value && strlen(value)) {
2820 status = _cairo_utf8_to_pdf_string (value, &s);
2821 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2822 return status;
2823 data->value = s;
2824 }
2825 return CAIRO_STATUS_SUCCESS;
2826 }
2827 }
2828
2829 /* Add new entry */
2830 status = CAIRO_STATUS_SUCCESS;
2831 if (value && strlen(value)) {
2832 new_data.name = strdup (name);
2833 status = _cairo_utf8_to_pdf_string (value, &s);
2834 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2835 return status;
2836 new_data.value = s;
2837 status = _cairo_array_append (&ic->custom_metadata, &new_data);
2838 }
2839
2840 return status;
2841}
2842
2843#if DEBUG_PDF_INTERCHANGE
2844static cairo_int_status_t
2845print_node (cairo_pdf_surface_t *surface,
2846 cairo_pdf_struct_tree_node_t *node,
2847 int depth)
2848{
2849 if (node == NULL((void*)0)) {
2850 printf("%*sNode: ptr: NULL\n", depth*2, "");
2851 } else if (node == surface->interchange.struct_root) {
2852 printf("%*sNode: ptr: %p root\n", depth*2, "", node);
2853 } else {
2854 printf("%*sNode: ptr: %p name: '%s'\n", depth*2, "", node, node->name);
2855 }
2856 depth++;
2857 printf("%*sType: ", depth*2, "");
2858 switch (node->type) {
2859 case PDF_NODE_STRUCT:
2860 printf("STRUCT\n");
2861 break;
2862 case PDF_NODE_CONTENT:
2863 printf("CONTENT\n");
2864 printf("%*sContent.id: %s\n", depth*2, "", node->attributes.content.id);
2865 printf("%*sContent.tag_name: %s\n", depth*2, "", node->attributes.content.tag_name);
2866 break;
2867 case PDF_NODE_CONTENT_REF:
2868 printf("CONTENT_REF\n");
2869 printf("%*sContent_Ref.ref: %s\n", depth*2, "", node->attributes.content_ref.ref);
2870 break;
2871 case PDF_NODE_ARTIFACT:
2872 printf("ARTIFACT\n");
2873 break;
2874 }
2875 printf("%*sres: %d\n", depth*2, "", node->res.id);
2876 printf("%*sparent: %p\n", depth*2, "", node->parent);
2877 printf("%*sannot:", depth*2, "");
2878 if (node->annot)
2879 printf(" node: %p res: %d", node->annot->node, node->annot->res.id);
2880 printf("\n");
2881 printf("%*sextents: ", depth*2, "");
2882 if (node->extents.valid) {
2883 printf("x: %d y: %d w: %d h: %d\n",
2884 node->extents.extents.x,
2885 node->extents.extents.y,
2886 node->extents.extents.width,
2887 node->extents.extents.height);
2888 } else {
2889 printf("not valid\n");
2890 }
2891
2892 printf("%*smcid: ", depth*2, "");
2893 int num_mcid = _cairo_array_num_elements (&node->mcid);
2894 for (int i = 0; i < num_mcid; i++) {
2895 cairo_pdf_page_mcid_t *mcid_elem = _cairo_array_index (&node->mcid, i);
2896 if (mcid_elem->child_node) {
2897 printf("(order: %d, %p) ", mcid_elem->order, mcid_elem->child_node);
2898 } else {
2899 printf("(order: %d, pg: %d, xobject_res: %d, mcid: %d) ",
2900 mcid_elem->order, mcid_elem->page, mcid_elem->xobject_res.id, mcid_elem->mcid);
2901 }
2902 }
2903 printf("\n");
2904 return CAIRO_STATUS_SUCCESS;
2905}
2906
2907static void
2908print_tree (cairo_pdf_surface_t *surface,
2909 cairo_pdf_struct_tree_node_t *node)
2910{
2911 printf("Structure Tree:\n");
2912 cairo_pdf_interchange_walk_struct_tree (surface, node, 0, print_node);
2913}
2914
2915static void
2916print_command (cairo_pdf_command_t *command, int indent)
2917{
2918 printf("%*s%d ", indent*2, "", command->command_id);
2919 switch (command->flags) {
2920 case PDF_CONTENT:
2921 printf("CONTENT");
2922 break;
2923 case PDF_BEGIN:
2924 printf("BEGIN");
2925 break;
2926 case PDF_END:
2927 printf("END");
2928 break;
2929 case PDF_GROUP:
2930 printf("GROUP: %p", command->group);
2931 break;
2932 case PDF_NONE:
2933 printf("NONE");
2934 break;
2935 }
2936 printf(" node: %p index: %d\n", command->node, command->mcid_index);
2937}
2938
2939static void
2940print_commands (cairo_pdf_command_list_t *command_list, int indent)
2941{
2942 cairo_pdf_command_t *command;
2943 unsigned i;
2944 unsigned num_elements = _cairo_array_num_elements (&command_list->commands);
2945
2946 for (i = 0; i < num_elements; i++) {
2947 command = _cairo_array_index (&command_list->commands, i);
2948 print_command (command, indent);
2949 if (command->flags == PDF_GROUP)
2950 print_commands (command->group, indent + 1);
2951 }
2952}
2953
2954static void
2955print_command_list(cairo_pdf_command_list_t *command_list)
2956{
2957 printf("Command List: %p\n", command_list);
2958 print_commands (command_list, 0);
2959 printf("end\n");
2960}
2961#endif