diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/image/pdftoepdf.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/pdftoepdf.c | 1049 |
1 files changed, 1049 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.c b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.c new file mode 100644 index 00000000000..b29493b073d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.c @@ -0,0 +1,1049 @@ +/* +pdftoepdf.w + +Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> +Copyright 2006-2015 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +*/ + +#define __STDC_FORMAT_MACROS /* for PRId64 etc. */ + +#include "image/epdf.h" + +/* Conflict with pdfgen.h */ + +#ifndef pdf_out + +#define pdf_out(pdf, A) do { pdf_room(pdf, 1); *(pdf->buf->p++) = A; } while (0) + +#define pdf_check_space(pdf) do { \ + if (pdf->cave > 0) { \ + pdf_out(pdf, ' '); \ + pdf->cave = 0; \ + } \ +} while (0) + +#define pdf_set_space(pdf) \ + pdf->cave = 1; + +#define pdf_reset_space(pdf) \ + pdf->cave = 0; + +#endif + +/* Maintain AVL tree of all PDF files for embedding */ + +static avl_table *PdfDocumentTree = NULL; + +/* AVL sort PdfDocument into PdfDocumentTree by file_path */ + +static int CompPdfDocument(const void *pa, const void *pb, void *p ) +{ + return strcmp(((const PdfDocument *) pa)->file_path, ((const PdfDocument *) pb)->file_path); +} + +/* Returns pointer to PdfDocument structure for PDF file. */ + +static PdfDocument *findPdfDocument(char *file_path) +{ + PdfDocument *pdf_doc, tmp; + if (file_path == NULL) { + normal_error("pdf backend","empty filename when loading pdf file"); + } else if (PdfDocumentTree == NULL) { + return NULL; + } + tmp.file_path = file_path; + pdf_doc = (PdfDocument *) avl_find(PdfDocumentTree, &tmp); + return pdf_doc; +} + +#define PDF_CHECKSUM_SIZE 32 + +static char *get_file_checksum(const char *a, file_error_mode fe) +{ + struct stat finfo; + char *ck = NULL; + if (stat(a, &finfo) == 0) { + off_t size = finfo.st_size; + time_t mtime = finfo.st_mtime; + ck = (char *) malloc(PDF_CHECKSUM_SIZE); + if (ck == NULL) + formatted_error("pdf inclusion","out of memory while processing '%s'", a); + snprintf(ck, PDF_CHECKSUM_SIZE, "%" PRIu64 "_%" PRIu64, (uint64_t) size,(uint64_t) mtime); + } else { + switch (fe) { + case FE_FAIL: + formatted_error("pdf inclusion","could not stat() file '%s'", a); + break; + case FE_RETURN_NULL: + if (ck != NULL) + free(ck); + ck = NULL; + break; + default: + assert(0); + } + } + return ck; +} + +static char *get_stream_checksum (const char *str, unsigned long long str_size){ + /* http://www.cse.yorku.ca/~oz/hash.html */ + /* djb2 */ + unsigned long hash ; + char *ck = NULL; + unsigned int i; + hash = 5381; + ck = (char *) malloc(STRSTREAM_CHECKSUM_SIZE+1); + if (ck == NULL) + normal_error("pdf inclusion","out of memory while processing a memstream"); + for(i=0; i<(unsigned int)(str_size); i++) { + hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + str[i] */ + } + snprintf(ck,STRSTREAM_CHECKSUM_SIZE+1,"%lx",hash); + ck[STRSTREAM_CHECKSUM_SIZE]='\0'; + return ck; +} + +/* + Returns pointer to PdfDocument structure for PDF file. + Creates a new PdfDocument structure if it doesn't exist yet. + When fe = FE_RETURN_NULL, the function returns NULL in error case. +*/ + +PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe, const char *userpassword, const char *ownerpassword) +{ + char *checksum, *path_copy; + PdfDocument *pdf_doc; + ppdoc *pdfe = NULL; + int new_flag = 0; + if ((checksum = get_file_checksum(file_path, fe)) == NULL) { + return (PdfDocument *) NULL; + } + path_copy = xstrdup(file_path); + if ((pdf_doc = findPdfDocument(path_copy)) == NULL) { + new_flag = 1; + pdf_doc = (PdfDocument*) xmalloc(sizeof( PdfDocument)); + pdf_doc->file_path = path_copy; + pdf_doc->checksum = checksum; + pdf_doc->pdfe = NULL; + pdf_doc->inObjList = NULL; + pdf_doc->ObjMapTree = NULL; + pdf_doc->occurences = 0; /* 0 = unreferenced */ + pdf_doc->pc = 0; + pdf_doc->is_mem = 0; + } else { + if (strncmp(pdf_doc->checksum, checksum, PDF_CHECKSUM_SIZE) != 0) { + formatted_error("pdf inclusion","file has changed '%s'", file_path); + } + free(checksum); + free(path_copy); + } + if (pdf_doc->pdfe == NULL) { + pdfe = ppdoc_load(file_path); + pdf_doc->pc++; + /* todo: check if we might print the document */ + if (pdfe == NULL) { + switch (fe) { + case FE_FAIL: + normal_error("pdf inclusion","reading image failed"); + break; + case FE_RETURN_NULL: + if (pdf_doc->pdfe != NULL) { + ppdoc_free(pdfe); + pdf_doc->pdfe = NULL; + } + /* delete docName */ + if (new_flag == 1) { + if (pdf_doc->file_path != NULL) + free(pdf_doc->file_path); + if (pdf_doc->checksum != NULL) + free(pdf_doc->checksum); + free(pdf_doc); + } + return (PdfDocument *) NULL; + break; + default: + assert(0); + } + } + if (pdfe != NULL) { + if (ppdoc_crypt_status(pdfe) < 0) { + ppdoc_crypt_pass(pdfe,userpassword,strlen(userpassword),NULL,0); + } + if (ppdoc_crypt_status(pdfe) < 0) { + ppdoc_crypt_pass(pdfe,NULL,0,ownerpassword,strlen(ownerpassword)); + } + if (ppdoc_crypt_status(pdfe) < 0) { + formatted_error("pdf inclusion","the pdf file '%s' is encrypted, provide proper passwords",file_path); + } + } + pdf_doc->pdfe = pdfe; + } + /* PDF file could be opened without problems, checksum ok. */ + if (PdfDocumentTree == NULL) + PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator); + if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) { + avl_probe(PdfDocumentTree, pdf_doc); + } + pdf_doc->occurences++; + return pdf_doc; +} + +/* + Returns pointer to PdfDocument structure for a PDF stream in memory of streamsize + dimension. As before, creates a new PdfDocument structure if it doesn't exist yet + with file_path = file_id +*/ + +PdfDocument *refMemStreamPdfDocument(char *docstream, unsigned long long streamsize,const char *file_id) +{ + char *checksum; + char *file_path; + PdfDocument *pdf_doc; + ppdoc *pdfe = NULL; + size_t cnt = 0; + checksum = get_stream_checksum(docstream, streamsize); + cnt = strlen(file_id); + file_path = (char *) malloc(cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE+1); /* 1 for \0 */ + strcpy(file_path,STREAM_URI); + strcat(file_path,file_id); + strcat(file_path,checksum); + file_path[cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE]='\0'; + if ((pdf_doc = findPdfDocument(file_path)) == NULL) { + /*new_flag = 1;*/ + pdf_doc = (PdfDocument*) xmalloc(sizeof( PdfDocument)); + pdf_doc->file_path = file_path; + pdf_doc->checksum = checksum; + pdf_doc->pdfe = NULL; + pdf_doc->inObjList = NULL; + pdf_doc->ObjMapTree = NULL; + pdf_doc->occurences = 0; /* 0 = unreferenced */ + pdf_doc->pc = 0; + pdf_doc->is_mem = 1; + pdf_doc->memstream = docstream; + } else { + /* As is now, checksum is in file_path, so this check should be useless. */ + if (strncmp(pdf_doc->checksum, checksum, STRSTREAM_CHECKSUM_SIZE) != 0) { + formatted_error("pdf inclusion","stream has changed '%s'", file_path); + } + free(file_path); + free(checksum); + } + if (pdf_doc->pdfe == NULL) { + pdfe = ppdoc_mem(docstream, streamsize); + pdf_doc->pc++; + if (pdfe == NULL) { + normal_error("pdf inclusion","reading pdf Stream failed"); + } + pdf_doc->pdfe = pdfe; + } + /* PDF file could be opened without problems, checksum ok. */ + if (PdfDocumentTree == NULL) + PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator); + if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) { + avl_probe(PdfDocumentTree, pdf_doc); + } + pdf_doc->occurences++; + return pdf_doc; +} + +/* + AVL sort ObjMap into ObjMapTree by object number and generation keep the ObjMap + struct small, as these are accumulated until the end +*/ + +typedef struct ObjMap ObjMap ; + +struct ObjMap { + ppref * in; + int out_num; +}; + +static int CompObjMap(const void *pa, const void *pb, void *p) +{ + const ppref *a = (((const ObjMap *) pa)->in); + const ppref *b = (((const ObjMap *) pb)->in); + if (a->number > b->number) + return 1; + else if (a->number < b->number) + return -1; + else if (a->version == b->version) + return 0; + else if (a->version < b->version) + return -1; + return 1; +} + +static ObjMap *findObjMap(PdfDocument * pdf_doc, ppref * in) +{ + ObjMap *obj_map, tmp; + if (pdf_doc->ObjMapTree == NULL) + return NULL; + tmp.in = in; + obj_map = (ObjMap *) avl_find(pdf_doc->ObjMapTree, &tmp); + return obj_map; +} + +static void addObjMap(PdfDocument * pdf_doc, ppref * in, int out_num) +{ + ObjMap *obj_map = NULL; + if (pdf_doc->ObjMapTree == NULL) + pdf_doc->ObjMapTree = avl_create(CompObjMap, NULL, &avl_xallocator); + obj_map = (ObjMap*)xmalloc(sizeof(ObjMap)); + obj_map->in = in; + obj_map->out_num = out_num; + avl_probe(pdf_doc->ObjMapTree, obj_map); +} + +/* + When copying the Resources of the selected page, all objects are + copied recursively top-down. The findObjMap() function checks if an + object has already been copied; if so, instead of copying just the + new object number will be referenced. The ObjMapTree guarantees, + that during the entire LuaTeX run any object from any embedded PDF + file will end up max. once in the output PDF file. Indirect objects + are not fetched during copying, but get a new object number from + LuaTeX and then will be appended into a linked list. +*/ + +static int addInObj(PDF pdf, PdfDocument * pdf_doc, ppref * ref) +{ + ObjMap *obj_map; + InObj *p, *q, *n; + if (ref->number == 0) { + normal_error("pdf inclusion","reference to invalid object (broken pdf)"); + } + if ((obj_map = findObjMap(pdf_doc, ref)) != NULL) { + return obj_map->out_num; + } + n = (InObj*)xmalloc(sizeof(InObj)); + n->ref = ref; + n->next = NULL; + n->num = pdf_create_obj(pdf, obj_type_others, 0); + addObjMap(pdf_doc, ref, n->num); + if (pdf_doc->inObjList == NULL) { + pdf_doc->inObjList = n; + } else { + /* + It is important to add new objects at the end of the list, + because new objects are being added while the list is being + written out by writeRefs(). + */ + for (p = pdf_doc->inObjList; p != NULL; p = p->next) + q = p; + q->next = n; + } + return n->num; +} + +static void copyObject(PDF, PdfDocument *, ppobj *); + +static void copyString(PDF pdf, ppstring str) +{ + pdf_check_space(pdf); + switch (ppstring_type((void *)(str))) { + case PPSTRING_PLAIN: + pdf_out(pdf, '('); + pdf_out_block(pdf, (const char *) str, ppstring_size((void *)(str))); + pdf_out(pdf, ')'); + break; + case PPSTRING_BASE16: + pdf_out(pdf, '<'); + pdf_out_block(pdf, (const char *) str, ppstring_size((void *)(str))); + pdf_out(pdf, '>'); + break; + case PPSTRING_BASE85: + pdf_out(pdf, '<'); + pdf_out(pdf, '~'); + pdf_out_block(pdf, (const char *) str, ppstring_size((void *)(str))); + pdf_out(pdf, '~'); + pdf_out(pdf, '>'); + break; + } + pdf_set_space(pdf); +} + +/* +static void copyName(PDF pdf, ppname *name) +{ + pdf_add_name(pdf, (const char *) name); +} +*/ + +static void copyArray(PDF pdf, PdfDocument * pdf_doc, pparray * array) +{ + int i; + int n = array->size; + pdf_begin_array(pdf); + for (i=0; i<n; ++i) { + copyObject(pdf, pdf_doc, pparray_at(array,i)); + } + pdf_end_array(pdf); +} + +static void copyDict(PDF pdf, PdfDocument * pdf_doc, ppdict *dict) +{ + int i; + int n = dict->size; + pdf_begin_dict(pdf); + for (i=0; i<n; ++i) { + pdf_add_name(pdf, (const char *) ppdict_key(dict,i)); + copyObject(pdf, pdf_doc, ppdict_at(dict,i)); + } + pdf_end_dict(pdf); +} + +static void copyStreamStream(PDF pdf, ppstream * stream, int decode) +{ + uint8_t *data = NULL; + size_t size = 0; + if (0) { + for (data = ppstream_first(stream, &size, decode); data != NULL; data = ppstream_next(stream, &size)) { + pdf_out_block(pdf, (const char *) data, size); + } + } else { + data = ppstream_all(stream,&size,decode); + if (data != NULL) { + pdf_out_block(pdf, (const char *) data, size); + } + } + ppstream_done(stream); +} + +static void copyStream(PDF pdf, PdfDocument * pdf_doc, ppstream * stream) +{ + ppdict *dict = stream->dict; /* bug in: stream_dict(stream) */ + if (pdf->compress_level == 0 || pdf->recompress) { + const char *ignoredkeys[] = { + "Filter", "Decode", "Length", "DL", NULL + }; + int i; + int n = dict->size; + pdf_begin_dict(pdf); + for (i=0; i<n; ++i) { + const char *key = ppdict_key(dict,i); + int okay = 1; + int k; + for (k = 0; ignoredkeys[k] != NULL; k++) { + if (strcmp(key,ignoredkeys[k]) == 0) { + okay = 0; + break; + } + } + if (okay) { + pdf_add_name(pdf, key); + copyObject(pdf, pdf_doc, ppdict_at(dict,i)); + } + } + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream, 1); + pdf_end_stream(pdf); + } else { + copyDict(pdf, pdf_doc, dict); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream, 0); + pdf_end_stream(pdf); + } +} + +static void copyObject(PDF pdf, PdfDocument * pdf_doc, ppobj * obj) +{ + switch (obj->type) { + case PPNULL: + pdf_add_null(pdf); + break; + case PPBOOL: + pdf_add_bool(pdf,obj->integer); /* ppobj_get_bool_value(obj) */ + break; + case PPINT: + pdf_add_int(pdf,obj->integer); /* ppobj_get_int_value(obj) */ + break; + case PPNUM: + pdf_add_real(pdf,obj->number); /* ppobj_get_num_value(obj) */ + break; + case PPNAME: + pdf_add_name(pdf, (const char *) obj->name); /* ppobj_get_name(obj) */ + break; + case PPSTRING: + copyString(pdf, obj->string); /* ppobj_get_string(obj) */ + break; + case PPARRAY: + copyArray(pdf, pdf_doc, obj->array); /* ppobj_get_array(obj) */ + break; + case PPDICT: + copyDict(pdf, pdf_doc, obj->dict); /* ppobj_get_dict(obj) */ + break; + case PPSTREAM: + copyStream(pdf, pdf_doc, obj->stream); /* ppobj_get_stream(obj) */ + break; + case PPREF: + pdf_add_ref(pdf, addInObj(pdf, pdf_doc, obj->ref)); /* ppobj_get_ref(obj) */ + break; + default: + break; + } +} + +static void writeRefs(PDF pdf, PdfDocument * pdf_doc) +{ + InObj *r, *n; + ppobj * obj; + for (r = pdf_doc->inObjList; r != NULL;) { + obj = ppref_obj(r->ref); + if (obj->type == PPSTREAM) + pdf_begin_obj(pdf, r->num, OBJSTM_NEVER); + else + pdf_begin_obj(pdf, r->num, 2); + copyObject(pdf, pdf_doc, obj); + pdf_end_obj(pdf); + n = r->next; + free(r); + r = n; + pdf_doc->inObjList = n; + } +} + +/* get the pagebox coordinates according to the pagebox_spec */ + +static void somebox(ppdict *page, const char * key, pprect * box) +{ + pprect * r = ppdict_get_box(page, key, box); + if (r != NULL) { + box->lx = r->lx; + box->ly = r->ly; + box->rx = r->rx; + box->ry = r->ry; + } +} + +static void get_pagebox(ppdict * page, int pagebox_spec, pprect * box) +{ + box->lx = box->rx = box->ly = box->ry = 0; + somebox(page,"MediaBox",box); + if (pagebox_spec == PDF_BOX_SPEC_MEDIA) { + return; + } + somebox(page,"CropBox",box); + if (pagebox_spec == PDF_BOX_SPEC_CROP) { + return; + } + switch (pagebox_spec) { + case PDF_BOX_SPEC_BLEED: + somebox(page,"BleedBox",box); + break; + case PDF_BOX_SPEC_TRIM: + somebox(page,"TrimBox",box); + break; + case PDF_BOX_SPEC_ART: + somebox(page,"ArtBox",box); + break; + default: + break; + } +} + +/* + Reads various information about the PDF and sets it up for later inclusion. + This will fail if the PDF version of the PDF is higher than minor_pdf_version_wanted + or page_name is given and can not be found. It makes no sense to give page_name and + page_num. Returns the page number. +*/ + +static ppdict * get_pdf_page_dict(ppdoc *pdfe, int n) +{ + ppref *r; + int i; + for (r=ppdoc_first_page(pdfe), i=1; r != NULL; r = ppdoc_next_page(pdfe), ++i) { + if (i == n) { + return ppref_obj(r)->dict; + } + } + return NULL; +} + +// static ppdict * get_pdf_page_dict(ppdoc *pdfe, int n) +// { +// return ppref_obj(ppdoc_page(pdfe,n))->dict; +// } + +void read_pdf_info(image_dict * idict) +{ + PdfDocument *pdf_doc = NULL; + ppdoc * pdfe = NULL; + ppdict *pageDict, *groupDict; + pprect pagebox; + ppint rotate = 0; + int pdf_major_version_found = 1; + int pdf_minor_version_found = 3; + float xsize, ysize, xorig, yorig; + if (img_type(idict) == IMG_TYPE_PDF) { + pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict)); + } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) { + pdf_doc = findPdfDocument(img_filepath(idict)) ; + if (pdf_doc == NULL ) + normal_error("pdf inclusion", "memstream not initialized"); + if (pdf_doc->pdfe == NULL) + normal_error("pdf inclusion", "memstream document is empty"); + pdf_doc->occurences++; + } else { + normal_error("pdf inclusion","unknown document"); + } + pdfe = pdf_doc->pdfe; + /* + Check PDF version. This works only for PDF 1.x but since any versions of + PDF newer than 1.x will not be backwards compatible to PDF 1.x, we will + then have to changed drastically anyway. + */ + pdf_major_version_found = ppdoc_version_number(pdfe,&pdf_minor_version_found); + if ((100 * pdf_major_version_found + pdf_major_version_found) > (100 * img_pdfmajorversion(idict) + img_pdfminorversion(idict))) { + const char *msg = "PDF inclusion: found PDF version '%d.%d', but at most version '%d.%d' allowed"; + if (img_errorlevel(idict) > 0) { + formatted_error("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict)); + } else { + formatted_warning("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict)); + } + } + img_totalpages(idict) = ppdoc_page_count(pdfe); + if (img_pagename(idict)) { + /* + get page by name is obsolete + */ + normal_error("pdf inclusion","named pages are not supported"); + } else { + /* + get page by number + */ + if (img_pagenum(idict) <= 0 + || img_pagenum(idict) > img_totalpages(idict)) + formatted_error("pdf inclusion","required page '%i' does not exist",(int) img_pagenum(idict)); + } + /* + get the required page + */ + pageDict = get_pdf_page_dict(pdfe,img_pagenum(idict)); + /* + get the pagebox coordinates (media, crop,...) to use + */ + get_pagebox(pageDict, img_pagebox(idict), &pagebox); + if (pagebox.rx > pagebox.lx) { + xorig = pagebox.lx; + xsize = pagebox.rx - pagebox.lx; + } else { + xorig = pagebox.rx; + xsize = pagebox.lx - pagebox.rx; + } + if (pagebox.ry > pagebox.ly) { + yorig = pagebox.ly; + ysize = pagebox.ry - pagebox.ly; + } else { + yorig = pagebox.ry; + ysize = pagebox.ly - pagebox.ry; + } + /* + The following 4 parameters are raw. Do _not_ modify by /Rotate! + */ + img_xsize(idict) = bp2sp(xsize); + img_ysize(idict) = bp2sp(ysize); + img_xorig(idict) = bp2sp(xorig); + img_yorig(idict) = bp2sp(yorig); + /* + Handle /Rotate parameter. Only multiples of 90 deg. are allowed (PDF Ref. v1.3, + p. 78). We also accept negative angles. Beware: PDF counts clockwise! + */ + if (ppdict_get_int(pageDict, "Rotate", &rotate)) { + switch ((((int)rotate % 360) + 360) % 360) { + case 0: + img_rotation(idict) = 0; + break; + case 90: + img_rotation(idict) = 3; + break; + case 180: + img_rotation(idict) = 2; + break; + case 270: + img_rotation(idict) = 1; + break; + default: + formatted_warning("pdf inclusion","/Rotate parameter in PDF file not multiple of 90 degrees"); + } + } + /* + currently unused info whether PDF contains a /Group + */ + groupDict = ppdict_get_dict(pageDict, "Group"); + if (groupDict != NULL) { + img_set_group(idict); + } + /* + LuaTeX pre 0.85 versions did this: + + if (readtype == IMG_CLOSEINBETWEEN) { + unrefPdfDocument(img_filepath(idict)); + } + + and also unref'd in the finalizer so we got an extra unrefs when garbage was + collected. However it is more efficient to keep the file open so we do that + now. The (slower) alternative is to unref here (which in most cases forcing a + close of the file) but then we must not call flush_pdf_info. + + A close (unref) can be forced by nilling the dict object at the lua end and + forcing a collectgarbage("collect") after that. + + */ + if (! img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } +} + +void flush_pdf_info(image_dict * idict) +{ + if (img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } +} + +/* + Write the current epf_doc. Here the included PDF is copied, so most errors + that can happen during PDF inclusion will arise here. +*/ + +void write_epdf(PDF pdf, image_dict * idict, int suppress_optional_info) +{ + PdfDocument *pdf_doc = NULL; + ppdoc *pdfe = NULL; + ppdict *pageDict, *infoDict; + ppobj *obj, *content, *resources; + pprect pagebox; + int i; + double bbox[4]; + const char *pagedictkeys[] = { + "Group", "LastModified", "Metadata", "PieceInfo", "SeparationInfo", NULL + }; + /* + open PDF file + */ + if (img_type(idict) == IMG_TYPE_PDF) { + pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict)); + } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) { + pdf_doc = findPdfDocument(img_filepath(idict)) ; + pdf_doc->occurences++; + } else { + normal_error("pdf inclusion","unknown document"); + } + pdfe = pdf_doc->pdfe; + pageDict = get_pdf_page_dict(pdfe,img_pagenum(idict)); + /* + write the Page header + */ + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Form"); + pdf_dict_add_int(pdf, "FormType", 1); + /* + write additional information + */ + pdf_dict_add_img_filename(pdf, idict); + if ((suppress_optional_info & 4) == 0) { + pdf_dict_add_int(pdf, "PTEX.PageNumber", (int) img_pagenum(idict)); + } + if ((suppress_optional_info & 8) == 0) { + infoDict = ppdoc_info(pdfe); + if (infoDict != NULL) { + /* todo : check this + pdf_dict_add_ref(pdf, "PTEX.InfoDict", addInObj(pdf, pdf_doc, infoDict)); + */ + pdf_add_name(pdf, "PTEX.InfoDict"); + copyDict(pdf, pdf_doc, infoDict); + } + } + if (img_is_bbox(idict)) { + bbox[0] = sp2bp(img_bbox(idict)[0]); + bbox[1] = sp2bp(img_bbox(idict)[1]); + bbox[2] = sp2bp(img_bbox(idict)[2]); + bbox[3] = sp2bp(img_bbox(idict)[3]); + } else { + /* + get the pagebox coordinates (media, crop,...) to use. + */ + get_pagebox(pageDict, img_pagebox(idict), &pagebox); + bbox[0] = pagebox.lx; + bbox[1] = pagebox.ly; + bbox[2] = pagebox.rx; + bbox[3] = pagebox.ry; + } + pdf_add_name(pdf, "BBox"); + pdf_begin_array(pdf); + pdf_add_real(pdf, bbox[0]); + pdf_add_real(pdf, bbox[1]); + pdf_add_real(pdf, bbox[2]); + pdf_add_real(pdf, bbox[3]); + pdf_end_array(pdf); + /* + Now all relevant parts of the Page dictionary are copied. Metadata validity + check is needed(as a stream it must be indirect). + */ + obj = ppdict_get_obj(pageDict, "Metadata"); + if (obj != NULL && obj->type != PPREF) { + formatted_warning("pdf inclusion","/Metadata must be indirect object"); + } + /* + copy selected items in Page dictionary + */ + for (i = 0; pagedictkeys[i] != NULL; i++) { + obj = ppdict_rget_obj(pageDict, pagedictkeys[i]); + if (obj != NULL) { + pdf_add_name(pdf, pagedictkeys[i]); + /* + preserves indirection + */ + copyObject(pdf, pdf_doc, obj); + } + } + resources = ppdict_rget_obj(pageDict, "Resources"); + if (resources == NULL) { + /* + If there are no Resources in the Page dict of the embedded page, + try to inherit the Resources from the Pages tree of the embedded + PDF file, climbing up the tree until the Resources are found. + (This fixes a problem with Scribus 1.3.3.14.) + */ + obj = ppdict_rget_obj(pageDict, "Parent"); + while (obj != NULL && obj->type == PPDICT) { + resources = ppdict_rget_obj(obj->dict, "Resources"); + if (resources != NULL) { + break; + } + obj = ppdict_get_obj(obj->dict, "Parent"); + } + } + if (resources != NULL) { + pdf_add_name(pdf, "Resources"); + copyObject(pdf, pdf_doc, resources); + } else { + formatted_warning("pdf inclusion","Page /Resources missing"); + } + /* + User supplied entries. + */ + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) { + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + } + /* + Write the Page contents. + */ + content = ppdict_rget_obj(pageDict, "Contents"); + if (content->type == PPSTREAM) { + if (pdf->compress_level == 0 || pdf->recompress) { + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, content->stream,1); /* decompress */ + } else { + /* copies compressed stream */ + ppstream * stream = content->stream; + ppdict *streamDict = stream->dict; /* */ + obj = ppdict_rget_obj(streamDict, "Length"); + if (obj != NULL) { + pdf_add_name(pdf, "Length"); + copyObject(pdf, pdf_doc, obj); + obj = ppdict_rget_obj(streamDict, "Filter"); + if (obj != NULL) { + pdf_add_name(pdf, "Filter"); + copyObject(pdf, pdf_doc, obj); + /* the next one is irrelevant, only for inline images: */ + /* + obj = ppdict_rget_obj(streamDict, "DecodeParms"); + if (obj != NULL) { + pdf_add_name(pdf, "DecodeParms"); + copyObject(pdf, pdf_doc, obj); + } + */ + } + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream,0); + } else { + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream,1); + } + } + pdf_end_stream(pdf); + } else if (content->type == PPARRAY) { + /* listens to compresslevel */ + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + { + int i; + int b = 0; + int n = content->array->size; + for (i=0; i<n; ++i) { + ppobj *o = pparray_at(content->array,i); + while (o != NULL && o->type == PPREF) { + o = ppref_obj((ppref *) o->ref); + } + if (o != NULL && o->type == PPSTREAM) { + if (b) { + /* + Put a space between streams to be on the safe side (streams + should have a trailing space here, but one never knows) + */ + pdf_out(pdf, ' '); + } else { + b = 1; + } + copyStreamStream(pdf, (ppstream *) o->stream,1); + } + } + } + pdf_end_stream(pdf); + } else { + /* + the contents are optional, but we need to include an empty stream + */ + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + pdf_end_stream(pdf); + } + pdf_end_obj(pdf); + /* + write out all indirect objects + */ + writeRefs(pdf, pdf_doc); + /* + unrefPdfDocument() must come after freeing whatever is used + + */ + if (! img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } +} + +/* a special simple case of inclusion, e.g. an appearance stream */ + +int write_epdf_object(PDF pdf, image_dict * idict, int n) +{ + int num = 0 ; + if (img_type(idict) != IMG_TYPE_PDF) { + normal_error("pdf inclusion","unknown document"); + } else { + PdfDocument * pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict)); + ppdoc * pdfe = pdf_doc->pdfe; + ppref * ref = ppxref_find(ppdoc_xref(pdfe), (ppuint) n); + if (ref != NULL) { + ppobj *obj; + num = pdf->obj_count++; + obj = ppref_obj(ref); + if (obj->type == PPSTREAM) { + pdf_begin_obj(pdf, num, OBJSTM_NEVER); + } else { + pdf_begin_obj(pdf, num, 2); + } + copyObject(pdf, pdf_doc, obj); + pdf_end_obj(pdf); + writeRefs(pdf, pdf_doc); + } + if (! img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } + } + return num; +} + +/* Deallocate a PdfDocument with all its resources. */ + +static void deletePdfDocumentPdfDoc(PdfDocument * pdf_doc) +{ + InObj *r, *n; + /* this may be probably needed for an emergency destroyPdfDocument() */ + for (r = pdf_doc->inObjList; r != NULL; r = n) { + n = r->next; + free(r); + } + if (pdf_doc->pdfe != NULL) { + ppdoc_free(pdf_doc->pdfe); + pdf_doc->pdfe = NULL; + } + if (pdf_doc->memstream != NULL) { + /* pplib does this: free(pdf_doc->memstream); */ + pdf_doc->memstream = NULL; + } + /* pdf_doc->pc++; */ + pdf_doc->pc = 0; +} + +static void destroyPdfDocument(void *pa, void * p) +{ + PdfDocument *pdf_doc = (PdfDocument *) pa; + deletePdfDocumentPdfDoc(pdf_doc); + /* TODO: delete rest of pdf_doc */ +} + +/* + Called when an image has been written and its resources in image_tab are + freed and it's not referenced anymore. +*/ + +void unrefPdfDocument(char *file_path) +{ + PdfDocument *pdf_doc = findPdfDocument(file_path); + if (pdf_doc == NULL) { + /* we're ok */ + } else if (pdf_doc->occurences > 0) { + pdf_doc->occurences--; + if (pdf_doc->occurences == 0) { + deletePdfDocumentPdfDoc(pdf_doc); + } + } else { + /* + We either have a mismatch in ref and unref or we're somehow out of sync + which can happen when we mess with the same file in lua and tex. + */ + formatted_warning("pdf inclusion","there can be a mismatch in opening and closing file '%s'",file_path); + } +} + +/* + For completeness, but it isn't currently used (unreferencing is done by mean + of file_path. +*/ + +void unrefMemStreamPdfDocument(char *file_id) +{ + (void) unrefPdfDocument(file_id); + +} + +/* + Called when PDF embedding system is finalized. We now deallocate all remaining + PdfDocuments. +*/ + +void epdf_free(void) +{ + if (PdfDocumentTree != NULL) + avl_destroy(PdfDocumentTree, destroyPdfDocument); + PdfDocumentTree = NULL; +} |