~ubuntu-branches/ubuntu/utopic/texlive-bin/utopic

« back to all changes in this revision

Viewing changes to texk/dvipdfmx/dvipdfmx-20110311/src/pdfdoc.c

  • Committer: Package Import Robot
  • Author(s): Norbert Preining
  • Date: 2012-05-07 10:47:49 UTC
  • mfrom: (1.2.4)
  • Revision ID: package-import@ubuntu.com-20120507104749-p00ot5sajjbkp1hp
Tags: 2011.20120507-1
* new upstream checkout: uptex 1.10
* drop patches for config file inclusion in (x)dvipdfmx, included upstream
* add man page for etex
* include pmpost patches and build it
* adapt/unfuzzify patches for current sources
* disable mtx building, we have prepmx package in Debian

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  $Header: /home/cvsroot/dvipdfmx/src/pdfdoc.c,v 1.76 2011/03/06 03:14:14 chofchof Exp $
2
 
 
3
 
    This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
4
 
 
5
 
    Copyright (C) 2008 by Jin-Hwan Cho, Matthias Franz, and Shunsaku Hirata,
6
 
    the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
7
 
    
8
 
    Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
9
 
 
10
 
    This program is free software; you can redistribute it and/or modify
11
 
    it under the terms of the GNU General Public License as published by
12
 
    the Free Software Foundation; either version 2 of the License, or
13
 
    (at your option) any later version.
14
 
    
15
 
    This program is distributed in the hope that it will be useful,
16
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 
    GNU General Public License for more details.
19
 
    
20
 
    You should have received a copy of the GNU General Public License
21
 
    along with this program; if not, write to the Free Software
22
 
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23
 
*/
24
 
 
25
 
/*
26
 
 * TODO: Many things...
27
 
 *  {begin,end}_{bead,article}, box stack, name tree (not limited to dests)...
28
 
 */
29
 
#if HAVE_CONFIG_H
30
 
#include "config.h"
31
 
#endif
32
 
 
33
 
#include <time.h>
34
 
 
35
 
#include "system.h"
36
 
#include "mem.h"
37
 
#include "error.h"
38
 
#include "mfileio.h"
39
 
 
40
 
#include "numbers.h"
41
 
 
42
 
#include "pdfobj.h"
43
 
#include "pdfparse.h"
44
 
#include "pdfnames.h"
45
 
 
46
 
#include "pdfencrypt.h"
47
 
 
48
 
#include "pdfdev.h"
49
 
#include "pdfdraw.h"
50
 
#include "pdfcolor.h"
51
 
 
52
 
#include "pdfresource.h"
53
 
#include "pdffont.h"
54
 
#include "pdfximage.h"
55
 
 
56
 
#include "pdflimits.h"
57
 
 
58
 
#if HAVE_LIBPNG
59
 
#include "pngimage.h"
60
 
#endif
61
 
#include "jpegimage.h"
62
 
 
63
 
#include "pdfdoc.h"
64
 
 
65
 
#define PDFDOC_PAGES_ALLOC_SIZE   128u
66
 
#define PDFDOC_ARTICLE_ALLOC_SIZE 16
67
 
#define PDFDOC_BEAD_ALLOC_SIZE    16
68
 
 
69
 
static int verbose = 0;
70
 
 
71
 
static char  manual_thumb_enabled  = 0;
72
 
static char *thumb_basename = NULL;
73
 
 
74
 
void
75
 
pdf_doc_enable_manual_thumbnails (void)
76
 
{
77
 
#if HAVE_LIBPNG
78
 
  manual_thumb_enabled = 1;
79
 
#else
80
 
  WARN("Manual thumbnail is not supported without the libpng library.");
81
 
#endif
82
 
}
83
 
 
84
 
static pdf_obj *
85
 
read_thumbnail (const char *thumb_filename) 
86
 
{
87
 
  pdf_obj *image_ref;
88
 
  int      xobj_id;
89
 
  FILE    *fp;
90
 
 
91
 
  fp = MFOPEN(thumb_filename, FOPEN_RBIN_MODE);
92
 
  if (!fp) {
93
 
    WARN("Could not open thumbnail file \"%s\"", thumb_filename);
94
 
    return NULL;
95
 
  }
96
 
  if (!check_for_png(fp) && !check_for_jpeg(fp)) {
97
 
    WARN("Thumbnail \"%s\" not a png/jpeg file!", thumb_filename);
98
 
    MFCLOSE(fp);
99
 
    return NULL;
100
 
  }
101
 
  MFCLOSE(fp);
102
 
 
103
 
  xobj_id = pdf_ximage_findresource(thumb_filename, 0, NULL);
104
 
  if (xobj_id < 0) {
105
 
    WARN("Could not read thumbnail file \"%s\".", thumb_filename);
106
 
    image_ref = NULL;
107
 
  } else {
108
 
    image_ref = pdf_ximage_get_reference(xobj_id);
109
 
  }
110
 
 
111
 
  return image_ref;
112
 
}
113
 
 
114
 
void
115
 
pdf_doc_set_verbose (void)
116
 
{
117
 
  verbose++;
118
 
  pdf_font_set_verbose();
119
 
  pdf_color_set_verbose();
120
 
  pdf_ximage_set_verbose();
121
 
}
122
 
 
123
 
typedef struct pdf_form
124
 
{
125
 
  char       *ident;
126
 
 
127
 
  pdf_tmatrix matrix;
128
 
  pdf_rect    cropbox;
129
 
 
130
 
  pdf_obj    *resources;
131
 
  pdf_obj    *contents;
132
 
} pdf_form;
133
 
 
134
 
struct form_list_node
135
 
{
136
 
  int      q_depth;
137
 
  pdf_form form;
138
 
 
139
 
  struct form_list_node *prev;
140
 
};
141
 
 
142
 
#define USE_MY_MEDIABOX (1 << 0)
143
 
typedef struct pdf_page
144
 
{
145
 
  pdf_obj  *page_obj;
146
 
  pdf_obj  *page_ref;
147
 
 
148
 
  int       flags;
149
 
 
150
 
  double    ref_x, ref_y;
151
 
  pdf_rect  cropbox;
152
 
 
153
 
  pdf_obj  *resources;
154
 
 
155
 
  /* Contents */
156
 
  pdf_obj  *background;
157
 
  pdf_obj  *contents;
158
 
 
159
 
  /* global bop, background, contents, global eop */
160
 
  pdf_obj  *content_refs[4];
161
 
 
162
 
  pdf_obj  *annots;
163
 
  pdf_obj  *beads;
164
 
} pdf_page;
165
 
 
166
 
typedef struct pdf_olitem
167
 
{
168
 
  pdf_obj *dict;
169
 
 
170
 
  int      is_open;
171
 
 
172
 
  struct pdf_olitem *first;
173
 
  struct pdf_olitem *parent;
174
 
 
175
 
  struct pdf_olitem *next;
176
 
} pdf_olitem;
177
 
 
178
 
typedef struct pdf_bead
179
 
{
180
 
  char    *id;
181
 
  long     page_no;
182
 
  pdf_rect rect;
183
 
} pdf_bead;
184
 
 
185
 
typedef struct pdf_article
186
 
{
187
 
  char     *id;
188
 
  pdf_obj  *info;
189
 
  long      num_beads;
190
 
  long      max_beads;
191
 
  pdf_bead *beads;
192
 
} pdf_article;
193
 
 
194
 
struct name_dict
195
 
{
196
 
  const char  *category;
197
 
  struct ht_table *data;
198
 
};
199
 
 
200
 
 
201
 
typedef struct pdf_doc
202
 
{
203
 
  struct {
204
 
    pdf_obj *dict;
205
 
 
206
 
    pdf_obj *viewerpref;
207
 
    pdf_obj *pagelabels;
208
 
    pdf_obj *pages;
209
 
    pdf_obj *names;
210
 
    pdf_obj *threads;
211
 
  } root;
212
 
 
213
 
  pdf_obj *info;
214
 
 
215
 
  struct {
216
 
    pdf_rect mediabox;
217
 
    pdf_obj *bop, *eop;
218
 
 
219
 
    long      num_entries; /* This is not actually total number of pages. */
220
 
    long      max_entries;
221
 
    pdf_page *entries;
222
 
  } pages;
223
 
 
224
 
  struct {
225
 
    pdf_olitem *first;
226
 
    pdf_olitem *current;
227
 
    int         current_depth;
228
 
  } outlines;
229
 
 
230
 
  struct {
231
 
    long         num_entries;
232
 
    long         max_entries;
233
 
    pdf_article *entries;
234
 
  } articles;
235
 
 
236
 
  struct name_dict *names;
237
 
 
238
 
  int check_gotos;
239
 
  struct ht_table gotos;
240
 
 
241
 
  struct {
242
 
    int    outline_open_depth;
243
 
    double annot_grow;
244
 
  } opt;
245
 
 
246
 
  struct form_list_node *pending_forms;
247
 
 
248
 
} pdf_doc;
249
 
static pdf_doc pdoc;
250
 
 
251
 
static void
252
 
pdf_doc_init_catalog (pdf_doc *p)
253
 
{
254
 
  p->root.viewerpref = NULL;
255
 
  p->root.pagelabels = NULL;
256
 
  p->root.pages      = NULL;
257
 
  p->root.names      = NULL;
258
 
  p->root.threads    = NULL;
259
 
  
260
 
  p->root.dict = pdf_new_dict();
261
 
  pdf_set_root(p->root.dict);
262
 
 
263
 
  return;
264
 
}
265
 
 
266
 
static void
267
 
pdf_doc_close_catalog (pdf_doc *p)
268
 
{
269
 
  pdf_obj *tmp;
270
 
 
271
 
  if (p->root.viewerpref) {
272
 
    tmp = pdf_lookup_dict(p->root.dict, "ViewerPreferences");
273
 
    if (!tmp) {
274
 
      pdf_add_dict(p->root.dict,
275
 
                   pdf_new_name("ViewerPreferences"),
276
 
                   pdf_ref_obj (p->root.viewerpref));
277
 
    } else if (PDF_OBJ_DICTTYPE(tmp)) {
278
 
      pdf_merge_dict(p->root.viewerpref, tmp);
279
 
      pdf_add_dict(p->root.dict,
280
 
                   pdf_new_name("ViewerPreferences"),
281
 
                   pdf_ref_obj (p->root.viewerpref));
282
 
    } else { /* Maybe reference */
283
 
      /* What should I do? */
284
 
      WARN("Could not modify ViewerPreferences.");
285
 
    }
286
 
    pdf_release_obj(p->root.viewerpref);
287
 
    p->root.viewerpref = NULL;
288
 
  }
289
 
 
290
 
  if (p->root.pagelabels) {
291
 
    tmp = pdf_lookup_dict(p->root.dict, "PageLabels");
292
 
    if (!tmp) {
293
 
      tmp = pdf_new_dict();
294
 
      pdf_add_dict(tmp, pdf_new_name("Nums"),  pdf_link_obj(p->root.pagelabels));
295
 
      pdf_add_dict(p->root.dict,
296
 
                   pdf_new_name("PageLabels"), pdf_ref_obj(tmp));
297
 
      pdf_release_obj(tmp);
298
 
    } else { /* Maybe reference */
299
 
      /* What should I do? */
300
 
      WARN("Could not modify PageLabels.");
301
 
    }
302
 
    pdf_release_obj(p->root.pagelabels);
303
 
    p->root.pagelabels = NULL;
304
 
  }
305
 
 
306
 
  pdf_add_dict(p->root.dict,
307
 
               pdf_new_name("Type"), pdf_new_name("Catalog"));
308
 
  pdf_release_obj(p->root.dict);
309
 
  p->root.dict = NULL;
310
 
 
311
 
  return;
312
 
}
313
 
 
314
 
/*
315
 
 * Pages are starting at 1.
316
 
 * The page count does not increase until the page is finished.
317
 
 */
318
 
#define LASTPAGE(p)  (&(p->pages.entries[p->pages.num_entries]))
319
 
#define FIRSTPAGE(p) (&(p->pages.entries[0]))
320
 
#define PAGECOUNT(p) (p->pages.num_entries)
321
 
#define MAXPAGES(p)  (p->pages.max_entries)
322
 
 
323
 
static void
324
 
doc_resize_page_entries (pdf_doc *p, long size)
325
 
{
326
 
  if (size > MAXPAGES(p)) {
327
 
    long i;
328
 
 
329
 
    p->pages.entries = RENEW(p->pages.entries, size, struct pdf_page);
330
 
    for (i = p->pages.max_entries; i < size; i++) {
331
 
      p->pages.entries[i].page_obj   = NULL;
332
 
      p->pages.entries[i].page_ref   = NULL;
333
 
      p->pages.entries[i].flags      = 0;
334
 
      p->pages.entries[i].resources  = NULL;
335
 
      p->pages.entries[i].background = NULL;
336
 
      p->pages.entries[i].contents   = NULL;
337
 
      p->pages.entries[i].content_refs[0] = NULL; /* global bop */
338
 
      p->pages.entries[i].content_refs[1] = NULL; /* background */
339
 
      p->pages.entries[i].content_refs[2] = NULL; /* page body  */
340
 
      p->pages.entries[i].content_refs[3] = NULL; /* global eop */
341
 
      p->pages.entries[i].annots    = NULL;
342
 
      p->pages.entries[i].beads     = NULL;
343
 
    }
344
 
    p->pages.max_entries = size;
345
 
  }
346
 
 
347
 
  return;
348
 
}
349
 
 
350
 
static pdf_page *
351
 
doc_get_page_entry (pdf_doc *p, unsigned long page_no)
352
 
{
353
 
  pdf_page *page;
354
 
 
355
 
  if (page_no > 65535ul) {
356
 
    ERROR("Page number %ul too large!", page_no);
357
 
  } else if (page_no == 0) {
358
 
    ERROR("Invalid Page number %ul.", page_no);
359
 
  }
360
 
 
361
 
  if (page_no > MAXPAGES(p)) {
362
 
    doc_resize_page_entries(p, page_no + PDFDOC_PAGES_ALLOC_SIZE);
363
 
  }
364
 
 
365
 
  page = &(p->pages.entries[page_no - 1]);
366
 
 
367
 
  return page;
368
 
}
369
 
 
370
 
static void pdf_doc_init_page_tree  (pdf_doc *p, double media_width, double media_height);
371
 
static void pdf_doc_close_page_tree (pdf_doc *p);
372
 
 
373
 
static void pdf_doc_init_names  (pdf_doc *p, int check_gotos);
374
 
