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