8e01c8dd9a
Files correlati : Ricompilazione Demo : [ ] Commento :Primo commit del modulo git-svn-id: svn://10.65.10.50/trunk@13958 c028cbd2-c16b-5b4b-a496-9718f37d4682
1439 lines
39 KiB
C
Executable File
1439 lines
39 KiB
C
Executable File
/*---------------------------------------------------------------------------*
|
|
| PDFlib - A library for generating PDF on the fly |
|
|
+---------------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2005 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 2006-05-04 16:36:51 brugno 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 */
|
|
pdc_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, PDF_MAXSTRINGSIZE, 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, PDF_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 = pdc_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,
|
|
pdc_bool verbose)
|
|
{
|
|
int minpage;
|
|
pdc_resopt *resopts;
|
|
pdc_encoding hypertextencoding;
|
|
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, NULL, verbose);
|
|
|
|
keyword = "name";
|
|
if (pdf_get_opt_textlist(p, keyword, resopts, hypertextencoding, pdc_true,
|
|
NULL, &dest->name, NULL))
|
|
{
|
|
if (dest->type != nameddest)
|
|
{
|
|
dest->name = NULL;
|
|
if (verbose)
|
|
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 && verbose)
|
|
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 && verbose)
|
|
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) && verbose)
|
|
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 && verbose)
|
|
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 && verbose)
|
|
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) && verbose)
|
|
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 && verbose)
|
|
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 = (pdc_fontstyle) inum;
|
|
if (destuse != pdf_bookmark && verbose)
|
|
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)
|
|
{
|
|
if (verbose)
|
|
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)
|
|
{
|
|
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, p->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;
|
|
pdc_id obj_id = PDC_BAD_ID;
|
|
pdc_bool verbose = pdc_true;
|
|
char *name2 = NULL;
|
|
pdf_dest *dest;
|
|
int codepage;
|
|
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, verbose);
|
|
|
|
hypertextencoding =
|
|
pdf_get_hypertextencoding_opt(p, resopts, &codepage, verbose);
|
|
|
|
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, codepage, &len,
|
|
pdc_true, verbose);
|
|
if (name2 == NULL)
|
|
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, verbose);
|
|
|
|
/* 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, PDF_MAXSTRINGSIZE, NULL},
|
|
|
|
{"destname", pdc_stringlist, PDC_OPT_IGNOREIF1, 1, 1,
|
|
0.0, PDF_MAXSTRINGSIZE, NULL},
|
|
|
|
{"action", pdc_stringlist, PDC_OPT_NONE, 1, 1,
|
|
0.0, PDF_MAXSTRINGSIZE, 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 */
|
|
pdc_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 = pdc_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;
|
|
pdc_bool verbose;
|
|
char **strlist = NULL;
|
|
int codepage = p->hypertextcodepage;
|
|
int ns, inum, outlen, retval = 0;
|
|
int jndex = -2;
|
|
|
|
/* Initialize */
|
|
pdf_init_outline(p, &self);
|
|
hypertextformat = p->hypertextformat;
|
|
hypertextencoding = p->hypertextencoding;
|
|
verbose = pdc_true;
|
|
|
|
/* Parsing option list */
|
|
if (optlist && strlen(optlist))
|
|
{
|
|
pdf_set_clientdata(p, &data);
|
|
resopts = pdc_parse_optionlist(p->pdc, optlist,
|
|
pdf_create_bookmark_options, &data, verbose);
|
|
|
|
hypertextencoding =
|
|
pdf_get_hypertextencoding_opt(p, resopts, &codepage, verbose);
|
|
|
|
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 = (pdc_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, verbose);
|
|
keyword = "destination";
|
|
}
|
|
else
|
|
{
|
|
dest = pdf_get_option_destname(p, resopts, hypertextencoding);
|
|
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;
|
|
if (verbose)
|
|
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, codepage, &outlen,
|
|
PDC_UTF8_FLAG, verbose);
|
|
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 != pdc_Normal)
|
|
{
|
|
int fontstyle = 0;
|
|
if (outline->fontstyle == pdc_Bold)
|
|
fontstyle = 2;
|
|
if (outline->fontstyle == pdc_Italic)
|
|
fontstyle = 1;
|
|
if (outline->fontstyle == pdc_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->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, pdc_false);
|
|
|
|
/* 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[256];
|
|
pdf_info *info;
|
|
pdc_id info_id;
|
|
char * product = "PDFlib Lite";
|
|
|
|
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);
|
|
|
|
/* 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->binding)
|
|
sprintf(producer, "%s %s (%s/%s)", product,
|
|
PDFLIB_VERSIONSTRING, p->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;
|
|
}
|
|
|