Files correlati : pdflib Ricompilazione Demo : [ ] Commento : Aggiornata PDFlib git-svn-id: svn://10.65.10.50/trunk@17433 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1470 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1470 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*---------------------------------------------------------------------------*
 | 
						|
 |              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.3 2008-10-20 14:34:15 guy 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;
 | 
						|
 | 
						|
    len = pdc_check_text_length(p->pdc, &name, len, PDF_MAXSTRINGSIZE);
 | 
						|
    if (!len)
 | 
						|
        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;
 | 
						|
 | 
						|
    len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXSTRINGSIZE);
 | 
						|
    if (!len)
 | 
						|
        pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "text", 0, 0, 0);
 | 
						|
 | 
						|
    /* 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)
 | 
						|
{
 | 
						|
    static const char *fn = "pdf__add_bookmark";
 | 
						|
    pdf_outline self;
 | 
						|
    pdf_dest *dest = (pdf_dest *) p->bookmark_dest;
 | 
						|
    char *hypertext = NULL;
 | 
						|
    int acthdl;
 | 
						|
    int retval = 0;
 | 
						|
 | 
						|
    len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXSTRINGSIZE);
 | 
						|
    if (!len)
 | 
						|
        pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "text", 0, 0, 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;
 | 
						|
 | 
						|
        actoptlist = (char *)
 | 
						|
            pdc_malloc(p->pdc, strlen(dest->filename) + 80, fn);
 | 
						|
        pdc_sprintf(p->pdc, pdc_false, actoptlist, "filename {%s} ",
 | 
						|
                    dest->filename);
 | 
						|
        acthdl = pdf__create_action(p, "Launch", actoptlist);
 | 
						|
        if (acthdl != -1)
 | 
						|
        {
 | 
						|
            if (p->pdc->hastobepos) acthdl++;
 | 
						|
            pdc_sprintf(p->pdc, pdc_false, actoptlist, "activate %d", acthdl);
 | 
						|
            self.action = pdc_strdup(p->pdc, actoptlist);
 | 
						|
        }
 | 
						|
 | 
						|
        pdc_free(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);
 | 
						|
 | 
						|
    len = pdc_check_text_length(p->pdc, &value, len, PDF_MAXSTRINGSIZE);
 | 
						|
    if (!len)
 | 
						|
        pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "value", 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 || !*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[PDC_GEN_BUFSIZE];
 | 
						|
    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)
 | 
						|
        pdc_sprintf(p->pdc, pdc_false, producer, "%s %s (%s/%s)", product,
 | 
						|
            PDFLIB_VERSIONSTRING, p->pdc->binding, PDF_PLATFORM);
 | 
						|
    else
 | 
						|
        pdc_sprintf(p->pdc, pdc_false, 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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 |