static void pdf_doc_close_names (pdf_doc *p);
375
 
 
376
 
static void pdf_doc_add_goto (pdf_obj *annot_dict);
377
 
 
378
 
static void pdf_doc_init_docinfo  (pdf_doc *p);
379
 
static void pdf_doc_close_docinfo (pdf_doc *p);
380
 
 
381
 
static void pdf_doc_init_articles    (pdf_doc *p);
382
 
static void pdf_doc_close_articles   (pdf_doc *p);
383
 
static void pdf_doc_init_bookmarks   (pdf_doc *p, int bm_open_depth);
384
 
static void pdf_doc_close_bookmarks  (pdf_doc *p);
385
 
 
386
 
void
387
 
pdf_doc_set_bop_content (const char *content, unsigned length)
388
 
{
389
 
  pdf_doc *p = &pdoc;
390
 
 
391
 
  ASSERT(p);
392
 
 
393
 
  if (p->pages.bop) {
394
 
    pdf_release_obj(p->pages.bop);
395
 
    p->pages.bop = NULL;
396
 
  }
397
 
 
398
 
  if (length > 0) {
399
 
    p->pages.bop = pdf_new_stream(STREAM_COMPRESS);
400
 
    pdf_add_stream(p->pages.bop, content, length);
401
 
  } else {
402
 
    p->pages.bop = NULL;
403
 
  }
404
 
 
405
 
  return;
406
 
}
407
 
 
408
 
void
409
 
pdf_doc_set_eop_content (const char *content, unsigned length)
410
 
{
411
 
  pdf_doc *p = &pdoc;
412
 
 
413
 
  if (p->pages.eop) {
414
 
    pdf_release_obj(p->pages.eop);
415
 
    p->pages.eop = NULL;
416
 
  }
417
 
 
418
 
  if (length > 0) {
419
 
    p->pages.eop = pdf_new_stream(STREAM_COMPRESS);
420
 
    pdf_add_stream(p->pages.eop, content, length);
421
 
  } else {
422
 
    p->pages.eop = NULL;
423
 
  }
424
 
 
425
 
  return;
426
 
}
427
 
 
428
 
#ifndef HAVE_TM_GMTOFF
429
 
#ifndef HAVE_TIMEZONE
430
 
 
431
 
/* auxiliary function to compute timezone offset on
432
 
   systems that do not support the tm_gmtoff in struct tm,
433
 
   or have a timezone variable.  Such as i386-solaris.  */
434
 
 
435
 
static long
436
 
compute_timezone_offset()
437
 
{
438
 
  const time_t now = time(NULL);
439
 
  struct tm tm;
440
 
  struct tm local;
441
 
  time_t gmtoff;
442
 
 
443
 
  localtime_r(&now, &local);
444
 
  gmtime_r(&now, &tm);
445
 
  return (mktime(&local) - mktime(&tm));
446
 
}
447
 
 
448
 
#endif /* HAVE_TIMEZONE */
449
 
#endif /* HAVE_TM_GMTOFF */
450
 
 
451
 
/*
452
 
 * Docinfo
453
 
 */
454
 
static long
455
 
asn_date (char *date_string)
456
 
{
457
 
  long        tz_offset;
458
 
  time_t      current_time;
459
 
  struct tm  *bd_time;
460
 
 
461
 
  time(&current_time);
462
 
  bd_time = localtime(&current_time);
463
 
 
464
 
#ifdef HAVE_TM_GMTOFF
465
 
  tz_offset = bd_time->tm_gmtoff;
466
 
#else
467
 
#  ifdef HAVE_TIMEZONE
468
 
  tz_offset = -timezone;
469
 
#  else
470
 
  tz_offset = compute_timezone_offset();
471
 
#  endif /* HAVE_TIMEZONE */
472
 
#endif /* HAVE_TM_GMTOFF */
473
 
 
474
 
  sprintf(date_string, "D:%04d%02d%02d%02d%02d%02d%c%02ld'%02ld'",
475
 
          bd_time->tm_year + 1900, bd_time->tm_mon + 1, bd_time->tm_mday,
476
 
          bd_time->tm_hour, bd_time->tm_min, bd_time->tm_sec,
477
 
          (tz_offset > 0) ? '+' : '-', labs(tz_offset) / 3600,
478
 
                                      (labs(tz_offset) / 60) % 60);
479
 
 
480
 
  return strlen(date_string);
481
 
}
482
 
 
483
 
static void
484
 
pdf_doc_init_docinfo (pdf_doc *p)
485
 
{
486
 
  p->info = pdf_new_dict();
487
 
  pdf_set_info(p->info);
488
 
 
489
 
  return;
490
 
}
491
 
 
492
 
static void
493
 
pdf_doc_close_docinfo (pdf_doc *p)
494
 
{
495
 
  pdf_obj *docinfo = p->info;
496
 
 
497
 
  /*
498
 
   * Excerpt from PDF Reference 4th ed., sec. 10.2.1.
499
 
   *
500
 
   * Any entry whose value is not known should be omitted from the dictionary,
501
 
   * rather than included with an empty string as its value.
502
 
   *
503
 
   * ....
504
 
   *
505
 
   * Note: Although viewer applications can store custom metadata in the document
506
 
   * information dictionary, it is inappropriate to store private content or
507
 
   * structural information there; such information should be stored in the
508
 
   * document catalog instead (see Section 3.6.1,  Document Catalog ).
509
 
   */
510
 
  const char *keys[] = {
511
 
    "Title", "Author", "Subject", "Keywords", "Creator", "Producer",
512
 
    "CreationDate", "ModDate", /* Date */
513
 
    NULL
514
 
  };
515
 
  pdf_obj *value;
516
 
  char    *banner;
517
 
  int      i;
518
 
 
519
 
  for (i = 0; keys[i] != NULL; i++) {
520
 
    value = pdf_lookup_dict(docinfo, keys[i]);
521
 
    if (value) {
522
 
      if (!PDF_OBJ_STRINGTYPE(value)) {
523
 
        WARN("\"%s\" in DocInfo dictionary not string type.", keys[i]);
524
 
        pdf_remove_dict(docinfo, keys[i]);
525
 
        WARN("\"%s\" removed from DocInfo.", keys[i]);
526
 
      } else if (pdf_string_length(value) == 0) {
527
 
        /* The hyperref package often uses emtpy strings. */
528
 
        pdf_remove_dict(docinfo, keys[i]);
529
 
      }
530
 
    }
531
 
  }
532
 
 
533
 
  banner = NEW(strlen(PACKAGE)+strlen(VERSION)+4, char);
534
 
  sprintf(banner, "%s (%s)", PACKAGE, VERSION);
535
 
  pdf_add_dict(docinfo,
536
 
               pdf_new_name("Producer"),
537
 
               pdf_new_string(banner, strlen(banner)));
538
 
  RELEASE(banner);
539
 
  
540
 
  if (!pdf_lookup_dict(docinfo, "CreationDate")) {
541
 
    char now[32];
542
 
 
543
 
    asn_date(now);
544
 
    pdf_add_dict(docinfo, 
545
 
                 pdf_new_name ("CreationDate"),
546
 
                 pdf_new_string(now, strlen(now)));
547
 
  }
548
 
 
549
 
  pdf_release_obj(docinfo);
550
 
  p->info = NULL;
551
 
 
552
 
  return;
553
 
}
554
 
 
555
 
static pdf_obj *
556
 
pdf_doc_get_page_resources (pdf_doc *p, const char *category)
557
 
{
558
 
  pdf_obj  *resources;
559
 
  pdf_page *currentpage;
560
 
  pdf_obj  *res_dict;
561
 
 
562
 
  if (!p || !category) {
563
 
    return NULL;
564
 
  }
565
 
 
566
 
  if (p->pending_forms) {
567
 
    if (p->pending_forms->form.resources) {
568
 
      res_dict = p->pending_forms->form.resources;
569
 
    } else {
570
 
      res_dict = p->pending_forms->form.resources = pdf_new_dict();
571
 
    }
572
 
  } else {
573
 
    currentpage = LASTPAGE(p);
574
 
    if (currentpage->resources) {
575
 
      res_dict = currentpage->resources;
576
 
    } else {
577
 
      res_dict = currentpage->resources = pdf_new_dict();
578
 
    }
579
 
  }
580
 
  resources = pdf_lookup_dict(res_dict, category);
581
 
  if (!resources) {
582
 
    resources = pdf_new_dict();
583
 
    pdf_add_dict(res_dict, pdf_new_name(category), resources);
584
 
  }
585
 
 
586
 
  return resources;
587
 
}
588
 
 
589
 
void
590
 
pdf_doc_add_page_resource (const char *category,
591
 
                           const char *resource_name, pdf_obj *resource_ref)
592
 
{
593
 
  pdf_doc *p = &pdoc;
594
 
  pdf_obj *resources;
595
 
  pdf_obj *duplicate;
596
 
 
597
 
  if (!PDF_OBJ_INDIRECTTYPE(resource_ref)) {
598
 
    WARN("Passed non indirect reference...");
599
 
    resource_ref = pdf_ref_obj(resource_ref); /* leak */
600
 
  }
601
 
  resources = pdf_doc_get_page_resources(p, category);
602
 
  duplicate = pdf_lookup_dict(resources, resource_name);
603
 
  if (duplicate && pdf_compare_reference(duplicate, resource_ref)) {
604
 
    WARN("Conflicting page resource found (page: %ld, category: %s, name: %s).",
605
 
         pdf_doc_current_page_number(), category, resource_name);
606
 
    WARN("Ignoring...");
607
 
    pdf_release_obj(resource_ref);
608
 
  } else {
609
 
    pdf_add_dict(resources, pdf_new_name(resource_name), resource_ref);
610
 
  }
611
 
 
612
 
  return;
613
 
}
614
 
 
615
 
static void
616
 
doc_flush_page (pdf_doc *p, pdf_page *page, pdf_obj *parent_ref)
617
 
{
618
 
  pdf_obj *contents_array;
619
 
  int      count;
620
 
 
621
 
  pdf_add_dict(page->page_obj,
622
 
               pdf_new_name("Type"), pdf_new_name("Page"));
623
 
  pdf_add_dict(page->page_obj,
624
 
               pdf_new_name("Parent"), parent_ref);
625
 
 
626
 
  /*
627
 
   * Clipping area specified by CropBox is affected by MediaBox which
628
 
   * might be inherit from parent node. If MediaBox of the root node
629
 
   * does not have enough size to cover all page's imaging area, using
630
 
   * CropBox here gives incorrect result.
631
 
   */
632
 
  if (page->flags & USE_MY_MEDIABOX) {
633
 
    pdf_obj *mediabox;
634
 
 
635
 
    mediabox = pdf_new_array();
636
 
    pdf_add_array(mediabox,
637
 
                  pdf_new_number(ROUND(page->cropbox.llx, 0.01)));
638
 
    pdf_add_array(mediabox,
639
 
                  pdf_new_number(ROUND(page->cropbox.lly, 0.01)));
640
 
    pdf_add_array(mediabox,
641
 
                  pdf_new_number(ROUND(page->cropbox.urx, 0.01)));
642
 
    pdf_add_array(mediabox,
643
 
                  pdf_new_number(ROUND(page->cropbox.ury, 0.01)));
644
 
    pdf_add_dict(page->page_obj, pdf_new_name("MediaBox"),  mediabox);
645
 
  }
646
 
 
647
 
  count = 0;
648
 
  contents_array = pdf_new_array();
649
 
  if (page->content_refs[0]) { /* global bop */
650
 
    pdf_add_array(contents_array, page->content_refs[0]);
651
 
    count++;
652
 
  } else if (p->pages.bop &&
653
 
             pdf_stream_length(p->pages.bop) > 0) {
654
 
    pdf_add_array(contents_array, pdf_ref_obj(p->pages.bop));
655
 
    count++;
656
 
  }
657
 
  if (page->content_refs[1]) { /* background */
658
 
    pdf_add_array(contents_array, page->content_refs[1]);
659
 
    count++;
660
 
  }
661
 
  if (page->content_refs[2]) { /* page body */
662
 
    pdf_add_array(contents_array, page->content_refs[2]);
663
 
    count++;
664
 
  }
665
 
  if (page->content_refs[3]) { /* global eop */
666
 
    pdf_add_array(contents_array, page->content_refs[3]);
667
 
    count++;
668
 
  } else if (p->pages.eop &&
669
 
             pdf_stream_length(p->pages.eop) > 0) {
670
 
    pdf_add_array(contents_array, pdf_ref_obj(p->pages.eop));
671
 
    count++;
672
 
  }
673
 
 
674
 
  if (count == 0) {
675
 
    WARN("Page with empty content found!!!");
676
 
  }
677
 
  page->content_refs[0] = NULL;
678
 
  page->content_refs[1] = NULL;
679
 
  page->content_refs[2] = NULL;
680
 
  page->content_refs[3] = NULL;
681
 
 
682
 
  pdf_add_dict(page->page_obj,
683
 
               pdf_new_name("Contents"), contents_array);
684
 
 
685
 
 
686
 
  if (page->annots) {
687
 
    pdf_add_dict(page->page_obj,
688
 
                 pdf_new_name("Annots"), pdf_ref_obj(page->annots));
689
 
    pdf_release_obj(page->annots);
690
 
  }
691
 
  if (page->beads) {
692
 
    pdf_add_dict(page->page_obj,
693
 
                 pdf_new_name("B"), pdf_ref_obj(page->beads));
694
 
    pdf_release_obj(page->beads);
695
 
  }
696
 
  pdf_release_obj(page->page_obj);
697
 
  pdf_release_obj(page->page_ref);
698
 
 
699
 
  page->page_obj = NULL;
700
 
  page->page_ref = NULL;
701
 
  page->annots   = NULL;
702
 
  page->beads    = NULL;
703
 
 
704
 
  return;
705
 
}
706
 
 
707
 
/* B-tree? */
708
 
#define PAGE_CLUSTER 4
709
 
static pdf_obj *
710
 
build_page_tree (pdf_doc  *p,
711
 
                 pdf_page *firstpage, long num_pages,
712
 
                 pdf_obj  *parent_ref)
713
 
