diff options
author | scuri <scuri> | 2008-10-17 06:10:33 +0000 |
---|---|---|
committer | scuri <scuri> | 2008-10-17 06:10:33 +0000 |
commit | 7b52cc13af4e85f1ca2deb6b6c77de9c95ea0dcf (patch) | |
tree | d0857278bde2eff784227c57dcaf930346ceb7ac /src/pdflib/pdflib/p_hyper.c |
First commit - moving from LuaForge to SourceForge
Diffstat (limited to 'src/pdflib/pdflib/p_hyper.c')
-rw-r--r-- | src/pdflib/pdflib/p_hyper.c | 1449 |
1 files changed, 1449 insertions, 0 deletions
diff --git a/src/pdflib/pdflib/p_hyper.c b/src/pdflib/pdflib/p_hyper.c new file mode 100644 index 0000000..644e311 --- /dev/null +++ b/src/pdflib/pdflib/p_hyper.c @@ -0,0 +1,1449 @@ +/*---------------------------------------------------------------------------* + | PDFlib - A library for generating PDF on the fly | + +---------------------------------------------------------------------------+ + | Copyright (c) 1997-2006 Thomas Merz and PDFlib GmbH. All rights reserved. | + +---------------------------------------------------------------------------+ + | | + | This software is subject to the PDFlib license. It is NOT in the | + | public domain. Extended versions and commercial licenses are | + | available, please check http://www.pdflib.com. | + | | + *---------------------------------------------------------------------------*/ + +/* $Id: p_hyper.c,v 1.1 2008/10/17 06:11:49 scuri Exp $ + * + * PDFlib routines for hypertext stuff: + * named destination, bookmarks, document info + * + */ + +#define P_HYPER_C + +#include "p_intern.h" +#include "p_color.h" + + + + +/* -------------------------- named destinations -------------------------- */ + +typedef enum +{ + fixed, + fitwindow, + fitwidth, + fitheight, + fitrect, + fitvisible, + fitvisiblewidth, + fitvisibleheight, + nameddest, + filedest +} +pdf_desttype; + +static const pdc_keyconn pdf_type_keylist[] = +{ + {"fixed", fixed}, + {"fitwindow", fitwindow}, + {"fitwidth", fitwidth}, + {"fitheight", fitheight}, + {"fitrect", fitrect}, + {"fitvisible", fitvisible}, + {"fitvisiblewidth", fitvisiblewidth}, + {"fitvisibleheight",fitvisibleheight}, + {"nameddest", nameddest}, + {"file", filedest}, + {NULL, 0} +}; + +/* Destination structure */ +struct pdf_dest_s +{ + pdf_desttype type; + char *filename; /* name of a file to be launched - deprecated */ + int remote_page; /* remote target page number */ + int pgnum; + pdc_id page; /* local target page object id */ + char *name; /* destination name, only for type=nameddest */ + int len; /* length of the name string */ + pdc_scalar zoom; /* magnification */ + pdc_scalar left; + pdc_scalar right; + pdc_scalar bottom; + pdc_scalar top; + pdc_scalar color[3]; /* rgb color of bookmark text - deprecated */ + fnt_fontstyle fontstyle; /* font style of bookmark text - deprecated */ +}; + +static const pdc_defopt pdf_destination_options[] = +{ + {"hypertextencoding", pdc_stringlist, PDC_OPT_NONE, 1, 1, + 0.0, PDF_MAX_NAMESTRING, NULL}, + + {"hypertextformat", pdc_keywordlist, PDC_OPT_NONE, 1, 1, + 0.0, 0.0, pdf_textformat_keylist}, + + {"fitbbox", pdc_booleanlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, NULL}, + + {"fitheight", pdc_booleanlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, NULL}, + + {"fitpage", pdc_booleanlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, NULL}, + + {"fitwidth", pdc_booleanlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, NULL}, + + {"retain", pdc_booleanlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, NULL}, + + {"type", pdc_keywordlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, pdf_type_keylist}, + + {"name", pdc_stringlist, PDC_OPT_NONE, + 1, 1, 1.0, PDC_INT_MAX, NULL}, + + {"page", pdc_integerlist, PDC_OPT_NONE, + 1, 1, 0, PDC_INT_MAX, NULL}, + + {"group", pdc_stringlist, PDC_OPT_NONE, 1, 1, + 1.0, PDF_MAX_NAMESTRING, NULL}, + + /* Acrobat 5 supports a maximum zoom of 1600%, but we allow some more */ + {"zoom", pdc_scalarlist, PDC_OPT_PERCENT, + 1, 1, 0.0, 10000, NULL}, + + {"left", pdc_scalarlist, PDC_OPT_NONE, + 1, 1, 0.0, PDF_ACRO_MAXPAGE, NULL}, + + {"right", pdc_scalarlist, PDC_OPT_NONE, + 1, 1, 0.0, PDF_ACRO_MAXPAGE, NULL}, + + {"bottom", pdc_scalarlist, PDC_OPT_REQUIRIF1, + 1, 1, 0.0, PDF_ACRO_MAXPAGE, NULL}, + + {"top", pdc_scalarlist, PDC_OPT_NONE, + 1, 1, 0.0, PDF_ACRO_MAXPAGE, NULL}, + + {"color", pdc_scalarlist, PDC_OPT_NONE, + 1, 3, 0.0, 1.0, NULL}, + + {"fontstyle", pdc_keywordlist, PDC_OPT_NONE, + 1, 1, 0.0, 0.0, pdf_fontstyle_pdfkeylist}, + + {"filename", pdc_stringlist, PDC_OPT_NONE, + 1, 1, 0.0, PDC_FILENAMELEN, NULL}, + + PDC_OPT_TERMINATE +}; + +pdf_dest * +pdf_init_destination(PDF *p) +{ + static const char fn[] = "pdf_init_destination"; + pdf_dest *dest = (pdf_dest *) pdc_malloc(p->pdc, sizeof(pdf_dest), fn); + + dest->type = fitwindow; + dest->remote_page = 0; + dest->pgnum = 0; + dest->page = PDC_BAD_ID; + dest->left = -1; + dest->right = -1; + dest->bottom = -1; + dest->top = -1; + dest->zoom = -1; + dest->name = NULL; + dest->color[0] = 0.0; + dest->color[1] = 0.0; + dest->color[2] = 0.0; + dest->fontstyle = fnt_Normal; + dest->filename = NULL; + + return dest; +} + +void +pdf_cleanup_destination(PDF *p, pdf_dest *dest) +{ + if (dest) + { + if (dest->name) + { + pdc_free(p->pdc, dest->name); + dest->name = NULL; + } + if (dest->filename) + { + pdc_free(p->pdc, dest->filename); + dest->filename = NULL; + } + + pdc_free(p->pdc, dest); + } +} + +pdf_dest * +pdf_parse_destination_optlist( + PDF *p, + const char *optlist, + int page, + pdf_destuse destuse) +{ + int minpage; + pdc_resopt *resopts; + pdc_encoding hypertextencoding; + int hypertextcodepage; + const char *keyword; + const char *type_name; + char **strlist = NULL; + int inum; + pdc_bool boolval; + + /* Defaults */ + pdf_dest *dest = pdf_init_destination(p); + + /* parse option list */ + resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_destination_options, + NULL, pdc_true); + + if (pdc_get_optvalues("fitbbox", resopts, &boolval, NULL) && + boolval == pdc_true) + dest->type = fitvisible; + + if (pdc_get_optvalues("fitheight", resopts, &boolval, NULL) && + boolval == pdc_true) + dest->type = fitheight; + + if (pdc_get_optvalues("fitpage", resopts, &boolval, NULL) && + boolval == pdc_true) + dest->type = fitwindow; + + if (pdc_get_optvalues("fitwidth", resopts, &boolval, NULL) && + boolval == pdc_true) + dest->type = fitwidth; + + if (pdc_get_optvalues("retain", resopts, &boolval, NULL) && + boolval == pdc_true) + dest->type = fixed; + + if (pdc_get_optvalues("type", resopts, &inum, NULL)) + dest->type = (pdf_desttype) inum; + type_name = pdc_get_keyword(dest->type, pdf_type_keylist); + + hypertextencoding = + pdf_get_hypertextencoding_opt(p, resopts, &hypertextcodepage, pdc_true); + + keyword = "name"; + if (pdf_get_opt_textlist(p, keyword, resopts, hypertextencoding, + hypertextcodepage, pdc_true, NULL, &dest->name, NULL)) + { + if (dest->type != nameddest) + { + dest->name = NULL; + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, + type_name, 0, 0); + } + else + pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); + } + + keyword = "page"; + if (pdc_get_optvalues(keyword, resopts, &page, NULL) && + dest->type == filedest) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, type_name, + 0, 0); + + keyword = "group"; + if (pdc_get_optvalues(keyword, resopts, NULL, &strlist)) + { + page = pdf_xlat_pageno(p, page, strlist[0]); + } + + keyword = "zoom"; + if (pdc_get_optvalues(keyword, resopts, &dest->zoom, NULL) && + dest->type != fixed) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, type_name, + 0, 0); + + keyword = "left"; + if (pdc_get_optvalues(keyword, resopts, &dest->left, NULL) && + (dest->type == fitwindow || dest->type == fitwidth || + dest->type == fitvisible || dest->type == fitvisiblewidth || + dest->type == nameddest || dest->type == filedest)) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, type_name, + 0, 0); + + keyword = "right"; + if (pdc_get_optvalues(keyword, resopts, &dest->right, NULL) && + dest->type != fitrect) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, type_name, + 0, 0); + + keyword = "bottom"; + if (pdc_get_optvalues(keyword, resopts, &dest->bottom, NULL) && + dest->type != fitrect) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, type_name, + 0, 0); + + keyword = "top"; + if (pdc_get_optvalues(keyword, resopts, &dest->top, NULL) && + (dest->type == fitwindow || dest->type == fitheight || + dest->type == fitvisible || dest->type == fitvisibleheight || + dest->type == nameddest || dest->type == filedest)) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, type_name, + 0, 0); + + keyword = "color"; + if (pdc_get_optvalues(keyword, resopts, &dest->color, NULL) && + destuse != pdf_bookmark) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORELEM, keyword, 0, 0, 0); + + keyword = "fontstyle"; + if (pdc_get_optvalues(keyword, resopts, &inum, NULL)) + { + dest->fontstyle = (fnt_fontstyle) inum; + if (destuse != pdf_bookmark) + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORELEM, keyword, 0, 0, 0); + } + + keyword = "filename"; + if (pdc_get_optvalues(keyword, resopts, NULL, NULL)) + { + if (dest->type != filedest) + { + pdc_warning(p->pdc, PDF_E_HYP_OPTIGNORE_FORTYPE, keyword, + type_name, 0, 0); + } + else + dest->filename = + (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); + } + + pdc_cleanup_optionlist(p->pdc, resopts); + + switch (dest->type) + { + case fitwidth: + /* Trick: we don't know the height of a future page yet, + * so we use a "large" value for top which will do for + * most pages. If it doesn't work, not much harm is done. + */ + if (dest->top == -1) + dest->top = 10000; + break; + + case fitrect: + case fitheight: + case fitvisiblewidth: + case fitvisibleheight: + if (dest->left == -1) + dest->left = 0; + if (dest->bottom == -1) + dest->bottom = 0; + if (dest->right == -1) + dest->right = 1000; + if (dest->top == -1) + dest->top = 1000; + break; + + case nameddest: + if (destuse == pdf_nameddest) + { + pdf_cleanup_destination(p, dest); + pdc_error(p->pdc, PDC_E_OPT_ILLKEYWORD, "type", type_name, 0, 0); + } + if (dest->name == NULL) + { + pdf_cleanup_destination(p, dest); + pdc_error(p->pdc, PDC_E_OPT_NOTFOUND, "name", 0, 0, 0); + } + break; + + case filedest: + if (destuse != pdf_bookmark) + { + pdf_cleanup_destination(p, dest); + pdc_error(p->pdc, PDC_E_OPT_ILLKEYWORD, "type", type_name, 0, 0); + } + if (dest->filename == NULL) + { + pdf_cleanup_destination(p, dest); + pdc_error(p->pdc, PDC_E_OPT_NOTFOUND, "filename", 0, 0, 0); + } + break; + + default: + break; + } + + /* check for minpage */ + minpage = (destuse == pdf_bookmark) ? 0 : 1; + switch (destuse) + { + case pdf_nameddest: + case pdf_locallink: + if (page == 0) + { + page = pdf_current_page(p); + } + + case pdf_openaction: + case pdf_bookmark: + case pdf_remotelink: + if (page < minpage) + { + const char *stemp = pdc_errprintf(p->pdc, "%d", page); + pdf_cleanup_destination(p, dest); + pdc_error(p->pdc, PDC_E_ILLARG_HANDLE, "page", stemp, 0, 0); + } + break; + } + + dest->pgnum = page; + + if (destuse != pdf_remotelink && destuse != pdf_openaction && page != 0) + { + dest->page = pdf_get_page_id(p, page); + } + + /* remote page number */ + if (destuse == pdf_remotelink) + dest->remote_page = page; + + return dest; +} + +#if defined(_MSC_VER) && defined(_MANAGED) +#pragma unmanaged +#endif +pdf_dest * +pdf_get_option_destname(PDF *p, pdc_resopt *resopts, + pdc_encoding hypertextencoding, + int hypertextcodepage) +{ + pdc_text_format hypertextformat = pdc_bytes; + pdf_dest *dest = NULL; + char **strlist; + int outlen; + + if (pdc_get_optvalues("destname", resopts, NULL, &strlist)) + { + dest = pdf_init_destination(p); + dest->type = nameddest; + + if (pdc_is_lastopt_utf8(resopts)) + hypertextformat = PDC_UTF8; + dest->name = pdf_convert_hypertext(p, strlist[0], 0, hypertextformat, + hypertextencoding, hypertextcodepage, + &outlen, PDC_UTF8_FLAG, pdc_true); + } + return dest; +} +#if defined(_MSC_VER) && defined(_MANAGED) +#pragma managed +#endif + + +void +pdf_write_destination(PDF *p, pdf_dest *dest) +{ + if (dest->type == nameddest) + { + pdf_put_hypertext(p, dest->name); + pdc_puts(p->out, "\n"); + return; + } + + pdc_begin_array(p->out); + + if (dest->remote_page) + { + pdc_printf(p->out, "%d", dest->remote_page - 1); /* zero-based */ + } + else + { + if (dest->page == PDC_BAD_ID) + dest->page = pdf_get_page_id(p, dest->pgnum); + + pdc_objref_c(p->out, dest->page); + } + + switch (dest->type) { + + case fixed: + pdc_puts(p->out, "/XYZ "); + + if (dest->left != -1) + pdc_printf(p->out, "%f ", dest->left); + else + pdc_puts(p->out, "null "); + + if (dest->top != -1) + pdc_printf(p->out, "%f ", dest->top); + else + pdc_puts(p->out, "null "); + + if (dest->zoom != -1) + pdc_printf(p->out, "%f", dest->zoom); + else + pdc_puts(p->out, "null"); + + break; + + case fitwindow: + pdc_puts(p->out, "/Fit"); + break; + + case fitwidth: + pdc_printf(p->out, "/FitH %f", dest->top); + break; + + case fitheight: + pdc_printf(p->out, "/FitV %f", dest->left); + break; + + case fitrect: + pdc_printf(p->out, "/FitR %f %f %f %f", + dest->left, dest->bottom, dest->right, dest->top); + break; + + case fitvisible: + pdc_puts(p->out, "/FitB"); + break; + + case fitvisiblewidth: + pdc_printf(p->out, "/FitBH %f", dest->top); + break; + + case fitvisibleheight: + pdc_printf(p->out, "/FitBV %f", dest->left); + break; + + default: + break; + } + + pdc_end_array(p->out); +} + +void +pdf__add_nameddest( + PDF *p, + const char *name, + int len, + const char *optlist) +{ + pdc_resopt *resopts = NULL; + pdc_text_format hypertextformat = p->hypertextformat; + pdc_encoding hypertextencoding; + int hypertextcodepage; + pdc_id obj_id = PDC_BAD_ID; + char *name2 = NULL; + pdf_dest *dest; + int inum; + + if (!name) + pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "name", 0, 0, 0); + + resopts = pdc_parse_optionlist(p->pdc, optlist, + pdf_destination_options, NULL, pdc_true); + + hypertextencoding = + pdf_get_hypertextencoding_opt(p, resopts, &hypertextcodepage, pdc_true); + + if (pdc_get_optvalues("hypertextformat", resopts, &inum, NULL)) + { + hypertextformat = (pdc_text_format) inum; + pdf_check_hypertextformat(p, hypertextformat); + } + + pdc_cleanup_optionlist(p->pdc, resopts); + + /* create hypertext string */ + name2 = pdf_convert_hypertext(p, name, len, hypertextformat, + hypertextencoding, hypertextcodepage, &len, + pdc_true, pdc_true); + if (name2 == NULL || len == 0) + pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "name", 0, 0, 0); + + /* parsing option list */ + dest = pdf_parse_destination_optlist(p, optlist, 0, pdf_nameddest); + + /* interrupt the content stream if we are on a page */ + if (PDF_GET_STATE(p) == pdf_state_page) + pdf_end_contents_section(p); + + obj_id = pdc_begin_obj(p->out, PDC_NEW_ID); /* Dest object */ + pdc_begin_dict(p->out); /* Destination dict */ + + pdc_puts(p->out, "/D"); + pdf_write_destination(p, dest); + + pdc_end_dict(p->out); /* Destination dict */ + pdc_end_obj(p->out); /* Dest object */ + + /* continue the contents stream */ + if (PDF_GET_STATE(p) == pdf_state_page) + pdf_begin_contents_section(p); + + pdf_cleanup_destination(p, dest); + + /* insert name in tree */ + pdf_insert_name(p, name2, names_dests, obj_id); +} + + +/* -------------------------- bookmarks -------------------------- */ + +static const pdc_defopt pdf_create_bookmark_options[] = +{ + {"hypertextencoding", pdc_stringlist, PDC_OPT_NONE, 1, 1, + 0.0, PDF_MAX_NAMESTRING, NULL}, + + {"hypertextformat", pdc_keywordlist, PDC_OPT_NONE, 1, 1, + 0.0, 0.0, pdf_textformat_keylist}, + + {"textcolor", pdc_stringlist, PDC_OPT_NONE, 2, 5, + 0.0, PDF_MAX_NAMESTRING, NULL}, + + {"fontstyle", pdc_keywordlist, PDC_OPT_NONE, 1, 1, + 0.0, 0.0, pdf_fontstyle_pdfkeylist}, + + {"parent", pdc_bookmarkhandle, PDC_OPT_NONE, 1, 1, + 0.0, 0.0, NULL}, + + {"index", pdc_integerlist, PDC_OPT_NONE, 1, 1, + -1, PDC_INT_MAX, NULL}, + + {"open", pdc_booleanlist, PDC_OPT_NONE, 1, 1, + 0.0, 0.0, NULL}, + + {"destination", pdc_stringlist, PDC_OPT_NONE, 1, 1, + 0.0, PDC_INT_MAX, NULL}, + + {"destname", pdc_stringlist, PDC_OPT_IGNOREIF1, 1, 1, + 0.0, PDC_INT_MAX, NULL}, + + {"action", pdc_stringlist, PDC_OPT_NONE, 1, 1, + 0.0, PDC_INT_MAX, NULL}, + + PDC_OPT_TERMINATE +}; + +struct pdf_outline_s { + pdc_id obj_id; /* id of this outline object */ + char *text; /* bookmark text */ + int count; /* number of open sub-entries */ + pdc_bool open; /* whether or not to display children */ + pdc_scalar textcolor[3]; /* rgb color of bookmark text */ + fnt_fontstyle fontstyle; /* font style of bookmark text */ + char *action; /* action optlist */ + pdf_dest *dest; /* outline destination */ + + /* these members control automatic ordering of bookmarks. + */ + pdc_bool in_order; /* this book mark is "in order" */ + pdc_id page_id; /* id of page where this bookmark */ + /* was defined */ + + /* the members below are indices into the p->outlines[] array. + */ + int prev; /* previous entry at this level */ + int next; /* next entry at this level */ + int parent; /* ancestor's index */ + int first; /* first sub-entry */ + int last; /* last sub-entry */ +}; + +static void +pdf_init_outline(PDF *p, pdf_outline *outline) +{ + (void) p; + + outline->obj_id = PDC_BAD_ID; + outline->text = NULL; + outline->count = 0; + outline->open = pdc_false; + outline->textcolor[0] = 0.0; + outline->textcolor[1] = 0.0; + outline->textcolor[2] = 0.0; + outline->fontstyle = fnt_Normal; + outline->action = NULL; + outline->dest = NULL; + outline->in_order = pdc_false; + outline->page_id = PDC_BAD_ID; + outline->prev = 0; + outline->next = 0; + outline->parent = 0; + outline->first = 0; + outline->last = 0; +} + +/* We can't work with pointers in the outline objects because + * the complete outline block may be reallocated. Therefore we use + * this simple mechanism for achieving indirection. + */ +#define COUNT(jndex) (p->outlines[jndex].count) +#define OPEN(jndex) (p->outlines[jndex].open) +#define IN_ORDER(jndex) (p->outlines[jndex].in_order) +#define PAGE_ID(jndex) (p->outlines[jndex].page_id) +#define LAST(jndex) (p->outlines[jndex].last) +#define PARENT(jndex) (p->outlines[jndex].parent) +#define FIRST(jndex) (p->outlines[jndex].first) +#define OBJ_ID(jndex) (p->outlines[jndex].obj_id) +#define PREV(jndex) (p->outlines[jndex].prev) +#define NEXT(jndex) (p->outlines[jndex].next) + +static int +search_forward(PDF *p, int start_page, int start_index) +{ + int idx; + + for (idx = start_index; idx != 0; idx = NEXT(idx)) + { + if (IN_ORDER(idx)) + return pdf_search_page_fwd(p, start_page, PAGE_ID(idx)); + } + + return PDC_INT_MAX; +} + +static int +search_backward(PDF *p, int start_page, int start_index) +{ + int idx; + + for (idx = start_index; idx != 0; idx = PREV(idx)) + { + if (IN_ORDER(idx)) + { + int pg = pdf_search_page_bwd(p, start_page, PAGE_ID(idx)); + + return (pg == -1) ? PDC_INT_MAX : pg; + } + } + + return -1; +} + +static int +pdf_insert_bookmark( + PDF *p, + const char *hypertext, + pdf_outline *outline, + int jndex) +{ + static const char fn[] = "pdf_insert_bookmark"; + pdf_outline *root, *self; + int parent; + int self_idx; + int pageno = pdf_current_page(p); + + /* allocation */ + if (p->outline_count == 0) + { + p->outline_capacity = OUTLINE_CHUNKSIZE; + p->outlines = (pdf_outline *) pdc_calloc(p->pdc, + sizeof(pdf_outline) * p->outline_capacity, fn); + + /* populate the root outline object */ + root = &p->outlines[0]; + pdf_init_outline(p, root); + root->obj_id = pdc_alloc_id(p->out); + root->open = pdc_true; + + /* set the open mode show bookmarks if we have at least one, + * and the client didn't already set his own open mode. + */ + pdf_fix_openmode(p); + } + else if (p->outline_count + 1 >= p->outline_capacity) + { + p->outline_capacity *= 2; + p->outlines = (pdf_outline *) pdc_realloc(p->pdc, p->outlines, + sizeof(pdf_outline) * p->outline_capacity, fn); + } + + /* copy */ + self_idx = ++p->outline_count; + self = &p->outlines[self_idx]; + memcpy(self, outline, sizeof(pdf_outline)); + + self->obj_id = pdc_alloc_id(p->out); + self->text = (char *) hypertext; + self->page_id = pdf_get_page_id(p, 0); + parent = self->parent; + + /* default destination */ + if (self->action == NULL && self->dest == NULL) + self->dest = pdf_init_destination(p); + + /* no destination */ + if (self->dest != NULL && + self->dest->name != NULL && !strlen(self->dest->name)) + { + pdf_cleanup_destination(p, self->dest); + self->dest = NULL; + } + + /* current page */ + if (self->dest) + { + /* this ugly code is for compatibility with the + ** obsolete "bookmarkdest" parameter. + */ + if (self->dest->pgnum == 0) + self->dest->pgnum = pdf_current_page(p); + + if (self->dest->pgnum == 0) + { + self->dest->pgnum = 1; + } + else if (self->dest->page == PDC_BAD_ID) + { + self->dest->page = pdf_get_page_id(p, self->dest->pgnum); + } + } + + /* special case: empty list. + */ + if (FIRST(parent) == 0) + { + if (jndex > 0) + pdc_error(p->pdc, PDC_E_OPT_ILLINTEGER, "index", + pdc_errprintf(p->pdc, "%d", jndex), 0, 0); + + FIRST(parent) = LAST(parent) = self_idx; + self->in_order = pdc_true; + } + else switch (jndex) + { + case -2: /* insert "in order" */ + { + /* the "natural" case: append to the end if appropriate. + */ + if (pageno >= search_backward(p, -1, LAST(parent))) + { + self->prev = LAST(parent); + NEXT(LAST(parent)) = self_idx; + LAST(parent) = self_idx; + } + else + { + int idx; + int curr_pg = 1; + int next_pg; + + for (idx = FIRST(parent); idx != 0; idx = NEXT(idx)) + { + if (!IN_ORDER(idx)) + continue; + + next_pg = pdf_search_page_fwd(p, curr_pg, PAGE_ID(idx)); + + /* TODO: understand why this can happen. + */ + if (next_pg < 1) + { + idx = 0; + break; + } + + if (next_pg > pageno) + { + self->next = idx; + self->prev = PREV(idx); + PREV(idx) = self_idx; + + if (self->prev == 0) + FIRST(parent) = self_idx; + else + NEXT(self->prev) = self_idx; + + break; + } + + curr_pg = next_pg; + } + + /* if there are no "in order" bookmarks yet, + ** we simply append this one to the end. + */ + if (idx == 0) + { + self->prev = LAST(parent); + NEXT(LAST(parent)) = self_idx; + LAST(parent) = self_idx; + } + } + + self->in_order = pdc_true; + break; + } + + case -1: /* append to the end */ + { + self->prev = LAST(parent); + NEXT(LAST(parent)) = self_idx; + LAST(parent) = self_idx; + + self->in_order = + (pageno >= search_backward(p, pageno, self->prev)); + break; + } + + case 0: /* insert at the beginning */ + { + self->next = FIRST(parent); + PREV(FIRST(parent)) = self_idx; + FIRST(parent) = self_idx; + + self->in_order = + (pageno <= search_forward(p, pageno, self->next)); + break; + } + + default: /* insert before [1..LAST] */ + { + int i; + int target = FIRST(parent); + + for (i = 0; i < jndex; ++i) + { + if (target == LAST(parent)) + pdc_error(p->pdc, PDC_E_OPT_ILLINTEGER, "index", + pdc_errprintf(p->pdc, "%d", jndex), 0, 0); + + target = NEXT(target); + } + + self->next = target; + self->prev = PREV(target); + NEXT(self->prev) = PREV(self->next) = self_idx; + + self->in_order = + ((pageno >= search_backward(p, pageno, self->prev)) && + (pageno <= search_forward(p, pageno, self->next))); + break; + } + } /* else switch */ + + /* increase the number of open sub-entries for all relevant ancestors */ + do { + COUNT(parent)++; + } while (OPEN(parent) && (parent = PARENT(parent)) != 0); + + return (self_idx); /* caller may use this as handle */ +} + +int +pdf__create_bookmark(PDF *p, const char *text, int len, const char *optlist) +{ + pdc_resopt *resopts = NULL; + pdc_clientdata data; + pdf_outline self; + pdf_dest *dest = NULL; + pdc_text_format hypertextformat; + pdc_encoding hypertextencoding; + pdf_coloropt textcolor; + char *hypertext = NULL; + const char *keyword = NULL; + char **strlist = NULL; + int hypertextcodepage; + int ns, inum, outlen, retval = 0; + int jndex = -2; + + /* Initialize */ + pdf_init_outline(p, &self); + hypertextformat = p->hypertextformat; + hypertextencoding = p->hypertextencoding; + hypertextcodepage = p->hypertextcodepage; + + /* Parsing option list */ + if (optlist && strlen(optlist)) + { + pdf_set_clientdata(p, &data); + resopts = pdc_parse_optionlist(p->pdc, optlist, + pdf_create_bookmark_options, &data, pdc_true); + + hypertextencoding = + pdf_get_hypertextencoding_opt(p, resopts, &hypertextcodepage, + pdc_true); + + if (pdc_get_optvalues("hypertextformat", resopts, &inum, NULL)) + { + hypertextformat = (pdc_text_format) inum; + pdf_check_hypertextformat(p, hypertextformat); + } + + ns = pdc_get_optvalues("textcolor", resopts, NULL, &strlist); + if (ns) + { + pdf_parse_coloropt(p, "textcolor", strlist, ns, (int) color_rgb, + &textcolor); + self.textcolor[0] = textcolor.value[0]; + self.textcolor[1] = textcolor.value[1]; + self.textcolor[2] = textcolor.value[2]; + } + + if (pdc_get_optvalues("fontstyle", resopts, &inum, NULL)) + self.fontstyle = (fnt_fontstyle) inum; + + pdc_get_optvalues("parent", resopts, &self.parent, NULL); + + pdc_get_optvalues("index", resopts, &jndex, NULL); + + pdc_get_optvalues("open", resopts, &self.open, NULL); + + if (pdc_get_optvalues("destination", resopts, NULL, &strlist)) + { + self.dest = pdf_parse_destination_optlist(p, strlist[0], 0, + pdf_bookmark); + keyword = "destination"; + } + else + { + dest = pdf_get_option_destname(p, resopts, hypertextencoding, + hypertextcodepage); + if (dest) + { + self.dest = dest; + keyword = "destname"; + } + } + + if (pdc_get_optvalues("action", resopts, NULL, &strlist)) + { + if (self.dest) + { + pdf_cleanup_destination(p, self.dest); + self.dest = NULL; + pdc_warning(p->pdc, PDC_E_OPT_IGNORE, keyword, "action", 0, 0); + } + + /* parsing of action list */ + pdf_parse_and_write_actionlist(p, event_bookmark, NULL, + (const char *) strlist[0]); + self.action = + (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); + } + + pdc_cleanup_optionlist(p->pdc, resopts); + } + + /* create hypertext string */ + hypertext = pdf_convert_hypertext(p, text, len, hypertextformat, + hypertextencoding, hypertextcodepage, + &outlen, PDC_UTF8_FLAG, pdc_true); + if (hypertext) + retval = pdf_insert_bookmark(p, hypertext, &self, jndex); + + return retval; +} + +static void +pdf_write_outline_dict(PDF *p, int entry) +{ + pdf_outline *outline = &p->outlines[entry]; + pdc_id act_idlist[PDF_MAX_EVENTS]; + + /* write action objects */ + if (outline->action) + pdf_parse_and_write_actionlist(p, event_bookmark, act_idlist, + (const char *) outline->action); + + pdc_begin_obj(p->out, OBJ_ID(entry)); /* outline object */ + pdc_begin_dict(p->out); + + pdc_objref(p->out, "/Parent", OBJ_ID(PARENT(entry))); + + /* outline destination */ + if (outline->dest) + { + pdc_puts(p->out, "/Dest"); + pdf_write_destination(p, outline->dest); + } + + /* write Action entries */ + else if (outline->action) + pdf_write_action_entries(p, event_bookmark, act_idlist); + + pdc_puts(p->out, "/Title"); /* outline text */ + pdf_put_hypertext(p, outline->text); + pdc_puts(p->out, "\n"); + + if (PREV(entry)) + pdc_objref(p->out, "/Prev", OBJ_ID(PREV(entry))); + if (NEXT(entry)) + pdc_objref(p->out, "/Next", OBJ_ID(NEXT(entry))); + + if (FIRST(entry)) { + pdc_objref(p->out, "/First", OBJ_ID(FIRST(entry))); + pdc_objref(p->out, "/Last", OBJ_ID(LAST(entry))); + } + if (COUNT(entry)) { + if (OPEN(entry)) + pdc_printf(p->out, "/Count %d\n", COUNT(entry)); /* open */ + else + pdc_printf(p->out, "/Count %d\n", -COUNT(entry));/* closed */ + } + + /* Color */ + if (outline->textcolor[0] != 0.0 || + outline->textcolor[1] != 0.0 || + outline->textcolor[2] != 0.0) + pdc_printf(p->out, "/C[%f %f %f]\n", outline->textcolor[0], + outline->textcolor[1], + outline->textcolor[2]); + + /* FontStyle */ + if (outline->fontstyle != fnt_Normal) + { + int fontstyle = 0; + if (outline->fontstyle == fnt_Bold) + fontstyle = 2; + if (outline->fontstyle == fnt_Italic) + fontstyle = 1; + if (outline->fontstyle == fnt_BoldItalic) + fontstyle = 3; + pdc_printf(p->out, "/F %d\n", fontstyle); + } + + pdc_end_dict(p->out); + pdc_end_obj(p->out); /* outline object */ +} + +void +pdf_write_outlines(PDF *p) +{ + int i; + + if (p->outline_count == 0) /* no outlines: return */ + return; + + pdc_begin_obj(p->out, p->outlines[0].obj_id); /* root outline object */ + pdc_begin_dict(p->out); + + if (p->outlines[0].count != 0) + pdc_printf(p->out, "/Count %d\n", COUNT(0)); + pdc_objref(p->out, "/First", OBJ_ID(FIRST(0))); + pdc_objref(p->out, "/Last", OBJ_ID(LAST(0))); + + pdc_end_dict(p->out); + pdc_end_obj(p->out); /* root outline object */ + +#define PDF_FLUSH_AFTER_MANY_OUTLINES 1000 /* ca. 50-100 KB */ + for (i = 1; i <= p->outline_count; i++) { + /* reduce memory usage for many outline entries */ + if (i % PDF_FLUSH_AFTER_MANY_OUTLINES == 0) + pdc_flush_stream(p->out); + + pdf_write_outline_dict(p, i); + } +} + +void +pdf_write_outline_root(PDF *p) +{ + if (p->outline_count != 0) + pdc_objref(p->out, "/Outlines", p->outlines[0].obj_id); +} + +void +pdf_init_outlines(PDF *p) +{ + p->outline_count = 0; +} + +/* Free outline entries */ +void +pdf_cleanup_outlines(PDF *p) +{ + int i; + + if (!p->outlines || p->outline_count == 0) + return; + + /* outlines[0] is the outline root object */ + for (i = 0; i <= p->outline_count; i++) + { + if (p->outlines[i].text) + { + pdc_free(p->pdc, p->outlines[i].text); + p->outlines[i].text = NULL; + } + if (p->outlines[i].action) + { + pdc_free(p->pdc, p->outlines[i].action); + p->outlines[i].action = NULL; + } + pdf_cleanup_destination(p, p->outlines[i].dest); + p->outlines[i].dest = NULL; + } + + pdc_free(p->pdc, (void*) p->outlines); + + p->outlines = NULL; +} + + +/*****************************************************************************/ +/** deprecated historical bookmark function **/ +/*****************************************************************************/ + +int +pdf__add_bookmark(PDF *p, const char *text, int len, int parent, int open) +{ + pdf_outline self; + pdf_dest *dest = (pdf_dest *) p->bookmark_dest; + char *hypertext = NULL; + int acthdl; + int retval = 0; + + pdf_init_outline(p, &self); + + if (parent != 0) + pdf_check_handle(p, parent, pdc_bookmarkhandle); + self.parent = parent; + self.open = open; + + /* creating a Launch action - defined via bookmarkdest */ + if (dest->filename) + { + char actoptlist[2048]; + + sprintf(actoptlist, "filename {%s} ", dest->filename); + acthdl = pdf__create_action(p, "Launch", actoptlist); + if (acthdl != -1) + { + if (p->pdc->hastobepos) acthdl++; + sprintf(actoptlist, "activate %d", acthdl); + self.action = pdc_strdup(p->pdc, actoptlist); + } + } + else + { + self.dest = pdf_init_destination(p); + *self.dest = *dest; + if (dest->name) + self.dest->name = pdc_strdup(p->pdc, dest->name); + } + + memcpy(self.textcolor, dest->color, 3 * sizeof(pdc_scalar)); + self.fontstyle = dest->fontstyle; + + hypertext = pdf_convert_hypertext_depr(p, text, len); + if (hypertext) + retval = pdf_insert_bookmark(p, hypertext, &self, -1); + + return retval; +} + +/* -------------------------- document info ------------------------------- */ + +struct pdf_info_s +{ + char *key; /* ASCII string */ + char *value; /* Unicode string */ + pdf_info *next; /* next info entry */ +}; + +void +pdf_cleanup_info(PDF *p) +{ + pdf_info *info, *last; + + if (p->userinfo) + { + for (info = p->userinfo; info != NULL; /* */) + { + last = info; + info = info->next; + + pdc_free(p->pdc, last->key); + pdc_free(p->pdc, last->value); + pdc_free(p->pdc, last); + } + + p->userinfo = NULL; + } +} + +static pdf_info * +pdf_have_infokey(PDF *p, const char *key) +{ + pdf_info *info; + + for (info = p->userinfo; info != NULL; info = info->next) + { + if (strlen(info->key) == strlen(key) && !strcmp(info->key, key)) + return info; + } + + return NULL; +} + +void +pdf_feed_digest_info(PDF *p) +{ + pdf_info *info; + + if (p->userinfo) + { + for (info = p->userinfo; info != NULL; info = info->next) + { + pdc_update_digest(p->out, + (unsigned char *) info->key, strlen(info->key)); + } + } +} + +#define PDF_TRAPPED_TRUE "True" +#define PDF_TRAPPED_FALSE "False" +#define PDF_TRAPPED_UNKNOWN "Unknown" + +/* Set Info dictionary entries */ +void +pdf__set_info(PDF *p, const char *key, const char *value, int len) +{ + static const char fn[] = "pdf__set_info"; + char *key_buf, *val_buf; + pdf_info *oldentry, *newentry; + + if (key == NULL || !*key) + pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "key", 0, 0, 0); + + if (!strcmp(key, "Producer") || + !strcmp(key, "CreationDate") || + !strcmp(key, "ModDate")) + pdc_error(p->pdc, PDC_E_ILLARG_STRING, "key", key, 0, 0); + + /* converting key */ + key_buf = pdf_convert_name(p, key, 0, 0); + + /* convert text string */ + val_buf = pdf_convert_hypertext_depr(p, value, len); + if (!val_buf) + pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "value", 0, 0, 0); + + /* special handling required for "Trapped" */ + if (!strcmp(key_buf, "Trapped")) + { + if (strcmp(val_buf, PDF_TRAPPED_TRUE) && + strcmp(val_buf, PDF_TRAPPED_FALSE) && + strcmp(val_buf, PDF_TRAPPED_UNKNOWN)) + { + pdc_free(p->pdc, val_buf); + pdc_free(p->pdc, key_buf); + pdc_error(p->pdc, PDC_E_PAR_ILLPARAM, value, key, 0, 0); + } + } + + oldentry = pdf_have_infokey(p, key_buf); + if (oldentry != NULL) + { + pdc_free(p->pdc, key_buf); + pdc_free(p->pdc, oldentry->value); + oldentry->value = val_buf; + } + else + { + newentry = (pdf_info *) + pdc_malloc(p->pdc, sizeof(pdf_info), fn); + newentry->key = key_buf; + newentry->value = val_buf; + newentry->next = p->userinfo; + + /* ordering doesn't matter so we insert at the beginning */ + p->userinfo = newentry; + } +} + + +pdc_id +pdf_write_info(PDF *p, pdc_bool moddate) +{ + char time_str[PDC_TIME_SBUF_SIZE]; + char producer[PDF_MAX_PARAMSTRING]; + pdf_info *info; + pdc_id info_id; + + + + const char *product = "PDFlib Lite"; + + + if (!p->pdc->smokerun) + pdc_logg_cond(p->pdc, 1, trc_api, + "[Full product name: \"%s\"]\n", product); + + info_id = pdc_begin_obj(p->out, PDC_NEW_ID); /* Info object */ + + pdc_begin_dict(p->out); + + /* + * Although it would be syntactically correct, we must not remove + * the space characters after the dictionary keys since this + * would break the PDF properties feature in Windows Explorer. + */ + + if (p->userinfo) + { + for (info = p->userinfo; info != NULL; info = info->next) + { + pdf_put_pdfname(p, info->key); + pdc_puts(p->out, " "); + + if (strcmp(info->key, "Trapped")) + pdf_put_hypertext(p, info->value); + else + pdf_put_pdfname(p, info->value); + + pdc_puts(p->out, "\n"); + } + } + + + pdc_get_timestr(time_str, pdc_false); + + /* creation date and time */ + pdc_puts(p->out, "/CreationDate "); + pdf_put_hypertext(p, time_str); + pdc_puts(p->out, "\n"); + + /* modification date and time */ + if (moddate) + { + pdc_puts(p->out, "/ModDate "); + pdf_put_hypertext(p, time_str); + pdc_puts(p->out, "\n"); + } + + /* + * If you change the /Producer entry your license to use + * PDFlib will be void! + */ + + if (p->pdc->binding) + sprintf(producer, "%s %s (%s/%s)", product, + PDFLIB_VERSIONSTRING, p->pdc->binding, PDF_PLATFORM); + else + sprintf(producer, "%s %s (%s)", product, + PDFLIB_VERSIONSTRING, PDF_PLATFORM); + + pdc_puts(p->out, "/Producer "); + pdf_put_hypertext(p, producer); + pdc_puts(p->out, "\n"); + + pdc_end_dict(p->out); + pdc_end_obj(p->out); /* Info object */ + + + + + return info_id; +} + + |