diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/image/pdftoepdf.w')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/pdftoepdf.w | 869 |
1 files changed, 869 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.w b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.w new file mode 100644 index 00000000000..2b07c303dd5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.w @@ -0,0 +1,869 @@ +% pdftoepdf.w +% +% Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org> +% Copyright 2006-2012 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/>. + +@ @c +static const char _svn_version[] = + "$Id: pdftoepdf.w 4479 2012-11-07 16:38:55Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/trunk/source/texk/web2c/luatexdir/image/pdftoepdf.w $"; + +// define DEBUG +#define __STDC_FORMAT_MACROS /* for PRId64 etc. */ + +#include "image/epdf.h" + +// This file is mostly C and not very much C++; it's just used to interface +// the functions of xpdf, which happens to be written in C++. + +static GBool isInit = gFalse; + +//********************************************************************** +// 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; + assert(file_path != NULL); + 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(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) + pdftex_fail("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: + pdftex_fail("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; +} + +// 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(char *file_path, file_error_mode fe) +{ + char *checksum; + PdfDocument *pdf_doc; + PDFDoc *doc = NULL; + GooString *docName = NULL; + int new_flag = 0; + if ((checksum = get_file_checksum(file_path, fe)) == NULL) { + assert(fe == FE_RETURN_NULL); + return (PdfDocument *) NULL; + } + assert(checksum != NULL); + if ((pdf_doc = findPdfDocument(file_path)) == NULL) { +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: New PdfDocument %s\n", file_path); +#endif + new_flag = 1; + pdf_doc = new PdfDocument; + pdf_doc->file_path = xstrdup(file_path); + pdf_doc->checksum = checksum; + pdf_doc->doc = NULL; + pdf_doc->inObjList = NULL; + pdf_doc->ObjMapTree = NULL; + pdf_doc->occurences = 0; // 0 = unreferenced + pdf_doc->pc = 0; + } else { +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: Found PdfDocument %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); +#endif + assert(pdf_doc->checksum != NULL); + if (strncmp(pdf_doc->checksum, checksum, PDF_CHECKSUM_SIZE) != 0) { + pdftex_fail("PDF inclusion: file has changed '%s'", file_path); + } + free(checksum); + } + assert(pdf_doc != NULL); + if (pdf_doc->doc == NULL) { +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: New PDFDoc %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); +#endif + docName = new GooString(file_path); + doc = new PDFDoc(docName); // takes ownership of docName + pdf_doc->pc++; + + if (!doc->isOk() || !doc->okToPrint()) { + switch (fe) { + case FE_FAIL: + pdftex_fail("xpdf: reading PDF image failed"); + break; + case FE_RETURN_NULL: + delete doc; + // 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); + delete pdf_doc; + } + return (PdfDocument *) NULL; + break; + default: + assert(0); + } + } + pdf_doc->doc = doc; + } + // 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) { + void **aa = avl_probe(PdfDocumentTree, pdf_doc); + assert(aa != NULL); + } + pdf_doc->occurences++; +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: Incrementing %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); +#endif + 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 + +struct ObjMap { + Ref in; // object num/gen in orig. PDF file + int out_num; // object num after embedding (gen == 0) +}; + +static int CompObjMap(const void *pa, const void *pb, void * /*p */ ) +{ + const Ref *a = &(((const ObjMap *) pa)->in); + const Ref *b = &(((const ObjMap *) pb)->in); + if (a->num > b->num) + return 1; + if (a->num < b->num) + return -1; + if (a->gen == b->gen) // most likely gen == 0 anyway + return 0; + if (a->gen < b->gen) + return -1; + return 1; +} + +static ObjMap *findObjMap(PdfDocument * pdf_doc, Ref in) +{ + ObjMap *obj_map, tmp; + assert(pdf_doc != NULL); + 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, Ref in, int out_num) +{ + ObjMap *obj_map = NULL; + assert(findObjMap(pdf_doc, in) == NULL); + if (pdf_doc->ObjMapTree == NULL) + pdf_doc->ObjMapTree = avl_create(CompObjMap, NULL, &avl_xallocator); + obj_map = new ObjMap; + obj_map->in = in; + obj_map->out_num = out_num; + void **aa = avl_probe(pdf_doc->ObjMapTree, obj_map); + assert(aa != NULL); +} + +// 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, Ref ref) +{ + ObjMap *obj_map; + InObj *p, *q, *n; + if (ref.num == 0) { + pdftex_fail("PDF inclusion: reference to invalid object" + " (is the included pdf broken?)"); + } + if ((obj_map = findObjMap(pdf_doc, ref)) != NULL) + return obj_map->out_num; + n = new 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; +} + +//********************************************************************** +// Function converts double to pdffloat; very small and very large numbers +// are NOT converted to scientific notation. +// n must be a number or real conforming to the implementation limits +// of PDF as specified in appendix C.1 of the PDF Ref. +// These are: +// maximum value of ints is +2^32 +// maximum value of reals is +2^15 +// smalles values of reals is 1/(2^16) + +static pdffloat conv_double_to_pdffloat(double n) +{ + pdffloat a; + a.e = 6; + a.m = lround(n * ten_pow[a.e]); + return a; +} + +static void copyObject(PDF, PdfDocument *, Object *); + +void copyReal(PDF pdf, double d) +{ + if (pdf->cave) + pdf_out(pdf, ' '); + print_pdffloat(pdf, conv_double_to_pdffloat(d)); + pdf->cave = true; +} + +static void copyString(PDF pdf, GooString * string) +{ + char *p; + unsigned char c; + size_t i, l; + p = string->getCString(); + l = (size_t) string->getLength(); + if (pdf->cave) + pdf_out(pdf, ' '); + if (strlen(p) == l) { + pdf_out(pdf, '('); + for (; *p != 0; p++) { + c = (unsigned char) *p; + if (c == '(' || c == ')' || c == '\\') + pdf_printf(pdf, "\\%c", c); + else if (c < 0x20 || c > 0x7F) + pdf_printf(pdf, "\\%03o", (int) c); + else + pdf_out(pdf, c); + } + pdf_out(pdf, ')'); + } else { + pdf_out(pdf, '<'); + for (i = 0; i < l; i++) { + c = (unsigned char) string->getChar(i); + pdf_printf(pdf, "%.2x", (int) c); + } + pdf_out(pdf, '>'); + } + pdf->cave = true; +} + +static void copyName(PDF pdf, char *s) +{ + pdf_out(pdf, '/'); + for (; *s != 0; s++) { + if (isdigit(*s) || isupper(*s) || islower(*s) || *s == '_' || + *s == '.' || *s == '-' || *s == '+') + pdf_out(pdf, *s); + else + pdf_printf(pdf, "#%.2X", *s & 0xFF); + } + pdf->cave = true; +} + +static void copyArray(PDF pdf, PdfDocument * pdf_doc, Array * array) +{ + int i, l; + Object obj1; + pdf_begin_array(pdf); + for (i = 0, l = array->getLength(); i < l; ++i) { + array->getNF(i, &obj1); + copyObject(pdf, pdf_doc, &obj1); + obj1.free(); + } + pdf_end_array(pdf); +} + +static void copyDict(PDF pdf, PdfDocument * pdf_doc, Dict * dict) +{ + int i, l; + Object obj1; + pdf_begin_dict(pdf); + for (i = 0, l = dict->getLength(); i < l; ++i) { + copyName(pdf, dict->getKey(i)); + dict->getValNF(i, &obj1); + copyObject(pdf, pdf_doc, &obj1); + obj1.free(); + } + pdf_end_dict(pdf); +} + +static void copyStreamStream(PDF pdf, Stream * str) +{ + int c, i, len = 1024; + str->reset(); + i = len; + while ((c = str->getChar()) != EOF) { + if (i == len) { + pdf_room(pdf, len); + i = 0; + } + pdf_quick_out(pdf, c); + i++; + } +} + +static void copyStream(PDF pdf, PdfDocument * pdf_doc, Stream * stream) +{ + copyDict(pdf, pdf_doc, stream->getDict()); + pdf_begin_stream(pdf); + assert(pdf->zip_write_state == NO_ZIP); + copyStreamStream(pdf, stream->getUndecodedStream()); + pdf_end_stream(pdf); +} + +static void copyObject(PDF pdf, PdfDocument * pdf_doc, Object * obj) +{ + switch (obj->getType()) { + case objBool: + pdf_add_bool(pdf, (int) obj->getBool()); + break; + case objInt: + pdf_add_int(pdf, obj->getInt()); + break; + case objReal: + copyReal(pdf, obj->getReal()); + break; + // not needed: + // case objNum: + // GBool isNum() { return type == objInt || type == objReal; } + case objString: + copyString(pdf, obj->getString()); + break; + case objName: + copyName(pdf, obj->getName()); + break; + case objNull: + pdf_add_null(pdf); + break; + case objArray: + copyArray(pdf, pdf_doc, obj->getArray()); + break; + case objDict: + copyDict(pdf, pdf_doc, obj->getDict()); + break; + case objStream: + copyStream(pdf, pdf_doc, obj->getStream()); + break; + case objRef: + pdf_add_ref(pdf, addInObj(pdf, pdf_doc, obj->getRef())); + break; + case objCmd: + case objError: + case objEOF: + case objNone: + pdftex_fail("PDF inclusion: type <%s> cannot be copied", + obj->getTypeName()); + break; + default: + assert(0); // xpdf doesn't have any other types + } +} + +//********************************************************************** + +static void writeRefs(PDF pdf, PdfDocument * pdf_doc) +{ + InObj *r, *n; + Object obj1; + XRef *xref; + PDFDoc *doc = pdf_doc->doc; + xref = doc->getXRef(); + for (r = pdf_doc->inObjList; r != NULL;) { + xref->fetch(r->ref.num, r->ref.gen, &obj1); + if (obj1.isStream()) + pdf_begin_obj(pdf, r->num, OBJSTM_NEVER); + else + pdf_begin_obj(pdf, r->num, 2); // \pdfobjcompresslevel = 2 is for this + copyObject(pdf, pdf_doc, &obj1); + obj1.free(); + pdf_end_obj(pdf); + n = r->next; + delete r; + pdf_doc->inObjList = r = n; + } +} + +// get the pagebox coordinates according to the pagebox_spec + +static PDFRectangle *get_pagebox(Page * page, int pagebox_spec) +{ + switch (pagebox_spec) { + case PDF_BOX_SPEC_MEDIA: + return page->getMediaBox(); + break; + case PDF_BOX_SPEC_CROP: + return page->getCropBox(); + break; + case PDF_BOX_SPEC_BLEED: + return page->getBleedBox(); + break; + case PDF_BOX_SPEC_TRIM: + return page->getTrimBox(); + break; + case PDF_BOX_SPEC_ART: + return page->getArtBox(); + break; + default: + pdftex_fail("PDF inclusion: unknown value of pagebox spec (%i)", + (int) pagebox_spec); + } + return page->getMediaBox(); // to make the compiler happy +} + +// 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. + +void +read_pdf_info(image_dict * idict, int minor_pdf_version_wanted, + int pdf_inclusion_errorlevel, img_readtype_e readtype) +{ + PdfDocument *pdf_doc; + PDFDoc *doc; + Catalog *catalog; + Page *page; + int rotate; + PDFRectangle *pagebox; + int pdf_major_version_found, pdf_minor_version_found; + float xsize, ysize, xorig, yorig; + assert(idict != NULL); + assert(img_type(idict) == IMG_TYPE_PDF); + assert(readtype == IMG_CLOSEINBETWEEN); // only this is implemented + // initialize + if (isInit == gFalse) { + globalParams = new GlobalParams(); + globalParams->setErrQuiet(gFalse); + isInit = gTrue; + } + // open PDF file + pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL); + doc = pdf_doc->doc; + catalog = doc->getCatalog(); + // 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, pdfTeX will + // then have to changed drastically anyway. + pdf_major_version_found = doc->getPDFMajorVersion(); + pdf_minor_version_found = doc->getPDFMinorVersion(); + if ((pdf_major_version_found > 1) + || (pdf_minor_version_found > minor_pdf_version_wanted)) { + const char *msg = + "PDF inclusion: found PDF version <%d.%d>, but at most version <1.%d> allowed"; + if (pdf_inclusion_errorlevel > 0) { + pdftex_fail(msg, pdf_major_version_found, pdf_minor_version_found, + minor_pdf_version_wanted); + } else { + pdftex_warn(msg, pdf_major_version_found, pdf_minor_version_found, + minor_pdf_version_wanted); + } + } + img_totalpages(idict) = catalog->getNumPages(); + if (img_pagename(idict)) { + // get page by name + GooString name(img_pagename(idict)); + LinkDest *link = doc->findDest(&name); + if (link == NULL || !link->isOk()) + pdftex_fail("PDF inclusion: invalid destination <%s>", + img_pagename(idict)); + Ref ref = link->getPageRef(); + img_pagenum(idict) = catalog->findPage(ref.num, ref.gen); + if (img_pagenum(idict) == 0) + pdftex_fail("PDF inclusion: destination is not a page <%s>", + img_pagename(idict)); + delete link; + } else { + // get page by number + if (img_pagenum(idict) <= 0 + || img_pagenum(idict) > img_totalpages(idict)) + pdftex_fail("PDF inclusion: required page <%i> does not exist", + (int) img_pagenum(idict)); + } + // get the required page + page = catalog->getPage(img_pagenum(idict)); + + // get the pagebox coordinates (media, crop,...) to use. + pagebox = get_pagebox(page, img_pagebox(idict)); + if (pagebox->x2 > pagebox->x1) { + xorig = pagebox->x1; + xsize = pagebox->x2 - pagebox->x1; + } else { + xorig = pagebox->x2; + xsize = pagebox->x1 - pagebox->x2; + } + if (pagebox->y2 > pagebox->y1) { + yorig = pagebox->y1; + ysize = pagebox->y2 - pagebox->y1; + } else { + yorig = pagebox->y2; + ysize = pagebox->y1 - pagebox->y2; + } + // 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). + rotate = page->getRotate(); + switch (((rotate % 360) + 360) % 360) { // handles also neg. angles + case 0: + img_rotation(idict) = 0; + break; + case 90: + img_rotation(idict) = 3; // PDF counts clockwise! + break; + case 180: + img_rotation(idict) = 2; + break; + case 270: + img_rotation(idict) = 1; + break; + default: + pdftex_warn + ("PDF inclusion: " + "/Rotate parameter in PDF file not multiple of 90 degrees."); + } + + // currently unused info whether PDF contains a /Group + if (page->getGroup() != NULL) + img_set_group(idict); + + if (readtype == IMG_CLOSEINBETWEEN) + unrefPdfDocument(img_filepath(idict)); +} + +//********************************************************************** +// Writes 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) +{ + PdfDocument *pdf_doc; + PDFDoc *doc; + Catalog *catalog; + Page *page; + Ref *pageref; + Dict *pageDict; + Object obj1, contents, pageobj, pagesobj1, pagesobj2, *op1, *op2, *optmp; + PDFRectangle *pagebox; + int i, l; + double bbox[4]; + char s[256]; + const char *pagedictkeys[] = + { "Group", "LastModified", "Metadata", "PieceInfo", "Resources", + "SeparationInfo", NULL + }; + assert(idict != NULL); + + // open PDF file + pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL); + doc = pdf_doc->doc; + catalog = doc->getCatalog(); + page = catalog->getPage(img_pagenum(idict)); + pageref = catalog->getPageRef(img_pagenum(idict)); + assert(pageref != NULL); // was checked already in read_pdf_info() + doc->getXRef()->fetch(pageref->num, pageref->gen, &pageobj); + pageDict = pageobj.getDict(); + + // 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"); + + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + pdf_dict_add_int(pdf, "FormType", 1); + + // write additional information + pdf_dict_add_img_filename(pdf, idict); + snprintf(s, 30, "%s.PageNumber", pdfkeyprefix); + pdf_dict_add_int(pdf, s, (int) img_pagenum(idict)); + doc->getDocInfoNF(&obj1); + if (obj1.isRef()) { + // the info dict must be indirect (PDF Ref p. 61) + snprintf(s, 30, "%s.InfoDict", pdfkeyprefix); + pdf_dict_add_ref(pdf, s, addInObj(pdf, pdf_doc, obj1.getRef())); + } + obj1.free(); + 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. + pagebox = get_pagebox(page, img_pagebox(idict)); + bbox[0] = pagebox->x1; + bbox[1] = pagebox->y1; + bbox[2] = pagebox->x2; + bbox[3] = pagebox->y2; + } + pdf_add_name(pdf, "BBox"); + pdf_begin_array(pdf); + copyReal(pdf, bbox[0]); + copyReal(pdf, bbox[1]); + copyReal(pdf, bbox[2]); + copyReal(pdf, bbox[3]); + pdf_end_array(pdf); + // The /Matrix calculation is replaced by transforms in out_img(). + + // Now all relevant parts of the Page dictionary are copied: + + // Metadata validity check (as a stream it must be indirect) + pageDict->lookupNF((char *) "Metadata", &obj1); + if (!obj1.isNull() && !obj1.isRef()) + pdftex_warn("PDF inclusion: /Metadata must be indirect object"); + obj1.free(); + + // copy selected items in Page dictionary + for (i = 0; pagedictkeys[i] != NULL; i++) { + pageDict->lookupNF((char *) pagedictkeys[i], &obj1); + if (!obj1.isNull()) { + pdf_add_name(pdf, pagedictkeys[i]); + copyObject(pdf, pdf_doc, &obj1); // preserves indirection + } + obj1.free(); + } + + // 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.) + pageDict->lookupNF((char *) "Resources", &obj1); + if (obj1.isNull()) { + op1 = &pagesobj1; + op2 = &pagesobj2; + pageDict->lookup((char *) "Parent", op1); + while (op1->isDict()) { + obj1.free(); + op1->dictLookupNF((char *) "Resources", &obj1); + if (!obj1.isNull()) { + pdf_add_name(pdf, (const char *) "Resources"); + copyObject(pdf, pdf_doc, &obj1); + break; + } + op1->dictLookup((char *) "Parent", op2); + optmp = op1; + op1 = op2; + op2 = optmp; + op2->free(); + }; + if (!op1->isDict()) + pdftex_warn("PDF inclusion: Page /Resources missing."); + op1->free(); + } + obj1.free(); + + // write the Page contents + page->getContents(&contents); + if (contents.isStream()) { + // Variant A: get stream and recompress under control + // of \pdfcompresslevel + // + // pdf_begin_stream(); + // copyStreamStream(contents->getStream()); + // pdf_end_stream(); + + // Variant B: copy stream without recompressing + // + contents.streamGetDict()->lookup((char *) "F", &obj1); + if (!obj1.isNull()) { + pdftex_fail("PDF inclusion: Unsupported external stream"); + } + obj1.free(); + contents.streamGetDict()->lookup((char *) "Length", &obj1); + assert(!obj1.isNull()); + pdf_add_name(pdf, (const char *) "Length"); + copyObject(pdf, pdf_doc, &obj1); + obj1.free(); + contents.streamGetDict()->lookup((char *) "Filter", &obj1); + if (!obj1.isNull()) { + pdf_add_name(pdf, (const char *) "Filter"); + copyObject(pdf, pdf_doc, &obj1); + obj1.free(); + contents.streamGetDict()->lookup((char *) "DecodeParms", &obj1); + if (!obj1.isNull()) { + pdf_add_name(pdf, (const char *) "DecodeParms"); + copyObject(pdf, pdf_doc, &obj1); + } + } + obj1.free(); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, contents.getStream()->getBaseStream()); + pdf_end_stream(pdf); + pdf_end_obj(pdf); + } else if (contents.isArray()) { + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + for (i = 0, l = contents.arrayGetLength(); i < l; ++i) { + copyStreamStream(pdf, (contents.arrayGet(i, &obj1))->getStream()); + obj1.free(); + if (i < (l - 1)) { + // 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, ' '); + } + } + pdf_end_stream(pdf); + pdf_end_obj(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); + contents.free(); + pageobj.free(); + // unrefPdfDocument() must come after contents.free() and pageobj.free()! + // TH: The next line makes repeated pdf inclusion unacceptably slow +#if 0 + unrefPdfDocument(img_filepath(idict)); +#endif +} + +//********************************************************************** +// Deallocate a PdfDocument with all its resources + +static void deletePdfDocumentPdfDoc(PdfDocument * pdf_doc) +{ + InObj *r, *n; + assert(pdf_doc != NULL); + // this may be probably needed for an emergency destroyPdfDocument() + for (r = pdf_doc->inObjList; r != NULL; r = n) { + n = r->next; + delete r; + } +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: Deleting PDFDoc %s\n", pdf_doc->file_path); +#endif + delete pdf_doc->doc; + pdf_doc->doc = NULL; + pdf_doc->pc++; +} + +static void destroyPdfDocument(void *pa, void * /*pb */ ) +{ + 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); + assert(pdf_doc != NULL); + assert(pdf_doc->occurences != 0); // aim for point landing + pdf_doc->occurences--; +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: Decrementing %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); +#endif + if (pdf_doc->occurences == 0) { + assert(pdf_doc->inObjList == NULL); // should be eaten up already + deletePdfDocumentPdfDoc(pdf_doc); + } +} + +// Called when PDF embedding system is finalized. +// Now deallocate all remaining PdfDocuments. + +void epdf_free() +{ + if (PdfDocumentTree != NULL) + avl_destroy(PdfDocumentTree, destroyPdfDocument); + PdfDocumentTree = NULL; + if (isInit == gTrue) + delete globalParams; + isInit = gFalse; +} |