{
714
 
  pdf_obj *self, *self_ref, *kids;
715
 
  long     i;
716
 
 
717
 
  self = pdf_new_dict();
718
 
  /*
719
 
   * This is a slight kludge which allow the subtree dictionary
720
 
   * generated by this routine to be merged with the real
721
 
   * page_tree dictionary, while keeping the indirect object
722
 
   * references right.
723
 
   */
724
 
  self_ref = parent_ref ? pdf_ref_obj(self) : pdf_ref_obj(p->root.pages);
725
 
 
726
 
  pdf_add_dict(self, pdf_new_name("Type"),  pdf_new_name("Pages"));
727
 
  pdf_add_dict(self, pdf_new_name("Count"), pdf_new_number((double) num_pages));
728
 
 
729
 
  if (parent_ref != NULL)
730
 
    pdf_add_dict(self, pdf_new_name("Parent"), parent_ref);
731
 
 
732
 
  kids = pdf_new_array();
733
 
  if (num_pages > 0 && num_pages <= PAGE_CLUSTER) {
734
 
    for (i = 0; i < num_pages; i++) {
735
 
      pdf_page *page;
736
 
 
737
 
      page = firstpage + i;
738
 
      if (!page->page_ref)
739
 
        page->page_ref = pdf_ref_obj(page->page_obj);
740
 
      pdf_add_array (kids, pdf_link_obj(page->page_ref));
741
 
      doc_flush_page(p, page, pdf_link_obj(self_ref));
742
 
    }
743
 
  } else if (num_pages > 0) {
744
 
    for (i = 0; i < PAGE_CLUSTER; i++) {
745
 
      long start, end;
746
 
 
747
 
      start = (i*num_pages)/PAGE_CLUSTER;
748
 
      end   = ((i+1)*num_pages)/PAGE_CLUSTER;
749
 
      if (end - start > 1) {
750
 
        pdf_obj *subtree;
751
 
 
752
 
        subtree = build_page_tree(p, firstpage + start, end - start,
753
 
                                  pdf_link_obj(self_ref));
754
 
        pdf_add_array(kids, pdf_ref_obj(subtree));
755
 
        pdf_release_obj(subtree);
756
 
      } else {
757
 
        pdf_page *page;
758
 
 
759
 
        page = firstpage + start;
760
 
        if (!page->page_ref)
761
 
          page->page_ref = pdf_ref_obj(page->page_obj);
762
 
        pdf_add_array (kids, pdf_link_obj(page->page_ref));
763
 
        doc_flush_page(p, page, pdf_link_obj(self_ref));
764
 
      }
765
 
    }
766
 
  }
767
 
  pdf_add_dict(self, pdf_new_name("Kids"), kids);
768
 
  pdf_release_obj(self_ref);
769
 
 
770
 
  return self;
771
 
}
772
 
 
773
 
static void
774
 
pdf_doc_init_page_tree (pdf_doc *p, double media_width, double media_height)
775
 
{
776
 
  /*
777
 
   * Create empty page tree.
778
 
   * The docroot.pages is kept open until the document is closed.
779
 
   * This allows the user to write to pages if he so choses.
780
 
   */
781
 
  p->root.pages = pdf_new_dict();
782
 
 
783
 
  p->pages.num_entries = 0;
784
 
  p->pages.max_entries = 0;
785
 
  p->pages.entries     = NULL;
786
 
 
787
 
  p->pages.bop = NULL;
788
 
  p->pages.eop = NULL;
789
 
 
790
 
  p->pages.mediabox.llx = 0.0;
791
 
  p->pages.mediabox.lly = 0.0;
792
 
  p->pages.mediabox.urx = media_width;
793
 
  p->pages.mediabox.ury = media_height;
794
 
 
795
 
  return;
796
 
}
797
 
 
798
 
static void
799
 
pdf_doc_close_page_tree (pdf_doc *p)
800
 
{
801
 
  pdf_obj *page_tree_root;
802
 
  pdf_obj *mediabox;
803
 
  long     page_no;
804
 
 
805
 
  /*
806
 
   * Do consistency check on forward references to pages.
807
 
   */
808
 
  for (page_no = PAGECOUNT(p) + 1; page_no <= MAXPAGES(p); page_no++) {
809
 
    pdf_page  *page;
810
 
 
811
 
    page = doc_get_page_entry(p, page_no);
812
 
    if (page->page_obj) {
813
 
      WARN("Nonexistent page #%ld refered.", page_no);
814
 
      pdf_release_obj(page->page_ref);
815
 
      page->page_ref = NULL;
816
 
    }
817
 
    if (page->page_obj) {
818
 
      WARN("Entry for a nonexistent page #%ld created.", page_no);
819
 
      pdf_release_obj(page->page_obj);
820
 
      page->page_obj = NULL;
821
 
    }
822
 
    if (page->annots) {
823
 
      WARN("Annotation attached to a nonexistent page #%ld.", page_no);
824
 
      pdf_release_obj(page->annots);
825
 
      page->annots = NULL;
826
 
    }
827
 
    if (page->beads) {
828
 
      WARN("Article beads attached to a nonexistent page #%ld.", page_no);
829
 
      pdf_release_obj(page->beads);
830
 
      page->beads = NULL;
831
 
    }
832
 
    if (page->resources) {
833
 
      pdf_release_obj(page->resources);
834
 
      page->resources = NULL;
835
 
    }
836
 
  }
837
 
 
838
 
  /*
839
 
   * Connect page tree to root node.
840
 
   */
841
 
  page_tree_root = build_page_tree(p, FIRSTPAGE(p), PAGECOUNT(p), NULL);
842
 
  pdf_merge_dict (p->root.pages, page_tree_root);
843
 
  pdf_release_obj(page_tree_root);
844
 
 
845
 
  /* They must be after build_page_tree() */
846
 
  if (p->pages.bop) {
847
 
    pdf_add_stream (p->pages.bop, "\n", 1);
848
 
    pdf_release_obj(p->pages.bop);
849
 
    p->pages.bop = NULL;
850
 
  }
851
 
  if (p->pages.eop) {
852
 
    pdf_add_stream (p->pages.eop, "\n", 1);
853
 
    pdf_release_obj(p->pages.eop);
854
 
    p->pages.eop = NULL;
855
 
  }
856
 
 
857
 
  /* Create media box at root node and let the other pages inherit it. */
858
 
  mediabox = pdf_new_array();
859
 
  pdf_add_array(mediabox, pdf_new_number(ROUND(p->pages.mediabox.llx, 0.01)));
860
 
  pdf_add_array(mediabox, pdf_new_number(ROUND(p->pages.mediabox.lly, 0.01)));
861
 
  pdf_add_array(mediabox, pdf_new_number(ROUND(p->pages.mediabox.urx, 0.01)));
862
 
  pdf_add_array(mediabox, pdf_new_number(ROUND(p->pages.mediabox.ury, 0.01)));
863
 
  pdf_add_dict(p->root.pages, pdf_new_name("MediaBox"), mediabox);
864
 
 
865
 
  pdf_add_dict(p->root.dict,
866
 
               pdf_new_name("Pages"),
867
 
               pdf_ref_obj (p->root.pages));
868
 
  pdf_release_obj(p->root.pages);
869
 
  p->root.pages  = NULL;
870
 
 
871
 
  RELEASE(p->pages.entries);
872
 
  p->pages.entries     = NULL;
873
 
  p->pages.num_entries = 0;
874
 
  p->pages.max_entries = 0;
875
 
 
876
 
  return;
877
 
}
878
 
 
879
 
/*
880
 
 * From PDFReference15_v6.pdf (p.119 and p.834)
881
 
 *
882
 
 * MediaBox rectangle (Required; inheritable)
883
 
 *
884
 
 * The media box defines the boundaries of the physical medium on which the
885
 
 * page is to be printed. It may include any extended area surrounding the
886
 
 * finished page for bleed, printing marks, or other such purposes. It may
887
 
 * also include areas close to the edges of the medium that cannot be marked
888
 
 * because of physical limitations of the output device. Content falling
889
 
 * outside this boundary can safely be discarded without affecting the
890
 
 * meaning of the PDF file.
891
 
 *
892
 
 * CropBox rectangle (Optional; inheritable)
893
 
 *
894
 
 * The crop box defines the region to which the contents of the page are to be
895
 
 * clipped (cropped) when displayed or printed. Unlike the other boxes, the
896
 
 * crop box has no defined meaning in terms of physical page geometry or
897
 
 * intended use; it merely imposes clipping on the page contents. However,
898
 
 * in the absence of additional information (such as imposition instructions
899
 
 * specified in a JDF or PJTF job ticket), the crop box will determine how
900
 
 * the page's contents are to be positioned on the output medium. The default
901
 
 * value is the page's media box. 
902
 
 *
903
 
 * BleedBox rectangle (Optional; PDF 1.3)
904
 
 *
905
 
 * The bleed box (PDF 1.3) defines the region to which the contents of the
906
 
 * page should be clipped when output in a production environment. This may
907
 
 * include any extra "bleed area" needed to accommodate the physical
908
 
 * limitations of cutting, folding, and trimming equipment. The actual printed
909
 
 * page may include printing marks that fall outside the bleed box.
910
 
 * The default value is the page's crop box. 
911
 
 *
912
 
 * TrimBox rectangle (Optional; PDF 1.3)
913
 
 *
914
 
 * The trim box (PDF 1.3) defines the intended dimensions of the finished page
915
 
 * after trimming. It may be smaller than the media box, to allow for
916
 
 * production-related content such as printing instructions, cut marks, or
917
 
 * color bars. The default value is the page's crop box. 
918
 
 *
919
 
 * ArtBox rectangle (Optional; PDF 1.3)
920
 
 *
921
 
 * The art box (PDF 1.3) defines the extent of the page's meaningful content
922
 
 * (including potential white space) as intended by the page's creator.
923
 
 * The default value is the page's crop box.
924
 
 *
925
 
 * Rotate integer (Optional; inheritable)
926
 
 *
927
 
 * The number of degrees by which the page should be rotated clockwise when
928
 
 * displayed or printed. The value must be a multiple of 90. Default value: 0.
929
 
 */
930
 
 
931
 
pdf_obj *
932
 
pdf_doc_get_page (pdf_file *pf, long page_no, long *count_p,
933
 
                  pdf_rect *bbox, pdf_obj **resources_p) {
934
 
  pdf_obj *page_tree = NULL;
935
 
  pdf_obj *resources = NULL, *box = NULL, *rotate = NULL;
936
 
  pdf_obj *catalog;
937
 
 
938
 
  catalog = pdf_file_get_catalog(pf);
939
 
 
940
 
  page_tree = pdf_deref_obj(pdf_lookup_dict(catalog, "Pages"));
941
 
 
942
 
  if (!PDF_OBJ_DICTTYPE(page_tree))
943
 
    goto error;
944
 
 
945
 
  {
946
 
    long count;
947
 
    pdf_obj *tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Count"));
948
 
    if (!PDF_OBJ_NUMBERTYPE(tmp)) {
949
 
      if (tmp)
950
 
        pdf_release_obj(tmp);
951
 
      goto error;
952
 
    }
953
 
    count = pdf_number_value(tmp);
954
 
    pdf_release_obj(tmp);
955
 
    if (count_p)
956
 
      *count_p = count;
957
 
    if (page_no <= 0 || page_no > count) {
958
 
        WARN("Page %ld does not exist.", page_no);
959
 
        goto error_silent;
960
 
      }
961
 
  }
962
 
 
963
 
  /*
964
 
   * Seek correct page. Get MediaBox, CropBox and Resources.
965
 
   * (Note that these entries can be inherited.)
966
 
   */
967
 
  {
968
 
    pdf_obj *media_box = NULL, *crop_box = NULL, *kids, *tmp;
969
 
    int depth = PDF_OBJ_MAX_DEPTH;
970
 
    long page_idx = page_no-1, kids_length = 1, i = 0;
971
 
 
972
 
    while (--depth && i != kids_length) {
973
 
      if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "MediaBox")))) {
974
 
        if (media_box)
975
 
          pdf_release_obj(media_box);
976
 
        media_box = tmp;
977
 
      }
978
 
 
979
 
      if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "CropBox")))) {
980
 
        if (crop_box)
981
 
          pdf_release_obj(crop_box);
982
 
        crop_box = tmp;
983
 
      }
984
 
 
985
 
      if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Rotate")))) {
986
 
        if (rotate)
987
 
          pdf_release_obj(rotate);
988
 
        rotate = tmp;
989
 
      }
990
 
 
991
 
      if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Resources")))) {
992
 
        if (resources)
993
 
          pdf_release_obj(resources);
994
 
        resources = tmp;
995
 
      }
996
 
 
997
 
      kids = pdf_deref_obj(pdf_lookup_dict(page_tree, "Kids"));
998
 
      if (!kids)
999
 
        break;
1000
 
      else if (!PDF_OBJ_ARRAYTYPE(kids)) {
1001
 
        pdf_release_obj(kids);
1002
 
        goto error;
1003
 
      }
1004
 
      kids_length = pdf_array_length(kids);
1005
 
 
1006
 
      for (i = 0; i < kids_length; i++) {
1007
 
        long count;
1008
 
 
1009
 
        pdf_release_obj(page_tree);
1010
 
        page_tree = pdf_deref_obj(pdf_get_array(kids, i));
1011
 
        if (!PDF_OBJ_DICTTYPE(page_tree))
1012
 
          goto error;
1013
 
 
1014
 
        tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Count"));
1015
 
        if (PDF_OBJ_NUMBERTYPE(tmp)) {
1016
 
          /* Pages object */
1017
 
          count = pdf_number_value(tmp);
1018
 
          pdf_release_obj(tmp);
1019
 
        } else if (!tmp)
1020
 
          /* Page object */
1021
 
          count = 1;
1022
 
        else {
1023
 
          pdf_release_obj(tmp);
1024
 
          goto error;
1025
 
        }
1026
 
 
1027
 
        if (page_idx < count)
1028
 
          break;
1029
 
 
1030
 
        page_idx -= count;
1031
 
      }
1032
 
      
1033
 
      pdf_release_obj(kids);
1034
 
    }
1035
 
 
1036
 
    if (!depth || kids_length == i) {
1037
 
      if (media_box)
1038
 
        pdf_release_obj(media_box);
1039
 
     if (crop_box)
1040
 
        pdf_release_obj(crop_box);
1041
 
      goto error;
1042
 
    }
1043
 
 
1044
 
    if (crop_box)
1045
 
      box = crop_box;
1046
 
    else
1047
 
      if (!(box = pdf_deref_obj(pdf_lookup_dict(page_tree, "ArtBox"))) &&
1048
 
          !(box = pdf_deref_obj(pdf_lookup_dict(page_tree, "TrimBox"))) &&
1049
 
          !(box = pdf_deref_obj(pdf_lookup_dict(page_tree, "BleedBox"))) &&
1050
 
          media_box) {
1051
 
          box = media_box;
1052
 
          media_box = NULL;
1053
 
      }
