Bug Summary

File:root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c
Warning:line 1260, column 6
2nd function call argument is an uninitialized value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -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-23/lib/clang/23 -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 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-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -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-character-conversion -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 -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-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 (__assert_single_arg (entry != ((void*)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_calloc (sizeof(cairo_pdf_command_list_t))((sizeof(cairo_pdf_command_list_t)) != 0 ? calloc(1,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 (__assert_single_arg (!"reached")), __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_calloc (sizeof(cairo_pdf_struct_tree_node_t))((sizeof(cairo_pdf_struct_tree_node_t)) != 0 ? calloc(1,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_calloc (sizeof (cairo_pdf_annotation_t))((sizeof (cairo_pdf_annotation_t)) != 0 ? calloc(1,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 (__assert_single_arg (mcid_elem->order != -
1)), __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 (__assert_single_arg (content_node->type ==
PDF_NODE_CONTENT)), __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 not equal to 0
18
Taking true branch
1260 _cairo_rectangle_union (&node->extents.extents, &content_node->extents.extents);
19
2nd function call argument is an uninitialized value
1261 } else {
1262 node->extents = content_node->extents;
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 = _cairo_calloc_ab (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_calloc (sizeof(cairo_pdf_command_entry_t))((sizeof(cairo_pdf_command_entry_t)) != 0 ? calloc(1,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_calloc (sizeof(cairo_pdf_content_tag_t))((sizeof(cairo_pdf_content_tag_t)) != 0 ? calloc(1,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 = _cairo_calloc (sizeof (cairo_pdf_named_dest_t))((sizeof (cairo_pdf_named_dest_t)) != 0 ? calloc(1,sizeof (cairo_pdf_named_dest_t
)) : ((void*)0))
;
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 (__assert_single_arg (ic->current_analyze_node
->parent != ((void*)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 } else {
1963 ASSERT_NOT_REACHEDdo { ((void) sizeof (__assert_single_arg (!"reached")), __extension__
({ if (!"reached") ; else __assert_fail ("!\"reached\"", "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 1963, __extension__ __PRETTY_FUNCTION__); })); } while (0)
;
1964 }
1965 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1966 return status;
1967
1968 tag_type = _cairo_tag_get_type (name);
1969 if (tag_type & (TAG_TYPE_STRUCTURE|TAG_TYPE_CONTENT|TAG_TYPE_CONTENT_REF|TAG_TYPE_ARTIFACT)) {
1970 status = _cairo_pdf_interchange_end_structure_tag (surface, tag_type, elem);
1971 if (unlikely (status)(__builtin_expect (!!(status), 0)))
1972 goto cleanup;
1973 }
1974
1975 cleanup:
1976 _cairo_tag_stack_free_elem (elem);
1977
1978 return status;
1979}
1980
1981cairo_int_status_t
1982_cairo_pdf_interchange_command_id (cairo_pdf_surface_t *surface,
1983 unsigned int recording_id,
1984 unsigned int command_id)
1985{
1986 cairo_pdf_interchange_t *ic = &surface->interchange;
1987 int mcid;
1988 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1989
1990 ic->recording_id = recording_id;
1991 ic->command_id = command_id;
1992
1993 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER && ic->current_render_node) {
1994 /* TODO If the group does not have tags we don't need to close the current tag. */
1995 if (command_list_is_group (surface, command_id)) {
1996 /* A "Do /xnnn" can not be inside a tag (since the
1997 * XObject may also contain tags). Close the tag.
1998 */
1999 if (ic->marked_content_open) {
2000 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
2001 ic->marked_content_open = FALSE0;
2002 }
2003 /* If there is any more content after this and we are
2004 * inside a tag (current node is not the root node),
2005 * ensure that the next command will open the tag.
2006 */
2007 if (command_list_has_content (surface, command_id, NULL((void*)0)) && ic->current_render_node->parent) {
2008 ic->render_next_command_has_content = TRUE1;
2009 }
2010 } else if (ic->render_next_command_has_content) {
2011 /* After a "Do /xnnn" operation, if there is more content, open the tag. */
2012 add_mcid_to_node (surface, ic->current_render_node, ic->command_id, &mcid);
2013 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators,
2014 ic->current_render_node->name, mcid);
2015 ic->marked_content_open = TRUE1;
2016 ic->render_next_command_has_content = FALSE0;
2017 }
2018 }
2019
2020 return status;
2021}
2022
2023/* Check if this use of recording surface is or will need to be part of the the struct tree */
2024cairo_bool_t
2025_cairo_pdf_interchange_struct_tree_requires_recording_surface (
2026 cairo_pdf_surface_t *surface,
2027 const cairo_surface_pattern_t *recording_surface_pattern,
2028 cairo_analysis_source_t source_type)
2029{
2030 cairo_surface_t *recording_surface = recording_surface_pattern->surface;
2031 cairo_surface_t *free_me = NULL((void*)0);
2032 cairo_bool_t requires_recording = FALSE0;
2033
2034 if (recording_surface_pattern->base.extend != CAIRO_EXTEND_NONE)
2035 return FALSE0;
2036
2037 if (_cairo_surface_is_snapshot (recording_surface))
2038 free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
2039
2040 if (_cairo_surface_is_recording (recording_surface) &&
2041 _cairo_recording_surface_has_tags (recording_surface))
2042 {
2043 /* Check if tags are to be ignored in this source */
2044 switch (source_type) {
2045 case CAIRO_ANALYSIS_SOURCE_PAINT:
2046 case CAIRO_ANALYSIS_SOURCE_FILL:
2047 requires_recording = TRUE1;
2048 break;
2049 case CAIRO_ANALYSIS_SOURCE_MASK: /* TODO: allow SOURCE_MASK with solid MASK_MASK */
2050 case CAIRO_ANALYSIS_MASK_MASK:
2051 case CAIRO_ANALYSIS_SOURCE_STROKE:
2052 case CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS:
2053 case CAIRO_ANALYSIS_SOURCE_NONE:
2054 break;
2055 }
2056 }
2057
2058 cairo_surface_destroy_moz_cairo_surface_destroy (free_me);
2059 return requires_recording;
2060}
2061
2062/* Called at the start of a recording group during analyze. This will
2063 * be called during the analysis of the drawing operation. */
2064cairo_int_status_t
2065_cairo_pdf_interchange_recording_source_surface_begin (
2066 cairo_pdf_surface_t *surface,
2067 const cairo_surface_pattern_t *recording_surface_pattern,
2068 unsigned int region_id,
2069 cairo_analysis_source_t source_type)
2070{
2071 cairo_pdf_interchange_t *ic = &surface->interchange;
2072 cairo_recording_surface_stack_entry_t element;
2073 cairo_int_status_t status;
2074
2075 /* A new recording surface is being replayed */
2076 ic->ignore_current_surface = TRUE1;
2077 if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface,
2078 recording_surface_pattern,
2079 source_type))
2080 {
2081 ic->ignore_current_surface = FALSE0;
2082 }
2083
2084 element.ignore_surface = ic->ignore_current_surface;
2085 element.current_node = ic->current_analyze_node;
2086 ic->content_emitted = FALSE0;
2087
2088 /* Push to stack so that the current source identifiers can be
2089 * restored after this recording surface has ended. */
2090 status = _cairo_array_append (&ic->recording_surface_stack, &element);
2091 if (status)
2092 return status;
2093
2094 if (ic->ignore_current_surface)
2095 return CAIRO_STATUS_SUCCESS;
2096
2097 status = command_list_push_group (surface, ic->command_id, recording_surface_pattern->surface, region_id);
2098 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2099 return status;
2100
2101 return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
2102}
2103
2104/* Called at the end of a recording group during analyze. */
2105cairo_int_status_t
2106_cairo_pdf_interchange_recording_source_surface_end (
2107 cairo_pdf_surface_t *surface,
2108 const cairo_surface_pattern_t *recording_surface_pattern,
2109 unsigned int region_id,
2110 cairo_analysis_source_t source_type)
2111{
2112 cairo_pdf_interchange_t *ic = &surface->interchange;
2113 cairo_recording_surface_stack_entry_t element;
2114 cairo_recording_surface_stack_entry_t *element_ptr;
2115
2116 if (!ic->ignore_current_surface)
2117 command_list_pop_group (surface);
2118
2119 if (_cairo_array_pop_element (&ic->recording_surface_stack, &element)) {
2120 element_ptr = _cairo_array_last_element (&ic->recording_surface_stack);
2121 if (element_ptr) {
2122 ic->ignore_current_surface = element_ptr->ignore_surface;
2123 assert (ic->current_analyze_node == element_ptr->current_node)((void) sizeof (__assert_single_arg (ic->current_analyze_node
== element_ptr->current_node)), __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"
, 2123, __extension__ __PRETTY_FUNCTION__); }))
;
2124 } else {
2125 /* Back at the page content. */
2126 ic->ignore_current_surface = FALSE0;
2127 }
2128 ic->content_emitted = FALSE0;
2129 return CAIRO_STATUS_SUCCESS;
2130 }
2131 ASSERT_NOT_REACHEDdo { ((void) sizeof (__assert_single_arg (!"reached")), __extension__
({ if (!"reached") ; else __assert_fail ("!\"reached\"", "/root/firefox-clang/gfx/cairo/cairo/src/cairo-pdf-interchange.c"
, 2131, __extension__ __PRETTY_FUNCTION__); })); } while (0)
; /* _recording_source_surface_begin/end mismatch */
2132
2133 return CAIRO_STATUS_SUCCESS;
2134}
2135
2136/* Called at the start of a recording group during render. This will
2137 * be called after the end of page content. */
2138cairo_int_status_t
2139_cairo_pdf_interchange_emit_recording_surface_begin (cairo_pdf_surface_t *surface,
2140 cairo_surface_t *recording_surface,
2141 int region_id,
2142 cairo_pdf_resource_t surface_resource,
2143 int *struct_parents)
2144{
2145 cairo_pdf_interchange_t *ic = &surface->interchange;
2146 cairo_int_status_t status;
2147
2148 /* When
2149 * _cairo_pdf_interchange_struct_tree_requires_recording_surface()
2150 * is false, the region_id of the recording surface is set to 0.
2151 */
2152 if (region_id == 0) {
2153 ic->ignore_current_surface = TRUE1;
2154 return CAIRO_STATUS_SUCCESS;
2155 }
2156
2157 command_list_set_current_recording_commands (surface, recording_surface, region_id);
2158
2159 ic->ignore_current_surface = FALSE0;
2160 _cairo_array_truncate (&ic->mcid_to_tree, 0);
2161 ic->current_recording_surface_res = surface_resource;
2162
2163 ic->content_parent_res = _cairo_pdf_surface_new_object (surface);
2164 if (ic->content_parent_res.id == 0)
2165 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2166
2167 status = _cairo_array_append (&ic->parent_tree, &ic->content_parent_res);
2168 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2169 return status;
2170
2171 *struct_parents = _cairo_array_num_elements (&ic->parent_tree) - 1;
2172
2173 ic->render_next_command_has_content = FALSE0;
2174
2175 return CAIRO_STATUS_SUCCESS;
2176}
2177
2178/* Called at the end of a recording group during render. */
2179cairo_int_status_t
2180_cairo_pdf_interchange_emit_recording_surface_end (cairo_pdf_surface_t *surface,
2181 cairo_surface_t *recording_surface)
2182{
2183 cairo_pdf_interchange_t *ic = &surface->interchange;
2184
2185 if (ic->ignore_current_surface)
2186 return CAIRO_STATUS_SUCCESS;
2187
2188 ic->current_recording_surface_res.id = 0;
2189 return cairo_pdf_interchange_write_content_parent_elems (surface);
2190}
2191
2192static void _add_operation_extents_to_dest_tag (cairo_tag_stack_elem_t *elem,
2193 void *closure)
2194{
2195 const cairo_rectangle_int_t *extents = (const cairo_rectangle_int_t *) closure;
2196 cairo_pdf_named_dest_t *dest;
2197
2198 if (_cairo_tag_get_type (elem->name) & TAG_TYPE_DEST) {
2199 if (elem->data) {
2200 dest = (cairo_pdf_named_dest_t *) elem->data;
2201 if (dest->extents.valid) {
2202 _cairo_rectangle_union (&dest->extents.extents, extents);
2203 } else {
2204 dest->extents.extents = *extents;
2205 dest->extents.valid = TRUE1;
2206 }
2207 }
2208 }
2209}
2210
2211cairo_int_status_t
2212_cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface,
2213 const cairo_rectangle_int_t *extents)
2214{
2215 cairo_pdf_interchange_t *ic = &surface->interchange;
2216
2217 /* Add extents to current node and all DEST tags on the stack */
2218 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2219 if (ic->current_analyze_node) {
2220 if (ic->current_analyze_node->extents.valid) {
2221 _cairo_rectangle_union (&ic->current_analyze_node->extents.extents, extents);
2222 } else {
2223 ic->current_analyze_node->extents.extents = *extents;
2224 ic->current_analyze_node->extents.valid = TRUE1;
2225 }
2226 }
2227
2228 _cairo_tag_stack_foreach (&ic->analysis_tag_stack,
2229 _add_operation_extents_to_dest_tag,
2230 (void*)extents);
2231 }
2232
2233 return CAIRO_STATUS_SUCCESS;
2234}
2235
2236cairo_int_status_t
2237_cairo_pdf_interchange_add_content (cairo_pdf_surface_t *surface)
2238{
2239 cairo_pdf_interchange_t *ic = &surface->interchange;
2240 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2241
2242 if (ic->ignore_current_surface)
2243 return CAIRO_STATUS_SUCCESS;
2244
2245 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2246 status = command_list_add (surface, ic->command_id, PDF_CONTENT);
2247 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2248 return status;
2249 }
2250
2251 return status;
2252}
2253
2254/* Called at the start of 1emiting the page content during analyze or render */
2255cairo_int_status_t
2256_cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface)
2257{
2258 cairo_pdf_interchange_t *ic = &surface->interchange;
2259 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2260 int mcid;
2261 unsigned int content_command_id;
2262 cairo_pdf_command_list_t *page_commands;
2263
2264 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2265 status = _cairo_array_allocate (&ic->page_commands, 1, (void**)&page_commands);
2266 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2267 return status;
2268
2269 _cairo_array_init (&page_commands->commands, sizeof(cairo_pdf_command_t));
2270 page_commands->parent = NULL((void*)0);
2271 ic->current_commands = page_commands;
2272 ic->ignore_current_surface = FALSE0;
2273 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
2274 ic->current_commands = _cairo_array_last_element (&ic->page_commands);
2275 /* Each page has its own parent tree to map MCID to nodes. */
2276 _cairo_array_truncate (&ic->mcid_to_tree, 0);
2277 ic->ignore_current_surface = FALSE0;
2278 ic->content_parent_res = _cairo_pdf_surface_new_object (surface);
2279 if (ic->content_parent_res.id == 0)
2280 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2281
2282 status = _cairo_array_append (&ic->parent_tree, &ic->content_parent_res);
2283 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2284 return status;
2285
2286 surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1;
2287
2288 if (ic->next_page_render_node && ic->next_page_render_node->parent &&
2289 command_list_has_content (surface, -1, &content_command_id))
2290 {
2291 add_mcid_to_node (surface, ic->next_page_render_node, content_command_id, &mcid);
2292 const char *tag_name = ic->next_page_render_node->name;
2293 if (ic->next_page_render_node->type == PDF_NODE_CONTENT)
2294 tag_name = ic->next_page_render_node->attributes.content.tag_name;
2295
2296 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, tag_name, mcid);
2297 ic->marked_content_open = TRUE1;
2298 }
2299 ic->render_next_command_has_content = FALSE0;
2300 }
2301
2302 return status;
2303}
2304
2305/* Called at the end of emiting the page content during analyze or render */
2306cairo_int_status_t
2307_cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface)
2308{
2309 cairo_pdf_interchange_t *ic = &surface->interchange;
2310 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2311
2312 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
2313 /* If a content tag is open across pages, the old page needs an EMC emitted. */
2314 if (ic->marked_content_open) {
2315 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
2316 ic->marked_content_open = FALSE0;
2317 }
2318 ic->next_page_render_node = ic->current_render_node;
2319 }
2320
2321 return status;
2322}
2323
2324cairo_int_status_t
2325_cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface)
2326{
2327 return cairo_pdf_interchange_write_content_parent_elems (surface);
2328}
2329
2330cairo_int_status_t
2331_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
2332{
2333 cairo_pdf_interchange_t *ic = &surface->interchange;
2334 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2335 cairo_tag_stack_structure_type_t tag_type;
2336 cairo_bool_t write_struct_tree = FALSE0;
2337
2338 status = cairo_pdf_interchange_update_extents (surface);
1
Calling 'cairo_pdf_interchange_update_extents'
2339 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2340 return status;
2341
2342 tag_type = _cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack);
2343 if (tag_type == TAG_TREE_TYPE_TAGGED || tag_type == TAG_TREE_TYPE_STRUCTURE ||
2344 tag_type == TAG_TREE_TYPE_LINK_ONLY)
2345 {
2346 write_struct_tree = TRUE1;
2347 }
2348
2349 status = cairo_pdf_interchange_write_annots (surface, write_struct_tree);
2350 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2351 return status;
2352
2353 if (write_struct_tree) {
2354 surface->struct_tree_root = _cairo_pdf_surface_new_object (surface);
2355 if (surface->struct_tree_root.id == 0)
2356 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2357
2358 ic->struct_root->res = surface->struct_tree_root;
2359
2360 status = cairo_pdf_interchange_write_parent_tree (surface);
2361 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2362 return status;
2363
2364 unsigned num_pages = _cairo_array_num_elements (&ic->page_commands);
2365 for (unsigned i = 0; i < num_pages; i++) {
2366 cairo_pdf_command_list_t *command_list;
2367 command_list = _cairo_array_index (&ic->page_commands, i);
2368 update_mcid_order (surface, command_list);
2369 }
2370
2371 status = cairo_pdf_interchange_write_struct_tree (surface);
2372 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2373 return status;
2374
2375 if (tag_type == TAG_TREE_TYPE_TAGGED)
2376 surface->tagged = TRUE1;
2377 }
2378
2379 status = cairo_pdf_interchange_write_outline (surface);
2380 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2381 return status;
2382
2383 status = cairo_pdf_interchange_write_page_labels (surface);
2384 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2385 return status;
2386
2387 status = cairo_pdf_interchange_write_names_dict (surface);
2388 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2389 return status;
2390
2391 status = cairo_pdf_interchange_write_docinfo (surface);
2392
2393 return status;
2394}
2395
2396static void
2397_cairo_pdf_interchange_set_create_date (cairo_pdf_surface_t *surface)
2398{
2399 time_t utc, local, offset;
2400 struct tm tm_local, tm_utc;
2401 char buf[50];
2402 int buf_size;
2403 char *p;
2404 cairo_pdf_interchange_t *ic = &surface->interchange;
2405
2406 utc = time (NULL((void*)0));
2407 localtime_r (&utc, &tm_local);
2408 strftime (buf, sizeof(buf), "(D:%Y%m%d%H%M%S", &tm_local);
2409
2410 /* strftime "%z" is non standard and does not work on windows (it prints zone name, not offset).
2411 * Calculate time zone offset by comparing local and utc time_t values for the same time.
2412 */
2413 gmtime_r (&utc, &tm_utc);
2414 tm_utc.tm_isdst = tm_local.tm_isdst;
2415 local = mktime (&tm_utc);
2416 offset = difftime (utc, local);
2417
2418 if (offset == 0) {
2419 strcat (buf, "Z");
2420 } else {
2421 if (offset > 0) {
2422 strcat (buf, "+");
2423 } else {
2424 strcat (buf, "-");
2425 offset = -offset;
2426 }
2427 p = buf + strlen (buf);
2428 buf_size = sizeof (buf) - strlen (buf);
2429 snprintf (p, buf_size, "%02d'%02d", (int)(offset/3600), (int)(offset%3600)/60);
2430 }
2431 strcat (buf, ")");
2432 ic->docinfo.create_date = strdup (buf);
2433}
2434
2435cairo_int_status_t
2436_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface)
2437{
2438 cairo_pdf_interchange_t *ic = &surface->interchange;
2439 cairo_pdf_outline_entry_t *outline_root;
2440 cairo_int_status_t status;
2441
2442 _cairo_tag_stack_init (&ic->analysis_tag_stack);
2443 _cairo_tag_stack_init (&ic->render_tag_stack);
2444 ic->struct_root = _cairo_calloc (sizeof(cairo_pdf_struct_tree_node_t))((sizeof(cairo_pdf_struct_tree_node_t)) != 0 ? calloc(1,sizeof
(cairo_pdf_struct_tree_node_t)) : ((void*)0))
;
2445 if (unlikely (ic->struct_root == NULL)(__builtin_expect (!!(ic->struct_root == ((void*)0)), 0)))
2446 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2447
2448 ic->struct_root->res.id = 0;
2449 cairo_list_init (&ic->struct_root->children);
2450 _cairo_array_init (&ic->struct_root->mcid, sizeof(cairo_pdf_page_mcid_t));
2451
2452 ic->current_analyze_node = ic->struct_root;
2453 ic->current_render_node = NULL((void*)0);
2454 ic->next_page_render_node = ic->struct_root;
2455 _cairo_array_init (&ic->recording_surface_stack, sizeof(cairo_recording_surface_stack_entry_t));
2456 ic->current_recording_surface_res.id = 0;
2457 ic->command_to_node_map = _cairo_hash_table_create (_cairo_pdf_command_equal);
2458 if (unlikely (ic->command_to_node_map == NULL)(__builtin_expect (!!(ic->command_to_node_map == ((void*)0
)), 0))
)
2459 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2460
2461 ic->content_tag_map = _cairo_hash_table_create (_cairo_pdf_content_tag_equal);
2462 if (unlikely (ic->content_tag_map == NULL)(__builtin_expect (!!(ic->content_tag_map == ((void*)0)), 0
))
)
2463 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2464
2465 _cairo_array_init (&ic->parent_tree, sizeof(cairo_pdf_resource_t));
2466 _cairo_array_init (&ic->mcid_to_tree, sizeof(cairo_pdf_struct_tree_node_t *));
2467 _cairo_array_init (&ic->annots, sizeof(cairo_pdf_annotation_t *));
2468 ic->parent_tree_res.id = 0;
2469 cairo_list_init (&ic->extents_list);
2470 ic->named_dests = _cairo_hash_table_create (_named_dest_equal);
2471 if (unlikely (ic->named_dests == NULL)(__builtin_expect (!!(ic->named_dests == ((void*)0)), 0)))
2472 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2473
2474 _cairo_array_init (&ic->page_commands, sizeof(cairo_pdf_command_list_t));
2475 ic->current_commands = NULL((void*)0);
2476 _cairo_array_init (&ic->recording_surface_commands, sizeof(cairo_pdf_recording_surface_commands_t));
2477
2478 ic->num_dests = 0;
2479 ic->sorted_dests = NULL((void*)0);
2480 ic->dests_res.id = 0;
2481 ic->ignore_current_surface = FALSE0;
2482 ic->content_emitted = FALSE0;
2483 ic->marked_content_open = FALSE0;
2484 ic->render_next_command_has_content = FALSE0;
2485 ic->mcid_order = 0;
2486
2487 _cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *));
2488 outline_root = _cairo_calloc (sizeof(cairo_pdf_outline_entry_t))((sizeof(cairo_pdf_outline_entry_t)) != 0 ? calloc(1,sizeof(cairo_pdf_outline_entry_t
)) : ((void*)0))
;
2489 if (unlikely (outline_root == NULL)(__builtin_expect (!!(outline_root == ((void*)0)), 0)))
2490 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2491
2492 memset (&ic->docinfo, 0, sizeof (ic->docinfo));
2493 _cairo_array_init (&ic->custom_metadata, sizeof(struct metadata));
2494 _cairo_pdf_interchange_set_create_date (surface);
2495 status = _cairo_array_append (&ic->outline, &outline_root);
2496
2497 return status;
2498}
2499
2500static void
2501_cairo_pdf_interchange_free_outlines (cairo_pdf_surface_t *surface)
2502{
2503 cairo_pdf_interchange_t *ic = &surface->interchange;
2504 int num_elems, i;
2505
2506 num_elems = _cairo_array_num_elements (&ic->outline);
2507 for (i = 0; i < num_elems; i++) {
2508 cairo_pdf_outline_entry_t *outline;
2509
2510 _cairo_array_copy_element (&ic->outline, i, &outline);
2511 free (outline->name);
2512 _cairo_tag_free_link_attributes (&outline->link_attrs);
2513 free (outline);
2514 }
2515 _cairo_array_fini (&ic->outline);
2516}
2517
2518void
2519_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface)
2520{
2521 cairo_pdf_interchange_t *ic = &surface->interchange;
2522 unsigned int i, num_elems;
2523 struct metadata *data;
2524
2525 _cairo_tag_stack_fini (&ic->analysis_tag_stack);
2526 _cairo_tag_stack_fini (&ic->render_tag_stack);
2527 _cairo_array_fini (&ic->mcid_to_tree);
2528 cairo_pdf_interchange_clear_annotations (surface);
2529 _cairo_array_fini (&ic->annots);
2530
2531 _cairo_array_fini (&ic->recording_surface_stack);
2532 _cairo_array_fini (&ic->parent_tree);
2533
2534 _cairo_hash_table_foreach (ic->command_to_node_map,
2535 _cairo_pdf_command_pluck,
2536 ic->command_to_node_map);
2537 _cairo_hash_table_destroy (ic->command_to_node_map);
2538
2539 _cairo_hash_table_foreach (ic->named_dests, _named_dest_pluck, ic->named_dests);
2540 _cairo_hash_table_destroy (ic->named_dests);
2541
2542 _cairo_hash_table_foreach (ic->content_tag_map, _cairo_pdf_content_tag_pluck, ic->content_tag_map);
2543 _cairo_hash_table_destroy(ic->content_tag_map);
2544
2545 free_node (ic->struct_root);
2546
2547 num_elems = _cairo_array_num_elements (&ic->recording_surface_commands);
2548 for (i = 0; i < num_elems; i++) {
2549 cairo_pdf_recording_surface_commands_t *recording_command;
2550 cairo_pdf_command_list_t *command_list;
2551
2552 recording_command = _cairo_array_index (&ic->recording_surface_commands, i);
2553 command_list = recording_command->command_list;
2554 _cairo_array_fini (&command_list->commands);
2555 free (command_list);
2556 }
2557 _cairo_array_fini (&ic->recording_surface_commands);
2558
2559 num_elems = _cairo_array_num_elements (&ic->page_commands);
2560 for (i = 0; i < num_elems; i++) {
2561 cairo_pdf_command_list_t *command_list;
2562 command_list = _cairo_array_index (&ic->page_commands, i);
2563 _cairo_array_fini (&command_list->commands);
2564 }
2565 _cairo_array_fini (&ic->page_commands);
2566
2567 free (ic->sorted_dests);
2568 _cairo_pdf_interchange_free_outlines (surface);
2569 free (ic->docinfo.title);
2570 free (ic->docinfo.author);
2571 free (ic->docinfo.subject);
2572 free (ic->docinfo.keywords);
2573 free (ic->docinfo.creator);
2574 free (ic->docinfo.create_date);
2575 free (ic->docinfo.mod_date);
2576
2577 num_elems = _cairo_array_num_elements (&ic->custom_metadata);
2578 for (i = 0; i < num_elems; i++) {
2579 data = _cairo_array_index (&ic->custom_metadata, i);
2580 free (data->name);
2581 free (data->value);
2582 }
2583 _cairo_array_fini (&ic->custom_metadata);
2584}
2585
2586cairo_int_status_t
2587_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
2588 int parent_id,
2589 const char *name,
2590 const char *link_attribs,
2591 cairo_pdf_outline_flags_t flags,
2592 int *id)
2593{
2594 cairo_pdf_interchange_t *ic = &surface->interchange;
2595 cairo_pdf_outline_entry_t *outline;
2596 cairo_pdf_outline_entry_t *parent;
2597 cairo_int_status_t status;
2598
2599 if (parent_id < 0 || parent_id >= (int)_cairo_array_num_elements (&ic->outline))
2600 return CAIRO_STATUS_SUCCESS;
2601
2602 outline = _cairo_calloc (sizeof(cairo_pdf_outline_entry_t))((sizeof(cairo_pdf_outline_entry_t)) != 0 ? calloc(1,sizeof(cairo_pdf_outline_entry_t
)) : ((void*)0))
;
2603 if (unlikely (outline == NULL)(__builtin_expect (!!(outline == ((void*)0)), 0)))
2604 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2605
2606 status = _cairo_tag_parse_link_attributes (link_attribs, &outline->link_attrs);
2607 if (unlikely (status)(__builtin_expect (!!(status), 0))) {
2608 free (outline);
2609 return status;
2610 }
2611
2612 outline->res = _cairo_pdf_surface_new_object (surface);
2613 if (outline->res.id == 0)
2614 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2615
2616 outline->name = strdup (name);
2617 outline->flags = flags;
2618 outline->count = 0;
2619
2620 _cairo_array_copy_element (&ic->outline, parent_id, &parent);
2621
2622 outline->parent = parent;
2623 outline->first_child = NULL((void*)0);
2624 outline->last_child = NULL((void*)0);
2625 outline->next = NULL((void*)0);
2626 if (parent->last_child) {
2627 parent->last_child->next = outline;
2628 outline->prev = parent->last_child;
2629 parent->last_child = outline;
2630 } else {
2631 parent->first_child = outline;
2632 parent->last_child = outline;
2633 outline->prev = NULL((void*)0);
2634 }
2635
2636 *id = _cairo_array_num_elements (&ic->outline);
2637 status = _cairo_array_append (&ic->outline, &outline);
2638 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2639 return status;
2640
2641 /* Update Count */
2642 outline = outline->parent;
2643 while (outline) {
2644 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_OPEN) {
2645 outline->count++;
2646 } else {
2647 outline->count--;
2648 break;
2649 }
2650 outline = outline->parent;
2651 }
2652
2653 return CAIRO_STATUS_SUCCESS;
2654}
2655
2656/*
2657 * Date must be in the following format:
2658 *
2659 * YYYY-MM-DDThh:mm:ss[Z+-]hh:mm
2660 *
2661 * Only the year is required. If a field is included all preceding
2662 * fields must be included.
2663 */
2664static char *
2665iso8601_to_pdf_date_string (const char *iso)
2666{
2667 char buf[40];
2668 const char *p;
2669 int i;
2670
2671 /* Check that utf8 contains only the characters "0123456789-T:Z+" */
2672 p = iso;
2673 while (*p) {
2674 if (!_cairo_isdigit (*p) && *p != '-' && *p != 'T' &&
2675 *p != ':' && *p != 'Z' && *p != '+')
2676 return NULL((void*)0);
2677 p++;
2678 }
2679
2680 p = iso;
2681 strcpy (buf, "(");
2682
2683 /* YYYY (required) */
2684 if (strlen (p) < 4)
2685 return NULL((void*)0);
2686
2687 strncat (buf, p, 4);
2688 p += 4;
2689
2690 /* -MM, -DD, Thh, :mm, :ss */
2691 for (i = 0; i < 5; i++) {
2692 if (strlen (p) < 3)
2693 goto finish;
2694
2695 strncat (buf, p + 1, 2);
2696 p += 3;
2697 }
2698
2699 /* Z, +, - */
2700 if (strlen (p) < 1)
2701 goto finish;
2702 strncat (buf, p, 1);
2703 p += 1;
2704
2705 /* hh */
2706 if (strlen (p) < 2)
2707 goto finish;
2708
2709 strncat (buf, p, 2);
2710 strcat (buf, "'");
2711 p += 2;
2712
2713 /* :mm */
2714 if (strlen (p) < 3)
2715 goto finish;
2716
2717 strncat (buf, p + 1, 2);
2718 strcat (buf, "'");
2719
2720 finish:
2721 strcat (buf, ")");
2722 return strdup (buf);
2723}
2724
2725cairo_int_status_t
2726_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface,
2727 cairo_pdf_metadata_t metadata,
2728 const char *utf8)
2729{
2730 cairo_pdf_interchange_t *ic = &surface->interchange;
2731 cairo_status_t status;
2732 char *s = NULL((void*)0);
2733
2734 if (utf8) {
2735 if (metadata == CAIRO_PDF_METADATA_CREATE_DATE ||
2736 metadata == CAIRO_PDF_METADATA_MOD_DATE) {
2737 s = iso8601_to_pdf_date_string (utf8);
2738 } else {
2739 status = _cairo_utf8_to_pdf_string (utf8, &s);
2740 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2741 return status;
2742 }
2743 }
2744
2745 switch (metadata) {
2746 case CAIRO_PDF_METADATA_TITLE:
2747 free (ic->docinfo.title);
2748 ic->docinfo.title = s;
2749 break;
2750 case CAIRO_PDF_METADATA_AUTHOR:
2751 free (ic->docinfo.author);
2752 ic->docinfo.author = s;
2753 break;
2754 case CAIRO_PDF_METADATA_SUBJECT:
2755 free (ic->docinfo.subject);
2756 ic->docinfo.subject = s;
2757 break;
2758 case CAIRO_PDF_METADATA_KEYWORDS:
2759 free (ic->docinfo.keywords);
2760 ic->docinfo.keywords = s;
2761 break;
2762 case CAIRO_PDF_METADATA_CREATOR:
2763 free (ic->docinfo.creator);
2764 ic->docinfo.creator = s;
2765 break;
2766 case CAIRO_PDF_METADATA_CREATE_DATE:
2767 free (ic->docinfo.create_date);
2768 ic->docinfo.create_date = s;
2769 break;
2770 case CAIRO_PDF_METADATA_MOD_DATE:
2771 free (ic->docinfo.mod_date);
2772 ic->docinfo.mod_date = s;
2773 break;
2774 }
2775
2776 return CAIRO_STATUS_SUCCESS;
2777}
2778
2779static const char *reserved_metadata_names[] = {
2780 "",
2781 "Title",
2782 "Author",
2783 "Subject",
2784 "Keywords",
2785 "Creator",
2786 "Producer",
2787 "CreationDate",
2788 "ModDate",
2789 "Trapped",
2790};
2791
2792cairo_int_status_t
2793_cairo_pdf_interchange_set_custom_metadata (cairo_pdf_surface_t *surface,
2794 const char *name,
2795 const char *value)
2796{
2797 cairo_pdf_interchange_t *ic = &surface->interchange;
2798 struct metadata *data;
2799 struct metadata new_data;
2800 int i, num_elems;
2801 cairo_int_status_t status;
2802 char *s = NULL((void*)0);
2803
2804 if (name == NULL((void*)0))
2805 return CAIRO_STATUS_NULL_POINTER;
2806
2807 for (i = 0; i < ARRAY_LENGTH (reserved_metadata_names)((int) (sizeof (reserved_metadata_names) / sizeof (reserved_metadata_names
[0])))
; i++) {
2808 if (strcmp(name, reserved_metadata_names[i]) == 0)
2809 return CAIRO_STATUS_INVALID_STRING;
2810 }
2811
2812 /* First check if we already have an entry for this name. If so,
2813 * update the value. A NULL value means the entry has been removed
2814 * and will not be emitted. */
2815 num_elems = _cairo_array_num_elements (&ic->custom_metadata);
2816 for (i = 0; i < num_elems; i++) {
2817 data = _cairo_array_index (&ic->custom_metadata, i);
2818 if (strcmp(name, data->name) == 0) {
2819 free (data->value);
2820 data->value = NULL((void*)0);
2821 if (value && strlen(value)) {
2822 status = _cairo_utf8_to_pdf_string (value, &s);
2823 if (unlikely (status)(__builtin_expect (!!(status), 0)))
2824 return status;
2825 data->value = s;
2826 }
2827 return CAIRO_STATUS_SUCCESS;
2828 }
2829 }
2830
2831 /* Add new entry */
2832 status = CAIRO_STATUS_SUCCESS;
2833 if (value && strlen(value)) {
2834 new_data.name = strdup (name);
2835 status = _cairo_utf8_to_pdf_string (value, &s);
2836 if (unlikely (status)(__builtin_expect (!!(status), 0))) {
2837 free (new_data.name);
2838 return status;
2839 }
2840 new_data.value = s;
2841 status = _cairo_array_append (&ic->custom_metadata, &new_data);
2842 }
2843
2844 return status;
2845}
2846
2847#if DEBUG_PDF_INTERCHANGE
2848static cairo_int_status_t
2849print_node (cairo_pdf_surface_t *surface,
2850 cairo_pdf_struct_tree_node_t *node,
2851 int depth)
2852{
2853 if (node == NULL((void*)0)) {
2854 printf("%*sNode: ptr: NULL\n", depth*2, "");
2855 } else if (node == surface->interchange.struct_root) {
2856 printf("%*sNode: ptr: %p root\n", depth*2, "", node);
2857 } else {
2858 printf("%*sNode: ptr: %p name: '%s'\n", depth*2, "", node, node->name);
2859 }
2860 depth++;
2861 printf("%*sType: ", depth*2, "");
2862 switch (node->type) {
2863 case PDF_NODE_STRUCT:
2864 printf("STRUCT\n");
2865 break;
2866 case PDF_NODE_CONTENT:
2867 printf("CONTENT\n");
2868 printf("%*sContent.id: %s\n", depth*2, "", node->attributes.content.id);
2869 printf("%*sContent.tag_name: %s\n", depth*2, "", node->attributes.content.tag_name);
2870 break;
2871 case PDF_NODE_CONTENT_REF:
2872 printf("CONTENT_REF\n");
2873 printf("%*sContent_Ref.ref: %s\n", depth*2, "", node->attributes.content_ref.ref);
2874 break;
2875 case PDF_NODE_ARTIFACT:
2876 printf("ARTIFACT\n");
2877 break;
2878 }
2879 printf("%*sres: %d\n", depth*2, "", node->res.id);
2880 printf("%*sparent: %p\n", depth*2, "", node->parent);
2881 printf("%*sannot:", depth*2, "");
2882 if (node->annot)
2883 printf(" node: %p res: %d", node->annot->node, node->annot->res.id);
2884 printf("\n");
2885 printf("%*sextents: ", depth*2, "");
2886 if (node->extents.valid) {
2887 printf("x: %d y: %d w: %d h: %d\n",
2888 node->extents.extents.x,
2889 node->extents.extents.y,
2890 node->extents.extents.width,
2891 node->extents.extents.height);
2892 } else {
2893 printf("not valid\n");
2894 }
2895
2896 printf("%*smcid: ", depth*2, "");
2897 int num_mcid = _cairo_array_num_elements (&node->mcid);
2898 for (int i = 0; i < num_mcid; i++) {
2899 cairo_pdf_page_mcid_t *mcid_elem = _cairo_array_index (&node->mcid, i);
2900 if (mcid_elem->child_node) {
2901 printf("(order: %d, %p) ", mcid_elem->order, mcid_elem->child_node);
2902 } else {
2903 printf("(order: %d, pg: %d, xobject_res: %d, mcid: %d) ",
2904 mcid_elem->order, mcid_elem->page, mcid_elem->xobject_res.id, mcid_elem->mcid);
2905 }
2906 }
2907 printf("\n");
2908 return CAIRO_STATUS_SUCCESS;
2909}
2910
2911static void
2912print_tree (cairo_pdf_surface_t *surface,
2913 cairo_pdf_struct_tree_node_t *node)
2914{
2915 printf("Structure Tree:\n");
2916 cairo_pdf_interchange_walk_struct_tree (surface, node, 0, print_node);
2917}
2918
2919static void
2920print_command (cairo_pdf_command_t *command, int indent)
2921{
2922 printf("%*s%d ", indent*2, "", command->command_id);
2923 switch (command->flags) {
2924 case PDF_CONTENT:
2925 printf("CONTENT");
2926 break;
2927 case PDF_BEGIN:
2928 printf("BEGIN");
2929 break;
2930 case PDF_END:
2931 printf("END");
2932 break;
2933 case PDF_GROUP:
2934 printf("GROUP: %p", command->group);
2935 break;
2936 case PDF_NONE:
2937 printf("NONE");
2938 break;
2939 }
2940 printf(" node: %p index: %d\n", command->node, command->mcid_index);
2941}
2942
2943static void
2944print_commands (cairo_pdf_command_list_t *command_list, int indent)
2945{
2946 cairo_pdf_command_t *command;
2947 unsigned i;
2948 unsigned num_elements = _cairo_array_num_elements (&command_list->commands);
2949
2950 for (i = 0; i < num_elements; i++) {
2951 command = _cairo_array_index (&command_list->commands, i);
2952 print_command (command, indent);
2953 if (command->flags == PDF_GROUP)
2954 print_commands (command->group, indent + 1);
2955 }
2956}
2957
2958static void
2959print_command_list(cairo_pdf_command_list_t *command_list)
2960{
2961 printf("Command List: %p\n", command_list);
2962 print_commands (command_list, 0);
2963 printf("end\n");
2964}
2965#endif