Files correlati : pdflib Ricompilazione Demo : [ ] Commento : Aggiornata pdflib.dll alla versione 7.0.4 git-svn-id: svn://10.65.10.50/trunk@18580 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1488 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1488 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.4 2009-03-23 08:51:17 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;
 | |
|     const char **kp;
 | |
|     static const char *forbidden_keys[] =
 | |
|     {
 | |
|         "Producer",
 | |
|         "CreationDate",
 | |
|         "ModDate",
 | |
|         "GTS_PDFXVersion",
 | |
|         "GTS_PDFXConformance",
 | |
|         "ISO_PDFEVersion"
 | |
|     };
 | |
| 
 | |
|     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);
 | |
| 
 | |
|     for (kp = forbidden_keys;
 | |
|         kp != sizeof(forbidden_keys) / sizeof(char *) + forbidden_keys;
 | |
|         kp += 1)
 | |
|     {
 | |
|         if (!strcmp(*kp, key))
 | |
|         {
 | |
|             pdc_error(p->pdc, PDC_E_ILLARG_STRING, "key", key, 0, 0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* converting key */
 | |
|     key_buf = pdf_convert_name(p, key, 0, 0);
 | |
| 
 | |
|     /* convert text string */
 | |
|     val_buf = pdf_convert_hypertext_depr(p, value, len);
 | |
|     if (!val_buf)
 | |
|         pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "value", 0, 0, 0);
 | |
| 
 | |
|     /* special handling required for "Trapped" */
 | |
|     if (!strcmp(key_buf, "Trapped"))
 | |
|     {
 | |
|         if (strcmp(val_buf, PDF_TRAPPED_TRUE) &&
 | |
|             strcmp(val_buf, PDF_TRAPPED_FALSE) &&
 | |
|             strcmp(val_buf, PDF_TRAPPED_UNKNOWN))
 | |
|         {
 | |
|             pdc_free(p->pdc, val_buf);
 | |
|             pdc_free(p->pdc, key_buf);
 | |
|             pdc_error(p->pdc, PDC_E_PAR_ILLPARAM, value, key, 0, 0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     oldentry = pdf_have_infokey(p, key_buf);
 | |
|     if (oldentry != NULL)
 | |
|     {
 | |
|         pdc_free(p->pdc, key_buf);
 | |
|         pdc_free(p->pdc, oldentry->value);
 | |
|         oldentry->value = val_buf;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         newentry = (pdf_info *)
 | |
|             pdc_malloc(p->pdc, sizeof(pdf_info), fn);
 | |
|         newentry->key  = key_buf;
 | |
|         newentry->value = val_buf;
 | |
|         newentry->next = p->userinfo;
 | |
| 
 | |
|         /* ordering doesn't matter so we insert at the beginning */
 | |
|         p->userinfo = newentry;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| pdc_id
 | |
| pdf_write_info(PDF *p, pdc_bool moddate)
 | |
| {
 | |
|     pdc_bool logg3 = pdc_logg_is_enabled(p->pdc, 3, trc_xmp);
 | |
|     char time_str[PDC_TIME_SBUF_SIZE];
 | |
|     char producer[PDC_GEN_BUFSIZE];
 | |
|     pdf_info    *info;
 | |
|     pdc_id      info_id;
 | |
| 
 | |
| 
 | |
| 
 | |
|     const char  *product = "PDFlib Lite";
 | |
| 
 | |
|     (void) logg3;
 | |
| 
 | |
| 
 | |
|     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;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 |