1054
 
    if (media_box)
1055
 
      pdf_release_obj(media_box);
1056
 
  }
1057
 
 
1058
 
  if (!PDF_OBJ_ARRAYTYPE(box) || pdf_array_length(box) != 4 ||
1059
 
      !PDF_OBJ_DICTTYPE(resources))
1060
 
    goto error;
1061
 
 
1062
 
  if (PDF_OBJ_NUMBERTYPE(rotate)) {
1063
 
    if (pdf_number_value(rotate))
1064
 
      WARN("<< /Rotate %d >> found. (Not supported yet)", 
1065
 
           (int) pdf_number_value(rotate));
1066
 
    pdf_release_obj(rotate);
1067
 
    rotate = NULL;
1068
 
  } else if (rotate)
1069
 
    goto error;
1070
 
 
1071
 
  {
1072
 
    int i;
1073
 
 
1074
 
    for (i = 4; i--; ) {
1075
 
      double x;
1076
 
      pdf_obj *tmp = pdf_deref_obj(pdf_get_array(box, i));
1077
 
      if (!PDF_OBJ_NUMBERTYPE(tmp)) {
1078
 
        pdf_release_obj(tmp);
1079
 
        goto error;
1080
 
      }
1081
 
      x = pdf_number_value(tmp);
1082
 
      switch (i) {
1083
 
      case 0: bbox->llx = x; break;
1084
 
      case 1: bbox->lly = x; break;
1085
 
      case 2: bbox->urx = x; break;
1086
 
      case 3: bbox->ury = x; break;
1087
 
      }
1088
 
      pdf_release_obj(tmp);
1089
 
    }
1090
 
  }
1091
 
 
1092
 
  pdf_release_obj(box);
1093
 
 
1094
 
  if (resources_p)
1095
 
    *resources_p = resources;
1096
 
  else if (resources)
1097
 
    pdf_release_obj(resources);
1098
 
 
1099
 
  return page_tree;
1100
 
 
1101
 
 error:
1102
 
  WARN("Cannot parse document. Broken PDF file?");
1103
 
 error_silent:
1104
 
  if (box)
1105
 
    pdf_release_obj(box);
1106
 
  if (rotate)
1107
 
    pdf_release_obj(rotate);
1108
 
  if (resources)
1109
 
    pdf_release_obj(resources);
1110
 
  if (page_tree)
1111
 
    pdf_release_obj(page_tree);
1112
 
 
1113
 
  return NULL;
1114
 
}
1115
 
 
1116
 
#ifndef BOOKMARKS_OPEN_DEFAULT
1117
 
#define BOOKMARKS_OPEN_DEFAULT 0
1118
 
#endif
1119
 
 
1120
 
static int clean_bookmarks (pdf_olitem *item);
1121
 
static int flush_bookmarks (pdf_olitem *item,
1122
 
                            pdf_obj *parent_ref,
1123
 
                            pdf_obj *parent_dict);
1124
 
 
1125
 
static void
1126
 
pdf_doc_init_bookmarks (pdf_doc *p, int bm_open_depth)
1127
 
{
1128
 
  pdf_olitem *item;
1129
 
 
1130
 
#define MAX_OUTLINE_DEPTH 256u
1131
 
  p->opt.outline_open_depth =
1132
 
    ((bm_open_depth >= 0) ?
1133
 
     bm_open_depth : MAX_OUTLINE_DEPTH - bm_open_depth);
1134
 
 
1135
 
  p->outlines.current_depth = 1;
1136
 
 
1137
 
  item = NEW(1, pdf_olitem);
1138
 
  item->dict    = NULL;
1139
 
  item->next    = NULL;
1140
 
  item->first   = NULL;
1141
 
  item->parent  = NULL;
1142
 
  item->is_open = 1;
1143
 
 
1144
 
  p->outlines.current = item;
1145
 
  p->outlines.first   = item;
1146
 
 
1147
 
  return;
1148
 
}
1149
 
 
1150
 
static int
1151
 
clean_bookmarks (pdf_olitem *item)
1152
 
{
1153
 
  pdf_olitem *next;
1154
 
 
1155
 
  while (item) {
1156
 
    next = item->next;
1157
 
    if (item->dict)
1158
 
      pdf_release_obj(item->dict);
1159
 
    if (item->first)
1160
 
      clean_bookmarks(item->first);
1161
 
    RELEASE(item);
1162
 
    
1163
 
    item = next;
1164
 
  }
1165
 
 
1166
 
  return 0;
1167
 
}
1168
 
 
1169
 
static int
1170
 
flush_bookmarks (pdf_olitem *node,
1171
 
                 pdf_obj *parent_ref, pdf_obj *parent_dict)
1172
 
{
1173
 
  int         retval;
1174
 
  int         count;
1175
 
  pdf_olitem *item;
1176
 
  pdf_obj    *this_ref, *prev_ref, *next_ref;
1177
 
 
1178
 
  ASSERT(node->dict);
1179
 
 
1180
 
  this_ref = pdf_ref_obj(node->dict);
1181
 
  pdf_add_dict(parent_dict,
1182
 
               pdf_new_name("First"), pdf_link_obj(this_ref));
1183
 
 
1184
 
  retval = 0;
1185
 
  for (item = node, prev_ref = NULL;
1186
 
       item && item->dict; item = item->next) {
1187
 
    if (item->first && item->first->dict) {
1188
 
      count = flush_bookmarks(item->first, this_ref, item->dict);
1189
 
      if (item->is_open) {
1190
 
        pdf_add_dict(item->dict,
1191
 
                     pdf_new_name("Count"),
1192
 
                     pdf_new_number(count));
1193
 
        retval += count;
1194
 
      } else {
1195
 
        pdf_add_dict(item->dict,
1196
 
                     pdf_new_name("Count"),
1197
 
                     pdf_new_number(-count));
1198
 
      }
1199
 
    }
1200
 
    pdf_add_dict(item->dict,
1201
 
                 pdf_new_name("Parent"),
1202
 
                 pdf_link_obj(parent_ref));
1203
 
    if (prev_ref) {
1204
 
      pdf_add_dict(item->dict,
1205
 
                   pdf_new_name("Prev"),
1206
 
                   prev_ref);
1207
 
    }
1208
 
    if (item->next && item->next->dict) {
1209
 
      next_ref = pdf_ref_obj(item->next->dict);
1210
 
      pdf_add_dict(item->dict,
1211
 
                   pdf_new_name("Next"),
1212
 
                   pdf_link_obj(next_ref));
1213
 
    } else {
1214
 
      next_ref = NULL;
1215
 
    }
1216
 
 
1217
 
    pdf_release_obj(item->dict);
1218
 
    item->dict = NULL;
1219
 
 
1220
 
    prev_ref = this_ref;
1221
 
    this_ref = next_ref;
1222
 
    retval++;    
1223
 
  }
1224
 
 
1225
 
  pdf_add_dict(parent_dict,
1226
 
               pdf_new_name("Last"),
1227
 
               pdf_link_obj(prev_ref));
1228
 
 
1229
 
  pdf_release_obj(prev_ref);
1230
 
  pdf_release_obj(node->dict);
1231
 
  node->dict = NULL;
1232
 
 
1233
 
  return retval;
1234
 
}
1235
 
  
1236
 
int
1237
 
pdf_doc_bookmarks_up (void)
1238
 
{
1239
 
  pdf_doc    *p = &pdoc;
1240
 
  pdf_olitem *parent, *item;
1241
 
 
1242
 
  item = p->outlines.current;
1243
 
  if (!item || !item->parent) {
1244
 
    WARN("Can't go up above the bookmark root node!");
1245
 
    return -1;
1246
 
  }
1247
 
  parent = item->parent;
1248
 
  item   = parent->next;
1249
 
  if (!parent->next) {
1250
 
    parent->next  = item = NEW(1, pdf_olitem);
1251
 
    item->dict    = NULL;
1252
 
    item->first   = NULL;
1253
 
    item->next    = NULL;
1254
 
    item->is_open = 0;
1255
 
    item->parent  = parent->parent;
1256
 
  }
1257
 
  p->outlines.current = item;
1258
 
  p->outlines.current_depth--;
1259
 
 
1260
 
  return 0;
1261
 
}
1262
 
 
1263
 
int
1264
 
pdf_doc_bookmarks_down (void)
1265
 
{
1266
 
  pdf_doc    *p = &pdoc;
1267
 
  pdf_olitem *item, *first;
1268
 
 
1269
 
  item = p->outlines.current;
1270
 
  if (!item->dict) {
1271
 
    pdf_obj *tcolor, *action;
1272
 
 
1273
 
    WARN("Empty bookmark node!");
1274
 
    WARN("You have tried to jump more than 1 level.");
1275
 
 
1276
 
    item->dict = pdf_new_dict();
1277
 
 
1278
 
#define TITLE_STRING "<No Title>"
1279
 
    pdf_add_dict(item->dict,
1280
 
                 pdf_new_name("Title"),
1281
 
                 pdf_new_string(TITLE_STRING, strlen(TITLE_STRING)));
1282
 
 
1283
 
    tcolor = pdf_new_array();
1284
 
    pdf_add_array(tcolor, pdf_new_number(1.0));
1285
 
    pdf_add_array(tcolor, pdf_new_number(0.0));
1286
 
    pdf_add_array(tcolor, pdf_new_number(0.0));
1287
 
    pdf_add_dict (item->dict,
1288
 
                  pdf_new_name("C"), pdf_link_obj(tcolor));
1289
 
    pdf_release_obj(tcolor);
1290
 
 
1291
 
    pdf_add_dict (item->dict,
1292
 
                  pdf_new_name("F"), pdf_new_number(1.0));
1293
 
 
1294
 
#define JS_CODE "app.alert(\"The author of this document made this bookmark item empty!\", 3, 0)"
1295
 
    action = pdf_new_dict();
1296
 
    pdf_add_dict(action,
1297
 
                 pdf_new_name("S"), pdf_new_name("JavaScript"));
1298
 
    pdf_add_dict(action, 
1299
 
                 pdf_new_name("JS"), pdf_new_string(JS_CODE, strlen(JS_CODE)));
1300
 
    pdf_add_dict(item->dict,
1301
 
                 pdf_new_name("A"), pdf_link_obj(action));
1302
 
    pdf_release_obj(action);
1303
 
  }
1304
 
 
1305
 
  item->first    = first = NEW(1, pdf_olitem);
1306
 
  first->dict    = NULL;
1307
 
  first->is_open = 0;
1308
 
  first->parent  = item;
1309
 
  first->next    = NULL;
1310
 
  first->first   = NULL;
1311
 
 
1312
 
  p->outlines.current = first;
1313
 
  p->outlines.current_depth++;
1314
 
 
1315
 
  return 0;
1316
 
}
1317
 
 
1318
 
int
1319
 
pdf_doc_bookmarks_depth (void)
1320
 
{
1321
 
  pdf_doc *p = &pdoc;
1322
 
 
1323
 
  return p->outlines.current_depth;
1324
 
}
1325
 
 
1326
 
void
1327
 
pdf_doc_bookmarks_add (pdf_obj *dict, int is_open)
1328
 
{
1329
 
  pdf_doc    *p = &pdoc;
1330
 
  pdf_olitem *item, *next;
1331
 
 
1332
 
  ASSERT(p && dict);
1333
 
 
1334
 
  item = p->outlines.current;
1335
 
 
1336
 
  if (!item) {
1337
 
    item = NEW(1, pdf_olitem);
1338
 
    item->parent = NULL;
1339
 
    p->outlines.first = item;
1340
 
  } else if (item->dict) { /* go to next item */
1341
 
    item = item->next;
1342
 
  }
1343
 
 
1344
 
#define BMOPEN(b,p) (((b) < 0) ? (((p)->outlines.current_depth > (p)->opt.outline_open_depth) ? 0 : 1) : (b))
1345
 
 
1346
 
#if 0
1347
 
  item->dict    = pdf_link_obj(dict);
1348
 
#endif
1349
 
  item->dict    = dict; 
1350
 
  item->first   = NULL;
1351
 
  item->is_open = BMOPEN(is_open, p);
1352
 
 
1353
 
  item->next    = next = NEW(1, pdf_olitem);
1354
 
  next->dict    = NULL;
1355
 
  next->parent  = item->parent;
1356
 
  next->first   = NULL;
1357
 
  next->is_open = -1;
1358
 
  next->next    = NULL;
1359
 
 
1360
 
  p->outlines.current = item;
1361
 
 
1362
 
  pdf_doc_add_goto(dict);
1363
 
 
1364
 
  return;
1365
 
}
1366
 
 
1367
 
static void
1368
 
pdf_doc_close_bookmarks (pdf_doc *p)
1369
 
{
1370
 
  pdf_obj     *catalog = p->root.dict;
1371
 
  pdf_olitem  *item;
1372
 
  int          count;
1373
 
  pdf_obj     *bm_root, *bm_root_ref;
1374
 
  
1375
 
  item = p->outlines.first;
1376
 
  if (item->dict) {
1377
 
    bm_root     = pdf_new_dict();
1378
 
    bm_root_ref = pdf_ref_obj(bm_root);
1379
 
    count       = flush_bookmarks(item, bm_root_ref, bm_root);
1380
 
    pdf_add_dict(bm_root,
1381
 
                 pdf_new_name("Count"),
1382
 
                 pdf_new_number(count));
1383
 
    pdf_add_dict(catalog,
1384
 
                 pdf_new_name("Outlines"),
1385
 
                 bm_root_ref);
1386
 
    pdf_release_obj(bm_root);
1387
 
  }
1388
 
  clean_bookmarks(item);
1389
 
 
1390
 
  p->outlines.first   = NULL;
1391
 
  p->outlines.current = NULL;
1392
 
  p->outlines.current_depth = 0;
1393
 
 
1394
 
  return;
1395
 
}
1396
 
 
1397
 
 
1398
 
static const char *name_dict_categories[] = {
1399
 
  "Dests", "AP", "JavaScript", "Pages",
1400
 
  "Templates", "IDS", "URLS", "EmbeddedFiles",
1401
 
  "AlternatePresentations", "Renditions"
1402
 
};
1403
 
#define NUM_NAME_CATEGORY (sizeof(name_dict_categories)/sizeof(name_dict_categories[0]))
1404
 
 
1405
 
