1/*---------------------------------------------------------------------------*
2 |              PDFlib - A library for generating PDF on the fly             |
3 +---------------------------------------------------------------------------+
4 | Copyright (c) 1997-2004 Thomas Merz and PDFlib GmbH. All rights reserved. |
5 +---------------------------------------------------------------------------+
6 |                                                                           |
7 |    This software is subject to the PDFlib license. It is NOT in the       |
8 |    public domain. Extended versions and commercial licenses are           |
9 |    available, please check http://www.pdflib.com.                         |
10 |                                                                           |
11 *---------------------------------------------------------------------------*/
12
13/* $Id: p_annots.c 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * PDFlib routines for annnotations
16 *
17 */
18
19#include "p_intern.h"
20
21/* Annotation types */
22typedef enum {
23    ann_text, ann_locallink,
24    ann_pdflink, ann_weblink,
25    ann_launchlink, ann_attach
26} pdf_annot_type;
27
28/* icons for file attachments and text annotations */
29typedef enum {
30    icon_file_graph, icon_file_paperclip,
31    icon_file_pushpin, icon_file_tag,
32
33    icon_text_comment, icon_text_insert,
34    icon_text_note, icon_text_paragraph,
35    icon_text_newparagraph, icon_text_key,
36    icon_text_help
37} pdf_icon;
38
39/* Annotations */
40struct pdf_annot_s {
41    pdf_annot_type	type;		/* used for all annotation types */
42    pdc_rectangle	rect;		/* used for all annotation types */
43    pdc_id		obj_id;		/* used for all annotation types */
44    pdf_annot		*next;		/* used for all annotation types */
45
46    pdf_icon		icon;		/* attach and text */
47    char		*filename;	/* attach, launchlink, pdflink,weblink*/
48    char		*contents;	/* text, attach, pdflink */
49    char		*mimetype;	/* attach */
50    char		*parameters;	/* launchlink */
51    char		*operation;	/* launchlink */
52    char		*defaultdir;	/* launchlink */
53
54    char		*title;		/* text */
55    int			open;		/* text */
56    pdf_dest		dest;		/* locallink, pdflink */
57
58    /* -------------- annotation border style and color -------------- */
59    pdf_border_style	border_style;
60    float		border_width;
61    float		border_red;
62    float		border_green;
63    float		border_blue;
64    float		border_dash1;
65    float		border_dash2;
66};
67
68static const char *pdf_border_style_names[] = {
69    "S",	/* solid border */
70    "D",	/* dashed border */
71    "B",	/* beveled (three-dimensional) border */
72    "I",	/* inset border */
73    "U"		/* underlined border */
74};
75
76static const char *pdf_icon_names[] = {
77    /* embedded file icon names */
78    "Graph", "Paperclip", "Pushpin", "Tag",
79
80    /* text annotation icon names */
81    "Comment", "Insert", "Note", "Paragraph", "NewParagraph", "Key", "Help"
82};
83
84/* flags for annotation properties */
85typedef enum {
86    pdf_ann_flag_invisible	= 1,
87    pdf_ann_flag_hidden		= 2,
88    pdf_ann_flag_print		= 4,
89    pdf_ann_flag_nozoom		= 8,
90    pdf_ann_flag_norotate	= 16,
91    pdf_ann_flag_noview		= 32,
92    pdf_ann_flag_readonly	= 64
93} pdf_ann_flag;
94
95void
96pdf_init_annots(PDF *p)
97{
98    /* annotation border style defaults */
99    p->border_style	= border_solid;
100    p->border_width	= (float) 1.0;
101    p->border_red	= (float) 0.0;
102    p->border_green	= (float) 0.0;
103    p->border_blue	= (float) 0.0;
104    p->border_dash1	= (float) 3.0;
105    p->border_dash2	= (float) 3.0;
106
107    /* auxiliary function parameters */
108    p->launchlink_parameters	= NULL;
109    p->launchlink_operation	= NULL;
110    p->launchlink_defaultdir	= NULL;
111}
112
113void
114pdf_cleanup_annots(PDF *p)
115{
116    if (p->launchlink_parameters) {
117	pdc_free(p->pdc, p->launchlink_parameters);
118	p->launchlink_parameters = NULL;
119    }
120
121    if (p->launchlink_operation) {
122	pdc_free(p->pdc, p->launchlink_operation);
123	p->launchlink_operation = NULL;
124    }
125
126    if (p->launchlink_defaultdir) {
127	pdc_free(p->pdc, p->launchlink_defaultdir);
128	p->launchlink_defaultdir = NULL;
129    }
130}
131
132static void
133pdf_init_annot(PDF *p, pdf_annot *ann)
134{
135    (void) p;
136
137    ann->next           = NULL;
138    ann->filename       = NULL;
139    ann->mimetype       = NULL;
140    ann->contents       = NULL;
141    ann->mimetype       = NULL;
142    ann->parameters     = NULL;
143    ann->operation      = NULL;
144    ann->defaultdir     = NULL;
145    ann->title          = NULL;
146}
147
148
149/* Write annotation border style and color */
150static void
151pdf_write_border_style(PDF *p, pdf_annot *ann)
152{
153    /* don't write the default values */
154    if (ann->border_style == border_solid &&
155        ann->border_width == (float) 1.0 &&
156        ann->border_red == (float) 0.0 &&
157        ann->border_green == (float) 0.0 &&
158        ann->border_blue == (float) 0.0 &&
159        ann->border_dash1 == (float) 3.0 &&
160        ann->border_dash2 == (float) 3.0)
161        return;
162
163    if (ann->type != ann_attach) {
164	pdc_puts(p->out, "/BS");
165	pdc_begin_dict(p->out);			/* BS dict */
166	pdc_puts(p->out, "/Type/Border\n");
167
168	/* Acrobat 6 requires this entry, and does not use /S/S as default */
169	pdc_printf(p->out, "/S/%s\n",
170	    pdf_border_style_names[ann->border_style]);
171
172	/* Acrobat 6 requires this entry */
173	pdc_printf(p->out, "/W %f\n", ann->border_width);
174
175	if (ann->border_style == border_dashed)
176	    pdc_printf(p->out, "/D[%f %f]\n",
177		ann->border_dash1, ann->border_dash2);
178
179	pdc_end_dict(p->out);			/* BS dict */
180
181	/* Write the Border key in old-style PDF 1.1 format */
182	pdc_printf(p->out, "/Border[0 0 %f", ann->border_width);
183
184	if (ann->border_style == border_dashed &&
185	    (ann->border_dash1 != (float) 0.0 || ann->border_dash2 !=
186	    (float) 0.0))
187	    /* set dashed border */
188	    pdc_printf(p->out, "[%f %f]", ann->border_dash1, ann->border_dash2);
189
190	pdc_puts(p->out, "]\n");
191
192    }
193
194    /* write annotation color */
195    pdc_printf(p->out, "/C[%f %f %f]\n",
196		    ann->border_red, ann->border_green, ann->border_blue);
197}
198
199void
200pdf_write_annots_root(PDF *p)
201{
202    pdf_annot *ann;
203
204    /* Annotations array */
205    if (p->annots) {
206	pdc_puts(p->out, "/Annots[");
207
208	for (ann = p->annots; ann != NULL; ann = ann->next) {
209	    ann->obj_id = pdc_alloc_id(p->out);
210	    pdc_printf(p->out, "%ld 0 R ", ann->obj_id);
211	}
212
213	pdc_puts(p->out, "]\n");
214    }
215}
216
217void
218pdf_write_page_annots(PDF *p)
219{
220    pdf_annot	*ann;
221
222    for (ann = p->annots; ann != NULL; ann = ann->next) {
223
224	pdc_begin_obj(p->out, ann->obj_id);	/* Annotation object */
225	pdc_begin_dict(p->out);		/* Annotation dict */
226
227
228	pdc_puts(p->out, "/Type/Annot\n");
229	switch (ann->type) {
230	    case ann_text:
231		pdc_puts(p->out, "/Subtype/Text\n");
232		pdc_printf(p->out, "/Rect[%f %f %f %f]\n",
233		    ann->rect.llx, ann->rect.lly, ann->rect.urx, ann->rect.ury);
234
235		pdf_write_border_style(p, ann);
236
237		if (ann->open)
238		    pdc_puts(p->out, "/Open true\n");
239
240		if (ann->icon != icon_text_note)	/* note is default */
241		    pdc_printf(p->out, "/Name/%s\n", pdf_icon_names[ann->icon]);
242
243		/* Contents key is required, but may be empty */
244		pdc_puts(p->out, "/Contents");
245
246		if (ann->contents) {
247		    pdc_put_pdfunistring(p->out, ann->contents);
248                    pdc_puts(p->out, "\n");
249		} else
250		    pdc_puts(p->out, "()\n"); /* empty contents is OK */
251
252		/* title is optional */
253		if (ann->title) {
254		    pdc_puts(p->out, "/T");
255		    pdc_put_pdfunistring(p->out, ann->title);
256                    pdc_puts(p->out, "\n");
257		}
258
259		break;
260
261	    case ann_locallink:
262		pdc_puts(p->out, "/Subtype/Link\n");
263		pdc_printf(p->out, "/Rect[%f %f %f %f]\n",
264		    ann->rect.llx, ann->rect.lly, ann->rect.urx, ann->rect.ury);
265
266		pdf_write_border_style(p, ann);
267
268		/* preallocate page object id for a later page */
269		if (ann->dest.page > p->current_page) {
270		    while (ann->dest.page >= p->pages_capacity)
271			pdf_grow_pages(p);
272
273		    /* if this page has already been used as a link target
274		     * it will already have an object id.
275		     */
276		    if (p->pages[ann->dest.page] == PDC_BAD_ID)
277			p->pages[ann->dest.page] = pdc_alloc_id(p->out);
278		}
279
280		pdc_puts(p->out, "/Dest");
281		pdf_write_destination(p, &ann->dest);
282		pdc_puts(p->out, "\n");
283
284		break;
285
286	    case ann_pdflink:
287		pdc_puts(p->out, "/Subtype/Link\n");
288		pdc_printf(p->out, "/Rect[%f %f %f %f]\n",
289		    ann->rect.llx, ann->rect.lly, ann->rect.urx, ann->rect.ury);
290
291		pdf_write_border_style(p, ann);
292
293		pdc_puts(p->out, "/A");
294		pdc_begin_dict(p->out);			/* A dict */
295		pdc_puts(p->out, "/Type/Action/S/GoToR\n");
296
297		pdc_puts(p->out, "/D");
298		pdf_write_destination(p, &ann->dest);
299		pdc_puts(p->out, "\n");
300
301		pdc_puts(p->out, "/F");
302		pdc_begin_dict(p->out);			/* F dict */
303		pdc_puts(p->out, "/Type/Filespec\n");
304		pdc_puts(p->out, "/F");
305		pdc_put_pdfstring(p->out, ann->filename,
306		    (int)strlen(ann->filename));
307		pdc_puts(p->out, "\n");
308		pdc_end_dict(p->out);			/* F dict */
309
310		pdc_end_dict(p->out);			/* A dict */
311
312		break;
313
314	    case ann_launchlink:
315		pdc_puts(p->out, "/Subtype/Link\n");
316		pdc_printf(p->out, "/Rect[%f %f %f %f]\n",
317		    ann->rect.llx, ann->rect.lly, ann->rect.urx, ann->rect.ury);
318
319		pdf_write_border_style(p, ann);
320
321		pdc_puts(p->out, "/A");
322		pdc_begin_dict(p->out);			/* A dict */
323		pdc_puts(p->out, "/Type/Action/S/Launch\n");
324
325		if (ann->parameters || ann->operation || ann->defaultdir) {
326		    pdc_puts(p->out, "/Win");
327		    pdc_begin_dict(p->out);			/* Win dict */
328		    pdc_printf(p->out, "/F");
329		    pdc_put_pdfstring(p->out, ann->filename,
330			(int)strlen(ann->filename));
331		    pdc_puts(p->out, "\n");
332		    if (ann->parameters) {
333			pdc_printf(p->out, "/P");
334			pdc_put_pdfstring(p->out, ann->parameters,
335			    (int)strlen(ann->parameters));
336			pdc_puts(p->out, "\n");
337			pdc_free(p->pdc, ann->parameters);
338			ann->parameters = NULL;
339		    }
340		    if (ann->operation) {
341			pdc_printf(p->out, "/O");
342			pdc_put_pdfstring(p->out, ann->operation,
343			    (int)strlen(ann->operation));
344			pdc_puts(p->out, "\n");
345			pdc_free(p->pdc, ann->operation);
346			ann->operation = NULL;
347		    }
348		    if (ann->defaultdir) {
349			pdc_printf(p->out, "/D");
350			pdc_put_pdfstring(p->out, ann->defaultdir,
351			    (int)strlen(ann->defaultdir));
352			pdc_puts(p->out, "\n");
353			pdc_free(p->pdc, ann->defaultdir);
354			ann->defaultdir = NULL;
355		    }
356		    pdc_end_dict(p->out);			/* Win dict */
357		} else {
358		    pdc_puts(p->out, "/F");
359		    pdc_begin_dict(p->out);			/* F dict */
360		    pdc_puts(p->out, "/Type/Filespec\n");
361		    pdc_printf(p->out, "/F");
362		    pdc_put_pdfstring(p->out, ann->filename,
363			(int)strlen(ann->filename));
364		    pdc_puts(p->out, "\n");
365		    pdc_end_dict(p->out);			/* F dict */
366		}
367
368		pdc_end_dict(p->out);			/* A dict */
369
370		break;
371
372	    case ann_weblink:
373		pdc_puts(p->out, "/Subtype/Link\n");
374		pdc_printf(p->out, "/Rect[%f %f %f %f]\n",
375		    ann->rect.llx, ann->rect.lly, ann->rect.urx, ann->rect.ury);
376
377		pdf_write_border_style(p, ann);
378
379		pdc_puts(p->out, "/A<</S/URI/URI");
380		pdc_put_pdfstring(p->out, ann->filename,
381		    (int)strlen(ann->filename));
382		pdc_puts(p->out, ">>\n");
383		break;
384
385	    case ann_attach:
386		pdc_puts(p->out, "/Subtype/FileAttachment\n");
387		pdc_printf(p->out, "/Rect[%f %f %f %f]\n",
388		    ann->rect.llx, ann->rect.lly, ann->rect.urx, ann->rect.ury);
389
390		pdf_write_border_style(p, ann);
391
392		if (ann->icon != icon_file_pushpin)	/* pushpin is default */
393		    pdc_printf(p->out, "/Name/%s\n",
394		    	pdf_icon_names[ann->icon]);
395
396		if (ann->title) {
397		    pdc_puts(p->out, "/T");
398		    pdc_put_pdfunistring(p->out, ann->title);
399                    pdc_puts(p->out, "\n");
400		}
401
402		if (ann->contents) {
403		    pdc_puts(p->out, "/Contents");
404		    pdc_put_pdfunistring(p->out, ann->contents);
405                    pdc_puts(p->out, "\n");
406		}
407
408		/* the icon is too small without these flags (=28) */
409		pdc_printf(p->out, "/F %d\n",
410			pdf_ann_flag_print |
411			pdf_ann_flag_nozoom |
412			pdf_ann_flag_norotate);
413
414		pdc_puts(p->out, "/FS");
415		pdc_begin_dict(p->out);			/* FS dict */
416		pdc_puts(p->out, "/Type/Filespec\n");
417
418		pdc_puts(p->out, "/F");
419		pdc_put_pdfstring(p->out, ann->filename,
420		    (int)strlen(ann->filename));
421		pdc_puts(p->out, "\n");
422
423		/* alloc id for the actual embedded file stream */
424		ann->obj_id = pdc_alloc_id(p->out);
425		pdc_printf(p->out, "/EF<</F %ld 0 R>>\n", ann->obj_id);
426		pdc_end_dict(p->out);			/* FS dict */
427
428		break;
429
430	    default:
431		pdc_error(p->pdc, PDF_E_INT_BADANNOT,
432		    pdc_errprintf(p->pdc, "%d", ann->type), 0, 0, 0);
433	}
434
435	pdc_end_dict(p->out);		/* Annotation dict */
436	pdc_end_obj(p->out);		/* Annotation object */
437    }
438
439    /* Write the actual embedded files with preallocated ids */
440    for (ann = p->annots; ann != NULL; ann = ann->next) {
441	pdc_id		length_id;
442	PDF_data_source src;
443
444	if (ann->type != ann_attach)
445	    continue;
446
447	pdc_begin_obj(p->out, ann->obj_id);	/* EmbeddedFile */
448	pdc_puts(p->out, "<</Type/EmbeddedFile\n");
449
450	if (ann->mimetype) {
451	    pdc_puts(p->out, "/Subtype");
452            pdc_put_pdfname(p->out, ann->mimetype, strlen(ann->mimetype));
453	    pdc_puts(p->out, "\n");
454	}
455
456	if (pdc_get_compresslevel(p->out))
457		pdc_puts(p->out, "/Filter/FlateDecode\n");
458
459	length_id = pdc_alloc_id(p->out);
460	pdc_printf(p->out, "/Length %ld 0 R\n", length_id);
461	pdc_end_dict(p->out);		/* F dict */
462
463	/* write the file in the PDF */
464	src.private_data = (void *) ann->filename;
465	src.init	= pdf_data_source_file_init;
466	src.fill	= pdf_data_source_file_fill;
467	src.terminate	= pdf_data_source_file_terminate;
468	src.length	= (long) 0;
469	src.offset	= (long) 0;
470
471	pdf_copy_stream(p, &src, pdc_true);	/* embedded file stream */
472
473	pdc_end_obj(p->out);			/* EmbeddedFile object */
474
475	pdc_put_pdfstreamlength(p->out, length_id);
476
477	if (p->flush & pdf_flush_content)
478	    pdc_flush_stream(p->out);
479    }
480}
481
482void
483pdf_init_page_annots(PDF *p)
484{
485    p->annots = NULL;
486}
487
488void
489pdf_cleanup_page_annots(PDF *p)
490{
491    pdf_annot *ann, *old;
492
493    for (ann = p->annots; ann != (pdf_annot *) NULL; /* */ ) {
494	switch (ann->type) {
495	    case ann_text:
496		if (ann->contents)
497		    pdc_free(p->pdc, ann->contents);
498		if (ann->title)
499		    pdc_free(p->pdc, ann->title);
500		break;
501
502	    case ann_locallink:
503		pdf_cleanup_destination(p, &ann->dest);
504		break;
505
506	    case ann_launchlink:
507		pdc_free(p->pdc, ann->filename);
508		break;
509
510	    case ann_pdflink:
511		pdf_cleanup_destination(p, &ann->dest);
512		pdc_free(p->pdc, ann->filename);
513		break;
514
515	    case ann_weblink:
516		pdc_free(p->pdc, ann->filename);
517		break;
518
519	    case ann_attach:
520		pdf_unlock_pvf(p, ann->filename);
521		pdc_free(p->pdc, ann->filename);
522		if (ann->contents)
523		    pdc_free(p->pdc, ann->contents);
524		if (ann->title)
525		    pdc_free(p->pdc, ann->title);
526		if (ann->mimetype)
527		    pdc_free(p->pdc, ann->mimetype);
528		break;
529
530	    default:
531		pdc_error(p->pdc, PDF_E_INT_BADANNOT,
532		    pdc_errprintf(p->pdc, "%d", ann->type), 0, 0, 0);
533	}
534	old = ann;
535	ann = old->next;
536	pdc_free(p->pdc, old);
537    }
538    p->annots = NULL;
539}
540
541/* Insert new annotation at the end of the annots chain */
542static void
543pdf_add_annot(PDF *p, pdf_annot *ann)
544{
545    pdf_annot *last;
546
547    /* fetch current border state from p */
548    ann->border_style	= p->border_style;
549    ann->border_width	= p->border_width;
550    ann->border_red	= p->border_red;
551    ann->border_green	= p->border_green;
552    ann->border_blue	= p->border_blue;
553    ann->border_dash1	= p->border_dash1;
554    ann->border_dash2	= p->border_dash2;
555
556    ann->next = NULL;
557
558    if (p->annots == NULL)
559	p->annots = ann;
560    else {
561	for (last = p->annots; last->next != NULL; /* */ )
562	    last = last->next;
563	last->next = ann;
564    }
565}
566
567static void
568pdf_init_rectangle(PDF *p, pdf_annot *ann,
569                   float llx, float lly, float urx, float ury)
570{
571    pdc_rect_init(&ann->rect, llx, lly, urx, ury);
572    if (p->usercoordinates == pdc_true)
573        pdc_rect_transform(&p->gstate[p->sl].ctm, &ann->rect, &ann->rect);
574}
575
576
577
578
579/* Attach an arbitrary file to the PDF. Note that the actual
580 * embedding takes place in PDF_end_page().
581 * description, author, and mimetype may be NULL.
582 */
583
584static const pdc_keyconn pdf_icon_attach_keylist[] =
585{
586    {"graph",     icon_file_graph},
587    {"paperclip", icon_file_paperclip},
588    {"pushpin",   icon_file_pushpin},
589    {"tag",       icon_file_tag},
590    {NULL, 0}
591};
592
593static void
594pdf__attach_file(
595    PDF *p,
596    float llx,
597    float lly,
598    float urx,
599    float ury,
600    const char *filename,
601    const char *description,
602    int len_descr,
603    const char *author,
604    int len_auth,
605    const char *mimetype,
606    const char *icon)
607{
608    static const char fn[] = "pdf__attach_file";
609    pdf_annot *ann;
610    pdc_file *attfile;
611
612    if (filename == NULL || *filename == '\0')
613	pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "filename", 0, 0, 0);
614
615    if ((attfile = pdf_fopen(p, filename, "attachment ", 0)) == NULL)
616	pdc_error(p->pdc, -1, 0, 0, 0, 0);
617
618    pdf_lock_pvf(p, filename);
619    pdc_fclose(attfile);
620
621    ann = (pdf_annot *) pdc_malloc(p->pdc, sizeof(pdf_annot), fn);
622
623    pdf_init_annot(p, ann);
624
625    ann->type	  = ann_attach;
626    pdf_init_rectangle(p, ann, llx, lly, urx, ury);
627
628    if (icon == NULL || !*icon)
629        icon = "pushpin";
630    ann->icon = (pdf_icon) pdc_get_keycode(icon, pdf_icon_attach_keylist);
631    if (ann->icon == PDC_KEY_NOTFOUND)
632        pdc_error(p->pdc, PDC_E_ILLARG_STRING, "icon", icon, 0, 0);
633
634    ann->filename = (char *) pdc_strdup(p->pdc, filename);
635
636    ann->contents = pdf_convert_hypertext(p, description, len_descr);
637
638    ann->title = pdf_convert_hypertext(p, author, len_auth);
639
640    if (mimetype != NULL) {
641	ann->mimetype = (char *) pdc_strdup(p->pdc, mimetype);
642    }
643
644    pdf_add_annot(p, ann);
645}
646
647PDFLIB_API void PDFLIB_CALL
648PDF_attach_file(
649    PDF *p,
650    float llx,
651    float lly,
652    float urx,
653    float ury,
654    const char *filename,
655    const char *description,
656    const char *author,
657    const char *mimetype,
658    const char *icon)
659{
660    static const char fn[] = "PDF_attach_file";
661
662    if (pdf_enter_api(p, fn, pdf_state_page,
663        "(p[%p], %g, %g, %g, %g, \"%s\", \"%s\", \"%s\", \"%s\", \"%s\")\n",
664        (void *) p, llx, lly, urx, ury, filename,
665        pdc_strprint(p->pdc, description, 0),
666        pdc_strprint(p->pdc, author, 0), mimetype, icon))
667    {
668        int len_descr = description ? (int) pdc_strlen(description) : 0;
669        int len_auth = author ? (int) pdc_strlen(author) : 0;
670        pdf__attach_file(p, llx, lly, urx, ury, filename,
671            description, len_descr, author, len_auth, mimetype, icon) ;
672    }
673}
674
675PDFLIB_API void PDFLIB_CALL
676PDF_attach_file2(
677    PDF *p,
678    float llx,
679    float lly,
680    float urx,
681    float ury,
682    const char *filename,
683    int reserved,
684    const char *description,
685    int len_descr,
686    const char *author,
687    int len_auth,
688    const char *mimetype,
689    const char *icon)
690{
691    static const char fn[] = "PDF_attach_file2";
692
693    if (pdf_enter_api(p, fn, pdf_state_page,
694        "(p[%p], %g, %g, %g, %g, \"%s\", %d, \"%s\", %d, "
695        "\"%s\", %d, \"%s\", \"%s\")\n",
696        (void *) p, llx, lly, urx, ury, filename, reserved,
697        pdc_strprint(p->pdc, description, len_descr), len_descr,
698        pdc_strprint(p->pdc, author, len_auth), len_auth, mimetype, icon))
699    {
700        pdf__attach_file(p, llx, lly, urx, ury, filename,
701            description, len_descr, author, len_auth, mimetype, icon) ;
702    }
703}
704
705static const pdc_keyconn pdf_icon_note_keylist[] =
706{
707    {"comment",      icon_text_comment},
708    {"insert",       icon_text_insert},
709    {"note",         icon_text_note},
710    {"paragraph",    icon_text_paragraph},
711    {"newparagraph", icon_text_newparagraph},
712    {"key",          icon_text_key},
713    {"help",         icon_text_help},
714    {NULL, 0}
715};
716
717static void
718pdf__add_note(
719    PDF *p,
720    float llx,
721    float lly,
722    float urx,
723    float ury,
724    const char *contents,
725    int len_cont,
726    const char *title,
727    int len_title,
728    const char *icon,
729    int open)
730{
731    static const char fn[] = "pdf__add_note";
732    pdf_annot *ann;
733
734    ann = (pdf_annot *) pdc_malloc(p->pdc, sizeof(pdf_annot), fn);
735
736    pdf_init_annot(p, ann);
737
738    ann->type	  = ann_text;
739    ann->open	  = open;
740    pdf_init_rectangle(p, ann, llx, lly, urx, ury);
741
742    if (icon == NULL || !*icon)
743        icon = "note";
744    ann->icon = (pdf_icon) pdc_get_keycode(icon, pdf_icon_note_keylist);
745    if (ann->icon == PDC_KEY_NOTFOUND)
746        pdc_error(p->pdc, PDC_E_ILLARG_STRING, "icon", icon, 0, 0);
747
748    /* title may be NULL */
749    ann->title = pdf_convert_hypertext(p, title, len_title);
750
751    /* It is legal to create an empty text annnotation */
752    ann->contents = pdf_convert_hypertext(p, contents, len_cont);
753
754    pdf_add_annot(p, ann);
755}
756
757PDFLIB_API void PDFLIB_CALL
758PDF_add_note(
759    PDF *p,
760    float llx,
761    float lly,
762    float urx,
763    float ury,
764    const char *contents,
765    const char *title,
766    const char *icon,
767    int open)
768{
769    static const char fn[] = "PDF_add_note";
770
771    if (pdf_enter_api(p, fn, pdf_state_page,
772        "(p[%p], %g, %g, %g, %g, \"%s\", \"%s\", \"%s\", %d)\n",
773        (void *) p, llx, lly, urx, ury,
774        pdc_strprint(p->pdc, contents, 0),
775        pdc_strprint(p->pdc, title, 0), icon, open))
776    {
777        int len_cont = contents ? (int) pdc_strlen(contents) : 0;
778        int len_title = title ? (int) pdc_strlen(title) : 0;
779        pdf__add_note(p, llx, lly, urx, ury, contents, len_cont,
780                      title, len_title, icon, open);
781    }
782}
783
784PDFLIB_API void PDFLIB_CALL
785PDF_add_note2(
786    PDF *p,
787    float llx,
788    float lly,
789    float urx,
790    float ury,
791    const char *contents,
792    int len_cont,
793    const char *title,
794    int len_title,
795    const char *icon,
796    int open)
797{
798    static const char fn[] = "PDF_add_note2";
799
800    if (pdf_enter_api(p, fn, pdf_state_page,
801        "(p[%p], %g, %g, %g, %g, \"%s\", %d, \"%s\", %d, \"%s\", %d)\n",
802        (void *) p, llx, lly, urx, ury,
803        pdc_strprint(p->pdc, contents, len_cont), len_cont,
804        pdc_strprint(p->pdc, title, len_title), len_title,
805        icon, open))
806    {
807        pdf__add_note(p, llx, lly, urx, ury, contents, len_cont,
808                      title, len_title, icon, open);
809    }
810}
811
812/* Add a link to another PDF file */
813PDFLIB_API void PDFLIB_CALL
814PDF_add_pdflink(
815    PDF *p,
816    float llx,
817    float lly,
818    float urx,
819    float ury,
820    const char *filename,
821    int page,
822    const char *optlist)
823{
824    static const char fn[] = "PDF_add_pdflink";
825    pdf_annot *ann;
826
827    if (!pdf_enter_api(p, fn, pdf_state_page,
828	"(p[%p], %g, %g, %g, %g, \"%s\", %d, \"%s\")\n",
829    	(void *) p, llx, lly, urx, ury, filename, page, optlist))
830    {
831	return;
832    }
833
834    if (filename == NULL)
835	pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "filename", 0, 0, 0);
836
837    ann = (pdf_annot *) pdc_malloc(p->pdc, sizeof(pdf_annot), fn);
838
839    pdf_init_annot(p, ann);
840
841    ann->filename = pdc_strdup(p->pdc, filename);
842
843    ann->type = ann_pdflink;
844
845    pdf_parse_destination_optlist(p, optlist, &ann->dest, page, pdf_remotelink);
846
847    pdf_init_rectangle(p, ann, llx, lly, urx, ury);
848
849    pdf_add_annot(p, ann);
850}
851
852/* Add a link to another file of an arbitrary type */
853PDFLIB_API void PDFLIB_CALL
854PDF_add_launchlink(
855    PDF *p,
856    float llx,
857    float lly,
858    float urx,
859    float ury,
860    const char *filename)
861{
862    static const char fn[] = "PDF_add_launchlink";
863    pdf_annot *ann;
864
865    if (!pdf_enter_api(p, fn, pdf_state_page,
866	"(p[%p], %g, %g, %g, %g, \"%s\")\n",
867	(void *)p, llx, lly, urx, ury, filename))
868    {
869	return;
870    }
871
872    if (filename == NULL)
873	pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "filename", 0, 0, 0);
874
875    ann = (pdf_annot *) pdc_malloc(p->pdc, sizeof(pdf_annot), fn);
876
877    pdf_init_annot(p, ann);
878
879    ann->filename = pdc_strdup(p->pdc, filename);
880
881    ann->type	  = ann_launchlink;
882
883    if (p->launchlink_parameters) {
884	ann->parameters = p->launchlink_parameters;
885	p->launchlink_parameters = NULL;
886    }
887
888    if (p->launchlink_operation) {
889	ann->operation = p->launchlink_operation;
890	p->launchlink_operation = NULL;
891    }
892
893    if (p->launchlink_defaultdir) {
894	ann->defaultdir = p->launchlink_defaultdir;
895	p->launchlink_defaultdir = NULL;
896    }
897
898    pdf_init_rectangle(p, ann, llx, lly, urx, ury);
899
900    pdf_add_annot(p, ann);
901}
902
903/* Add a link to a destination in the current PDF file */
904PDFLIB_API void PDFLIB_CALL
905PDF_add_locallink(
906    PDF *p,
907    float llx,
908    float lly,
909    float urx,
910    float ury,
911    int page,
912    const char *optlist)
913{
914    static const char fn[] = "PDF_add_locallink";
915    pdf_annot *ann;
916
917    if (!pdf_enter_api(p, fn, pdf_state_page,
918	"(p[%p], %g, %g, %g, %g, %d, \"%s\")\n",
919    	(void *) p, llx, lly, urx, ury, page, optlist))
920    {
921	return;
922    }
923
924    ann = (pdf_annot *) pdc_malloc(p->pdc, sizeof(pdf_annot), fn);
925
926    pdf_init_annot(p, ann);
927
928    ann->type = ann_locallink;
929
930    pdf_parse_destination_optlist(p, optlist, &ann->dest, page, pdf_locallink);
931
932    pdf_init_rectangle(p, ann, llx, lly, urx, ury);
933
934    pdf_add_annot(p, ann);
935}
936
937/* Add a link to an arbitrary Internet resource (URL) */
938PDFLIB_API void PDFLIB_CALL
939PDF_add_weblink(
940    PDF *p,
941    float llx,
942    float lly,
943    float urx,
944    float ury,
945    const char *url)
946{
947    static const char fn[] = "PDF_add_weblink";
948    pdf_annot *ann;
949
950    if (!pdf_enter_api(p, fn, pdf_state_page,
951	"(p[%p], %g, %g, %g, %g, \"%s\")\n",
952	(void *) p, llx, lly, urx, ury, url))
953    {
954	return;
955    }
956
957    if (url == NULL || *url == '\0')
958	pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "url", 0, 0, 0);
959
960    ann = (pdf_annot *) pdc_malloc(p->pdc, sizeof(pdf_annot), fn);
961
962    pdf_init_annot(p, ann);
963
964    ann->filename = pdc_strdup(p->pdc, url);
965
966    ann->type	  = ann_weblink;
967    pdf_init_rectangle(p, ann, llx, lly, urx, ury);
968
969    pdf_add_annot(p, ann);
970}
971
972PDFLIB_API void PDFLIB_CALL
973PDF_set_border_style(PDF *p, const char *style, float width)
974{
975    static const char fn[] = "PDF_set_border_style";
976
977    if (!pdf_enter_api(p, fn,
978        (pdf_state) (pdf_state_document | pdf_state_page),
979	"(p[%p], \"%s\", %g)\n", (void *) p, style, width))
980    {
981	return;
982    }
983
984    if (style == NULL)
985	p->border_style = border_solid;
986    else if (!strcmp(style, "solid"))
987	p->border_style = border_solid;
988    else if (!strcmp(style, "dashed"))
989	p->border_style = border_dashed;
990    else
991	pdc_error(p->pdc, PDC_E_ILLARG_STRING, "style", style, 0, 0);
992
993    if (width < 0.0)
994	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
995	    "width", pdc_errprintf(p->pdc, "%f", width), 0, 0);
996
997    p->border_width = width;
998}
999
1000PDFLIB_API void PDFLIB_CALL
1001PDF_set_border_color(PDF *p, float red, float green, float blue)
1002{
1003    static const char fn[] = "PDF_set_border_color";
1004
1005    if (!pdf_enter_api(p, fn,
1006        (pdf_state) (pdf_state_document | pdf_state_page),
1007	"(p[%p], %g, %g, %g)\n", (void *) p, red, green, blue))
1008    {
1009	return;
1010    }
1011
1012    if (red < 0.0 || red > 1.0)
1013	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1014	    "red", pdc_errprintf(p->pdc, "%f", red), 0, 0);
1015    if (green < 0.0 || green > 1.0)
1016	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1017	    "green", pdc_errprintf(p->pdc, "%f", green), 0, 0);
1018    if (blue < 0.0 || blue > 1.0)
1019	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1020	    "blue", pdc_errprintf(p->pdc, "%f", blue), 0, 0);
1021
1022    p->border_red = red;
1023    p->border_green = green;
1024    p->border_blue = blue;
1025}
1026
1027PDFLIB_API void PDFLIB_CALL
1028PDF_set_border_dash(PDF *p, float b, float w)
1029{
1030    static const char fn[] = "PDF_set_border_dash";
1031
1032    if (!pdf_enter_api(p, fn,
1033        (pdf_state) (pdf_state_document | pdf_state_page),
1034	"(p[%p], %g, %g)\n", (void *) p, b, w))
1035    {
1036	return;
1037    }
1038
1039    if (b < 0.0)
1040	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1041	    "b", pdc_errprintf(p->pdc, "%f", b), 0, 0);
1042
1043    if (w < 0.0)
1044	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1045	    "w", pdc_errprintf(p->pdc, "%f", w), 0, 0);
1046
1047    p->border_dash1 = b;
1048    p->border_dash2 = w;
1049}
1050