Coverage Report

Created: 2025-07-07 10:01

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