static void
1406
 
pdf_doc_init_names (pdf_doc *p, int check_gotos)
1407
 
{
1408
 
  int    i;
1409
 
 
1410
 
  p->root.names   = NULL;
1411
 
  
1412
 
  p->names = NEW(NUM_NAME_CATEGORY + 1, struct name_dict);
1413
 
  for (i = 0; i < NUM_NAME_CATEGORY; i++) {
1414
 
    p->names[i].category = name_dict_categories[i];
1415
 
    p->names[i].data     = strcmp(name_dict_categories[i], "Dests") ?
1416
 
                             NULL : pdf_new_name_tree();
1417
 
    /*
1418
 
     * We need a non-null entry for PDF destinations in order to find
1419
 
     * broken links even if no destination is defined in the DVI file.
1420
 
     */
1421
 
  }
1422
 
  p->names[NUM_NAME_CATEGORY].category = NULL;
1423
 
  p->names[NUM_NAME_CATEGORY].data     = NULL;
1424
 
 
1425
 
  p->check_gotos   = check_gotos;
1426
 
  ht_init_table(&p->gotos, (void (*) (void *)) pdf_release_obj);
1427
 
 
1428
 
  return;
1429
 
}
1430
 
 
1431
 
int
1432
 
pdf_doc_add_names (const char *category,
1433
 
                   const void *key, int keylen, pdf_obj *value)
1434
 
{
1435
 
  pdf_doc *p = &pdoc;
1436
 
  int      i;
1437
 
 
1438
 
  for (i = 0; p->names[i].category != NULL; i++) {
1439
 
    if (!strcmp(p->names[i].category, category)) {
1440
 
      break;
1441
 
    }
1442
 
  }
1443
 
  if (p->names[i].category == NULL) {
1444
 
    WARN("Unknown name dictionary category \"%s\".", category);
1445
 
    return -1;
1446
 
  }
1447
 
  if (!p->names[i].data) {
1448
 
    p->names[i].data = pdf_new_name_tree();
1449
 
  }
1450
 
 
1451
 
  return pdf_names_add_object(p->names[i].data, key, keylen, value);
1452
 
}
1453
 
 
1454
 
static void
1455
 
pdf_doc_add_goto (pdf_obj *annot_dict)
1456
 
{
1457
 
  pdf_obj *subtype = NULL, *A = NULL, *S = NULL, *D = NULL, *D_new, *dict;
1458
 
  const char *dest, *key;
1459
 
 
1460
 
  if (!pdoc.check_gotos)
1461
 
    return;
1462
 
 
1463
 
  /*
1464
 
   * An annotation dictionary coming from an annotation special
1465
 
   * must have a "Subtype". An annotation dictionary coming from
1466
 
   * an outline special has none.
1467
 
   */
1468
 
  subtype = pdf_deref_obj(pdf_lookup_dict(annot_dict, "Subtype"));
1469
 
  if (subtype) {
1470
 
    if (PDF_OBJ_UNDEFINED(subtype))
1471
 
      goto undefined;
1472
 
    else if (!PDF_OBJ_NAMETYPE(subtype))
1473
 
      goto error;
1474
 
    else if (strcmp(pdf_name_value(subtype), "Link"))
1475
 
      goto cleanup;
1476
 
  }
1477
 
 
1478
 
  dict = annot_dict;
1479
 
  key = "Dest";
1480
 
  D = pdf_deref_obj(pdf_lookup_dict(annot_dict, key));
1481
 
  if (PDF_OBJ_UNDEFINED(D))
1482
 
    goto undefined;
1483
 
 
1484
 
  A = pdf_deref_obj(pdf_lookup_dict(annot_dict, "A"));
1485
 
  if (A) {
1486
 
    if (PDF_OBJ_UNDEFINED(A))
1487
 
      goto undefined;
1488
 
    else if (D || !PDF_OBJ_DICTTYPE(A))
1489
 
      goto error;
1490
 
    else {
1491
 
      S = pdf_deref_obj(pdf_lookup_dict(A, "S"));
1492
 
      if (PDF_OBJ_UNDEFINED(S))
1493
 
        goto undefined;
1494
 
      else if (!PDF_OBJ_NAMETYPE(S))
1495
 
        goto error;
1496
 
      else if (strcmp(pdf_name_value(S), "GoTo"))
1497
 
        goto cleanup;
1498
 
 
1499
 
      dict = A;
1500
 
      key = "D";
1501
 
      D = pdf_deref_obj(pdf_lookup_dict(A, key));
1502
 
    }
1503
 
  }
1504
 
 
1505
 
  if (PDF_OBJ_STRINGTYPE(D))
1506
 
    dest = (char *) pdf_string_value(D);
1507
 
#if 0
1508
 
  /* Names as destinations are not supported by dvipdfmx */
1509
 
  else if (PDF_OBJ_NAMETYPE(D))
1510
 
    dest = pdf_name_value(D);
1511
 
#endif
1512
 
  else if (PDF_OBJ_ARRAYTYPE(D))
1513
 
    goto cleanup;
1514
 
  else if (PDF_OBJ_UNDEFINED(D))
1515
 
    goto undefined;
1516
 
  else
1517
 
    goto error;
1518
 
 
1519
 
  D_new = ht_lookup_table(&pdoc.gotos, dest, strlen(dest));
1520
 
  if (!D_new) {
1521
 
    char buf[10];
1522
 
 
1523
 
    /* We use hexadecimal notation for our numeric destinations.
1524
 
     * Other bases (e.g., 10+26 or 10+2*26) would be more efficient.
1525
 
     */
1526
 
    sprintf(buf, "%lx", ht_table_size(&pdoc.gotos));
1527
 
    D_new = pdf_new_string(buf, strlen(buf));
1528
 
    ht_append_table(&pdoc.gotos, dest, strlen(dest), D_new);
1529
 
  }
1530
 
 
1531
 
  {
1532
 
    pdf_obj *key_obj = pdf_new_name(key);
1533
 
    if (!pdf_add_dict(dict, key_obj, pdf_link_obj(D_new)))
1534
 
      pdf_release_obj(key_obj);
1535
 
  }
1536
 
 
1537
 
 cleanup:
1538
 
  if (subtype)
1539
 
    pdf_release_obj(subtype);
1540
 
  if (A)
1541
 
    pdf_release_obj(A);
1542
 
  if (S)
1543
 
    pdf_release_obj(S);
1544
 
  if (D)
1545
 
    pdf_release_obj(D);
1546
 
 
1547
 
  return;
1548
 
 
1549
 
 error:
1550
 
  WARN("Unknown PDF annotation format. Output file may be broken.");
1551
 
  goto cleanup;
1552
 
 
1553
 
 undefined:
1554
 
  WARN("Cannot optimize PDF annotations. Output file may be broken."
1555
 
       " Please restart with option \"-C 0x10\"\n");
1556
 
  goto cleanup;
1557
 
}
1558
 
 
1559
 
static void
1560
 
warn_undef_dests (struct ht_table *dests, struct ht_table *gotos)
1561
 
{
1562
 
  struct ht_iter iter;
1563
 
 
1564
 
  if (ht_set_iter(gotos, &iter) < 0)
1565
 
    return;
1566
 
 
1567
 
  do {
1568
 
    int keylen;
1569
 
    char *key = ht_iter_getkey(&iter, &keylen);
1570
 
    if (!ht_lookup_table(dests, key, keylen)) {
1571
 
      char *dest = NEW(keylen+1, char);
1572
 
      memcpy(dest, key, keylen);
1573
 
      dest[keylen] = 0;
1574
 
      WARN("PDF destination \"%s\" not defined.", dest);
1575
 
      RELEASE(dest);
1576
 
    }
1577
 
  } while (ht_iter_next(&iter) >= 0);
1578
 
 
1579
 
  ht_clear_iter(&iter);
1580
 
}
1581
 
 
1582
 
static void
1583
 
pdf_doc_close_names (pdf_doc *p)
1584
 
{
1585
 
  pdf_obj  *tmp;
1586
 
  int       i;
1587
 
 
1588
 
  for (i = 0; p->names[i].category != NULL; i++) {
1589
 
    if (p->names[i].data) {
1590
 
      struct ht_table *data = p->names[i].data;
1591
 
      pdf_obj  *name_tree;
1592
 
      long count;
1593
 
 
1594
 
      if (!pdoc.check_gotos || strcmp(p->names[i].category, "Dests"))
1595
 
        name_tree = pdf_names_create_tree(data, &count, NULL);
1596
 
      else {
1597
 
        name_tree = pdf_names_create_tree(data, &count, &pdoc.gotos);
1598
 
 
1599
 
        if (verbose && count < data->count)
1600
 
          MESG("\nRemoved %ld unused PDF destinations\n", data->count-count);
1601
 
 
1602
 
        if (count < pdoc.gotos.count)
1603
 
          warn_undef_dests(data, &pdoc.gotos);
1604
 
      }
1605
 
 
1606
 
      if (name_tree) {
1607
 
        if (!p->root.names)
1608
 
          p->root.names = pdf_new_dict();
1609
 
        pdf_add_dict(p->root.names,
1610
 
                     pdf_new_name(p->names[i].category),
1611
 
                     pdf_ref_obj(name_tree));
1612
 
        pdf_release_obj(name_tree);
1613
 
      }
1614
 
      pdf_delete_name_tree(&p->names[i].data);
1615
 
    }
1616
 
  }
1617
 
 
1618
 
  if (p->root.names) {
1619
 
    tmp = pdf_lookup_dict(p->root.dict, "Names");
1620
 
    if (!tmp) {
1621
 
      pdf_add_dict(p->root.dict,
1622
 
                   pdf_new_name("Names"),
1623
 
                   pdf_ref_obj (p->root.names));
1624
 
    } else if (PDF_OBJ_DICTTYPE(tmp)) {
1625
 
      pdf_merge_dict(p->root.names, tmp);
1626
 
      pdf_add_dict(p->root.dict,
1627
 
                   pdf_new_name("Names"),
1628
 
                   pdf_ref_obj (p->root.names));
1629
 
    } else { /* Maybe reference */
1630
 
      /* What should I do? */
1631
 
      WARN("Could not modify Names dictionary.");
1632
 
    }
1633
 
    pdf_release_obj(p->root.names);
1634
 
    p->root.names = NULL;
1635
 
  }
1636
 
 
1637
 
  RELEASE(p->names);
1638
 
  p->names = NULL;
1639
 
 
1640
 
  ht_clear_table(&p->gotos);
1641
 
 
1642
 
  return;
1643
 
}
1644
 
 
1645
 
 
1646
 
void
1647
 
pdf_doc_add_annot (unsigned page_no, const pdf_rect *rect,
1648
 
                   pdf_obj *annot_dict, int new_annot)
1649
 
{
1650
 
  pdf_doc  *p = &pdoc;
1651
 
  pdf_page *page;
1652
 
  pdf_obj  *rect_array;
1653
 
  double    annot_grow = p->opt.annot_grow;
1654
 
  double    xpos, ypos;
1655
 
  pdf_rect  mediabox, annbox;
1656
 
 
1657
 
  page = doc_get_page_entry(p, page_no);
1658
 
  if (!page->annots)
1659
 
    page->annots = pdf_new_array();
1660
 
 
1661
 
  pdf_doc_get_mediabox(page_no, &mediabox);
1662
 
  pdf_dev_get_coord(&xpos, &ypos);
1663
 
  annbox.llx = rect->llx - xpos; annbox.lly = rect->lly - ypos;
1664
 
  annbox.urx = rect->urx - xpos; annbox.ury = rect->ury - ypos;
1665
 
 
1666
 
  if (annbox.llx < mediabox.llx || annbox.urx > mediabox.urx ||
1667
 
      annbox.lly < mediabox.lly || annbox.ury > mediabox.ury) {
1668
 
    WARN("Annotation out of page boundary.");
1669
 
    WARN("Current page's MediaBox: [%g %g %g %g]",
1670
 
         mediabox.llx, mediabox.lly, mediabox.urx, mediabox.ury);
1671
 
    WARN("Annotation: [%g %g %g %g]",
1672
 
         annbox.llx, annbox.lly, annbox.urx, annbox.ury);
1673
 
    WARN("Maybe incorrect paper size specified.");
1674
 
  }
1675
 
  if (annbox.llx > annbox.urx || annbox.lly > annbox.ury) {
1676
 
    WARN("Rectangle with negative width/height: [%g %g %g %g]",
1677
 
         annbox.llx, annbox.lly, annbox.urx, annbox.ury);
1678
 
  }
1679
 
 
1680
 
  rect_array = pdf_new_array();
1681
 
  pdf_add_array(rect_array, pdf_new_number(ROUND(annbox.llx - annot_grow, 0.001)));
1682
 
  pdf_add_array(rect_array, pdf_new_number(ROUND(annbox.lly - annot_grow, 0.001)));
1683
 
  pdf_add_array(rect_array, pdf_new_number(ROUND(annbox.urx + annot_grow, 0.001)));
1684
 
  pdf_add_array(rect_array, pdf_new_number(ROUND(annbox.ury + annot_grow, 0.001)));
1685
 
  pdf_add_dict (annot_dict, pdf_new_name("Rect"), rect_array);
1686
 
 
1687
 
  pdf_add_array(page->annots, pdf_ref_obj(annot_dict));
1688
 
 
1689
 
  if (new_annot)
1690
 
    pdf_doc_add_goto(annot_dict);
1691
 
 
1692
 
  return;
1693
 
}
1694
 
 
1695
 
 
1696
 
/*
1697
 
 * PDF Article Thread
1698
 
 */
1699
 
static void
1700
 
pdf_doc_init_articles (pdf_doc *p)
1701
 
{
1702
 
  p->root.threads = NULL;
1703
 
 
1704
 
  p->articles.num_entries = 0;
1705
 
  p->articles.max_entries = 0;
1706
 
  p->articles.entries     = NULL;
1707
 
 
1708
 
  return;
1709
 
}
1710
 
 
1711
 
void
1712
 
pdf_doc_begin_article (const char *article_id, pdf_obj *article_info)
1713
 
