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_SOURCE /* 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
56 #include <time.h>
57
58 #ifndef HAVE_LOCALTIME_R
59 #define localtime_r(T, BUF) (*(BUF) = *localtime (T))
60 #endif
61 #ifndef HAVE_GMTIME_R
62 #define gmtime_r(T, BUF) (*(BUF) = *gmtime (T))
63 #endif
64
65 static void
write_rect_to_pdf_quad_points(cairo_output_stream_t * stream,const cairo_rectangle_t * rect,double surface_height)66 write_rect_to_pdf_quad_points (cairo_output_stream_t *stream,
67 const cairo_rectangle_t *rect,
68 double surface_height)
69 {
70 _cairo_output_stream_printf (stream,
71 "%f %f %f %f %f %f %f %f",
72 rect->x,
73 surface_height - rect->y,
74 rect->x + rect->width,
75 surface_height - rect->y,
76 rect->x + rect->width,
77 surface_height - (rect->y + rect->height),
78 rect->x,
79 surface_height - (rect->y + rect->height));
80 }
81
82 static void
write_rect_int_to_pdf_bbox(cairo_output_stream_t * stream,const cairo_rectangle_int_t * rect,double surface_height)83 write_rect_int_to_pdf_bbox (cairo_output_stream_t *stream,
84 const cairo_rectangle_int_t *rect,
85 double surface_height)
86 {
87 _cairo_output_stream_printf (stream,
88 "%d %f %d %f",
89 rect->x,
90 surface_height - (rect->y + rect->height),
91 rect->x + rect->width,
92 surface_height - rect->y);
93 }
94
95 static cairo_int_status_t
add_tree_node(cairo_pdf_surface_t * surface,cairo_pdf_struct_tree_node_t * parent,const char * name,cairo_pdf_struct_tree_node_t ** new_node)96 add_tree_node (cairo_pdf_surface_t *surface,
97 cairo_pdf_struct_tree_node_t *parent,
98 const char *name,
99 cairo_pdf_struct_tree_node_t **new_node)
100 {
101 cairo_pdf_struct_tree_node_t *node;
102
103 node = _cairo_malloc (sizeof(cairo_pdf_struct_tree_node_t));
104 if (unlikely (node == NULL))
105 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
106
107 node->name = strdup (name);
108 node->res = _cairo_pdf_surface_new_object (surface);
109 if (node->res.id == 0)
110 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
111
112 node->parent = parent;
113 cairo_list_init (&node->children);
114 _cairo_array_init (&node->mcid, sizeof(struct page_mcid));
115 node->annot_res.id = 0;
116 node->extents.valid = FALSE;
117 cairo_list_init (&node->extents.link);
118
119 cairo_list_add_tail (&node->link, &parent->children);
120
121 *new_node = node;
122 return CAIRO_STATUS_SUCCESS;
123 }
124
125 static cairo_bool_t
is_leaf_node(cairo_pdf_struct_tree_node_t * node)126 is_leaf_node (cairo_pdf_struct_tree_node_t *node)
127 {
128 return node->parent && cairo_list_is_empty (&node->children) ;
129 }
130
131 static void
free_node(cairo_pdf_struct_tree_node_t * node)132 free_node (cairo_pdf_struct_tree_node_t *node)
133 {
134 cairo_pdf_struct_tree_node_t *child, *next;
135
136 if (!node)
137 return;
138
139 cairo_list_foreach_entry_safe (child, next, cairo_pdf_struct_tree_node_t,
140 &node->children, link)
141 {
142 cairo_list_del (&child->link);
143 free_node (child);
144 }
145 free (node->name);
146 _cairo_array_fini (&node->mcid);
147 free (node);
148 }
149
150 static cairo_status_t
add_mcid_to_node(cairo_pdf_surface_t * surface,cairo_pdf_struct_tree_node_t * node,int page,int * mcid)151 add_mcid_to_node (cairo_pdf_surface_t *surface,
152 cairo_pdf_struct_tree_node_t *node,
153 int page,
154 int *mcid)
155 {
156 struct page_mcid mcid_elem;
157 cairo_int_status_t status;
158 cairo_pdf_interchange_t *ic = &surface->interchange;
159
160 status = _cairo_array_append (&ic->mcid_to_tree, &node);
161 if (unlikely (status))
162 return status;
163
164 mcid_elem.page = page;
165 mcid_elem.mcid = _cairo_array_num_elements (&ic->mcid_to_tree) - 1;
166 *mcid = mcid_elem.mcid;
167 return _cairo_array_append (&node->mcid, &mcid_elem);
168 }
169
170 static cairo_int_status_t
add_annotation(cairo_pdf_surface_t * surface,cairo_pdf_struct_tree_node_t * node,const char * name,const char * attributes)171 add_annotation (cairo_pdf_surface_t *surface,
172 cairo_pdf_struct_tree_node_t *node,
173 const char *name,
174 const char *attributes)
175 {
176 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
177 cairo_pdf_interchange_t *ic = &surface->interchange;
178 cairo_pdf_annotation_t *annot;
179
180 annot = malloc (sizeof(cairo_pdf_annotation_t));
181 if (unlikely (annot == NULL))
182 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
183
184 status = _cairo_tag_parse_link_attributes (attributes, &annot->link_attrs);
185 if (unlikely (status)) {
186 free (annot);
187 return status;
188 }
189
190 annot->node = node;
191
192 status = _cairo_array_append (&ic->annots, &annot);
193
194 return status;
195 }
196
197 static void
free_annotation(cairo_pdf_annotation_t * annot)198 free_annotation (cairo_pdf_annotation_t *annot)
199 {
200 _cairo_array_fini (&annot->link_attrs.rects);
201 free (annot->link_attrs.dest);
202 free (annot->link_attrs.uri);
203 free (annot->link_attrs.file);
204 free (annot);
205 }
206
207 static void
cairo_pdf_interchange_clear_annotations(cairo_pdf_surface_t * surface)208 cairo_pdf_interchange_clear_annotations (cairo_pdf_surface_t *surface)
209 {
210 cairo_pdf_interchange_t *ic = &surface->interchange;
211 int num_elems, i;
212
213 num_elems = _cairo_array_num_elements (&ic->annots);
214 for (i = 0; i < num_elems; i++) {
215 cairo_pdf_annotation_t * annot;
216
217 _cairo_array_copy_element (&ic->annots, i, &annot);
218 free_annotation (annot);
219 }
220 _cairo_array_truncate (&ic->annots, 0);
221 }
222
223 static cairo_int_status_t
cairo_pdf_interchange_write_node_object(cairo_pdf_surface_t * surface,cairo_pdf_struct_tree_node_t * node)224 cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface,
225 cairo_pdf_struct_tree_node_t *node)
226 {
227 struct page_mcid *mcid_elem;
228 int i, num_mcid, first_page;
229 cairo_pdf_resource_t *page_res;
230 cairo_pdf_struct_tree_node_t *child;
231
232 _cairo_pdf_surface_update_object (surface, node->res);
233 _cairo_output_stream_printf (surface->output,
234 "%d 0 obj\n"
235 "<< /Type /StructElem\n"
236 " /S /%s\n"
237 " /P %d 0 R\n",
238 node->res.id,
239 node->name,
240 node->parent->res.id);
241
242 if (! cairo_list_is_empty (&node->children)) {
243 if (cairo_list_is_singular (&node->children) && node->annot_res.id == 0) {
244 child = cairo_list_first_entry (&node->children, cairo_pdf_struct_tree_node_t, link);
245 _cairo_output_stream_printf (surface->output, " /K %d 0 R\n", child->res.id);
246 } else {
247 _cairo_output_stream_printf (surface->output, " /K [ ");
248 if (node->annot_res.id != 0) {
249 _cairo_output_stream_printf (surface->output,
250 "<< /Type /OBJR /Obj %d 0 R >> ",
251 node->annot_res.id);
252 }
253 cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,
254 &node->children, link)
255 {
256 _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id);
257 }
258 _cairo_output_stream_printf (surface->output, "]\n");
259 }
260 } else {
261 num_mcid = _cairo_array_num_elements (&node->mcid);
262 if (num_mcid > 0 ) {
263 mcid_elem = _cairo_array_index (&node->mcid, 0);
264 first_page = mcid_elem->page;
265 page_res = _cairo_array_index (&surface->pages, first_page - 1);
266 _cairo_output_stream_printf (surface->output, " /Pg %d 0 R\n", page_res->id);
267
268 if (num_mcid == 1 && node->annot_res.id == 0) {
269 _cairo_output_stream_printf (surface->output, " /K %d\n", mcid_elem->mcid);
270 } else {
271 _cairo_output_stream_printf (surface->output, " /K [ ");
272 if (node->annot_res.id != 0) {
273 _cairo_output_stream_printf (surface->output,
274 "<< /Type /OBJR /Obj %d 0 R >> ",
275 node->annot_res.id);
276 }
277 for (i = 0; i < num_mcid; i++) {
278 mcid_elem = _cairo_array_index (&node->mcid, i);
279 page_res = _cairo_array_index (&surface->pages, mcid_elem->page - 1);
280 if (mcid_elem->page == first_page) {
281 _cairo_output_stream_printf (surface->output, "%d ", mcid_elem->mcid);
282 } else {
283 _cairo_output_stream_printf (surface->output,
284 "\n << /Type /MCR /Pg %d 0 R /MCID %d >> ",
285 page_res->id,
286 mcid_elem->mcid);
287 }
288 }
289 _cairo_output_stream_printf (surface->output, "]\n");
290 }
291 }
292 }
293 _cairo_output_stream_printf (surface->output,
294 ">>\n"
295 "endobj\n");
296
297 return _cairo_output_stream_get_status (surface->output);
298 }
299
300 static void
init_named_dest_key(cairo_pdf_named_dest_t * dest)301 init_named_dest_key (cairo_pdf_named_dest_t *dest)
302 {
303 dest->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
304 dest->attrs.name,
305 strlen (dest->attrs.name));
306 }
307
308 static cairo_bool_t
_named_dest_equal(const void * key_a,const void * key_b)309 _named_dest_equal (const void *key_a, const void *key_b)
310 {
311 const cairo_pdf_named_dest_t *a = key_a;
312 const cairo_pdf_named_dest_t *b = key_b;
313
314 return strcmp (a->attrs.name, b->attrs.name) == 0;
315 }
316
317 static void
_named_dest_pluck(void * entry,void * closure)318 _named_dest_pluck (void *entry, void *closure)
319 {
320 cairo_pdf_named_dest_t *dest = entry;
321 cairo_hash_table_t *table = closure;
322
323 _cairo_hash_table_remove (table, &dest->base);
324 free (dest->attrs.name);
325 free (dest);
326 }
327
328 static cairo_int_status_t
cairo_pdf_interchange_write_explicit_dest(cairo_pdf_surface_t * surface,int page,cairo_bool_t has_pos,double x,double y)329 cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface,
330 int page,
331 cairo_bool_t has_pos,
332 double x,
333 double y)
334 {
335 cairo_pdf_resource_t res;
336 double height;
337
338 if (page < 1 || page > (int)_cairo_array_num_elements (&surface->pages))
339 return CAIRO_INT_STATUS_TAG_ERROR;
340
341 _cairo_array_copy_element (&surface->page_heights, page - 1, &height);
342 _cairo_array_copy_element (&surface->pages, page - 1, &res);
343 if (has_pos) {
344 _cairo_output_stream_printf (surface->output,
345 " /Dest [%d 0 R /XYZ %f %f 0]\n",
346 res.id,
347 x,
348 height - y);
349 } else {
350 _cairo_output_stream_printf (surface->output,
351 " /Dest [%d 0 R /XYZ null null 0]\n",
352 res.id);
353 }
354
355 return CAIRO_STATUS_SUCCESS;
356 }
357
358 static cairo_int_status_t
cairo_pdf_interchange_write_dest(cairo_pdf_surface_t * surface,cairo_link_attrs_t * link_attrs)359 cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
360 cairo_link_attrs_t *link_attrs)
361 {
362 cairo_int_status_t status;
363 cairo_pdf_interchange_t *ic = &surface->interchange;
364 char *dest = NULL;
365
366 if (link_attrs->dest) {
367 cairo_pdf_named_dest_t key;
368 cairo_pdf_named_dest_t *named_dest;
369
370 /* check if this is a link to an internal named dest */
371 key.attrs.name = link_attrs->dest;
372 init_named_dest_key (&key);
373 named_dest = _cairo_hash_table_lookup (ic->named_dests, &key.base);
374 if (named_dest && named_dest->attrs.internal) {
375 /* if dests exists and has internal attribute, use a direct
376 * reference instead of the name */
377 double x = 0;
378 double y = 0;
379
380 if (named_dest->extents.valid) {
381 x = named_dest->extents.extents.x;
382 y = named_dest->extents.extents.y;
383 }
384
385 if (named_dest->attrs.x_valid)
386 x = named_dest->attrs.x;
387
388 if (named_dest->attrs.y_valid)
389 y = named_dest->attrs.y;
390
391 status = cairo_pdf_interchange_write_explicit_dest (surface,
392 named_dest->page,
393 TRUE,
394 x, y);
395 return status;
396 }
397 }
398
399 if (link_attrs->dest) {
400 status = _cairo_utf8_to_pdf_string (link_attrs->dest, &dest);
401 if (unlikely (status))
402 return status;
403
404 _cairo_output_stream_printf (surface->output,
405 " /Dest %s\n",
406 dest);
407 free (dest);
408 } else {
409 status = cairo_pdf_interchange_write_explicit_dest (surface,
410 link_attrs->page,
411 link_attrs->has_pos,
412 link_attrs->pos.x,
413 link_attrs->pos.y);
414 }
415
416 return CAIRO_STATUS_SUCCESS;
417 }
418
419 static cairo_int_status_t
cairo_pdf_interchange_write_link_action(cairo_pdf_surface_t * surface,cairo_link_attrs_t * link_attrs)420 cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface,
421 cairo_link_attrs_t *link_attrs)
422 {
423 cairo_int_status_t status;
424 char *dest = NULL;
425
426 if (link_attrs->link_type == TAG_LINK_DEST) {
427 status = cairo_pdf_interchange_write_dest (surface, link_attrs);
428 if (unlikely (status))
429 return status;
430
431 } else if (link_attrs->link_type == TAG_LINK_URI) {
432 _cairo_output_stream_printf (surface->output,
433 " /A <<\n"
434 " /Type /Action\n"
435 " /S /URI\n"
436 " /URI (%s)\n"
437 " >>\n",
438 link_attrs->uri);
439 } else if (link_attrs->link_type == TAG_LINK_FILE) {
440 _cairo_output_stream_printf (surface->output,
441 " /A <<\n"
442 " /Type /Action\n"
443 " /S /GoToR\n"
444 " /F (%s)\n",
445 link_attrs->file);
446 if (link_attrs->dest) {
447 status = _cairo_utf8_to_pdf_string (link_attrs->dest, &dest);
448 if (unlikely (status))
449 return status;
450
451 _cairo_output_stream_printf (surface->output,
452 " /D %s\n",
453 dest);
454 free (dest);
455 } else {
456 if (link_attrs->has_pos) {
457 _cairo_output_stream_printf (surface->output,
458 " /D [%d %f %f 0]\n",
459 link_attrs->page,
460 link_attrs->pos.x,
461 link_attrs->pos.y);
462 } else {
463 _cairo_output_stream_printf (surface->output,
464 " /D [%d null null 0]\n",
465 link_attrs->page);
466 }
467 }
468 _cairo_output_stream_printf (surface->output,
469 " >>\n");
470 }
471
472 return CAIRO_STATUS_SUCCESS;
473 }
474
475 static cairo_int_status_t
cairo_pdf_interchange_write_annot(cairo_pdf_surface_t * surface,cairo_pdf_annotation_t * annot)476 cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
477 cairo_pdf_annotation_t *annot)
478 {
479 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
480 cairo_pdf_interchange_t *ic = &surface->interchange;
481 cairo_pdf_struct_tree_node_t *node = annot->node;
482 int sp;
483 int i, num_rects;
484 double height;
485
486 num_rects = _cairo_array_num_elements (&annot->link_attrs.rects);
487 if (strcmp (node->name, CAIRO_TAG_LINK) == 0 &&
488 annot->link_attrs.link_type != TAG_LINK_EMPTY &&
489 (node->extents.valid || num_rects > 0))
490 {
491 status = _cairo_array_append (&ic->parent_tree, &node->res);
492 if (unlikely (status))
493 return status;
494
495 sp = _cairo_array_num_elements (&ic->parent_tree) - 1;
496
497 node->annot_res = _cairo_pdf_surface_new_object (surface);
498
499 status = _cairo_array_append (&surface->page_annots, &node->annot_res);
500 if (unlikely (status))
501 return status;
502
503 _cairo_pdf_surface_update_object (surface, node->annot_res);
504 _cairo_output_stream_printf (surface->output,
505 "%d 0 obj\n"
506 "<< /Type /Annot\n"
507 " /Subtype /Link\n"
508 " /StructParent %d\n",
509 node->annot_res.id,
510 sp);
511
512 height = surface->height;
513 if (num_rects > 0) {
514 cairo_rectangle_int_t bbox_rect;
515
516 _cairo_output_stream_printf (surface->output,
517 " /QuadPoints [ ");
518 for (i = 0; i < num_rects; i++) {
519 cairo_rectangle_t rectf;
520 cairo_rectangle_int_t recti;
521
522 _cairo_array_copy_element (&annot->link_attrs.rects, i, &rectf);
523 _cairo_rectangle_int_from_double (&recti, &rectf);
524 if (i == 0)
525 bbox_rect = recti;
526 else
527 _cairo_rectangle_union (&bbox_rect, &recti);
528
529 write_rect_to_pdf_quad_points (surface->output, &rectf, height);
530 _cairo_output_stream_printf (surface->output, " ");
531 }
532 _cairo_output_stream_printf (surface->output,
533 "]\n"
534 " /Rect [ ");
535 write_rect_int_to_pdf_bbox (surface->output, &bbox_rect, height);
536 _cairo_output_stream_printf (surface->output, " ]\n");
537 } else {
538 _cairo_output_stream_printf (surface->output,
539 " /Rect [ ");
540 write_rect_int_to_pdf_bbox (surface->output, &node->extents.extents, height);
541 _cairo_output_stream_printf (surface->output, " ]\n");
542 }
543
544 status = cairo_pdf_interchange_write_link_action (surface, &annot->link_attrs);
545 if (unlikely (status))
546 return status;
547
548 _cairo_output_stream_printf (surface->output,
549 " /BS << /W 0 >>"
550 ">>\n"
551 "endobj\n");
552
553 status = _cairo_output_stream_get_status (surface->output);
554 }
555
556 return status;
557 }
558
559 static cairo_int_status_t
cairo_pdf_interchange_walk_struct_tree(cairo_pdf_surface_t * surface,cairo_pdf_struct_tree_node_t * node,cairo_int_status_t (* func)(cairo_pdf_surface_t * surface,cairo_pdf_struct_tree_node_t * node))560 cairo_pdf_interchange_walk_struct_tree (cairo_pdf_surface_t *surface,
561 cairo_pdf_struct_tree_node_t *node,
562 cairo_int_status_t (*func) (cairo_pdf_surface_t *surface,
563 cairo_pdf_struct_tree_node_t *node))
564 {
565 cairo_int_status_t status;
566 cairo_pdf_struct_tree_node_t *child;
567
568 if (node->parent) {
569 status = func (surface, node);
570 if (unlikely (status))
571 return status;
572 }
573
574 cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,
575 &node->children, link)
576 {
577 status = cairo_pdf_interchange_walk_struct_tree (surface, child, func);
578 if (unlikely (status))
579 return status;
580 }
581
582 return CAIRO_STATUS_SUCCESS;
583 }
584
585 static cairo_int_status_t
cairo_pdf_interchange_write_struct_tree(cairo_pdf_surface_t * surface)586 cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface)
587 {
588 cairo_pdf_interchange_t *ic = &surface->interchange;
589 cairo_pdf_struct_tree_node_t *child;
590
591 if (cairo_list_is_empty (&ic->struct_root->children))
592 return CAIRO_STATUS_SUCCESS;
593
594 surface->struct_tree_root = _cairo_pdf_surface_new_object (surface);
595 ic->struct_root->res = surface->struct_tree_root;
596
597 cairo_pdf_interchange_walk_struct_tree (surface, ic->struct_root, cairo_pdf_interchange_write_node_object);
598
599 child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link);
600 _cairo_pdf_surface_update_object (surface, surface->struct_tree_root);
601 _cairo_output_stream_printf (surface->output,
602 "%d 0 obj\n"
603 "<< /Type /StructTreeRoot\n"
604 " /ParentTree %d 0 R\n",
605 surface->struct_tree_root.id,
606 ic->parent_tree_res.id);
607
608 if (cairo_list_is_singular (&ic->struct_root->children)) {
609 child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link);
610 _cairo_output_stream_printf (surface->output, " /K [ %d 0 R ]\n", child->res.id);
611 } else {
612 _cairo_output_stream_printf (surface->output, " /K [ ");
613
614 cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,
615 &ic->struct_root->children, link)
616 {
617 _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id);
618 }
619 _cairo_output_stream_printf (surface->output, "]\n");
620 }
621
622 _cairo_output_stream_printf (surface->output,
623 ">>\n"
624 "endobj\n");
625
626 return CAIRO_STATUS_SUCCESS;
627 }
628
629 static cairo_int_status_t
cairo_pdf_interchange_write_page_annots(cairo_pdf_surface_t * surface)630 cairo_pdf_interchange_write_page_annots (cairo_pdf_surface_t *surface)
631 {
632 cairo_pdf_interchange_t *ic = &surface->interchange;
633 int num_elems, i;
634 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
635
636 num_elems = _cairo_array_num_elements (&ic->annots);
637 for (i = 0; i < num_elems; i++) {
638 cairo_pdf_annotation_t * annot;
639
640 _cairo_array_copy_element (&ic->annots, i, &annot);
641 status = cairo_pdf_interchange_write_annot (surface, annot);
642 if (unlikely (status))
643 return status;
644 }
645
646 return status;
647 }
648
649 static cairo_int_status_t
cairo_pdf_interchange_write_page_parent_elems(cairo_pdf_surface_t * surface)650 cairo_pdf_interchange_write_page_parent_elems (cairo_pdf_surface_t *surface)
651 {
652 int num_elems, i;
653 cairo_pdf_struct_tree_node_t *node;
654 cairo_pdf_resource_t res;
655 cairo_pdf_interchange_t *ic = &surface->interchange;
656 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
657
658 surface->page_parent_tree = -1;
659 num_elems = _cairo_array_num_elements (&ic->mcid_to_tree);
660 if (num_elems > 0) {
661 res = _cairo_pdf_surface_new_object (surface);
662 _cairo_output_stream_printf (surface->output,
663 "%d 0 obj\n"
664 "[\n",
665 res.id);
666 for (i = 0; i < num_elems; i++) {
667 _cairo_array_copy_element (&ic->mcid_to_tree, i, &node);
668 _cairo_output_stream_printf (surface->output, " %d 0 R\n", node->res.id);
669 }
670 _cairo_output_stream_printf (surface->output,
671 "]\n"
672 "endobj\n");
673 status = _cairo_array_append (&ic->parent_tree, &res);
674 surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1;
675 }
676
677 return status;
678 }
679
680 static cairo_int_status_t
cairo_pdf_interchange_write_parent_tree(cairo_pdf_surface_t * surface)681 cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface)
682 {
683 int num_elems, i;
684 cairo_pdf_resource_t *res;
685 cairo_pdf_interchange_t *ic = &surface->interchange;
686
687 num_elems = _cairo_array_num_elements (&ic->parent_tree);
688 if (num_elems > 0) {
689 ic->parent_tree_res = _cairo_pdf_surface_new_object (surface);
690 _cairo_output_stream_printf (surface->output,
691 "%d 0 obj\n"
692 "<< /Nums [\n",
693 ic->parent_tree_res.id);
694 for (i = 0; i < num_elems; i++) {
695 res = _cairo_array_index (&ic->parent_tree, i);
696 if (res->id) {
697 _cairo_output_stream_printf (surface->output,
698 " %d %d 0 R\n",
699 i,
700 res->id);
701 }
702 }
703 _cairo_output_stream_printf (surface->output,
704 " ]\n"
705 ">>\n"
706 "endobj\n");
707 }
708
709 return CAIRO_STATUS_SUCCESS;
710 }
711
712 static cairo_int_status_t
cairo_pdf_interchange_write_outline(cairo_pdf_surface_t * surface)713 cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
714 {
715 int num_elems, i;
716 cairo_pdf_outline_entry_t *outline;
717 cairo_pdf_interchange_t *ic = &surface->interchange;
718 cairo_int_status_t status;
719 char *name = NULL;
720
721 num_elems = _cairo_array_num_elements (&ic->outline);
722 if (num_elems < 2)
723 return CAIRO_INT_STATUS_SUCCESS;
724
725 _cairo_array_copy_element (&ic->outline, 0, &outline);
726 outline->res = _cairo_pdf_surface_new_object (surface);
727 surface->outlines_dict_res = outline->res;
728 _cairo_output_stream_printf (surface->output,
729 "%d 0 obj\n"
730 "<< /Type /Outlines\n"
731 " /First %d 0 R\n"
732 " /Last %d 0 R\n"
733 " /Count %d\n"
734 ">>\n"
735 "endobj\n",
736 outline->res.id,
737 outline->first_child->res.id,
738 outline->last_child->res.id,
739 outline->count);
740
741 for (i = 1; i < num_elems; i++) {
742 _cairo_array_copy_element (&ic->outline, i, &outline);
743 _cairo_pdf_surface_update_object (surface, outline->res);
744
745 status = _cairo_utf8_to_pdf_string (outline->name, &name);
746 if (unlikely (status))
747 return status;
748
749 _cairo_output_stream_printf (surface->output,
750 "%d 0 obj\n"
751 "<< /Title %s\n"
752 " /Parent %d 0 R\n",
753 outline->res.id,
754 name,
755 outline->parent->res.id);
756 free (name);
757
758 if (outline->prev) {
759 _cairo_output_stream_printf (surface->output,
760 " /Prev %d 0 R\n",
761 outline->prev->res.id);
762 }
763
764 if (outline->next) {
765 _cairo_output_stream_printf (surface->output,
766 " /Next %d 0 R\n",
767 outline->next->res.id);
768 }
769
770 if (outline->first_child) {
771 _cairo_output_stream_printf (surface->output,
772 " /First %d 0 R\n"
773 " /Last %d 0 R\n"
774 " /Count %d\n",
775 outline->first_child->res.id,
776 outline->last_child->res.id,
777 outline->count);
778 }
779
780 if (outline->flags) {
781 int flags = 0;
782 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_ITALIC)
783 flags |= 1;
784 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_BOLD)
785 flags |= 2;
786 _cairo_output_stream_printf (surface->output,
787 " /F %d\n",
788 flags);
789 }
790
791 status = cairo_pdf_interchange_write_link_action (surface, &outline->link_attrs);
792 if (unlikely (status))
793 return status;
794
795 _cairo_output_stream_printf (surface->output,
796 ">>\n"
797 "endobj\n");
798 }
799
800 return status;
801 }
802
803 /*
804 * Split a page label into a text prefix and numeric suffix. Leading '0's are
805 * included in the prefix. eg
806 * "3" => NULL, 3
807 * "cover" => "cover", 0
808 * "A-2" => "A-", 2
809 * "A-002" => "A-00", 2
810 */
811 static char *
split_label(const char * label,int * num)812 split_label (const char* label, int *num)
813 {
814 int len, i;
815
816 *num = 0;
817 len = strlen (label);
818 if (len == 0)
819 return NULL;
820
821 i = len;
822 while (i > 0 && _cairo_isdigit (label[i-1]))
823 i--;
824
825 while (i < len && label[i] == '0')
826 i++;
827
828 if (i < len)
829 sscanf (label + i, "%d", num);
830
831 if (i > 0) {
832 char *s;
833 s = _cairo_malloc (i + 1);
834 if (!s)
835 return NULL;
836
837 memcpy (s, label, i);
838 s[i] = 0;
839 return s;
840 }
841
842 return NULL;
843 }
844
845 /* strcmp that handles NULL arguments */
846 static cairo_bool_t
strcmp_null(const char * s1,const char * s2)847 strcmp_null (const char *s1, const char *s2)
848 {
849 if (s1 && s2)
850 return strcmp (s1, s2) == 0;
851
852 if (!s1 && !s2)
853 return TRUE;
854
855 return FALSE;
856 }
857
858 static cairo_int_status_t
cairo_pdf_interchange_write_page_labels(cairo_pdf_surface_t * surface)859 cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
860 {
861 int num_elems, i;
862 char *label;
863 char *prefix;
864 char *prev_prefix;
865 int num, prev_num;
866 cairo_int_status_t status;
867 cairo_bool_t has_labels;
868
869 /* Check if any labels defined */
870 num_elems = _cairo_array_num_elements (&surface->page_labels);
871 has_labels = FALSE;
872 for (i = 0; i < num_elems; i++) {
873 _cairo_array_copy_element (&surface->page_labels, i, &label);
874 if (label) {
875 has_labels = TRUE;
876 break;
877 }
878 }
879
880 if (!has_labels)
881 return CAIRO_STATUS_SUCCESS;
882
883 surface->page_labels_res = _cairo_pdf_surface_new_object (surface);
884 _cairo_output_stream_printf (surface->output,
885 "%d 0 obj\n"
886 "<< /Nums [\n",
887 surface->page_labels_res.id);
888 prefix = NULL;
889 prev_prefix = NULL;
890 num = 0;
891 prev_num = 0;
892 for (i = 0; i < num_elems; i++) {
893 _cairo_array_copy_element (&surface->page_labels, i, &label);
894 if (label) {
895 prefix = split_label (label, &num);
896 } else {
897 prefix = NULL;
898 num = i + 1;
899 }
900
901 if (!strcmp_null (prefix, prev_prefix) || num != prev_num + 1) {
902 _cairo_output_stream_printf (surface->output, " %d << ", i);
903
904 if (num)
905 _cairo_output_stream_printf (surface->output, "/S /D /St %d ", num);
906
907 if (prefix) {
908 char *s;
909 status = _cairo_utf8_to_pdf_string (prefix, &s);
910 if (unlikely (status))
911 return status;
912
913 _cairo_output_stream_printf (surface->output, "/P %s ", s);
914 free (s);
915 }
916
917 _cairo_output_stream_printf (surface->output, ">>\n");
918 }
919 free (prev_prefix);
920 prev_prefix = prefix;
921 prefix = NULL;
922 prev_num = num;
923 }
924 free (prefix);
925 free (prev_prefix);
926 _cairo_output_stream_printf (surface->output,
927 " ]\n"
928 ">>\n"
929 "endobj\n");
930
931 return CAIRO_STATUS_SUCCESS;
932 }
933
934 static void
_collect_dest(void * entry,void * closure)935 _collect_dest (void *entry, void *closure)
936 {
937 cairo_pdf_named_dest_t *dest = entry;
938 cairo_pdf_surface_t *surface = closure;
939 cairo_pdf_interchange_t *ic = &surface->interchange;
940
941 ic->sorted_dests[ic->num_dests++] = dest;
942 }
943
944 static int
_dest_compare(const void * a,const void * b)945 _dest_compare (const void *a, const void *b)
946 {
947 const cairo_pdf_named_dest_t * const *dest_a = a;
948 const cairo_pdf_named_dest_t * const *dest_b = b;
949
950 return strcmp ((*dest_a)->attrs.name, (*dest_b)->attrs.name);
951 }
952
953 static cairo_int_status_t
_cairo_pdf_interchange_write_document_dests(cairo_pdf_surface_t * surface)954 _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
955 {
956 int i;
957 cairo_pdf_interchange_t *ic = &surface->interchange;
958
959 if (ic->num_dests == 0) {
960 ic->dests_res.id = 0;
961 return CAIRO_STATUS_SUCCESS;
962 }
963
964 ic->sorted_dests = calloc (ic->num_dests, sizeof (cairo_pdf_named_dest_t *));
965 if (unlikely (ic->sorted_dests == NULL))
966 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
967
968 ic->num_dests = 0;
969 _cairo_hash_table_foreach (ic->named_dests, _collect_dest, surface);
970 qsort (ic->sorted_dests, ic->num_dests, sizeof (cairo_pdf_named_dest_t *), _dest_compare);
971
972 ic->dests_res = _cairo_pdf_surface_new_object (surface);
973 _cairo_output_stream_printf (surface->output,
974 "%d 0 obj\n"
975 "<< /Names [\n",
976 ic->dests_res.id);
977 for (i = 0; i < ic->num_dests; i++) {
978 cairo_pdf_named_dest_t *dest = ic->sorted_dests[i];
979 cairo_pdf_resource_t page_res;
980 double x = 0;
981 double y = 0;
982 double height;
983
984 if (dest->attrs.internal)
985 continue;
986
987 if (dest->extents.valid) {
988 x = dest->extents.extents.x;
989 y = dest->extents.extents.y;
990 }
991
992 if (dest->attrs.x_valid)
993 x = dest->attrs.x;
994
995 if (dest->attrs.y_valid)
996 y = dest->attrs.y;
997
998 _cairo_array_copy_element (&surface->pages, dest->page - 1, &page_res);
999 _cairo_array_copy_element (&surface->page_heights, dest->page - 1, &height);
1000 _cairo_output_stream_printf (surface->output,
1001 " (%s) [%d 0 R /XYZ %f %f 0]\n",
1002 dest->attrs.name,
1003 page_res.id,
1004 x,
1005 height - y);
1006 }
1007 _cairo_output_stream_printf (surface->output,
1008 " ]\n"
1009 ">>\n"
1010 "endobj\n");
1011
1012 return CAIRO_STATUS_SUCCESS;
1013 }
1014
1015 static cairo_int_status_t
cairo_pdf_interchange_write_names_dict(cairo_pdf_surface_t * surface)1016 cairo_pdf_interchange_write_names_dict (cairo_pdf_surface_t *surface)
1017 {
1018 cairo_pdf_interchange_t *ic = &surface->interchange;
1019 cairo_int_status_t status;
1020
1021 status = _cairo_pdf_interchange_write_document_dests (surface);
1022 if (unlikely (status))
1023 return status;
1024
1025 surface->names_dict_res.id = 0;
1026 if (ic->dests_res.id != 0) {
1027 surface->names_dict_res = _cairo_pdf_surface_new_object (surface);
1028 _cairo_output_stream_printf (surface->output,
1029 "%d 0 obj\n"
1030 "<< /Dests %d 0 R >>\n"
1031 "endobj\n",
1032 surface->names_dict_res.id,
1033 ic->dests_res.id);
1034 }
1035
1036 return CAIRO_STATUS_SUCCESS;
1037 }
1038
1039 static cairo_int_status_t
cairo_pdf_interchange_write_docinfo(cairo_pdf_surface_t * surface)1040 cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface)
1041 {
1042 cairo_pdf_interchange_t *ic = &surface->interchange;
1043
1044 surface->docinfo_res = _cairo_pdf_surface_new_object (surface);
1045 if (surface->docinfo_res.id == 0)
1046 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1047
1048 _cairo_output_stream_printf (surface->output,
1049 "%d 0 obj\n"
1050 "<< /Producer (cairo %s (https://cairographics.org))\n",
1051 surface->docinfo_res.id,
1052 cairo_version_string ());
1053
1054 if (ic->docinfo.title)
1055 _cairo_output_stream_printf (surface->output, " /Title %s\n", ic->docinfo.title);
1056
1057 if (ic->docinfo.author)
1058 _cairo_output_stream_printf (surface->output, " /Author %s\n", ic->docinfo.author);
1059
1060 if (ic->docinfo.subject)
1061 _cairo_output_stream_printf (surface->output, " /Subject %s\n", ic->docinfo.subject);
1062
1063 if (ic->docinfo.keywords)
1064 _cairo_output_stream_printf (surface->output, " /Keywords %s\n", ic->docinfo.keywords);
1065
1066 if (ic->docinfo.creator)
1067 _cairo_output_stream_printf (surface->output, " /Creator %s\n", ic->docinfo.creator);
1068
1069 if (ic->docinfo.create_date)
1070 _cairo_output_stream_printf (surface->output, " /CreationDate %s\n", ic->docinfo.create_date);
1071
1072 if (ic->docinfo.mod_date)
1073 _cairo_output_stream_printf (surface->output, " /ModDate %s\n", ic->docinfo.mod_date);
1074
1075 _cairo_output_stream_printf (surface->output,
1076 ">>\n"
1077 "endobj\n");
1078
1079 return CAIRO_STATUS_SUCCESS;
1080 }
1081
1082 static cairo_int_status_t
_cairo_pdf_interchange_begin_structure_tag(cairo_pdf_surface_t * surface,cairo_tag_type_t tag_type,const char * name,const char * attributes)1083 _cairo_pdf_interchange_begin_structure_tag (cairo_pdf_surface_t *surface,
1084 cairo_tag_type_t tag_type,
1085 const char *name,
1086 const char *attributes)
1087 {
1088 int page_num, mcid;
1089 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1090 cairo_pdf_interchange_t *ic = &surface->interchange;
1091
1092 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1093 status = add_tree_node (surface, ic->current_node, name, &ic->current_node);
1094 if (unlikely (status))
1095 return status;
1096
1097 _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, ic->current_node);
1098
1099 if (tag_type & TAG_TYPE_LINK) {
1100 status = add_annotation (surface, ic->current_node, name, attributes);
1101 if (unlikely (status))
1102 return status;
1103
1104 cairo_list_add_tail (&ic->current_node->extents.link, &ic->extents_list);
1105 }
1106
1107 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1108 ic->current_node = _cairo_tag_stack_top_elem (&ic->render_tag_stack)->data;
1109 assert (ic->current_node != NULL);
1110 if (is_leaf_node (ic->current_node)) {
1111 page_num = _cairo_array_num_elements (&surface->pages);
1112 add_mcid_to_node (surface, ic->current_node, page_num, &mcid);
1113 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, name, mcid);
1114 }
1115 }
1116
1117 return status;
1118 }
1119
1120 static cairo_int_status_t
_cairo_pdf_interchange_begin_dest_tag(cairo_pdf_surface_t * surface,cairo_tag_type_t tag_type,const char * name,const char * attributes)1121 _cairo_pdf_interchange_begin_dest_tag (cairo_pdf_surface_t *surface,
1122 cairo_tag_type_t tag_type,
1123 const char *name,
1124 const char *attributes)
1125 {
1126 cairo_pdf_named_dest_t *dest;
1127 cairo_pdf_interchange_t *ic = &surface->interchange;
1128 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1129
1130 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1131 dest = calloc (1, sizeof (cairo_pdf_named_dest_t));
1132 if (unlikely (dest == NULL))
1133 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1134
1135 status = _cairo_tag_parse_dest_attributes (attributes, &dest->attrs);
1136 if (unlikely (status))
1137 {
1138 free (dest);
1139 return status;
1140 }
1141
1142 dest->page = _cairo_array_num_elements (&surface->pages);
1143 init_named_dest_key (dest);
1144 status = _cairo_hash_table_insert (ic->named_dests, &dest->base);
1145 if (unlikely (status))
1146 {
1147 free (dest->attrs.name);
1148 free (dest);
1149 return status;
1150 }
1151
1152 _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, dest);
1153 cairo_list_add_tail (&dest->extents.link, &ic->extents_list);
1154 ic->num_dests++;
1155 }
1156
1157 return status;
1158 }
1159
1160 cairo_int_status_t
_cairo_pdf_interchange_tag_begin(cairo_pdf_surface_t * surface,const char * name,const char * attributes)1161 _cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface,
1162 const char *name,
1163 const char *attributes)
1164 {
1165 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1166 cairo_tag_type_t tag_type;
1167 cairo_pdf_interchange_t *ic = &surface->interchange;
1168 void *ptr;
1169
1170 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1171 status = _cairo_tag_stack_push (&ic->analysis_tag_stack, name, attributes);
1172
1173 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1174 status = _cairo_tag_stack_push (&ic->render_tag_stack, name, attributes);
1175 _cairo_array_copy_element (&ic->push_data, ic->push_data_index++, &ptr);
1176 _cairo_tag_stack_set_top_data (&ic->render_tag_stack, ptr);
1177 }
1178
1179 if (unlikely (status))
1180 return status;
1181
1182 tag_type = _cairo_tag_get_type (name);
1183 if (tag_type & TAG_TYPE_STRUCTURE) {
1184 status = _cairo_pdf_interchange_begin_structure_tag (surface, tag_type, name, attributes);
1185 if (unlikely (status))
1186 return status;
1187 }
1188
1189 if (tag_type & TAG_TYPE_DEST) {
1190 status = _cairo_pdf_interchange_begin_dest_tag (surface, tag_type, name, attributes);
1191 if (unlikely (status))
1192 return status;
1193 }
1194
1195 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1196 ptr = _cairo_tag_stack_top_elem (&ic->analysis_tag_stack)->data;
1197 status = _cairo_array_append (&ic->push_data, &ptr);
1198 }
1199
1200 return status;
1201 }
1202
1203 static cairo_int_status_t
_cairo_pdf_interchange_end_structure_tag(cairo_pdf_surface_t * surface,cairo_tag_type_t tag_type,cairo_tag_stack_elem_t * elem)1204 _cairo_pdf_interchange_end_structure_tag (cairo_pdf_surface_t *surface,
1205 cairo_tag_type_t tag_type,
1206 cairo_tag_stack_elem_t *elem)
1207 {
1208 const cairo_pdf_struct_tree_node_t *node;
1209 struct tag_extents *tag, *next;
1210 cairo_pdf_interchange_t *ic = &surface->interchange;
1211 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1212
1213 assert (elem->data != NULL);
1214 node = elem->data;
1215 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1216 if (tag_type & TAG_TYPE_LINK) {
1217 cairo_list_foreach_entry_safe (tag, next, struct tag_extents,
1218 &ic->extents_list, link) {
1219 if (tag == &node->extents) {
1220 cairo_list_del (&tag->link);
1221 break;
1222 }
1223 }
1224 }
1225 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1226 if (is_leaf_node (ic->current_node)) {
1227 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
1228 if (unlikely (status))
1229 return status;
1230 }
1231 }
1232
1233 ic->current_node = ic->current_node->parent;
1234 assert (ic->current_node != NULL);
1235
1236 return status;
1237 }
1238
1239 static cairo_int_status_t
_cairo_pdf_interchange_end_dest_tag(cairo_pdf_surface_t * surface,cairo_tag_type_t tag_type,cairo_tag_stack_elem_t * elem)1240 _cairo_pdf_interchange_end_dest_tag (cairo_pdf_surface_t *surface,
1241 cairo_tag_type_t tag_type,
1242 cairo_tag_stack_elem_t *elem)
1243 {
1244 struct tag_extents *tag, *next;
1245 cairo_pdf_named_dest_t *dest;
1246 cairo_pdf_interchange_t *ic = &surface->interchange;
1247
1248 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1249 assert (elem->data != NULL);
1250 dest = (cairo_pdf_named_dest_t *) elem->data;
1251 cairo_list_foreach_entry_safe (tag, next, struct tag_extents,
1252 &ic->extents_list, link) {
1253 if (tag == &dest->extents) {
1254 cairo_list_del (&tag->link);
1255 break;
1256 }
1257 }
1258 }
1259
1260 return CAIRO_STATUS_SUCCESS;
1261 }
1262
1263 cairo_int_status_t
_cairo_pdf_interchange_tag_end(cairo_pdf_surface_t * surface,const char * name)1264 _cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface,
1265 const char *name)
1266 {
1267 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1268 cairo_pdf_interchange_t *ic = &surface->interchange;
1269 cairo_tag_type_t tag_type;
1270 cairo_tag_stack_elem_t *elem;
1271
1272 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
1273 status = _cairo_tag_stack_pop (&ic->analysis_tag_stack, name, &elem);
1274 else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER)
1275 status = _cairo_tag_stack_pop (&ic->render_tag_stack, name, &elem);
1276
1277 if (unlikely (status))
1278 return status;
1279
1280 tag_type = _cairo_tag_get_type (name);
1281 if (tag_type & TAG_TYPE_STRUCTURE) {
1282 status = _cairo_pdf_interchange_end_structure_tag (surface, tag_type, elem);
1283 if (unlikely (status))
1284 goto cleanup;
1285 }
1286
1287 if (tag_type & TAG_TYPE_DEST) {
1288 status = _cairo_pdf_interchange_end_dest_tag (surface, tag_type, elem);
1289 if (unlikely (status))
1290 goto cleanup;
1291 }
1292
1293 cleanup:
1294 _cairo_tag_stack_free_elem (elem);
1295
1296 return status;
1297 }
1298
1299 cairo_int_status_t
_cairo_pdf_interchange_add_operation_extents(cairo_pdf_surface_t * surface,const cairo_rectangle_int_t * extents)1300 _cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface,
1301 const cairo_rectangle_int_t *extents)
1302 {
1303 cairo_pdf_interchange_t *ic = &surface->interchange;
1304 struct tag_extents *tag;
1305
1306 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1307 cairo_list_foreach_entry (tag, struct tag_extents, &ic->extents_list, link) {
1308 if (tag->valid) {
1309 _cairo_rectangle_union (&tag->extents, extents);
1310 } else {
1311 tag->extents = *extents;
1312 tag->valid = TRUE;
1313 }
1314 }
1315 }
1316
1317 return CAIRO_STATUS_SUCCESS;
1318 }
1319
1320 cairo_int_status_t
_cairo_pdf_interchange_begin_page_content(cairo_pdf_surface_t * surface)1321 _cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface)
1322 {
1323 cairo_pdf_interchange_t *ic = &surface->interchange;
1324 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1325 int page_num, mcid;
1326
1327 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
1328 _cairo_array_truncate (&ic->mcid_to_tree, 0);
1329 _cairo_array_truncate (&ic->push_data, 0);
1330 ic->begin_page_node = ic->current_node;
1331 } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1332 ic->push_data_index = 0;
1333 ic->current_node = ic->begin_page_node;
1334 if (ic->end_page_node && is_leaf_node (ic->end_page_node)) {
1335 page_num = _cairo_array_num_elements (&surface->pages);
1336 add_mcid_to_node (surface, ic->end_page_node, page_num, &mcid);
1337 status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators,
1338 ic->end_page_node->name,
1339 mcid);
1340 }
1341 }
1342
1343 return status;
1344 }
1345
1346 cairo_int_status_t
_cairo_pdf_interchange_end_page_content(cairo_pdf_surface_t * surface)1347 _cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface)
1348 {
1349 cairo_pdf_interchange_t *ic = &surface->interchange;
1350 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1351
1352 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
1353 ic->end_page_node = ic->current_node;
1354 if (is_leaf_node (ic->current_node))
1355 status = _cairo_pdf_operators_tag_end (&surface->pdf_operators);
1356 }
1357
1358 return status;
1359 }
1360
1361 cairo_int_status_t
_cairo_pdf_interchange_write_page_objects(cairo_pdf_surface_t * surface)1362 _cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface)
1363 {
1364 cairo_int_status_t status;
1365
1366 status = cairo_pdf_interchange_write_page_annots (surface);
1367 if (unlikely (status))
1368 return status;
1369
1370 cairo_pdf_interchange_clear_annotations (surface);
1371
1372 return cairo_pdf_interchange_write_page_parent_elems (surface);
1373 }
1374
1375 cairo_int_status_t
_cairo_pdf_interchange_write_document_objects(cairo_pdf_surface_t * surface)1376 _cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
1377 {
1378 cairo_pdf_interchange_t *ic = &surface->interchange;
1379 cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
1380 cairo_tag_stack_structure_type_t tag_type;
1381
1382 tag_type = _cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack);
1383 if (tag_type == TAG_TREE_TYPE_TAGGED || tag_type == TAG_TREE_TYPE_STRUCTURE ||
1384 tag_type == TAG_TREE_TYPE_LINK_ONLY) {
1385
1386 status = cairo_pdf_interchange_write_parent_tree (surface);
1387 if (unlikely (status))
1388 return status;
1389
1390 status = cairo_pdf_interchange_write_struct_tree (surface);
1391 if (unlikely (status))
1392 return status;
1393
1394 if (tag_type == TAG_TREE_TYPE_TAGGED)
1395 surface->tagged = TRUE;
1396 }
1397
1398 status = cairo_pdf_interchange_write_outline (surface);
1399 if (unlikely (status))
1400 return status;
1401
1402 status = cairo_pdf_interchange_write_page_labels (surface);
1403 if (unlikely (status))
1404 return status;
1405
1406 status = cairo_pdf_interchange_write_names_dict (surface);
1407 if (unlikely (status))
1408 return status;
1409
1410 status = cairo_pdf_interchange_write_docinfo (surface);
1411
1412 return status;
1413 }
1414
1415 static void
_cairo_pdf_interchange_set_create_date(cairo_pdf_surface_t * surface)1416 _cairo_pdf_interchange_set_create_date (cairo_pdf_surface_t *surface)
1417 {
1418 time_t utc, local, offset;
1419 struct tm tm_local, tm_utc;
1420 char buf[50];
1421 int buf_size;
1422 char *p;
1423 cairo_pdf_interchange_t *ic = &surface->interchange;
1424
1425 utc = time (NULL);
1426 localtime_r (&utc, &tm_local);
1427 strftime (buf, sizeof(buf), "(D:%Y%m%d%H%M%S", &tm_local);
1428
1429 /* strftime "%z" is non standard and does not work on windows (it prints zone name, not offset).
1430 * Calculate time zone offset by comparing local and utc time_t values for the same time.
1431 */
1432 gmtime_r (&utc, &tm_utc);
1433 tm_utc.tm_isdst = tm_local.tm_isdst;
1434 local = mktime (&tm_utc);
1435 offset = difftime (utc, local);
1436
1437 if (offset == 0) {
1438 strcat (buf, "Z");
1439 } else {
1440 if (offset > 0) {
1441 strcat (buf, "+");
1442 } else {
1443 strcat (buf, "-");
1444 offset = -offset;
1445 }
1446 p = buf + strlen (buf);
1447 buf_size = sizeof (buf) - strlen (buf);
1448 snprintf (p, buf_size, "%02d'%02d", (int)(offset/3600), (int)(offset%3600)/60);
1449 }
1450 strcat (buf, ")");
1451 ic->docinfo.create_date = strdup (buf);
1452 }
1453
1454 cairo_int_status_t
_cairo_pdf_interchange_init(cairo_pdf_surface_t * surface)1455 _cairo_pdf_interchange_init (cairo_pdf_surface_t *surface)
1456 {
1457 cairo_pdf_interchange_t *ic = &surface->interchange;
1458 cairo_pdf_outline_entry_t *outline_root;
1459 cairo_int_status_t status;
1460
1461 _cairo_tag_stack_init (&ic->analysis_tag_stack);
1462 _cairo_tag_stack_init (&ic->render_tag_stack);
1463 _cairo_array_init (&ic->push_data, sizeof(void *));
1464 ic->struct_root = calloc (1, sizeof(cairo_pdf_struct_tree_node_t));
1465 if (unlikely (ic->struct_root == NULL))
1466 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1467
1468 cairo_list_init (&ic->struct_root->children);
1469 _cairo_array_init (&ic->struct_root->mcid, sizeof(struct page_mcid));
1470 ic->current_node = ic->struct_root;
1471 ic->begin_page_node = NULL;
1472 ic->end_page_node = NULL;
1473 _cairo_array_init (&ic->parent_tree, sizeof(cairo_pdf_resource_t));
1474 _cairo_array_init (&ic->mcid_to_tree, sizeof(cairo_pdf_struct_tree_node_t *));
1475 _cairo_array_init (&ic->annots, sizeof(cairo_pdf_annotation_t *));
1476 ic->parent_tree_res.id = 0;
1477 cairo_list_init (&ic->extents_list);
1478 ic->named_dests = _cairo_hash_table_create (_named_dest_equal);
1479 if (unlikely (ic->named_dests == NULL))
1480 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1481
1482 ic->num_dests = 0;
1483 ic->sorted_dests = NULL;
1484 ic->dests_res.id = 0;
1485
1486 _cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *));
1487 outline_root = calloc (1, sizeof(cairo_pdf_outline_entry_t));
1488 if (unlikely (outline_root == NULL))
1489 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1490
1491 memset (&ic->docinfo, 0, sizeof (ic->docinfo));
1492 _cairo_pdf_interchange_set_create_date (surface);
1493 status = _cairo_array_append (&ic->outline, &outline_root);
1494
1495 return status;
1496 }
1497
1498 static void
_cairo_pdf_interchange_free_outlines(cairo_pdf_surface_t * surface)1499 _cairo_pdf_interchange_free_outlines (cairo_pdf_surface_t *surface)
1500 {
1501 cairo_pdf_interchange_t *ic = &surface->interchange;
1502 int num_elems, i;
1503
1504 num_elems = _cairo_array_num_elements (&ic->outline);
1505 for (i = 0; i < num_elems; i++) {
1506 cairo_pdf_outline_entry_t *outline;
1507
1508 _cairo_array_copy_element (&ic->outline, i, &outline);
1509 free (outline->name);
1510 free (outline->link_attrs.dest);
1511 free (outline->link_attrs.uri);
1512 free (outline->link_attrs.file);
1513 free (outline);
1514 }
1515 _cairo_array_fini (&ic->outline);
1516 }
1517
1518 cairo_int_status_t
_cairo_pdf_interchange_fini(cairo_pdf_surface_t * surface)1519 _cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface)
1520 {
1521 cairo_pdf_interchange_t *ic = &surface->interchange;
1522
1523 _cairo_tag_stack_fini (&ic->analysis_tag_stack);
1524 _cairo_tag_stack_fini (&ic->render_tag_stack);
1525 _cairo_array_fini (&ic->push_data);
1526 free_node (ic->struct_root);
1527 _cairo_array_fini (&ic->mcid_to_tree);
1528 cairo_pdf_interchange_clear_annotations (surface);
1529 _cairo_array_fini (&ic->annots);
1530 _cairo_array_fini (&ic->parent_tree);
1531 _cairo_hash_table_foreach (ic->named_dests, _named_dest_pluck, ic->named_dests);
1532 _cairo_hash_table_destroy (ic->named_dests);
1533 free (ic->sorted_dests);
1534 _cairo_pdf_interchange_free_outlines (surface);
1535 free (ic->docinfo.title);
1536 free (ic->docinfo.author);
1537 free (ic->docinfo.subject);
1538 free (ic->docinfo.keywords);
1539 free (ic->docinfo.creator);
1540 free (ic->docinfo.create_date);
1541 free (ic->docinfo.mod_date);
1542
1543 return CAIRO_STATUS_SUCCESS;
1544 }
1545
1546 cairo_int_status_t
_cairo_pdf_interchange_add_outline(cairo_pdf_surface_t * surface,int parent_id,const char * name,const char * link_attribs,cairo_pdf_outline_flags_t flags,int * id)1547 _cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
1548 int parent_id,
1549 const char *name,
1550 const char *link_attribs,
1551 cairo_pdf_outline_flags_t flags,
1552 int *id)
1553 {
1554 cairo_pdf_interchange_t *ic = &surface->interchange;
1555 cairo_pdf_outline_entry_t *outline;
1556 cairo_pdf_outline_entry_t *parent;
1557 cairo_int_status_t status;
1558
1559 if (parent_id < 0 || parent_id >= (int)_cairo_array_num_elements (&ic->outline))
1560 return CAIRO_STATUS_SUCCESS;
1561
1562 outline = _cairo_malloc (sizeof(cairo_pdf_outline_entry_t));
1563 if (unlikely (outline == NULL))
1564 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1565
1566 status = _cairo_tag_parse_link_attributes (link_attribs, &outline->link_attrs);
1567 if (unlikely (status)) {
1568 free (outline);
1569 return status;
1570 }
1571
1572 outline->res = _cairo_pdf_surface_new_object (surface);
1573 if (outline->res.id == 0)
1574 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1575
1576 outline->name = strdup (name);
1577 outline->flags = flags;
1578 outline->count = 0;
1579
1580 _cairo_array_copy_element (&ic->outline, parent_id, &parent);
1581
1582 outline->parent = parent;
1583 outline->first_child = NULL;
1584 outline->last_child = NULL;
1585 outline->next = NULL;
1586 if (parent->last_child) {
1587 parent->last_child->next = outline;
1588 outline->prev = parent->last_child;
1589 parent->last_child = outline;
1590 } else {
1591 parent->first_child = outline;
1592 parent->last_child = outline;
1593 outline->prev = NULL;
1594 }
1595
1596 *id = _cairo_array_num_elements (&ic->outline);
1597 status = _cairo_array_append (&ic->outline, &outline);
1598 if (unlikely (status))
1599 return status;
1600
1601 /* Update Count */
1602 outline = outline->parent;
1603 while (outline) {
1604 if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_OPEN) {
1605 outline->count++;
1606 } else {
1607 outline->count--;
1608 break;
1609 }
1610 outline = outline->parent;
1611 }
1612
1613 return CAIRO_STATUS_SUCCESS;
1614 }
1615
1616 /*
1617 * Date must be in the following format:
1618 *
1619 * YYYY-MM-DDThh:mm:ss[Z+-]hh:mm
1620 *
1621 * Only the year is required. If a field is included all preceding
1622 * fields must be included.
1623 */
1624 static char *
iso8601_to_pdf_date_string(const char * iso)1625 iso8601_to_pdf_date_string (const char *iso)
1626 {
1627 char buf[40];
1628 const char *p;
1629 int i;
1630
1631 /* Check that utf8 contains only the characters "0123456789-T:Z+" */
1632 p = iso;
1633 while (*p) {
1634 if (!_cairo_isdigit (*p) && *p != '-' && *p != 'T' &&
1635 *p != ':' && *p != 'Z' && *p != '+')
1636 return NULL;
1637 p++;
1638 }
1639
1640 p = iso;
1641 strcpy (buf, "(");
1642
1643 /* YYYY (required) */
1644 if (strlen (p) < 4)
1645 return NULL;
1646
1647 strncat (buf, p, 4);
1648 p += 4;
1649
1650 /* -MM, -DD, Thh, :mm, :ss */
1651 for (i = 0; i < 5; i++) {
1652 if (strlen (p) < 3)
1653 goto finish;
1654
1655 strncat (buf, p + 1, 2);
1656 p += 3;
1657 }
1658
1659 /* Z, +, - */
1660 if (strlen (p) < 1)
1661 goto finish;
1662 strncat (buf, p, 1);
1663 p += 1;
1664
1665 /* hh */
1666 if (strlen (p) < 2)
1667 goto finish;
1668
1669 strncat (buf, p, 2);
1670 strcat (buf, "'");
1671 p += 2;
1672
1673 /* :mm */
1674 if (strlen (p) < 3)
1675 goto finish;
1676
1677 strncat (buf, p + 1, 2);
1678 strcat (buf, "'");
1679
1680 finish:
1681 strcat (buf, ")");
1682 return strdup (buf);
1683 }
1684
1685 cairo_int_status_t
_cairo_pdf_interchange_set_metadata(cairo_pdf_surface_t * surface,cairo_pdf_metadata_t metadata,const char * utf8)1686 _cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface,
1687 cairo_pdf_metadata_t metadata,
1688 const char *utf8)
1689 {
1690 cairo_pdf_interchange_t *ic = &surface->interchange;
1691 cairo_status_t status;
1692 char *s = NULL;
1693
1694 if (utf8) {
1695 if (metadata == CAIRO_PDF_METADATA_CREATE_DATE ||
1696 metadata == CAIRO_PDF_METADATA_MOD_DATE) {
1697 s = iso8601_to_pdf_date_string (utf8);
1698 } else {
1699 status = _cairo_utf8_to_pdf_string (utf8, &s);
1700 if (unlikely (status))
1701 return status;
1702 }
1703 }
1704
1705 switch (metadata) {
1706 case CAIRO_PDF_METADATA_TITLE:
1707 free (ic->docinfo.title);
1708 ic->docinfo.title = s;
1709 break;
1710 case CAIRO_PDF_METADATA_AUTHOR:
1711 free (ic->docinfo.author);
1712 ic->docinfo.author = s;
1713 break;
1714 case CAIRO_PDF_METADATA_SUBJECT:
1715 free (ic->docinfo.subject);
1716 ic->docinfo.subject = s;
1717 break;
1718 case CAIRO_PDF_METADATA_KEYWORDS:
1719 free (ic->docinfo.keywords);
1720 ic->docinfo.keywords = s;
1721 break;
1722 case CAIRO_PDF_METADATA_CREATOR:
1723 free (ic->docinfo.creator);
1724 ic->docinfo.creator = s;
1725 break;
1726 case CAIRO_PDF_METADATA_CREATE_DATE:
1727 free (ic->docinfo.create_date);
1728 ic->docinfo.create_date = s;
1729 break;
1730 case CAIRO_PDF_METADATA_MOD_DATE:
1731 free (ic->docinfo.mod_date);
1732 ic->docinfo.mod_date = s;
1733 break;
1734 }
1735
1736 return CAIRO_STATUS_SUCCESS;
1737 }
1738