/*---------------------------------------------------------------------------* | 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_document.c,v 1.2 2009/10/20 18:14:16 scuri Exp $ * * PDFlib document related routines * */ #define P_DOCUMENT_C /* For checking the beta expiration date */ #include #include "p_intern.h" #include "p_image.h" #include "p_layer.h" #include "p_page.h" #include "p_tagged.h" #include "pc_strconst.h" #if (defined(WIN32) || defined(OS2)) && !defined(WINCE) #include #include #endif /* file attachment structure */ typedef struct { char *filename; char *name; char *description; char *mimetype; pdc_off_t filesize; } pdf_attachments; #define PDF_MAX_LANGCODE 8 /* Document open modes */ typedef enum { open_auto, open_none, open_bookmarks, open_thumbnails, open_fullscreen, open_attachments, open_layers } pdf_openmode; static const pdc_keyconn pdf_openmode_keylist[] = { {"none", open_none}, {"bookmarks", open_bookmarks}, {"thumbnails", open_thumbnails}, {"fullscreen", open_fullscreen}, {"attachments", open_attachments}, {"layers", open_layers}, {NULL, 0} }; static const pdc_keyconn pdf_openmode_pdfkeylist[] = { {"UseNone", open_auto}, {"UseNone", open_none}, {"UseOutlines", open_bookmarks}, {"UseThumbs", open_thumbnails}, {"FullScreen", open_fullscreen}, {"UseAttachments", open_attachments}, {"UseOC", open_layers}, {NULL, 0} }; /* Document page layout */ typedef enum { layout_default, layout_singlepage, layout_onecolumn, layout_twocolumnleft, layout_twocolumnright, layout_twopageleft, layout_twopageright } pdf_pagelayout; static const pdc_keyconn pdf_pagelayout_pdfkeylist[] = { {"Default", layout_default}, {"SinglePage", layout_singlepage}, {"OneColumn", layout_onecolumn}, {"TwoColumnLeft", layout_twocolumnleft}, {"TwoColumnRight", layout_twocolumnright}, {"TwoPageLeft", layout_twopageleft}, {"TwoPageRight", layout_twopageright}, {NULL, 0} }; /* NonFullScreenPageMode */ static const pdc_keyconn pdf_nonfullscreen_keylist[] = { {"none", open_none}, {"bookmarks", open_bookmarks}, {"thumbnails", open_thumbnails}, {"layers", open_layers}, {NULL, 0} }; typedef enum { doc_none, doc_l2r, doc_r2l, doc_appdefault, doc_simplex, doc_duplexflipshortedge, doc_duplexfliplongedge } pdf_viewerprefence; /* Direction */ static const pdc_keyconn pdf_textdirection_pdfkeylist[] = { {"L2R", doc_l2r}, {"R2L", doc_r2l}, {NULL, 0} }; /* PrintScaling */ static const pdc_keyconn pdf_printscaling_pdfkeylist[] = { {"None", doc_none}, {"AppDefault", doc_appdefault}, {NULL, 0} }; /* Duplex */ static const pdc_keyconn pdf_duplex_pdfkeylist[] = { {"None", doc_none}, {"Simplex", doc_simplex}, {"DuplexFlipShortEdge", doc_duplexflipshortedge}, {"DuplexFlipLongEdge", doc_duplexfliplongedge}, {NULL, 0} }; static const pdc_keyconn pdf_pdfa_keylist[] = { {NULL, 0} }; static const pdc_keyconn pdf_pdfx_keylist[] = { {NULL, 0} }; /* configurable flush points */ static const pdc_keyconn pdf_flush_keylist[] = { {"none", pdc_flush_none}, {"page", pdc_flush_page}, {"content", pdc_flush_content}, {"heavy", pdc_flush_heavy}, {NULL, 0} }; static const pdc_keyconn pl_pwencoding_keylist[] = { {"ebcdic", pdc_ebcdic}, {"ebcdic_37", pdc_ebcdic_37}, {"ebcdic_winansi", pdc_ebcdic_winansi}, {"pdfdoc", pdc_pdfdoc}, {"winansi", pdc_winansi}, {"macroman", pdc_macroman_apple}, {NULL, 0} }; #define PDF_MAXPW 0 static const pdc_keyconn pdc_permissions_keylist[] = { {NULL, 0} }; #define PDF_PDFA_FLAG PDC_OPT_UNSUPP #define PDF_SECURITY_FLAG PDC_OPT_UNSUPP #define PDF_LINEARIZE_FLAG PDC_OPT_UNSUPP #define PDF_ICC_FLAG PDC_OPT_UNSUPP #define PDF_TAGGED_FLAG PDC_OPT_UNSUPP #define PDF_METADATA_FLAG PDC_OPT_UNSUPP #define PDF_UPDATE_FLAG PDC_OPT_UNSUPP #define PDF_DOCUMENT_OPTIONS1 \ \ {"pdfa", pdc_keywordlist, PDF_PDFA_FLAG, 1, 1, \ 0.0, 0.0, pdf_pdfa_keylist}, \ \ {"pdfx", pdc_keywordlist, PDF_ICC_FLAG, 1, 1, \ 0.0, 0.0, pdf_pdfx_keylist}, \ \ {"compatibility", pdc_keywordlist, PDC_OPT_NONE, 1, 1, \ 0.0, 0.0, pdf_compatibility_keylist}, \ \ {"flush", pdc_keywordlist, PDC_OPT_NONE, 1, 1, \ 0.0, 0.0, pdf_flush_keylist}, \ \ {"passwordencoding", pdc_keywordlist, PDF_SECURITY_FLAG, 1, 1, \ 0.0, 0.0, pl_pwencoding_keylist}, \ \ {"attachmentpassword", pdc_stringlist, PDF_SECURITY_FLAG, 1, 1, \ 0.0, PDF_MAXPW, NULL}, \ \ {"masterpassword", pdc_stringlist, PDF_SECURITY_FLAG, 1, 1, \ 0.0, PDF_MAXPW, NULL}, \ \ {"userpassword", pdc_stringlist, PDF_SECURITY_FLAG, 1, 1, \ 0.0, PDF_MAXPW, NULL}, \ \ {"permissions", pdc_keywordlist, \ PDF_SECURITY_FLAG | PDC_OPT_BUILDOR | PDC_OPT_DUPORIGVAL, 1, 9,\ 0.0, 0.0, pdc_permissions_keylist}, \ \ {"update", pdc_booleanlist, PDF_UPDATE_FLAG, 1, 1, \ 0.0, 0.0, NULL}, \ \ {"tagged", pdc_booleanlist, PDF_TAGGED_FLAG, 1, 1, \ 0.0, 0.0, NULL}, \ \ {"lang", pdc_stringlist, PDF_TAGGED_FLAG, 1, 1, \ 0.0, PDF_MAX_LANGCODE, NULL}, \ \ {"search", pdc_stringlist, PDC_OPT_NONE, 1, 1, \ 0.0, PDC_INT_MAX, NULL}, \ \ {"groups", pdc_stringlist, PDC_OPT_NONE, 1, PDC_USHRT_MAX, \ 0.0, PDF_MAX_NAMESTRING, NULL}, \ \ {"optimize", pdc_booleanlist, PDF_LINEARIZE_FLAG, 1, 1, \ 0.0, 0.0, NULL}, \ \ {"linearize", pdc_booleanlist, PDF_LINEARIZE_FLAG, 1, 1, \ 0.0, 0.0, NULL}, \ \ {"inmemory", pdc_booleanlist, PDF_LINEARIZE_FLAG, 1, 1,\ 0.0, 0.0, NULL}, \ \ {"tempdirname", pdc_stringlist, PDF_LINEARIZE_FLAG, 1, 1, \ 4.0, 400.0, NULL}, \ #if defined(MVS) || defined(MVS_TEST) #define PDF_DOCUMENT_OPTIONS10 \ \ {"filemode", pdc_stringlist, PDC_OPT_NONE, 1, 1, \ 0.0, PDF_MAX_NAMESTRING, NULL}, \ \ {"recordsize", pdc_integerlist, PDF_LINEARIZE_FLAG, 1, 1, \ 0.0, 32768.0, NULL}, \ \ {"tempfilenames", pdc_stringlist, PDF_LINEARIZE_FLAG, 2, 2, \ 4.0, 400.0, NULL}, \ #endif #define PDF_DOCUMENT_OPTIONS2 \ \ {"hypertextencoding", pdc_stringlist, PDC_OPT_NONE, 1, 1, \ 0.0, PDF_MAX_NAMESTRING, NULL}, \ \ {"moddate", 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}, \ \ {"labels", pdc_stringlist, PDC_OPT_NONE, 1, PDC_USHRT_MAX, \ 0.0, PDC_USHRT_MAX, NULL}, \ \ {"openmode", pdc_keywordlist, PDC_OPT_NONE, 1, 1, \ 0.0, 0.0, pdf_openmode_keylist}, \ \ {"pagelayout", pdc_keywordlist, PDC_OPT_NONE, 1, 1, \ 0.0, 0.0, pdf_pagelayout_pdfkeylist}, \ \ {"uri", pdc_stringlist, PDC_OPT_NONE, 1, 1, \ 0.0, PDC_INT_MAX, NULL}, \ \ {"viewerpreferences", pdc_stringlist, PDC_OPT_NONE, 1, 1, \ 0.0, PDC_USHRT_MAX, NULL}, \ \ {"autoxmp", pdc_booleanlist, PDF_METADATA_FLAG, 1, 1, \ 0.0, 0.0, NULL}, \ \ {"metadata", pdc_stringlist, PDF_METADATA_FLAG, 1, 1, \ 0.0, PDC_INT_MAX, NULL}, \ \ {"attachments", pdc_stringlist, PDC_OPT_NONE, 1, PDC_USHRT_MAX, \ 0.0, PDC_INT_MAX, NULL}, \ /* document struct */ struct pdf_document_s { int compatibility; /* PDF version number * 10 */ pdc_flush_state flush; /* output flushing points */ #if defined(MVS) || defined(MVS_TEST) char *fopenparams; /* additional fopen() parameter string */ char **tempfilenames; /* 2 temporary file names */ #endif pdc_bool moddate; /* modified date will be created */ char lang[PDF_MAX_LANGCODE + 1]; /* default natural language */ char *action; /* document actions */ pdf_dest *dest; /* destination as open action */ char *uri; /* document's base url */ char *viewerpreferences; /* option list with viewer preferences */ pdc_bool writevpdict; /* viewer preferences dictionary * must be written */ pdf_openmode openmode; /* document open mode */ pdf_pagelayout pagelayout; /* page layout within document */ char *searchindexname; /* file name for search index */ char *searchindextype; /* type for search index */ pdf_attachments *attachments; /* temporarily file attachments */ int nattachs; /* number of file attachments */ char *filename; /* file name of document */ size_t (*writeproc)(PDF *p, void *data, size_t size); /* output procedure */ FILE *fp; /* file id - deprecated */ int len; /* length of custom */ }; static pdf_document * pdf_init_get_document(PDF *p) { static const char fn[] = "pdf_init_get_document"; if (p->document == NULL) { pdf_document *doc = (pdf_document *) pdc_malloc(p->pdc, sizeof(pdf_document), fn); doc->compatibility = PDF_DEF_COMPATIBILITY; doc->flush = pdc_flush_page; #if defined(MVS) || defined(MVS_TEST) doc->fopenparams = NULL; doc->tempfilenames = NULL; #endif doc->moddate = pdc_false; doc->lang[0] = 0; doc->action = NULL; doc->dest = NULL; doc->uri = NULL; doc->viewerpreferences = NULL; doc->writevpdict = pdc_false; doc->openmode = open_auto; doc->pagelayout = layout_default; doc->searchindexname = NULL; doc->searchindextype = NULL; doc->attachments = NULL; doc->nattachs = 0; doc->fp = NULL; doc->filename = NULL; doc->writeproc = NULL; doc->len = 0; p->document = doc; } return p->document; } static void pdf_cleanup_document_internal(PDF *p) { pdf_document *doc = (pdf_document *) p->document; if (doc) { pdf_cleanup_destination(p, doc->dest); doc->dest = NULL; if (doc->action) { pdc_free(p->pdc, doc->action); doc->action = NULL; } if (doc->uri) { pdc_free(p->pdc, doc->uri); doc->uri = NULL; } if (doc->viewerpreferences) { pdc_free(p->pdc, doc->viewerpreferences); doc->viewerpreferences = NULL; } #if defined(MVS) || defined(MVS_TEST) if (doc->fopenparams) { pdc_free(p->pdc, doc->fopenparams); doc->fopenparams = NULL; } if (doc->tempfilenames) { pdc_cleanup_optstringlist(p->pdc, doc->tempfilenames, 2); doc->tempfilenames = NULL; } #endif if (doc->searchindexname) { pdc_free(p->pdc, doc->searchindexname); doc->searchindexname = NULL; } if (doc->searchindextype) { pdc_free(p->pdc, doc->searchindextype); doc->searchindextype = NULL; } if (doc->filename) { pdc_free(p->pdc, doc->filename); doc->filename = NULL; } pdc_free(p->pdc, doc); p->document = NULL; } } /* ---------------------------- PDFA / PDFX -------------------------- */ void pdf_fix_openmode(PDF *p) { pdf_document *doc = pdf_init_get_document(p); if (doc->openmode == open_auto) doc->openmode = open_bookmarks; } /* ------------------------- viewerpreferences ----------------------- */ static const pdc_defopt pdf_viewerpreferences_options[] = { {"centerwindow", pdc_booleanlist, PDC_OPT_NONE, 1, 1, 0.0, 0, NULL}, {"direction", pdc_keywordlist, PDC_OPT_NONE, 1, 1, 0.0, 0.0, pdf_textdirection_pdfkeylist}, {"displaydoctitle", pdc_booleanlist, PDC_OPT_NONE, 1, 1, 0.0, 0, NULL}, {"fitwindow", pdc_booleanlist, PDC_OPT_NONE, 1, 1, 0.0, 0, NULL}, {"hidemenubar", pdc_booleanlist, PDC_OPT_NONE, 1, 1, 0.0, 0, NULL}, {"hidetoolbar", pdc_booleanlist, PDC_OPT_NONE, 1, 1, 0.0, 0, NULL}, {"hidewindowui", pdc_booleanlist, PDC_OPT_NONE, 1, 1, 0.0, 0, NULL}, {"nonfullscreenpagemode", pdc_keywordlist, PDC_OPT_NONE, 1, 1, 0.0, 0.0, pdf_nonfullscreen_keylist}, {"viewarea", pdc_keywordlist, PDC_OPT_NONE, 1, 1, 0.0, 0.0, pdf_usebox_keylist}, {"viewclip", pdc_keywordlist, PDC_OPT_NONE, 1, 1, 0.0, 0.0, pdf_usebox_keylist}, {"printarea", pdc_keywordlist, PDC_OPT_NONE, 1, 1, 0.0, 0.0, pdf_usebox_keylist}, {"printclip", pdc_keywordlist, PDC_OPT_NONE, 1, 1, 0.0, 0.0, pdf_usebox_keylist}, {"printscaling", pdc_keywordlist, PDC_OPT_PDC_1_6, 1, 1, 0.0, 0.0, pdf_printscaling_pdfkeylist}, {"duplex", pdc_keywordlist, PDC_OPT_PDC_1_7, 1, 1, 0.0, 0.0, pdf_duplex_pdfkeylist}, {"picktraybypdfsize", pdc_booleanlist, PDC_OPT_PDC_1_7, 1, 1, 0.0, 0, NULL}, {"printpagerange", pdc_integerlist, PDC_OPT_PDC_1_7 | PDC_OPT_EVENNUM, 1, PDC_USHRT_MAX, 1.0, PDC_INT_MAX, NULL}, \ {"numcopies", pdc_integerlist, PDC_OPT_PDC_1_7, 1, 1, \ 1.0, 5.0, NULL}, \ PDC_OPT_TERMINATE }; static int pdf_parse_and_write_viewerpreferences(PDF *p, const char *optlist, pdc_bool output) { pdc_resopt *resopts = NULL; pdc_clientdata cdata; char **strlist; pdc_bool writevpdict = pdc_false; pdc_bool flag; int i, nv, inum; /* parsing option list */ pdf_set_clientdata(p, &cdata); resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_viewerpreferences_options, &cdata, pdc_true); if (pdc_get_optvalues("hidetoolbar", resopts, &flag, NULL) && flag) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/HideToolbar true\n"); } if (pdc_get_optvalues("hidemenubar", resopts, &flag, NULL) && flag) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/HideMenubar true\n"); } if (pdc_get_optvalues("hidewindowui", resopts, &flag, NULL) && flag) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/HideWindowUI true\n"); } if (pdc_get_optvalues("fitwindow", resopts, &flag, NULL) && flag) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/FitWindow true\n"); } if (pdc_get_optvalues("centerwindow", resopts, &flag, NULL) && flag) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/CenterWindow true\n"); } if (pdc_get_optvalues("displaydoctitle", resopts, &flag, NULL) && flag) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/DisplayDocTitle true\n"); } if (pdc_get_optvalues("nonfullscreenpagemode", resopts, &inum, NULL) && inum != (int) open_none) { if (inum == (int) open_layers) pdc_error(p->pdc, PDF_E_UNSUPP_LAYER, 0, 0, 0, 0); writevpdict = pdc_true; if (output) pdc_printf(p->out, "/NonFullScreenPageMode/%s\n", pdc_get_keyword(inum, pdf_openmode_pdfkeylist)); } if (pdc_get_optvalues("direction", resopts, &inum, NULL) && inum != (int) doc_l2r) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/Direction/%s\n", pdc_get_keyword(inum, pdf_textdirection_pdfkeylist)); } if (pdc_get_optvalues("viewarea", resopts, &inum, NULL) && inum != (int) pdc_pbox_crop) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/ViewArea%s\n", pdc_get_keyword(inum, pdf_usebox_pdfkeylist)); } if (pdc_get_optvalues("viewclip", resopts, &inum, NULL) && inum != (int) pdc_pbox_crop) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/ViewClip%s\n", pdc_get_keyword(inum, pdf_usebox_pdfkeylist)); } if (pdc_get_optvalues("printarea", resopts, &inum, NULL) && inum != (int) pdc_pbox_crop) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/PrintArea%s\n", pdc_get_keyword(inum, pdf_usebox_pdfkeylist)); } if (pdc_get_optvalues("printclip", resopts, &inum, NULL) && inum != (int) pdc_pbox_crop) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/PrintClip%s\n", pdc_get_keyword(inum, pdf_usebox_pdfkeylist)); } if (pdc_get_optvalues("printscaling", resopts, &inum, NULL) && inum != (int) doc_appdefault) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/PrintScaling/%s\n", pdc_get_keyword(inum, pdf_printscaling_pdfkeylist)); } if (pdc_get_optvalues("duplex", resopts, &inum, NULL) && inum != (int) doc_none) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/Duplex/%s\n", pdc_get_keyword(inum, pdf_duplex_pdfkeylist)); } if (pdc_get_optvalues("picktraybypdfsize", resopts, &flag, NULL)) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/PickTrayByPDFSize %s\n", PDC_BOOLSTR(flag)); } nv = pdc_get_optvalues("printpagerange", resopts, NULL, &strlist); if (nv) { writevpdict = pdc_true; if (output) { int *prs = (int *) strlist; pdc_printf(p->out, "/PrintPageRange"); pdc_begin_array(p->out); for (i = 0; i < nv; i++) /* because of bug #1623: -1 */ pdc_printf(p->out, "%d ", prs[i] - 1); pdc_end_array(p->out); } } if (pdc_get_optvalues("numcopies", resopts, &inum, NULL)) { writevpdict = pdc_true; if (output) pdc_printf(p->out, "/NumCopies %d\n", inum); } pdc_cleanup_optionlist(p->pdc, resopts); return writevpdict; } /* ------------------------- search ----------------------- */ static const pdc_defopt pdf_search_options[] = { {"filename", pdc_stringlist, PDC_OPT_REQUIRED, 1, 1, 1.0, PDC_FILENAMELEN, NULL}, {"indextype", pdc_stringlist, PDC_OPT_NONE, 1, 1, 0.0, PDF_MAX_NAMESTRING, NULL}, PDC_OPT_TERMINATE }; static void pdf_parse_search_optlist(PDF *p, const char *optlist, pdc_encoding htenc, int htcp) { pdf_document *doc = p->document; pdc_resopt *resopts = NULL; /* parsing option list */ resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_search_options, NULL, pdc_true); if (pdf_get_opt_textlist(p, "filename", resopts, htenc, htcp, pdc_true, NULL, &doc->searchindexname, NULL)) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); if (pdc_get_optvalues("indextype", resopts, NULL, NULL)) doc->searchindextype = (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); else doc->searchindextype = pdc_strdup(p->pdc, "PDX"); pdc_cleanup_optionlist(p->pdc, resopts); } static void pdf_write_search_indexes(PDF *p) { pdf_document *doc = p->document; if (doc->searchindexname != NULL) { pdc_puts(p->out, "/Search"); pdc_begin_dict(p->out); /* Search */ pdc_puts(p->out, "/Indexes"); pdc_begin_array(p->out); pdc_begin_dict(p->out); /* Indexes */ pdc_puts(p->out, "/Name"); pdc_printf(p->out, "/%s", doc->searchindextype); pdc_puts(p->out, "/Index"); pdc_begin_dict(p->out); /* Index */ pdc_puts(p->out, "/Type/Filespec"); pdc_puts(p->out, "/F"); pdf_put_pdffilename(p, doc->searchindexname); if (p->compatibility >= PDC_1_7) { pdc_printf(p->out, "/UF"); pdf_put_pdfunifilename(p, doc->searchindexname); } pdc_end_dict(p->out); /* Index */ pdc_end_dict(p->out); /* Indexes */ pdc_end_array(p->out); pdc_end_dict(p->out); /* Search */ } } /* ---------------------- file attachements -------------------- */ static void pdc_cleanup_attachments_tmp(void *opaque, void *mem) { if (mem) { PDF *p = (PDF *) opaque; pdf_document *doc = p->document; int i; if (doc != NULL) { for (i = 0; i < doc->nattachs; i++) { pdf_attachments *fat = &doc->attachments[i]; if (fat->filename != NULL) pdc_free(p->pdc, fat->filename); if (fat->name != NULL) pdc_free(p->pdc, fat->name); if (fat->description != NULL) pdc_free(p->pdc, fat->description); if (fat->mimetype != NULL) pdc_free(p->pdc, fat->mimetype); } doc->attachments = NULL; doc->nattachs = 0; } } } static const pdc_defopt pdf_attachments_options[] = { {"filename", pdc_stringlist, PDC_OPT_REQUIRED, 1, 1, 1.0, PDC_FILENAMELEN, NULL}, {"description", pdc_stringlist, PDC_OPT_PDC_1_6, 1, 1, 0.0, PDC_INT_MAX, NULL}, {"name", pdc_stringlist, PDC_OPT_NONE, 1, 1, 0.0, PDF_MAX_NAMESTRING, NULL}, {"mimetype", pdc_stringlist, PDC_OPT_NONE, 1, 1, 0.0, PDF_MAX_NAMESTRING, NULL}, PDC_OPT_TERMINATE }; static void pdf_parse_attachments_optlist(PDF *p, char **optlists, int ns, pdc_encoding htenc, int htcp) { static const char fn[] = "pdf_parse_attachments_optlist"; pdf_document *doc = p->document; pdc_resopt *resopts = NULL; pdc_clientdata cdata; int i; doc->attachments = (pdf_attachments *) pdc_malloc_tmp(p->pdc, ns * sizeof(pdf_attachments), fn, p, pdc_cleanup_attachments_tmp); doc->nattachs = ns; pdf_set_clientdata(p, &cdata); for (i = 0; i < ns; i++) { pdf_attachments *fat = &doc->attachments[i]; fat->filename = NULL; fat->name = NULL; fat->description = NULL; fat->mimetype = NULL; fat->filesize = 0; } for (i = 0; i < ns; i++) { pdf_attachments *fat = &doc->attachments[i]; /* parsing option list */ resopts = pdc_parse_optionlist(p->pdc, optlists[i], pdf_attachments_options, &cdata, pdc_true); if (pdf_get_opt_textlist(p, "filename", resopts, htenc, htcp, pdc_true, NULL, &fat->filename, NULL)) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); if (pdf_get_opt_textlist(p, "description", resopts, htenc, htcp, pdc_true, NULL, &fat->description, NULL)) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); if (pdf_get_opt_textlist(p, "name", resopts, htenc, htcp, pdc_true, NULL, &fat->name, NULL)) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); if (pdc_get_optvalues("mimetype", resopts, NULL, NULL)) fat->mimetype = (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); pdc_cleanup_optionlist(p->pdc, resopts); fat->filesize = pdf_check_file(p, fat->filename, pdc_true); } } static void pdf_write_attachments(PDF *p) { static const char fn[] = "pdf_write_attachments"; pdf_document *doc = p->document; pdc_id attachment_id, obj_id; const char *basename; char *name; int i; for (i = 0; i < doc->nattachs; i++) { pdf_attachments *fat = &doc->attachments[i]; if (fat->filesize > 0) { /* create file specification dictionary */ attachment_id = pdc_begin_obj(p->out, PDC_NEW_ID); pdc_begin_dict(p->out); /* FS dict */ /* see bug #1439 */ basename = pdc_file_strip_dirs(fat->filename); pdc_puts(p->out, "/Type/Filespec\n"); pdc_printf(p->out, "/F"); pdf_put_pdffilename(p, basename); pdc_puts(p->out, "\n"); if (p->compatibility >= PDC_1_7) { pdc_printf(p->out, "/UF"); pdf_put_pdfunifilename(p, basename); pdc_puts(p->out, "\n"); } if (fat->description != NULL) { pdc_puts(p->out, "/Desc"); pdf_put_hypertext(p, fat->description); pdc_puts(p->out, "\n"); } obj_id = pdc_alloc_id(p->out); pdc_puts(p->out, "/EF"); pdc_begin_dict(p->out); pdc_objref(p->out, "/F", obj_id); pdc_end_dict(p->out); pdc_end_dict(p->out); /* FS dict */ pdc_end_obj(p->out); /* embed file */ pdf_embed_file(p, obj_id, fat->filename, fat->mimetype, fat->filesize); /* insert name in tree */ if (fat->name == NULL) name = pdc_strdup_ext(p->pdc, basename, 0, fn); else name = pdc_strdup_ext(p->pdc, fat->name, 0, fn); pdf_insert_name(p, name, names_embeddedfiles, attachment_id); } } } pdc_off_t pdf_check_file(PDF *p, const char *filename, pdc_bool verbose) { pdc_off_t filesize = 0; const char *qualifier = "attachment "; pdc_file *fp; fp = pdc_fsearch_fopen(p->pdc, filename, NULL, qualifier, PDC_FILE_BINARY); if (fp == NULL) { if (verbose) pdc_error(p->pdc, -1, 0, 0, 0, 0); } else { filesize = pdc_file_size(fp); pdc_fclose(fp); if (filesize == 0) { pdc_set_errmsg(p->pdc, PDC_E_IO_FILE_EMPTY, qualifier, filename, 0, 0); if (verbose) pdc_error(p->pdc, -1, 0, 0, 0, 0); } } return filesize; } void pdf_embed_file(PDF *p, pdc_id obj_id, const char *filename, const char *mimetype, pdc_off_t filesize) { pdc_id length_id; PDF_data_source src; pdc_begin_obj(p->out, obj_id); pdc_begin_dict(p->out); /* F dict */ pdc_puts(p->out, "/Type/EmbeddedFile\n"); if (mimetype && *mimetype) { pdc_puts(p->out, "/Subtype"); pdf_put_pdfname(p, mimetype); pdc_puts(p->out, "\n"); } pdc_puts(p->out, "/Params"); pdc_begin_dict(p->out); /* Params */ pdc_printf(p->out, "/Size %lld", filesize); pdc_end_dict(p->out); /* Params */ if (pdc_get_compresslevel(p->out)) { pdc_puts(p->out, "/Filter/FlateDecode\n"); } length_id = pdc_alloc_id(p->out); pdc_objref(p->out, "/Length", length_id); pdc_end_dict(p->out); /* F dict */ /* write the file in the PDF */ src.private_data = (void *) filename; src.init = pdf_data_source_file_init; src.fill = pdf_data_source_file_fill; src.terminate = pdf_data_source_file_terminate; src.length = (long) 0; src.offset = (long) 0; pdf_copy_stream(p, &src, pdc_true); pdc_end_obj(p->out); pdc_put_pdfstreamlength(p->out, length_id); if (p->flush & pdc_flush_content) pdc_flush_stream(p->out); } /* ---------------------- linearize -------------------- */ /* ------------------ document options ----------------- */ static void pdf_get_document_common_options(PDF *p, pdc_resopt *resopts, int fcode) { pdf_document *doc = p->document; pdc_encoding htenc; int htcp; char **strlist; int i, inum, ns; htenc = pdf_get_hypertextencoding_opt(p, resopts, &htcp, pdc_true); if (pdc_get_optvalues("destination", resopts, NULL, &strlist)) { if (doc->dest) pdc_free(p->pdc, doc->dest); doc->dest = pdf_parse_destination_optlist(p, strlist[0], 1, pdf_openaction); } else { pdf_dest *dest = pdf_get_option_destname(p, resopts, htenc, htcp); if (dest) { if (doc->dest) pdc_free(p->pdc, doc->dest); doc->dest = dest; } } if (pdc_get_optvalues("action", resopts, NULL, NULL)) { if (doc->action) pdc_free(p->pdc, doc->action); doc->action = (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); pdf_parse_and_write_actionlist(p, event_document, NULL, doc->action); } inum = pdc_get_optvalues("labels", resopts, NULL, &strlist); for (i = 0; i < inum; i++) pdf_set_pagelabel(p, strlist[i], fcode); if (pdc_get_optvalues("openmode", resopts, &inum, NULL)) doc->openmode = (pdf_openmode) inum; if (doc->openmode == open_layers) pdc_error(p->pdc, PDF_E_UNSUPP_LAYER, 0, 0, 0, 0); if (doc->openmode == open_attachments && p->compatibility < PDC_1_6) pdc_error(p->pdc, PDC_E_OPT_VERSION, "openmode=attachments", pdc_get_pdfversion(p->pdc, p->compatibility), 0, 0); if (pdc_get_optvalues("pagelayout", resopts, &inum, NULL)) doc->pagelayout = (pdf_pagelayout) inum; if (p->compatibility < PDC_1_5) { if (doc->pagelayout == layout_twopageleft) pdc_error(p->pdc, PDC_E_OPT_VERSION, "pagelayout=TwoPageLeft", pdc_get_pdfversion(p->pdc, p->compatibility), 0, 0); if (doc->pagelayout == layout_twopageright) pdc_error(p->pdc, PDC_E_OPT_VERSION, "pagelayout=TwoPageRight", pdc_get_pdfversion(p->pdc, p->compatibility), 0, 0); } if (pdc_get_optvalues("uri", resopts, NULL, NULL)) { if (doc->uri) pdc_free(p->pdc, doc->uri); doc->uri = (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); } if (pdc_get_optvalues("viewerpreferences", resopts, NULL, NULL)) { if (doc->viewerpreferences) pdc_free(p->pdc, doc->viewerpreferences); doc->viewerpreferences = (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); doc->writevpdict |= pdf_parse_and_write_viewerpreferences(p, doc->viewerpreferences, pdc_false); } if (pdc_get_optvalues("search", resopts, NULL, &strlist)) pdf_parse_search_optlist(p, strlist[0], htenc, htcp); pdc_get_optvalues("moddate", resopts, &doc->moddate, NULL); ns = pdc_get_optvalues("attachments", resopts, NULL, &strlist); if (ns) pdf_parse_attachments_optlist(p, strlist, ns, htenc, htcp); } static const pdc_defopt pdf_begin_document_options[] = { PDF_DOCUMENT_OPTIONS1 #if defined(MVS) || defined(MVS_TEST) PDF_DOCUMENT_OPTIONS10 #endif PDF_DOCUMENT_OPTIONS2 PDF_ERRORPOLICY_OPTION PDC_OPT_TERMINATE }; /* * The external callback interface requires a PDF* as the first argument, * while the internal interface uses pdc_output* and doesn't know about PDF*. * We use a wrapper to bridge the gap, and store the PDF* within the * pdc_output structure opaquely. */ static size_t writeproc_wrapper(pdc_output *out, void *data, size_t size) { size_t ret; PDF *p = (PDF *) pdc_get_opaque(out); ret = (p->writeproc)(p, data, size); pdc_logg_cond(p->pdc, 1, trc_api, "/* writeproc(data[%p], %d)[%d] */\n", data, size, ret); return ret; } /* ---------------------------- begin document -------------------------- */ static int pdf_begin_document_internal(PDF *p, const char *optlist, pdc_bool callback) { pdf_document *doc = p->document; pdc_resopt *resopts = NULL; char **groups = NULL; int n_groups = 0; pdc_bool verbose = p->debug[(int) 'o']; pdc_outctl oc; (void) callback; verbose = pdf_get_errorpolicy(p, NULL, verbose); /* parsing option list */ if (optlist && *optlist) { int inum; resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_begin_document_options, NULL, pdc_true); verbose = pdf_get_errorpolicy(p, resopts, verbose); pdc_get_optvalues("compatibility", resopts, &doc->compatibility, NULL); if (pdc_get_optvalues("flush", resopts, &inum, NULL)) doc->flush = (pdc_flush_state) inum; pdc_get_optvalues("lang", resopts, doc->lang, NULL); #if defined(MVS) || defined(MVS_TEST) if (pdc_get_optvalues("filemode", resopts, NULL, NULL)) { doc->fopenparams = (char *) pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); } #endif n_groups = pdc_get_optvalues("groups", resopts, NULL, &groups); } /* copy for easy access */ p->compatibility = doc->compatibility; p->pdc->compatibility = doc->compatibility; p->flush = doc->flush; /* * None of these functions must call pdc_alloc_id() or generate * any output since the output machinery is not yet initialized! */ pdf_init_pages(p, (const char **) groups, n_groups); /* common options */ pdf_get_document_common_options(p, resopts, PDF_FC_BEGIN_DOCUMENT); /* create document digest */ pdc_init_digest(p->out); if (!p->pdc->ptfrun) { if (doc->fp) pdc_update_digest(p->out, (pdc_byte *) doc->fp, doc->len); else if (doc->writeproc) pdc_update_digest(p->out, (pdc_byte *) &doc->writeproc, doc->len); else if (doc->filename) pdc_update_digest(p->out, (pdc_byte *) doc->filename, doc->len); } pdf_feed_digest_info(p); if (!p->pdc->ptfrun) { pdc_update_digest(p->out, (pdc_byte *) &p, sizeof(PDF*)); pdc_update_digest(p->out, (pdc_byte *) p, sizeof(PDF)); } pdc_finish_digest(p->out, !p->pdc->ptfrun); /* preparing output struct */ pdc_init_outctl(&oc); oc.flush = doc->flush; if (doc->fp) oc.fp = doc->fp; else if (doc->writeproc) { oc.writeproc = writeproc_wrapper; p->writeproc = doc->writeproc; } else if (doc->filename) oc.filename = doc->filename; else oc.filename = ""; #if defined(MVS) || defined(MVS_TEST) oc.fopenparams = doc->fopenparams; #endif PDC_TRY(p->pdc) { if (!pdc_init_output((void *) p, p->out, doc->compatibility, &oc)) { if (oc.filename && *oc.filename) { pdc_set_fopen_errmsg(p->pdc, pdc_get_fopen_errnum(p->pdc, PDC_E_IO_WROPEN), "PDF ", pdc_errprintf(p->pdc, "%.*s", PDC_ERR_MAXSTRLEN, oc.filename)); if (verbose) { pdf_cleanup_document_internal(p); PDC_RETHROW(p->pdc); } } pdf_cleanup_document_internal(p); PDC_EXIT_TRY(p->pdc); return -1; } } PDC_CATCH(p->pdc) { pdf_cleanup_document_internal(p); if (verbose) PDC_RETHROW(p->pdc); return -1; } /* deprecated */ p->bookmark_dest = pdf_init_destination(p); pdf_init_images(p); pdf_init_xobjects(p); pdf_init_fonts(p); pdf_init_outlines(p); pdf_init_annot_params(p); pdf_init_colorspaces(p); pdf_init_pattern(p); pdf_init_shadings(p); pdf_init_extgstates(p); /* Write the constant /ProcSet array once at the beginning */ p->procset_id = pdc_begin_obj(p->out, PDC_NEW_ID); pdc_puts(p->out, "[/PDF/ImageB/ImageC/ImageI/Text]\n"); pdc_end_obj(p->out); pdf_init_pages2(p); pdf_write_attachments(p); return 1; } #if defined(_MSC_VER) && defined(_MANAGED) #pragma unmanaged #endif int pdf__begin_document(PDF *p, const char *filename, int len, const char *optlist) { pdf_document *doc; pdc_bool verbose = p->debug[(int) 'o']; int retval; verbose = pdf_get_errorpolicy(p, NULL, verbose); doc = pdf_init_get_document(p); /* file ID or filename */ if (len == -1) { FILE *fp = (FILE *) filename; /* * It is the callers responsibility to open the file in binary mode, * but it doesn't hurt to make sure it really is. * The Intel version of the Metrowerks compiler doesn't have setmode(). */ #if !defined(__MWERKS__) && (defined(WIN32) || defined(OS2)) #if !defined(__BORLANDC__) && !defined(OS2) _setmode(_fileno(fp), _O_BINARY); #else setmode(fileno(fp), O_BINARY); #endif #endif doc->fp = fp; doc->len = sizeof(FILE); } else if (filename && (*filename || len > 0)) { filename = pdf_convert_filename(p, filename, len, "filename", PDC_CONV_WITHBOM); doc->filename = pdc_strdup(p->pdc, filename); doc->len = (int) strlen(doc->filename); } retval = pdf_begin_document_internal(p, optlist, pdc_false); if (retval > -1) PDF_SET_STATE(p, pdf_state_document); if (!p->pdc->smokerun) pdc_logg_cond(p->pdc, 1, trc_api, "[Begin document]\n"); return retval; } #if defined(_MSC_VER) && defined(_MANAGED) #pragma managed #endif void pdf__begin_document_callback(PDF *p, size_t (*i_writeproc)(PDF *p, void *data, size_t size), const char *optlist) { size_t (*writeproc)(PDF *, void *, size_t) = i_writeproc; pdf_document *doc; if (writeproc == NULL) pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "writeproc", 0, 0, 0); doc = pdf_init_get_document(p); /* initializing and opening the document */ doc->writeproc = writeproc; doc->len = sizeof(writeproc); (void) pdf_begin_document_internal(p, optlist, pdc_true); PDF_SET_STATE(p, pdf_state_document); if (!p->pdc->smokerun) pdc_logg_cond(p->pdc, 1, trc_api, "[Begin document]\n"); } /* ----------------------------- name tree ----------------------------- */ struct pdf_name_s { pdc_id obj_id; /* id of this name object */ char * name; /* name string */ pdf_nametree_type type; /* name tree type */ }; static void pdf_cleanup_names(PDF *p) { int i; if (p->names == NULL) return; for (i = 0; i < p->names_number; i++) { pdc_free(p->pdc, p->names[i].name); } pdc_free(p->pdc, p->names); p->names_number = 0; p->names = NULL; } void pdf_insert_name(PDF *p, const char *name, pdf_nametree_type type, pdc_id obj_id) { static const char fn[] = "pdf_insert_name"; int i; if (p->names == NULL || p->names_number == p->names_capacity) { if (p->names == NULL) { p->names_number = 0; p->names_capacity = NAMES_CHUNKSIZE; p->names = (pdf_name *) pdc_malloc(p->pdc, sizeof(pdf_name) * p->names_capacity, fn); } else { p->names_capacity *= 2; p->names = (pdf_name *) pdc_realloc(p->pdc, p->names, sizeof(pdf_name) * p->names_capacity, fn); } for (i = p->names_number; i < p->names_capacity; i++) { p->names[i].obj_id = PDC_BAD_ID; p->names[i].name = NULL; p->names[i].type = names_undef; } } /* check identity */ for (i = 0; i < p->names_number; i++) { if (p->names[i].type == type && !strcmp(p->names[i].name, name)) { pdc_free(p->pdc, p->names[i].name); p->names[i].name = (char *) name; return; } } p->names[i].obj_id = obj_id; p->names[i].name = (char *) name; p->names[i].type = type; p->names_number++; } pdc_id pdf_get_id_from_nametree(PDF *p, pdf_nametree_type type, const char *name) { int i; for (i = 0; i < p->names_number; i++) { if (p->names[i].type == type && !strcmp(name, p->names[i].name)) return p->names[i].obj_id; } return PDC_BAD_ID; } #define PDF_TREE_LEAF_SIZE 32 static char * pdf_get_numbered_name(PDF *p, pdf_nametree_type type, int ia, int *in, int nn) { int i, j = ia, n = 0; for (i = ia; i < p->names_number; i++) { if (p->names[i].type == type) { n++; if (n == nn) { if (in != NULL) *in = i; return p->names[i].name; } j = i; } } return (in != NULL) ? NULL : p->names[j].name; } static pdc_id pdf_write_names(PDF *p, pdf_nametree_type type) { static const char fn[] = "pdf_write_names"; pdc_id ret = PDC_BAD_ID; int i, nnames = 0; for (i = 0; i < p->names_number; i++) { if (p->names[i].type == type) { nnames++; } } if (nnames) { char *name; int nleafs, nnodes, ik, il, ia, nn; pdc_id *idlist; nnodes = nnames / PDF_TREE_LEAF_SIZE; if (!nnodes) nleafs = nnames; else nleafs = PDF_TREE_LEAF_SIZE; if (nnames > nnodes * nleafs) nnodes++; idlist = (pdc_id *) pdc_malloc(p->pdc, (size_t) (nnodes * sizeof(pdc_id)), fn); for (i = 0; i < nnodes; i++) idlist[i] = pdc_alloc_id(p->out); ret = pdc_begin_obj(p->out, PDC_NEW_ID); /* Names object */ pdc_begin_dict(p->out); pdc_puts(p->out, "/Kids"); pdc_begin_array(p->out); for (i = 0; i < nnodes; i++) pdc_objref_c(p->out, idlist[i]); pdc_end_array(p->out); pdc_end_dict(p->out); pdc_end_obj(p->out); /* Names object */ ia = 0; for (ik = 0; ik < nnodes; ik++) { pdc_begin_obj(p->out, idlist[ik]); pdc_begin_dict(p->out); pdc_puts(p->out, "/Limits"); pdc_begin_array(p->out); name = pdf_get_numbered_name(p, type, ia, NULL, 1); pdc_put_pdfstring(p->out, name, pdc_strlen(name)); nn = (ik == nnodes - 1) ? p->names_number : nleafs; name = pdf_get_numbered_name(p, type, ia, NULL, nn); pdc_put_pdfstring(p->out, name, pdc_strlen(name)); pdc_end_array(p->out); pdc_puts(p->out, "/Names"); pdc_begin_array(p->out); for (il = 0; il < nn; il++) { name = pdf_get_numbered_name(p, type, ia, &ia, 1); if (name == NULL) break; pdc_put_pdfstring(p->out, name, pdc_strlen(name)); pdc_objref(p->out, "", p->names[ia].obj_id); ia++; } pdc_end_array(p->out); pdc_end_dict(p->out); pdc_end_obj(p->out); } pdc_free(p->pdc, idlist); } return ret; } static int name_compare( const void* a, const void* b) { pdf_name *p1 = (pdf_name *) a; pdf_name *p2 = (pdf_name *) b; return pdc_wstrcmp(p1->name, p2->name); } /* ---------------------------- write document -------------------------- */ static pdc_id pdf_write_pages_and_catalog(PDF *p, pdc_id orig_root_id) { pdf_document *doc = p->document; pdc_bool openact = pdc_false; pdc_bool forpdfa = pdc_false; pdc_id act_idlist[PDF_MAX_EVENTS]; pdc_id root_id = PDC_BAD_ID; pdc_id names_dests_id = PDC_BAD_ID; pdc_id names_javascript_id = PDC_BAD_ID; pdc_id names_ap_id = PDC_BAD_ID; pdc_id names_embeddedfiles_id = PDC_BAD_ID; pdc_id outintents1_id = PDC_BAD_ID; pdc_id outintents2_id = PDC_BAD_ID; pdc_id pages_id = pdf_write_pages_tree(p); pdc_id labels_id = pdf_write_pagelabels(p); (void) orig_root_id; /* name tree dictionaries */ if (p->names_number) { char *name; int i, outlen, inlen; for (i = 0; i < p->names_number; i++) { inlen = strlen(p->names[i].name); name = pdf_convert_pdfstring(p, p->names[i].name, inlen, PDC_CONV_WITHBOM | PDC_CONV_TRYBYTES, &outlen); if (name != p->names[i].name) pdc_free(p->pdc, p->names[i].name); p->names[i].name = name; } qsort(p->names, (size_t) p->names_number, sizeof(pdf_name), name_compare); names_dests_id = pdf_write_names(p, names_dests); names_javascript_id = pdf_write_names(p, names_javascript); names_ap_id = pdf_write_names(p, names_ap); names_embeddedfiles_id = pdf_write_names(p, names_embeddedfiles); } (void) forpdfa; /* write action objects */ if (doc->action) pdf_parse_and_write_actionlist(p, event_document, act_idlist, (const char *) doc->action); root_id = pdc_begin_obj(p->out, PDC_NEW_ID); /* Catalog */ pdc_begin_dict(p->out); pdc_puts(p->out, "/Type/Catalog\n"); pdc_objref(p->out, "/Pages", pages_id); /* Pages object */ if (labels_id != PDC_BAD_ID) { pdc_objref(p->out, "/PageLabels", labels_id); } if (p->names_number) { pdc_printf(p->out, "/Names"); pdc_begin_dict(p->out); /* Names */ if (names_dests_id != PDC_BAD_ID) pdc_objref(p->out, "/Dests", names_dests_id); if (names_javascript_id != PDC_BAD_ID) pdc_objref(p->out, "/JavaScript", names_javascript_id); if (names_ap_id != PDC_BAD_ID) pdc_objref(p->out, "/AP", names_ap_id); if (names_embeddedfiles_id != PDC_BAD_ID) pdc_objref(p->out, "/EmbeddedFiles", names_embeddedfiles_id); pdc_end_dict(p->out); /* Names */ } if (doc->writevpdict) { pdc_printf(p->out, "/ViewerPreferences\n"); pdc_begin_dict(p->out); /* ViewerPreferences */ pdf_parse_and_write_viewerpreferences(p, doc->viewerpreferences, pdc_true); pdc_end_dict(p->out); /* ViewerPreferences */ } if (doc->pagelayout != layout_default) pdc_printf(p->out, "/PageLayout/%s\n", pdc_get_keyword(doc->pagelayout, pdf_pagelayout_pdfkeylist)); if (doc->openmode != open_auto && doc->openmode != open_none) pdc_printf(p->out, "/PageMode/%s\n", pdc_get_keyword(doc->openmode, pdf_openmode_pdfkeylist)); pdf_write_outline_root(p); /* /Outlines */ if (doc->action) /* /AA */ openact = pdf_write_action_entries(p, event_document, act_idlist); if (doc->dest && !openact) { pdc_puts(p->out, "/OpenAction"); pdf_write_destination(p, doc->dest); } if (doc->uri) { pdc_puts(p->out, "/URI"); pdc_begin_dict(p->out); pdc_printf(p->out, "/Base"); pdf_put_hypertext(p, doc->uri); pdc_end_dict(p->out); } if (doc->lang[0]) { pdc_puts(p->out, "/Lang"); pdf_put_hypertext(p, doc->lang); pdc_puts(p->out, "\n"); } /* /StructTreeRoot /MarkInfo */ /* /OCProperties */ if (outintents1_id != PDC_BAD_ID || outintents2_id != PDC_BAD_ID) { pdc_puts(p->out, "/OutputIntents"); pdc_begin_array(p->out); if (outintents1_id != PDC_BAD_ID) pdc_objref(p->out, "", outintents1_id); if (outintents2_id != PDC_BAD_ID) pdc_objref(p->out, "", outintents2_id); pdc_end_array(p->out); } /* /Search */ pdf_write_search_indexes(p); /* /Metadata */ /* not supported: /Threads /PieceInfo /Perms /Legal */ pdc_end_dict(p->out); /* Catalog */ pdc_end_obj(p->out); return root_id; } static void pdf_write_document(PDF *p) { if (PDF_GET_STATE(p) != pdf_state_error) { pdf_document *doc = p->document; pdc_id info_id = PDC_BAD_ID; pdc_id root_id = PDC_BAD_ID; if (pdf_last_page(p) == 0) pdc_error(p->pdc, PDF_E_DOC_EMPTY, 0, 0, 0, 0); pdf_write_attachments(p); /* Write all pending document information up to xref table + trailer */ info_id = pdf_write_info(p, doc->moddate); pdf_write_doc_fonts(p); /* font objects */ pdf_write_doc_colorspaces(p); /* color space resources */ pdf_write_doc_extgstates(p); /* ExtGState resources */ root_id = pdf_write_pages_and_catalog(p, root_id); pdf_write_outlines(p); pdc_write_xref(p->out); pdc_write_trailer(p->out, info_id, root_id, 0, -1, -1, -1); } pdc_close_output(p->out); } /* ------------------------------ end document ---------------------------- */ void pdf_cleanup_document(PDF *p) { pdf_cleanup_pages(p); if (PDF_GET_STATE(p) != pdf_state_object) { /* Don't call pdc_cleanup_output() here because we may still need * the buffer contents for pdf__get_buffer() after pdf__end_document(). */ pdf_delete_actions(p); pdf_cleanup_destination(p, p->bookmark_dest); /* deprecated */ p->bookmark_dest = NULL; pdf_cleanup_document_internal(p); pdf_cleanup_info(p); pdf_cleanup_fonts(p); pdf_cleanup_outlines(p); pdf_cleanup_annot_params(p); pdf_cleanup_names(p); pdf_cleanup_colorspaces(p); pdf_cleanup_pattern(p); pdf_cleanup_shadings(p); pdf_cleanup_images(p); pdf_cleanup_xobjects(p); pdf_cleanup_extgstates(p); pdf_cleanup_stringlists(p); PDF_SET_STATE(p, pdf_state_object); } } static const pdc_defopt pdf_end_document_options[] = { PDF_DOCUMENT_OPTIONS2 PDC_OPT_TERMINATE }; void pdf__end_document(PDF *p, const char *optlist) { pdf_document *doc; /* check if there are any suspended pages left. */ pdf_check_suspended_pages(p); /* get document pointer */ doc = pdf_init_get_document(p); if (optlist && *optlist) { pdc_resopt *resopts = NULL; pdc_clientdata cdata; /* parsing option list */ pdf_set_clientdata(p, &cdata); resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_end_document_options, &cdata, pdc_true); /* get options */ pdf_get_document_common_options(p, resopts, PDF_FC_END_DOCUMENT); } pdf_write_document(p); pdf_cleanup_document(p); if (!p->pdc->smokerun) pdc_logg_cond(p->pdc, 1, trc_api, "[End document]\n\n"); } const char * pdf__get_buffer(PDF *p, long *size) { const char *ret; pdc_off_t llsize; ret = pdc_get_stream_contents(p->out, &llsize); if (llsize > LONG_MAX) pdc_error(p->pdc, PDF_E_DOC_GETBUF_2GB, 0, 0, 0, 0); *size = (long) llsize; return ret; } /*****************************************************************************/ /** deprecated historical document functions **/ /*****************************************************************************/ void pdf_set_flush(PDF *p, const char *flush) { if (p->pdc->binding != NULL && strcmp(p->pdc->binding, "C++")) return; if (flush != NULL && *flush) { int i = pdc_get_keycode_ci(flush, pdf_flush_keylist); if (i != PDC_KEY_NOTFOUND) { pdf_document *doc = pdf_init_get_document(p); doc->flush = (pdc_flush_state) i; p->flush = doc->flush; return; } pdc_error(p->pdc, PDC_E_PAR_ILLPARAM, flush, "flush", 0, 0); } } void pdf_set_uri(PDF *p, const char *uri) { pdf_document *doc = pdf_init_get_document(p); if (doc->uri) pdc_free(p->pdc, doc->uri); doc->uri = pdc_strdup(p->pdc, uri); } void pdf_set_compatibility(PDF *p, const char *compatibility) { if (compatibility != NULL && *compatibility) { int i = pdc_get_keycode_ci(compatibility, pdf_compatibility_keylist); if (i != PDC_KEY_NOTFOUND) { pdf_document *doc = pdf_init_get_document(p); p->compatibility = i; doc->compatibility = i; p->pdc->compatibility = i; return; } pdc_error(p->pdc, PDC_E_PAR_ILLPARAM, compatibility, "compatibility", 0, 0); } } void pdf_set_openaction(PDF *p, const char *openaction) { pdf_document *doc = pdf_init_get_document(p); if (openaction != NULL && *openaction) { pdf_cleanup_destination(p, doc->dest); doc->dest = pdf_parse_destination_optlist(p, openaction, 1, pdf_openaction); } } void pdf_set_openmode(PDF *p, const char *openmode) { int i; if (openmode == NULL || !*openmode) openmode = "none"; i = pdc_get_keycode_ci(openmode, pdf_openmode_keylist); if (i != PDC_KEY_NOTFOUND) { pdf_document *doc = pdf_init_get_document(p); doc->openmode = (pdf_openmode) i; } else pdc_error(p->pdc, PDC_E_PAR_ILLPARAM, openmode, "openmode", 0, 0); } void pdf_set_viewerpreference(PDF *p, const char *viewerpreference) { static const char fn[] = "pdf_set_viewerpreference"; pdf_document *doc = pdf_init_get_document(p); char *optlist; size_t nb1 = 0, nb2 = 0; if (doc->viewerpreferences) nb1 = strlen(doc->viewerpreferences) * sizeof(char *); nb2 = strlen(viewerpreference) * sizeof(char *); optlist = (char *) pdc_malloc(p->pdc, nb1 + nb2 + 2, fn); optlist[0] = 0; if (doc->viewerpreferences) { strcat(optlist, doc->viewerpreferences); strcat(optlist, " "); } strcat(optlist, viewerpreference); if (doc->viewerpreferences) pdc_free(p->pdc, doc->viewerpreferences); doc->viewerpreferences = optlist; doc->writevpdict |= pdf_parse_and_write_viewerpreferences(p, optlist, pdc_false); }