{
1714
 
  pdf_doc     *p = &pdoc;
1715
 
  pdf_article *article;
1716
 
 
1717
 
  if (article_id == NULL || strlen(article_id) == 0)
1718
 
    ERROR("Article thread without internal identifier.");
1719
 
 
1720
 
  if (p->articles.num_entries >= p->articles.max_entries) {
1721
 
    p->articles.max_entries += PDFDOC_ARTICLE_ALLOC_SIZE;
1722
 
    p->articles.entries = RENEW(p->articles.entries,
1723
 
                                p->articles.max_entries, struct pdf_article);
1724
 
  }
1725
 
  article = &(p->articles.entries[p->articles.num_entries]);
1726
 
 
1727
 
  article->id = NEW(strlen(article_id)+1, char);
1728
 
  strcpy(article->id, article_id);
1729
 
  article->info = article_info;
1730
 
  article->num_beads = 0;
1731
 
  article->max_beads = 0;
1732
 
  article->beads     = NULL;
1733
 
 
1734
 
  p->articles.num_entries++;
1735
 
 
1736
 
  return;
1737
 
}
1738
 
 
1739
 
#if 0
1740
 
void
1741
 
pdf_doc_end_article (const char *article_id)
1742
 
{
1743
 
  return; /* no-op */
1744
 
}
1745
 
#endif
1746
 
 
1747
 
static pdf_bead *
1748
 
find_bead (pdf_article *article, const char *bead_id)
1749
 
{
1750
 
  pdf_bead *bead;
1751
 
  long      i;
1752
 
 
1753
 
  bead = NULL;
1754
 
  for (i = 0; i < article->num_beads; i++) {
1755
 
    if (!strcmp(article->beads[i].id, bead_id)) {
1756
 
      bead = &(article->beads[i]);
1757
 
      break;
1758
 
    }
1759
 
  }
1760
 
 
1761
 
  return bead;
1762
 
}
1763
 
 
1764
 
void
1765
 
pdf_doc_add_bead (const char *article_id,
1766
 
                  const char *bead_id, long page_no, const pdf_rect *rect)
1767
 
{
1768
 
  pdf_doc     *p = &pdoc;
1769
 
  pdf_article *article;
1770
 
  pdf_bead    *bead;
1771
 
  long         i;
1772
 
 
1773
 
  if (!article_id) {
1774
 
    ERROR("No article identifier specified.");
1775
 
  }
1776
 
 
1777
 
  article = NULL;
1778
 
  for (i = 0; i < p->articles.num_entries; i++) {
1779
 
    if (!strcmp(p->articles.entries[i].id, article_id)) {
1780
 
      article = &(p->articles.entries[i]);
1781
 
      break;
1782
 
    }
1783
 
  }
1784
 
  if (!article) {
1785
 
    ERROR("Specified article thread that doesn't exist.");
1786
 
    return;
1787
 
  }
1788
 
 
1789
 
  bead = bead_id ? find_bead(article, bead_id) : NULL;
1790
 
  if (!bead) {
1791
 
    if (article->num_beads >= article->max_beads) {
1792
 
      article->max_beads += PDFDOC_BEAD_ALLOC_SIZE;
1793
 
      article->beads = RENEW(article->beads,
1794
 
                             article->max_beads, struct pdf_bead);
1795
 
      for (i = article->num_beads; i < article->max_beads; i++) {
1796
 
        article->beads[i].id = NULL;
1797
 
        article->beads[i].page_no = -1;
1798
 
      }
1799
 
    }
1800
 
    bead = &(article->beads[article->num_beads]);
1801
 
    if (bead_id) {
1802
 
      bead->id = NEW(strlen(bead_id)+1, char);
1803
 
      strcpy(bead->id, bead_id);
1804
 
    } else {
1805
 
      bead->id = NULL;
1806
 
    }
1807
 
    article->num_beads++;
1808
 
  }
1809
 
  bead->rect.llx = rect->llx;
1810
 
  bead->rect.lly = rect->lly;
1811
 
  bead->rect.urx = rect->urx;
1812
 
  bead->rect.ury = rect->ury;
1813
 
  bead->page_no  = page_no;
1814
 
 
1815
 
  return;
1816
 
}
1817
 
 
1818
 
static pdf_obj *
1819
 
make_article (pdf_doc *p,
1820
 
              pdf_article *article,
1821
 
              const char **bead_ids, int num_beads,
1822
 
              pdf_obj *article_info)
1823
 
{
1824
 
  pdf_obj *art_dict;
1825
 
  pdf_obj *first, *prev, *last;
1826
 
  long     i, n;
1827
 
 
1828
 
  if (!article)
1829
 
    return NULL;
1830
 
 
1831
 
  art_dict = pdf_new_dict();
1832
 
  first = prev = last = NULL;
1833
 
  /*
1834
 
   * The bead_ids represents logical order of beads in an article thread.
1835
 
   * If bead_ids is not given, we create an article thread in the order of
1836
 
   * beads appeared.
1837
 
   */
1838
 
  n = bead_ids ? num_beads : article->num_beads;
1839
 
  for (i = 0; i < n; i++) {
1840
 
    pdf_bead *bead;
1841
 
 
1842
 
    bead = bead_ids ? find_bead(article, bead_ids[i]) : &(article->beads[i]);
1843
 
    if (!bead || bead->page_no < 0) {
1844
 
      continue;
1845
 
    }
1846
 
    last = pdf_new_dict();
1847
 
    if (prev == NULL) {
1848
 
      first = last;
1849
 
      pdf_add_dict(first,
1850
 
                   pdf_new_name("T"), pdf_ref_obj(art_dict));
1851
 
    } else {
1852
 
      pdf_add_dict(prev,
1853
 
                   pdf_new_name("N"), pdf_ref_obj(last));
1854
 
      pdf_add_dict(last,
1855
 
                   pdf_new_name("V"), pdf_ref_obj(prev));
1856
 
      /* We must link first to last. */
1857
 
      if (prev != first)
1858
 
        pdf_release_obj(prev);
1859
 
    }
1860
 
 
1861
 
    /* Realize bead now. */
1862
 
    {
1863
 
      pdf_page *page;
1864
 
      pdf_obj  *rect;
1865
 
 
1866
 
      page = doc_get_page_entry(p, bead->page_no);
1867
 
      if (!page->beads) {
1868
 
        page->beads = pdf_new_array();
1869
 
      }
1870
 
      pdf_add_dict(last, pdf_new_name("P"), pdf_link_obj(page->page_ref));
1871
 
      rect = pdf_new_array();
1872
 
      pdf_add_array(rect, pdf_new_number(ROUND(bead->rect.llx, 0.01)));
1873
 
      pdf_add_array(rect, pdf_new_number(ROUND(bead->rect.lly, 0.01)));
1874
 
      pdf_add_array(rect, pdf_new_number(ROUND(bead->rect.urx, 0.01)));
1875
 
      pdf_add_array(rect, pdf_new_number(ROUND(bead->rect.ury, 0.01)));
1876
 
      pdf_add_dict (last, pdf_new_name("R"), rect);
1877
 
      pdf_add_array(page->beads, pdf_ref_obj(last));
1878
 
    }
1879
 
 
1880
 
    prev = last;
1881
 
  }
1882
 
 
1883
 
  if (first && last) {
1884
 
    pdf_add_dict(last,
1885
 
                 pdf_new_name("N"), pdf_ref_obj(first));
1886
 
    pdf_add_dict(first,
1887
 
                 pdf_new_name("V"), pdf_ref_obj(last));
1888
 
    if (first != last) {
1889
 
      pdf_release_obj(last);
1890
 
    }
1891
 
    pdf_add_dict(art_dict,
1892
 
                 pdf_new_name("F"), pdf_ref_obj(first));
1893
 
    /* If article_info is supplied, we override article->info. */
1894
 
    if (article_info) {
1895
 
      pdf_add_dict(art_dict,
1896
 
                   pdf_new_name("I"), article_info);
1897
 
    } else if (article->info) {
1898
 
      pdf_add_dict(art_dict,
1899
 
                   pdf_new_name("I"), pdf_ref_obj(article->info));
1900
 
      pdf_release_obj(article->info);
1901
 
      article->info = NULL; /* We do not write as object reference. */
1902
 
    }
1903
 
    pdf_release_obj(first);
1904
 
  } else {
1905
 
    pdf_release_obj(art_dict);
1906
 
    art_dict = NULL;
1907
 
  }
1908
 
 
1909
 
  return art_dict;
1910
 
}
1911
 
 
1912
 
static void
1913
 
clean_article (pdf_article *article)
1914
 
{
1915
 
  if (!article)
1916
 
    return;
1917
 
    
1918
 
  if (article->beads) {
1919
 
    long  i;
1920
 
 
1921
 
    for (i = 0; i < article->num_beads; i++) {
1922
 
      if (article->beads[i].id)
1923
 
        RELEASE(article->beads[i].id);
1924
 
    }
1925
 
    RELEASE(article->beads);
1926
 
    article->beads = NULL;
1927
 
  }
1928
 
    
1929
 
  if (article->id)
1930
 
    RELEASE(article->id);
1931
 
  article->id = NULL;
1932
 
  article->num_beads = 0;
1933
 
  article->max_beads = 0;
1934
 
 
1935
 
  return;
1936
 
}
1937
 
 
1938
 
static void
1939
 
pdf_doc_close_articles (pdf_doc *p)
1940
 
{
1941
 
  int  i;
1942
 
 
1943
 
  for (i = 0; i < p->articles.num_entries; i++) {
1944
 
    pdf_article *article;
1945
 
 
1946
 
    article = &(p->articles.entries[i]);
1947
 
    if (article->beads) {
1948
 
      pdf_obj *art_dict;
1949
 
 
1950
 
      art_dict = make_article(p, article, NULL, 0, NULL);
1951
 
      if (!p->root.threads) {
1952
 
        p->root.threads = pdf_new_array();
1953
 
      }
1954
 
      pdf_add_array(p->root.threads, pdf_ref_obj(art_dict));
1955
 
      pdf_release_obj(art_dict);
1956
 
    }
1957
 
    clean_article(article);
1958
 
  }
1959
 
  RELEASE(p->articles.entries);
1960
 
  p->articles.entries = NULL;
1961
 
  p->articles.num_entries = 0;
1962
 
  p->articles.max_entries = 0;
1963
 
 
1964
 
  if (p->root.threads) {
1965
 
    pdf_add_dict(p->root.dict,
1966
 
                 pdf_new_name("Threads"),
1967
 
                 pdf_ref_obj (p->root.threads));
1968
 
    pdf_release_obj(p->root.threads);
1969
 
    p->root.threads = NULL;
1970
 
  }
1971
 
 
1972
 
  return;
1973
 
}
1974
 
 
1975
 
/* page_no = 0 for root page tree node. */
1976
 
void
1977
 
pdf_doc_set_mediabox (unsigned page_no, const pdf_rect *mediabox)
1978
 
{
1979
 
  pdf_doc  *p = &pdoc;
1980
 
  pdf_page *page;
1981
 
 
1982
 
  if (page_no == 0) {
1983
 
    p->pages.mediabox.llx = mediabox->llx;
1984
 
    p->pages.mediabox.lly = mediabox->lly;
1985
 
    p->pages.mediabox.urx = mediabox->urx;
1986
 
    p->pages.mediabox.ury = mediabox->ury;
1987
 
  } else {
1988
 
    page = doc_get_page_entry(p, page_no);
1989
 
    page->cropbox.llx = mediabox->llx;
1990
 
    page->cropbox.lly = mediabox->lly;
1991
 
    page->cropbox.urx = mediabox->urx;
1992
 
    page->cropbox.ury = mediabox->ury;
1993
 
    page->flags |= USE_MY_MEDIABOX;
1994
 
  }
1995
 
 
1996
 
  return;
1997
 
}
1998
 
 
1999
 
void
2000
 
pdf_doc_get_mediabox (unsigned page_no, pdf_rect *mediabox)
2001
 
{
2002
 
  pdf_doc  *p = &pdoc;
2003
 
  pdf_page *page;
2004
 
 
2005
 
  if (page_no == 0) {
2006
 
    mediabox->llx = p->pages.mediabox.llx;
2007
 
    mediabox->lly = p->pages.mediabox.lly;
2008
 
    mediabox->urx = p->pages.mediabox.urx;
2009
 
    mediabox->ury = p->pages.mediabox.ury;
2010
 
  } else {
2011
 
    page = doc_get_page_entry(p, page_no);
2012
 
    if (page->flags & USE_MY_MEDIABOX) {
2013
 
      mediabox->llx = page->cropbox.llx;
2014
 
      mediabox->lly = page->cropbox.lly;
2015
 
      mediabox->urx = page->cropbox.urx;
2016
 
      mediabox->ury = page->cropbox.ury;
2017
 
    } else {
2018
 
      mediabox->llx = p->pages.mediabox.llx;
2019
 
      mediabox->lly = p->pages.mediabox.lly;
2020
 
      mediabox->urx = p->pages.mediabox.urx;
2021
 
      mediabox->ury = p->pages.mediabox.ury;
2022
 
    }
2023
 
  }
2024
 
 
2025
 
  return;
2026
 
}
2027
 
 
2028
 
pdf_obj *
2029
 
pdf_doc_current_page_resources (void)
2030
 
{
2031
 
  pdf_obj  *resources;
2032
 
  pdf_doc  *p = &pdoc;
2033
 
  pdf_page *currentpage;
2034
 
 
2035
 
  if (p->pending_forms) {
2036
 
    if (p->pending_forms->form.resources) {
2037
 
      resources = p->pending_forms->form.resources;
2038
 
    } else {
2039
 
      resources = p->pending_forms->form.resources = pdf_new_dict();
2040
 
    }
2041
 
  } else {
2042
 
    currentpage = LASTPAGE(p);
2043
 
    if (currentpage->resources) {
2044
 
      resources = currentpage->resources;
2045
 
    } else {
2046
 
      resources = currentpage->resources = pdf_new_dict();
2047
 
    }
2048
 
  }
2049
 
 
2050
 
  return resources;
2051
 
}
2052
 
 
2053
 
pdf_obj *
2054
 
pdf_doc_get_dictionary (const char *category)
2055
 
{
2056
 
  pdf_doc *p    = &pdoc;
2057
 
  pdf_obj *dict = NULL;
2058
 
 
2059
 
  ASSERT(category);
2060
 
 
2061
 
  if (!strcmp(category, "Names")) {
2062
 
    if (!p->root.names)
2063
 
      p->root.names = pdf_new_dict();
2064
 
    dict = p->root.names;
2065
 
  } else if (!strcmp(category, "Pages")) {
2066
 
    if (!p->root.pages)
2067
 
      p->root.pages = pdf_new_dict();
2068
 
    dict = p->root.pages;
2069
 
  } else if (!strcmp(category, "Catalog")) {
2070
 
    if (!p->root.dict)
2071
 
      p->root.dict = pdf_new_dict();
2072
 
    dict = p->root.dict;
2073
 
  } else if (!strcmp(category, "Info")) {
2074
 
    if (!p->info)
2075
 
      p->info = pdf_new_dict();
2076
 
    dict = p->info;
2077
 
  } else if (!strcmp(category, "@THISPAGE")) {
2078
 
    /* Sorry for this... */
2079
 
    pdf_page *currentpage;
2080
 
 
2081
 
    currentpage = LASTPAGE(p);
2082
 
    dict =  currentpage->page_obj;
2083
 
  }
2084
 
 
2085
 
  if (!dict) {
2086
 
    ERROR("Document dict. \"%s\" not exist. ", category);
2087
 
  }
2088
 
 
2089
 
  return dict;
2090
 
}
2091
 
 
2092
 
long
2093
 
pdf_doc_current_page_number (void)
2094
 
{
2095
 
  pdf_doc *p = &pdoc;
2096
 
 
2097
 
  return (long) (PAGECOUNT(p) + 1);
2098
 
}
2099
 
 
2100
 
pdf_obj *
2101
 
pdf_doc_ref_page (unsigned long page_no)
2102
 
{
2103
 
  pdf_doc  *p = &pdoc;
2104
 
  pdf_page *page;
2105
 
 
2106
 
  page = doc_get_page_entry(p, page_no);
2107
 
  if (!page->page_obj) {
2108
 
    page->page_obj = pdf_new_dict();
2109
 
    page->page_ref = pdf_ref_obj(page->page_obj);
2110
 
  }
2111
 
 
2112
 
  return pdf_link_obj(page->page_ref);
2113
 
}
2114
 
 
2115
 
pdf_obj *
2116
 
pdf_doc_get_reference (const char *category)
2117
 
{
2118
 
  pdf_obj *ref = NULL;
2119
 
  long     page_no;
2120
 
 
2121
 
  ASSERT(category);
2122
 
 
2123
 
  page_no = pdf_doc_current_page_number();
2124
 
  if (!strcmp(category, "@THISPAGE")) {
2125
 
    ref = pdf_doc_ref_page(page_no);
2126
 
  } else if (!strcmp(category, "@PREVPAGE")) {
2127
 
    if (page_no <= 1) {
2128
 
      ERROR("Reference to previous page, but no pages have been completed yet.");
2129
 
    }
2130
 
    ref = pdf_doc_ref_page(page_no - 1);
2131
 
  } else if (!strcmp(category, "@NEXTPAGE")) {
2132
 
    ref = pdf_doc_ref_page(page_no + 1);
2133
 
  }
2134
 
 
2135
 
  if (!ref) {
2136
 
    ERROR("Reference to \"%s\" not exist. ", category);
2137
 
  }
2138
 
 
2139
 
  return ref;
2140
 
}
2141
 
 
2142
 
static void
2143
 
pdf_doc_new_page (pdf_doc *p)
2144
 
{
2145
 
  pdf_page *currentpage;
2146
 
 
2147
 
  if (PAGECOUNT(p) >= MAXPAGES(p)) {
2148
 
    doc_resize_page_entries(p, MAXPAGES(p) + PDFDOC_PAGES_ALLOC_SIZE);
2149
 
  }
2150
 
 
2151
 
  /*
2152
 
   * This is confusing. pdf_doc_finish_page() have increased page count!
2153
 
   */
2154
 
  currentpage = LASTPAGE(p);
2155
 
  /* Was this page already instantiated by a forward reference to it? */
2156
 
  if (!currentpage->page_ref) {
2157
 
    currentpage->page_obj = pdf_new_dict();
2158
 
    currentpage->page_ref = pdf_ref_obj(currentpage->page_obj);
2159
 
  }
2160
 
 
2161
 
  currentpage->background = NULL;
2162
 
  currentpage->contents   = pdf_new_stream(STREAM_COMPRESS);
2163
 
  currentpage->resources  = pdf_new_dict();
2164
 
 
2165
 
  currentpage->annots = NULL;
2166
 
  currentpage->beads  = NULL;
2167
 
 
2168
 
  return;
2169
 
}
2170
 
 
2171
 
/* This only closes contents and resources. */
2172
 
static void
2173
 
pdf_doc_finish_page (pdf_doc *p)
2174
 
{
2175
 
  pdf_page *currentpage;
2176
 
 
2177
 
  if (p->pending_forms) {
2178
 
    ERROR("A pending form XObject at the end of page.");
2179
 
  }
2180
 
 
2181
 
  currentpage = LASTPAGE(p);
2182
 
  if (!currentpage->page_obj)
2183
 
    currentpage->page_obj = pdf_new_dict();
2184
 
 
2185
 
  /*
2186
 
   * Make Contents array.
2187
 
   */
2188
 
 
2189
 
  /*
2190
 
   * Global BOP content stream.
2191
 
   * pdf_ref_obj() returns reference itself when the object is
2192
 
   * indirect reference, not reference to the indirect reference.
2193
 
   * We keep bop itself but not reference to it since it is
2194
 
   * expected to be small.
2195
 
   */
2196
 
  if (p->pages.bop &&
2197
 
      pdf_stream_length(p->pages.bop) > 0) {
2198
 
    currentpage->content_refs[0] = pdf_ref_obj(p->pages.bop);
2199
 
  } else {
2200
 
    currentpage->content_refs[0] = NULL;
2201
 
  }
2202
 
  /*
2203
 
   * Current page background content stream.
2204
 
   */
2205
 
  if (currentpage->background) {
2206
 
    if (pdf_stream_length(currentpage->background) > 0) {
2207
 
      currentpage->content_refs[1] = pdf_ref_obj(currentpage->background);
2208
 
      pdf_add_stream (currentpage->background, "\n", 1);
2209
 
    }
2210
 
    pdf_release_obj(currentpage->background);
2211
 
    currentpage->background = NULL;
2212
 
  } else {
2213
 
    currentpage->content_refs[1] = NULL;
2214
 
  }
2215
 
 
2216
 
  /* Content body of current page */
2217
 
  currentpage->content_refs[2] = pdf_ref_obj(currentpage->contents);
2218
 
  pdf_add_stream (currentpage->contents, "\n", 1);
2219
 
  pdf_release_obj(currentpage->contents);
2220
 
  currentpage->contents = NULL;
2221
 
 
2222
 
  /*
2223
 
   * Global EOP content stream.
2224
 
   */
2225
 
  if (p->pages.eop &&
2226
 
      pdf_stream_length(p->pages.eop) > 0) {
2227
 
    currentpage->content_refs[3] = pdf_ref_obj(p->pages.eop);
2228
 
  } else {
2229
 
    currentpage->content_refs[3] = NULL;
2230
 
  }
2231
 
 
2232
 
  /*
2233
 
   * Page resources.
2234
 
   */
2235
 
  if (currentpage->resources) {
2236
 
    pdf_obj *procset;
2237
 
    /*
2238
 
     * ProcSet is obsolete in PDF-1.4 but recommended for compatibility.
2239
 
     */
2240
 
 
2241
 
    procset = pdf_new_array ();
2242
 
    pdf_add_array(procset, pdf_new_name("PDF"));
2243
 
    pdf_add_array(procset, pdf_new_name("Text"));
2244
 
    pdf_add_array(procset, pdf_new_name("ImageC"));
2245
 
    pdf_add_array(procset, pdf_new_name("ImageB"));
2246
 
    pdf_add_array(procset, pdf_new_name("ImageI"));
2247
 
    pdf_add_dict(currentpage->resources, pdf_new_name("ProcSet"), procset);
2248
 
 
2249
 
    pdf_add_dict(currentpage->page_obj,
2250
 
                 pdf_new_name("Resources"),
2251
 
                 pdf_ref_obj(currentpage->resources));
2252
 
    pdf_release_obj(currentpage->resources);
2253
 
    currentpage->resources = NULL;
2254
 
  }
2255
 
 
2256
 
  if (manual_thumb_enabled) {
2257
 
    char    *thumb_filename;
2258
 
    pdf_obj *thumb_ref;
2259
 
 
2260
 
    thumb_filename = NEW(strlen(thumb_basename)+7, char);
2261
 
    sprintf(thumb_filename, "%s.%ld",
2262
 
            thumb_basename, (p->pages.num_entries % 99999) + 1L);
2263
 
    thumb_ref = read_thumbnail(thumb_filename);
2264
 
    RELEASE(thumb_filename);
2265
 
    if (thumb_ref)
2266
 
      pdf_add_dict(currentpage->page_obj, pdf_new_name("Thumb"), thumb_ref);
2267
 
  }
2268
 
 
2269
 
  p->pages.num_entries++;
2270
 
 
2271
 
  return;
2272
 
}
2273
 
 
2274
 
 
2275
 
static pdf_color bgcolor;
2276
 
 
2277
 
void
2278
 
pdf_doc_set_bgcolor (const pdf_color *color)
2279
 
{
2280
 
  if (color)
2281
 
    pdf_color_copycolor(&bgcolor, color);
2282
 
  else { /* as clear... */
2283
 
    pdf_color_white(&bgcolor);
2284
 
  }
2285
 
}
2286
 
 
2287
 
static void
2288
 
doc_fill_page_background (pdf_doc *p)
2289
 
{
2290
 
  pdf_page  *currentpage;
2291
 
  pdf_rect   r;
2292
 
  int        cm;
2293
 
  pdf_obj   *saved_content;
2294
 
 
2295
 
  cm = pdf_dev_get_param(PDF_DEV_PARAM_COLORMODE);
2296
 
  if (!cm || pdf_color_is_white(&bgcolor)) {
2297
 
    return;
2298
 
  }
2299
 
 
2300
 
  pdf_doc_get_mediabox(pdf_doc_current_page_number(), &r);
2301
 
 
2302
 
  currentpage = LASTPAGE(p);
2303
 
  ASSERT(currentpage);
2304
 
 
2305
 
  if (!currentpage->background)
2306
 
    currentpage->background = pdf_new_stream(STREAM_COMPRESS);
2307
 
 
2308
 
  saved_content = currentpage->contents;
2309
 
  currentpage->contents = currentpage->background;
2310
 
 
2311
 
  pdf_dev_gsave();
2312
 
  pdf_dev_set_nonstrokingcolor(&bgcolor);
2313
 
  pdf_dev_rectfill(r.llx, r.lly, r.urx - r.llx, r.ury - r.lly);
2314
 
  pdf_dev_grestore();
2315
 
 
2316
 
  currentpage->contents = saved_content;
2317
 
 
2318
 
  return;
2319
 
}
2320
 
 
2321
 
void
2322
 
pdf_doc_begin_page (double scale, double x_origin, double y_origin)
2323
 
{
2324
 
  pdf_doc     *p = &pdoc;
2325
 
  pdf_tmatrix  M;
2326
 
 
2327
 
  M.a = scale; M.b = 0.0;
2328
 
  M.c = 0.0  ; M.d = scale;
2329
 
  M.e = x_origin;
2330
 
  M.f = y_origin;
2331
 
 
2332
 
  /* pdf_doc_new_page() allocates page content stream. */
2333
 
  pdf_doc_new_page(p);
2334
 
  pdf_dev_bop(&M);
2335
 
 
2336
 
  return;
2337
 
}
2338
 
 
2339
 
void
2340
 
pdf_doc_end_page (void)
2341
 
{
2342
 
  pdf_doc *p = &pdoc;
2343
 
 
2344
 
  pdf_dev_eop();
2345
 
  doc_fill_page_background(p);
2346
 
 
2347
 
  pdf_doc_finish_page(p);
2348
 
 
2349
 
  return;
2350
 
}
2351
 
 
2352
 
void
2353
 
pdf_doc_add_page_content (const char *buffer, unsigned length)
2354
 
{
2355
 
  pdf_doc  *p = &pdoc;
2356
 
  pdf_page *currentpage;
2357
 
 
2358
 
  if (p->pending_forms) {
2359
 
    pdf_add_stream(p->pending_forms->form.contents, buffer, length);
2360
 
  } else {
2361
 
    currentpage = LASTPAGE(p);
2362
 
    pdf_add_stream(currentpage->contents, buffer, length);
2363
 
  }
2364
 
 
2365
 
  return;
2366
 
}
2367
 
 
2368
 
static char *doccreator = NULL; /* Ugh */
2369
 
 
2370
 
void
2371
 
pdf_open_document (const char *filename,
2372
 
                   int do_encryption,
2373
 
                   double media_width, double media_height,
2374
 
                   double annot_grow_amount, int bookmark_open_depth,
2375
 
                   int check_gotos)
2376
 
{
2377
 
  pdf_doc *p = &pdoc;
2378
 
 
2379
 
  pdf_out_init(filename, do_encryption);
2380
 
 
2381
 
  pdf_doc_init_catalog(p);
2382
 
 
2383
 
  p->opt.annot_grow = annot_grow_amount;
2384
 
  p->opt.outline_open_depth = bookmark_open_depth;
2385
 
 
2386
 
  pdf_init_resources();
2387
 
  pdf_init_colors();
2388
 
  pdf_init_fonts();
2389
 
  /* Thumbnail want this to be initialized... */
2390
 
  pdf_init_images();
2391
 
 
2392
 
  pdf_doc_init_docinfo(p);
2393
 
  if (doccreator) {
2394
 
    pdf_add_dict(p->info,
2395
 
                 pdf_new_name("Creator"),
2396
 
                 pdf_new_string(doccreator, strlen(doccreator)));
2397
 
    RELEASE(doccreator); doccreator = NULL;
2398
 
  }
2399
 
 
2400
 
  pdf_doc_init_bookmarks(p, bookmark_open_depth);
2401
 
  pdf_doc_init_articles (p);
2402
 
  pdf_doc_init_names    (p, check_gotos);
2403
 
  pdf_doc_init_page_tree(p, media_width, media_height);
2404
 
 
2405
 
  pdf_doc_set_bgcolor(NULL);
2406
 
 
2407
 
  if (do_encryption) {
2408
 
    pdf_obj *encrypt = pdf_encrypt_obj();
2409
 
    pdf_set_encrypt(encrypt);
2410
 
    pdf_release_obj(encrypt);
2411
 
  }
2412
 
  pdf_set_id(pdf_enc_id_array());
2413
 
 
2414
 
  /* Create a default name for thumbnail image files */
2415
 
  if (manual_thumb_enabled) {
2416
 
    if (strlen(filename) > 4 &&
2417
 
        !strncmp(".pdf", filename + strlen(filename) - 4, 4)) {
2418
 
      thumb_basename = NEW(strlen(filename)-4+1, char);
2419
 
      strncpy(thumb_basename, filename, strlen(filename)-4);
2420
 
      thumb_basename[strlen(filename)-4] = 0;
2421
 
    } else {
2422
 
      thumb_basename = NEW(strlen(filename)+1, char);
2423
 
      strcpy(thumb_basename, filename);
2424
 
    }
2425
 
  }
2426
 
 
2427
 
  p->pending_forms = NULL;
2428
 
   
2429
 
  return;
2430
 
}
2431
 
 
2432
 
void
2433
 
pdf_doc_set_creator (const char *creator)
2434
 
{
2435
 
  if (!creator ||
2436
 
      creator[0] == '\0')
2437
 
    return;
2438
 
 
2439
 
  doccreator = NEW(strlen(creator)+1, char);
2440
 
  strcpy(doccreator, creator); /* Ugh */
2441
 
}
2442
 
 
2443
 
 
2444
 
void
2445
 
pdf_close_document (void)
2446
 
{
2447
 
  pdf_doc *p = &pdoc;
2448
 
 
2449
 
  /*
2450
 
   * Following things were kept around so user can add dictionary items.
2451
 
   */
2452
 
  pdf_doc_close_articles (p);
2453
 
  pdf_doc_close_names    (p);
2454
 
  pdf_doc_close_bookmarks(p);
2455
 
  pdf_doc_close_page_tree(p);
2456
 
  pdf_doc_close_docinfo  (p);
2457
 
 
2458
 
  pdf_doc_close_catalog  (p);
2459
 
 
2460
 
  pdf_close_images();
2461
 
  pdf_close_fonts ();
2462
 
  pdf_close_colors();
2463
 
 
2464
 
  pdf_close_resources(); /* Should be at last. */
2465
 
 
2466
 
  pdf_out_flush();
2467
 
 
2468
 
  if (thumb_basename)
2469
 
    RELEASE(thumb_basename);
2470
 
 
2471
 
  return;
2472
 
}
2473
 
 
2474
 
/*
2475
 
 * All this routine does is give the form a name and add a unity scaling matrix.
2476
 
 * It fills in required fields.  The caller must initialize the stream.
2477
 
 */
2478
 
static void
2479
 
pdf_doc_make_xform (pdf_obj     *xform,
2480
 
                    pdf_rect    *bbox,
2481
 
                    pdf_tmatrix *matrix,
2482
 
                    pdf_obj     *resources,
2483
 
                    pdf_obj     *attrib)
2484
 
{
2485
 
  pdf_obj *xform_dict;
2486
 
  pdf_obj *tmp;
2487
 
 
2488
 
  xform_dict = pdf_stream_dict(xform);
2489
 
  pdf_add_dict(xform_dict,
2490
 
               pdf_new_name("Type"),     pdf_new_name("XObject"));
2491
 
  pdf_add_dict(xform_dict,
2492
 
               pdf_new_name("Subtype"),  pdf_new_name("Form"));
2493
 
  pdf_add_dict(xform_dict,
2494
 
               pdf_new_name("FormType"), pdf_new_number(1.0));
2495
 
 
2496
 
  if (!bbox)
2497
 
    ERROR("No BoundingBox supplied.");
2498
 
 
2499
 
  tmp = pdf_new_array();
2500
 
  pdf_add_array(tmp, pdf_new_number(ROUND(bbox->llx, .001)));
2501
 
  pdf_add_array(tmp, pdf_new_number(ROUND(bbox->lly, .001)));
2502
 
  pdf_add_array(tmp, pdf_new_number(ROUND(bbox->urx, .001)));
2503
 
  pdf_add_array(tmp, pdf_new_number(ROUND(bbox->ury, .001)));
2504
 
  pdf_add_dict(xform_dict, pdf_new_name("BBox"), tmp);
2505
 
 
2506
 
  if (matrix) {
2507
 
    tmp = pdf_new_array();
2508
 
    pdf_add_array(tmp, pdf_new_number(ROUND(matrix->a, .00001)));
2509
 
    pdf_add_array(tmp, pdf_new_number(ROUND(matrix->b, .00001)));
2510
 
    pdf_add_array(tmp, pdf_new_number(ROUND(matrix->c, .00001)));
2511
 
    pdf_add_array(tmp, pdf_new_number(ROUND(matrix->d, .00001)));
2512
 
    pdf_add_array(tmp, pdf_new_number(ROUND(matrix->e, .001  )));
2513
 
    pdf_add_array(tmp, pdf_new_number(ROUND(matrix->f, .001  )));
2514
 
    pdf_add_dict(xform_dict, pdf_new_name("Matrix"), tmp);
2515
 
  }
2516
 
 
2517
 
  if (attrib) {
2518
 
    pdf_merge_dict(xform_dict, attrib);
2519
 
  }
2520
 
 
2521
 
  pdf_add_dict(xform_dict, pdf_new_name("Resources"), resources);
2522
 
 
2523
 
  return;
2524
 
}
2525
 
 
2526
 
/*
2527
 
 * begin_form_xobj creates an xobject with its "origin" at
2528
 
 * xpos and ypos that is clipped to the specified bbox. Note
2529
 
 * that the origin is not the lower left corner of the bbox.
2530
 
 */
2531
 
int
2532
 
pdf_doc_begin_grabbing (const char *ident,
2533
 
                        double ref_x, double ref_y, const pdf_rect *cropbox)
2534
 
{
2535
 
  int         xobj_id = -1;
2536
 
  pdf_doc    *p = &pdoc;
2537
 
  pdf_form   *form;
2538
 
  struct form_list_node *fnode;
2539
 
  xform_info  info;
2540
 
 
2541
 
  pdf_dev_push_gstate();
2542
 
 
2543
 
  fnode = NEW(1, struct form_list_node);
2544
 
 
2545
 
  fnode->prev    = p->pending_forms;
2546
 
  fnode->q_depth = pdf_dev_current_depth();
2547
 
  form           = &fnode->form;
2548
 
 
2549
 
  /*
2550
 
  * The reference point of an Xobject is at the lower left corner
2551
 
  * of the bounding box.  Since we would like to have an arbitrary
2552
 
  * reference point, we use a transformation matrix, translating
2553
 
  * the reference point to (0,0).
2554
 
  */
2555
 
 
2556
 
  form->matrix.a = 1.0; form->matrix.b = 0.0;
2557
 
  form->matrix.c = 0.0; form->matrix.d = 1.0;
2558
 
  form->matrix.e = -ref_x;
2559
 
  form->matrix.f = -ref_y;
2560
 
 
2561
 
  form->cropbox.llx = ref_x + cropbox->llx;
2562
 
  form->cropbox.lly = ref_y + cropbox->lly;
2563
 
  form->cropbox.urx = ref_x + cropbox->urx;
2564
 
  form->cropbox.ury = ref_y + cropbox->ury;
2565
 
 
2566
 
  form->contents  = pdf_new_stream(STREAM_COMPRESS);
2567
 
  form->resources = pdf_new_dict();
2568
 
 
2569
 
  pdf_ximage_init_form_info(&info);
2570
 
 
2571
 
  info.matrix.a = 1.0; info.matrix.b = 0.0;
2572
 
  info.matrix.c = 0.0; info.matrix.d = 1.0;
2573
 
  info.matrix.e = -ref_x;
2574
 
  info.matrix.f = -ref_y;
2575
 
 
2576
 
  info.bbox.llx = cropbox->llx;
2577
 
  info.bbox.lly = cropbox->lly;
2578
 
  info.bbox.urx = cropbox->urx;
2579
 
  info.bbox.ury = cropbox->ury;
2580
 
 
2581
 
  /* Use reference since content itself isn't available yet. */
2582
 
  xobj_id = pdf_ximage_defineresource(ident,
2583
 
                                      PDF_XOBJECT_TYPE_FORM,
2584
 
                                      &info, pdf_ref_obj(form->contents));
2585
 
 
2586
 
  p->pending_forms = fnode;
2587
 
 
2588
 
  /*
2589
 
   * Make sure the object is self-contained by adding the
2590
 
   * current font and color to the object stream.
2591
 
   */
2592
 
  pdf_dev_reset_fonts();
2593
 
  pdf_dev_reset_color(1);  /* force color operators to be added to stream */
2594
 
 
2595
 
  return xobj_id;
2596
 
}
2597
 
 
2598
 
void
2599
 
pdf_doc_end_grabbing (pdf_obj *attrib)
2600
 
{
2601
 
  pdf_form *form;
2602
 
  pdf_obj  *procset;
2603
 
  pdf_doc  *p = &pdoc;
2604
 
  struct form_list_node *fnode;
2605
 
 
2606
 
  if (!p->pending_forms) {
2607
 
    WARN("Tried to close a nonexistent form XOject.");
2608
 
    return;
2609
 
  }
2610
 
  
2611
 
  fnode = p->pending_forms;
2612
 
  form  = &fnode->form;
2613
 
 
2614
 
  pdf_dev_grestore_to(fnode->q_depth);
2615
 
 
2616
 
  /*
2617
 
   * ProcSet is obsolete in PDF-1.4 but recommended for compatibility.
2618
 
   */
2619
 
  procset = pdf_new_array();
2620
 
  pdf_add_array(procset, pdf_new_name("PDF"));
2621
 
  pdf_add_array(procset, pdf_new_name("Text"));
2622
 
  pdf_add_array(procset, pdf_new_name("ImageC"));
2623
 
  pdf_add_array(procset, pdf_new_name("ImageB"));
2624
 
  pdf_add_array(procset, pdf_new_name("ImageI"));
2625
 
  pdf_add_dict (form->resources, pdf_new_name("ProcSet"), procset);
2626
 
 
2627
 
  pdf_doc_make_xform(form->contents,
2628
 
                     &form->cropbox, &form->matrix,
2629
 
                     pdf_ref_obj(form->resources), attrib);
2630
 
  pdf_release_obj(form->resources);
2631
 
  pdf_release_obj(form->contents);
2632
 
  if (attrib) pdf_release_obj(attrib);
2633
 
 
2634
 
  p->pending_forms = fnode->prev;
2635
 
  
2636
 
  pdf_dev_pop_gstate();
2637
 
 
2638
 
  pdf_dev_reset_fonts();
2639
 
  pdf_dev_reset_color(0);
2640
 
 
2641
 
  RELEASE(fnode);
2642
 
 
2643
 
  return;
2644
 
}
2645
 
 
2646
 
static struct
2647
 
{
2648
 
  int      dirty;
2649
 
  int      broken;
2650
 
  pdf_obj *annot_dict;
2651
 
  pdf_rect rect;
2652
 
} breaking_state = {0, 0, NULL, {0.0, 0.0, 0.0, 0.0}};
2653
 
 
2654
 
static void
2655
 
reset_box (void)
2656
 
{
2657
 
  breaking_state.rect.llx = breaking_state.rect.lly =  HUGE_VAL;
2658
 
  breaking_state.rect.urx = breaking_state.rect.ury = -HUGE_VAL;
2659
 
  breaking_state.dirty    = 0;
2660
 
}
2661
 
 
2662
 
void
2663
 
pdf_doc_begin_annot (pdf_obj *dict)
2664
 
{
2665
 
  breaking_state.annot_dict = dict;
2666
 
  breaking_state.broken = 0;
2667
 
  reset_box();
2668
 
}
2669
 
 
2670
 
void
2671
 
pdf_doc_end_annot (void)
2672
 
{
2673
 
  pdf_doc_break_annot();
2674
 
  breaking_state.annot_dict = NULL;
2675
 
}
2676
 
 
2677
 
void
2678
 
pdf_doc_break_annot (void)
2679
 
{
2680
 
  if (breaking_state.dirty) {
2681
 
    pdf_obj  *annot_dict;
2682
 
 
2683
 
    /* Copy dict */
2684
 
    annot_dict = pdf_new_dict();
2685
 
    pdf_merge_dict(annot_dict, breaking_state.annot_dict);
2686
 
    pdf_doc_add_annot(pdf_doc_current_page_number(), &(breaking_state.rect),
2687
 
                      annot_dict, !breaking_state.broken);
2688
 
    pdf_release_obj(annot_dict);
2689
 
 
2690
 
    breaking_state.broken = 1;
2691
 
  }
2692
 
  reset_box();
2693
 
}
2694
 
 
2695
 
void
2696
 
pdf_doc_expand_box (const pdf_rect *rect)
2697
 
{
2698
 
  breaking_state.rect.llx = MIN(breaking_state.rect.llx, rect->llx);
2699
 
  breaking_state.rect.lly = MIN(breaking_state.rect.lly, rect->lly);
2700
 
  breaking_state.rect.urx = MAX(breaking_state.rect.urx, rect->urx);
2701
 
  breaking_state.rect.ury = MAX(breaking_state.rect.ury, rect->ury);
2702
 
  breaking_state.dirty    = 1;
2703
 
}
2704
 
 
2705
 
#if 0
2706
 
/* This should be number tree */
2707
 
void
2708
 
pdf_doc_set_pagelabel (long  pg_start,
2709
 
                       const char *type,
2710
 
                       const void *prefix, int prfx_len, long start)
2711
 
{
2712
 
  pdf_doc *p = &pdoc;
2713
 
  pdf_obj *label_dict;
2714
 
 
2715
 
  if (!p->root.pagelabels)
2716
 
    p->root.pagelabels = pdf_new_array();
2717
 
 
2718
 
  label_dict = pdf_new_dict();
2719
 
  if (!type || type[0] == '\0') /* Set back to default. */
2720
 
    pdf_add_dict(label_dict, pdf_new_name("S"),  pdf_new_name("D"));
2721
 
  else {
2722
 
    if (type)
2723
 
      pdf_add_dict(label_dict, pdf_new_name("S"), pdf_new_name(type));
2724
 
    if (prefix && prfx_len > 0)
2725
 
      pdf_add_dict(label_dict,
2726
 
                   pdf_new_name("P"),
2727
 
                   pdf_new_string(prefix, prfx_len));
2728
 
    if (start != 1)
2729
 
      pdf_add_dict(label_dict,
2730
 
                   pdf_new_name("St"), pdf_new_number(start));
2731
 
  }
2732
 
 
2733
 
  pdf_add_array(p->root.pagelabels, pdf_new_number(pg_start));
2734
 
  pdf_add_array(p->root.pagelabels, label_dict);
2735
 
 
2736
 
  return;
2737
 
}
2738
 
#endif