diff options
Diffstat (limited to 'Build/source/libs/poppler/poppler-0.18.4/poppler/PDFDoc.cc')
-rw-r--r-- | Build/source/libs/poppler/poppler-0.18.4/poppler/PDFDoc.cc | 1534 |
1 files changed, 1534 insertions, 0 deletions
diff --git a/Build/source/libs/poppler/poppler-0.18.4/poppler/PDFDoc.cc b/Build/source/libs/poppler/poppler-0.18.4/poppler/PDFDoc.cc new file mode 100644 index 00000000000..1c8538534d5 --- /dev/null +++ b/Build/source/libs/poppler/poppler-0.18.4/poppler/PDFDoc.cc @@ -0,0 +1,1534 @@ +//======================================================================== +// +// PDFDoc.cc +// +// Copyright 1996-2003 Glyph & Cog, LLC +// +//======================================================================== + +//======================================================================== +// +// Modified under the Poppler project - http://poppler.freedesktop.org +// +// All changes made under the Poppler project to this file are licensed +// under GPL version 2 or later +// +// Copyright (C) 2005, 2006, 2008 Brad Hards <bradh@frogmouth.net> +// Copyright (C) 2005, 2007-2009, 2011 Albert Astals Cid <aacid@kde.org> +// Copyright (C) 2008 Julien Rebetez <julienr@svn.gnome.org> +// Copyright (C) 2008, 2010 Pino Toscano <pino@kde.org> +// Copyright (C) 2008, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org> +// Copyright (C) 2009 Eric Toombs <ewtoombs@uwaterloo.ca> +// Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net> +// Copyright (C) 2009, 2011 Axel Struebing <axel.struebing@freenet.de> +// Copyright (C) 2010, 2011 Hib Eris <hib@hiberis.nl> +// Copyright (C) 2010 Jakub Wilk <ubanus@users.sf.net> +// Copyright (C) 2010 Ilya Gorenbein <igorenbein@finjan.com> +// Copyright (C) 2010 Srinivas Adicherla <srinivas.adicherla@geodesic.com> +// Copyright (C) 2010 Philip Lorenz <lorenzph+freedesktop@gmail.com> +// Copyright (C) 2011, 2012 Thomas Freitag <Thomas.Freitag@alfa.de> +// +// To see a description of the changes please see the Changelog file that +// came with your tarball or type make ChangeLog if you are building from git +// +//======================================================================== + +#include <config.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <ctype.h> +#include <locale.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <time.h> +#ifdef _WIN32 +# include <windows.h> +#endif +#include <sys/stat.h> +#include "goo/gstrtod.h" +#include "goo/GooString.h" +#include "poppler-config.h" +#include "GlobalParams.h" +#include "Page.h" +#include "Catalog.h" +#include "Stream.h" +#include "XRef.h" +#include "Linearization.h" +#include "Link.h" +#include "OutputDev.h" +#include "Error.h" +#include "ErrorCodes.h" +#include "Lexer.h" +#include "Parser.h" +#include "SecurityHandler.h" +#include "Decrypt.h" +#ifndef DISABLE_OUTLINE +#include "Outline.h" +#endif +#include "PDFDoc.h" +#include "Hints.h" + +//------------------------------------------------------------------------ + +#define headerSearchSize 1024 // read this many bytes at beginning of + // file to look for '%PDF' +#define pdfIdLength 32 // PDF Document IDs (PermanentId, UpdateId) length + +#define linearizationSearchSize 1024 // read this many bytes at beginning of + // file to look for linearization + // dictionary + +#define xrefSearchSize 1024 // read this many bytes at end of file + // to look for 'startxref' + +//------------------------------------------------------------------------ +// PDFDoc +//------------------------------------------------------------------------ + +void PDFDoc::init() +{ + ok = gFalse; + errCode = errNone; + fileName = NULL; + file = NULL; + str = NULL; + xref = NULL; + linearization = NULL; + catalog = NULL; + hints = NULL; +#ifndef DISABLE_OUTLINE + outline = NULL; +#endif + startXRefPos = ~(Guint)0; + secHdlr = NULL; + pageCache = NULL; +} + +PDFDoc::PDFDoc() +{ + init(); +} + +PDFDoc::PDFDoc(GooString *fileNameA, GooString *ownerPassword, + GooString *userPassword, void *guiDataA) { + Object obj; + int size = 0; + + init(); + + fileName = fileNameA; + guiData = guiDataA; + + struct stat buf; + if (stat(fileName->getCString(), &buf) == 0) { + size = buf.st_size; + } + + // try to open file +#ifdef VMS + file = fopen(fileName->getCString(), "rb", "ctx=stm"); +#else + file = fopen(fileName->getCString(), "rb"); +#endif + if (file == NULL) { + // fopen() has failed. + // Keep a copy of the errno returned by fopen so that it can be + // referred to later. + fopenErrno = errno; + error(-1, "Couldn't open file '%s': %s.", fileName->getCString(), + strerror(errno)); + errCode = errOpenFile; + return; + } + + // create stream + obj.initNull(); + str = new FileStream(file, 0, gFalse, size, &obj); + + ok = setup(ownerPassword, userPassword); +} + +#ifdef _WIN32 +PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GooString *ownerPassword, + GooString *userPassword, void *guiDataA) { + OSVERSIONINFO version; + wchar_t fileName2[MAX_PATH + 1]; + Object obj; + int i; + + init(); + + guiData = guiDataA; + + //~ file name should be stored in Unicode (?) + fileName = new GooString(); + for (i = 0; i < fileNameLen; ++i) { + fileName->append((char)fileNameA[i]); + } + + // zero-terminate the file name string + for (i = 0; i < fileNameLen && i < MAX_PATH; ++i) { + fileName2[i] = fileNameA[i]; + } + fileName2[i] = 0; + + // try to open file + // NB: _wfopen is only available in NT + struct _stat buf; + int size; + version.dwOSVersionInfoSize = sizeof(version); + GetVersionEx(&version); + if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { + if (_wstat(fileName2, &buf) == 0) { + size = buf.st_size; + } + file = _wfopen(fileName2, L"rb"); + } else { + if (_stat(fileName->getCString(), &buf) == 0) { + size = buf.st_size; + } + file = fopen(fileName->getCString(), "rb"); + } + if (!file) { + error(-1, "Couldn't open file '%s'", fileName->getCString()); + errCode = errOpenFile; + return; + } + + // create stream + obj.initNull(); + str = new FileStream(file, 0, gFalse, size, &obj); + + ok = setup(ownerPassword, userPassword); +} +#endif + +PDFDoc::PDFDoc(BaseStream *strA, GooString *ownerPassword, + GooString *userPassword, void *guiDataA) { + + init(); + guiData = guiDataA; + if (strA->getFileName()) { + fileName = strA->getFileName()->copy(); + } else { + fileName = NULL; + } + str = strA; + ok = setup(ownerPassword, userPassword); +} + +GBool PDFDoc::setup(GooString *ownerPassword, GooString *userPassword) { + str->setPos(0, -1); + if (str->getPos() < 0) + { + error(-1, "Document base stream is not seekable"); + return gFalse; + } + + str->reset(); + + // check footer + // Adobe does not seem to enforce %%EOF, so we do the same +// if (!checkFooter()) return gFalse; + + // check header + checkHeader(); + + GBool wasReconstructed = false; + + // read xref table + xref = new XRef(str, getStartXRef(), getMainXRefEntriesOffset(), &wasReconstructed); + if (!xref->isOk()) { + error(-1, "Couldn't read xref table"); + errCode = xref->getErrorCode(); + return gFalse; + } + + // check for encryption + if (!checkEncryption(ownerPassword, userPassword)) { + errCode = errEncrypted; + return gFalse; + } + + // read catalog + catalog = new Catalog(xref); + if (catalog && !catalog->isOk()) { + if (!wasReconstructed) + { + // try one more time to contruct the Catalog, maybe the problem is damaged XRef + delete catalog; + delete xref; + xref = new XRef(str, 0, 0, NULL, true); + catalog = new Catalog(xref); + } + + if (catalog && !catalog->isOk()) { + error(-1, "Couldn't read page catalog"); + errCode = errBadCatalog; + return gFalse; + } + } + + // done + return gTrue; +} + +PDFDoc::~PDFDoc() { + if (pageCache) { + for (int i = 0; i < getNumPages(); i++) { + if (pageCache[i]) { + delete pageCache[i]; + } + } + gfree(pageCache); + } + delete secHdlr; +#ifndef DISABLE_OUTLINE + if (outline) { + delete outline; + } +#endif + if (catalog) { + delete catalog; + } + if (xref) { + delete xref; + } + if (hints) { + delete hints; + } + if (linearization) { + delete linearization; + } + if (str) { + delete str; + } + if (file) { + fclose(file); + } + if (fileName) { + delete fileName; + } +} + + +// Check for a %%EOF at the end of this stream +GBool PDFDoc::checkFooter() { + // we look in the last 1024 chars because Adobe does the same + char *eof = new char[1025]; + int pos = str->getPos(); + str->setPos(1024, -1); + int i, ch; + for (i = 0; i < 1024; i++) + { + ch = str->getChar(); + if (ch == EOF) + break; + eof[i] = ch; + } + eof[i] = '\0'; + + bool found = false; + for (i = i - 5; i >= 0; i--) { + if (strncmp (&eof[i], "%%EOF", 5) == 0) { + found = true; + break; + } + } + if (!found) + { + error(-1, "Document has not the mandatory ending %%EOF"); + errCode = errDamaged; + delete[] eof; + return gFalse; + } + delete[] eof; + str->setPos(pos); + return gTrue; +} + +// Check for a PDF header on this stream. Skip past some garbage +// if necessary. +void PDFDoc::checkHeader() { + char hdrBuf[headerSearchSize+1]; + char *p; + char *tokptr; + int i; + + pdfMajorVersion = 0; + pdfMinorVersion = 0; + for (i = 0; i < headerSearchSize; ++i) { + hdrBuf[i] = str->getChar(); + } + hdrBuf[headerSearchSize] = '\0'; + for (i = 0; i < headerSearchSize - 5; ++i) { + if (!strncmp(&hdrBuf[i], "%PDF-", 5)) { + break; + } + } + if (i >= headerSearchSize - 5) { + error(-1, "May not be a PDF file (continuing anyway)"); + return; + } + str->moveStart(i); + if (!(p = strtok_r(&hdrBuf[i+5], " \t\n\r", &tokptr))) { + error(-1, "May not be a PDF file (continuing anyway)"); + return; + } + sscanf(p, "%d.%d", &pdfMajorVersion, &pdfMinorVersion); + // We don't do the version check. Don't add it back in. +} + +GBool PDFDoc::checkEncryption(GooString *ownerPassword, GooString *userPassword) { + Object encrypt; + GBool encrypted; + GBool ret; + + xref->getTrailerDict()->dictLookup("Encrypt", &encrypt); + if ((encrypted = encrypt.isDict())) { + if ((secHdlr = SecurityHandler::make(this, &encrypt))) { + if (secHdlr->checkEncryption(ownerPassword, userPassword)) { + // authorization succeeded + xref->setEncryption(secHdlr->getPermissionFlags(), + secHdlr->getOwnerPasswordOk(), + secHdlr->getFileKey(), + secHdlr->getFileKeyLength(), + secHdlr->getEncVersion(), + secHdlr->getEncRevision(), + secHdlr->getEncAlgorithm()); + ret = gTrue; + } else { + // authorization failed + ret = gFalse; + } + } else { + // couldn't find the matching security handler + ret = gFalse; + } + } else { + // document is not encrypted + ret = gTrue; + } + encrypt.free(); + return ret; +} + +void PDFDoc::displayPage(OutputDev *out, int page, + double hDPI, double vDPI, int rotate, + GBool useMediaBox, GBool crop, GBool printing, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData, + GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), + void *annotDisplayDecideCbkData) { + if (globalParams->getPrintCommands()) { + printf("***** page %d *****\n", page); + } + + if (getPage(page)) + getPage(page)->display(out, hDPI, vDPI, + rotate, useMediaBox, crop, printing, catalog, + abortCheckCbk, abortCheckCbkData, + annotDisplayDecideCbk, annotDisplayDecideCbkData); + +} + +void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage, + double hDPI, double vDPI, int rotate, + GBool useMediaBox, GBool crop, GBool printing, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData, + GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), + void *annotDisplayDecideCbkData) { + int page; + + for (page = firstPage; page <= lastPage; ++page) { + displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing, + abortCheckCbk, abortCheckCbkData, + annotDisplayDecideCbk, annotDisplayDecideCbkData); + } +} + +void PDFDoc::displayPageSlice(OutputDev *out, int page, + double hDPI, double vDPI, int rotate, + GBool useMediaBox, GBool crop, GBool printing, + int sliceX, int sliceY, int sliceW, int sliceH, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData, + GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), + void *annotDisplayDecideCbkData) { + if (getPage(page)) + getPage(page)->displaySlice(out, hDPI, vDPI, + rotate, useMediaBox, crop, + sliceX, sliceY, sliceW, sliceH, + printing, catalog, + abortCheckCbk, abortCheckCbkData, + annotDisplayDecideCbk, annotDisplayDecideCbkData); +} + +Links *PDFDoc::getLinks(int page) { + Page *p = getPage(page); + if (!p) { + return new Links (NULL); + } + return p->getLinks(catalog); +} + +void PDFDoc::processLinks(OutputDev *out, int page) { + if (getPage(page)) + getPage(page)->processLinks(out, catalog); +} + +Linearization *PDFDoc::getLinearization() +{ + if (!linearization) { + linearization = new Linearization(str); + } + return linearization; +} + +GBool PDFDoc::isLinearized() { + if ((str->getLength()) && + (getLinearization()->getLength() == str->getLength())) + return gTrue; + else + return gFalse; +} + +static GBool +get_id (GooString *encodedidstring, GooString *id) { + const char *encodedid = encodedidstring->getCString(); + char pdfid[pdfIdLength + 1]; + int n; + + if (encodedidstring->getLength() != pdfIdLength / 2) + return gFalse; + + n = sprintf(pdfid, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + encodedid[0] & 0xff, encodedid[1] & 0xff, encodedid[2] & 0xff, encodedid[3] & 0xff, + encodedid[4] & 0xff, encodedid[5] & 0xff, encodedid[6] & 0xff, encodedid[7] & 0xff, + encodedid[8] & 0xff, encodedid[9] & 0xff, encodedid[10] & 0xff, encodedid[11] & 0xff, + encodedid[12] & 0xff, encodedid[13] & 0xff, encodedid[14] & 0xff, encodedid[15] & 0xff); + if (n != pdfIdLength) + return gFalse; + + id->Set(pdfid, pdfIdLength); + return gTrue; +} + +GBool PDFDoc::getID(GooString *permanent_id, GooString *update_id) { + Object obj; + xref->getTrailerDict()->dictLookup ("ID", &obj); + + if (obj.isArray() && obj.arrayGetLength() == 2) { + Object obj2; + + if (permanent_id) { + if (obj.arrayGet(0, &obj2)->isString()) { + if (!get_id (obj2.getString(), permanent_id)) { + obj2.free(); + return gFalse; + } + } else { + error(-1, "Invalid permanent ID"); + obj2.free(); + return gFalse; + } + obj2.free(); + } + + if (update_id) { + if (obj.arrayGet(1, &obj2)->isString()) { + if (!get_id (obj2.getString(), update_id)) { + obj2.free(); + return gFalse; + } + } else { + error(-1, "Invalid update ID"); + obj2.free(); + return gFalse; + } + obj2.free(); + } + + obj.free(); + + return gTrue; + } + obj.free(); + + return gFalse; +} + +Hints *PDFDoc::getHints() +{ + if (!hints && isLinearized()) { + hints = new Hints(str, getLinearization(), getXRef(), secHdlr); + } + + return hints; +} + +int PDFDoc::savePageAs(GooString *name, int pageNo) +{ + FILE *f; + OutStream *outStr; + XRef *yRef, *countRef; + int rootNum = getXRef()->getSize() + 1; + + if (pageNo < 1 || pageNo > getNumPages()) { + error(-1, "Illegal pageNo: %d(%d)", pageNo, getNumPages() ); + return errOpenFile; + } + PDFRectangle *cropBox = NULL; + if (getCatalog()->getPage(pageNo)->isCropped()) { + cropBox = getCatalog()->getPage(pageNo)->getCropBox(); + } + replacePageDict(pageNo, + getCatalog()->getPage(pageNo)->getRotate(), + getCatalog()->getPage(pageNo)->getMediaBox(), + cropBox, NULL); + Ref *refPage = getCatalog()->getPageRef(pageNo); + Object page; + getXRef()->fetch(refPage->num, refPage->gen, &page); + + if (!(f = fopen(name->getCString(), "wb"))) { + error(-1, "Couldn't open file '%s'", name->getCString()); + return errOpenFile; + } + outStr = new FileOutStream(f,0); + + yRef = new XRef(); + countRef = new XRef(); + yRef->add(0, 65535, 0, gFalse); + writeHeader(outStr, getPDFMajorVersion(), getPDFMinorVersion()); + + // get and mark optional content groups + OCGs *ocgs = getCatalog()->getOptContentConfig(); + if (ocgs != NULL) { + Object catDict, optContentProps; + getXRef()->getCatalog(&catDict); + catDict.dictLookup("OCProperties", &optContentProps); + Dict *pageDict = optContentProps.getDict(); + markPageObjects(pageDict, yRef, countRef, 0); + catDict.free(); + optContentProps.free(); + } + + Dict *pageDict = page.getDict(); + markPageObjects(pageDict, yRef, countRef, 0); + Guint objectsCount = writePageObjects(outStr, yRef, 0); + + yRef->add(rootNum,0,outStr->getPos(),gTrue); + outStr->printf("%d 0 obj\n", rootNum); + outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1); + if (ocgs != NULL) { + Object catDict, optContentProps; + getXRef()->getCatalog(&catDict); + catDict.dictLookup("OCProperties", &optContentProps); + outStr->printf(" /OCProperties <<"); + Dict *pageDict = optContentProps.getDict(); + for (int n = 0; n < pageDict->getLength(); n++) { + if (n > 0) outStr->printf(" "); + const char *key = pageDict->getKey(n); + Object value; pageDict->getValNF(n, &value); + outStr->printf("/%s ", key); + writeObject(&value, NULL, outStr, getXRef(), 0); + value.free(); + } + outStr->printf(" >> "); + catDict.free(); + optContentProps.free(); + } + outStr->printf(">>\nendobj\n"); + objectsCount++; + + yRef->add(rootNum + 1,0,outStr->getPos(),gTrue); + outStr->printf("%d 0 obj\n", rootNum + 1); + outStr->printf("<< /Type /Pages /Kids [ %d 0 R ] /Count 1 >>\n", rootNum + 2); + outStr->printf("endobj\n"); + objectsCount++; + + yRef->add(rootNum + 2,0,outStr->getPos(),gTrue); + outStr->printf("%d 0 obj\n", rootNum + 2); + outStr->printf("<< "); + for (int n = 0; n < pageDict->getLength(); n++) { + if (n > 0) outStr->printf(" "); + const char *key = pageDict->getKey(n); + Object value; pageDict->getValNF(n, &value); + if (strcmp(key, "Parent") == 0) { + outStr->printf("/Parent %d 0 R", rootNum + 1); + } else { + outStr->printf("/%s ", key); + writeObject(&value, NULL, outStr, getXRef(), 0); + } + value.free(); + } + outStr->printf(" >>\nendobj\n"); + objectsCount++; + page.free(); + + Guint uxrefOffset = outStr->getPos(); + yRef->writeToFile(outStr, gFalse /* do not write unnecessary entries */); + + Ref ref; + ref.num = rootNum; + ref.gen = 0; + writeTrailer(uxrefOffset, objectsCount, outStr, gFalse, 0, &ref, getXRef(), name->getCString(), outStr->getPos()); + + outStr->close(); + fclose(f); + delete yRef; + delete countRef; + + return errNone; +} + +int PDFDoc::saveAs(GooString *name, PDFWriteMode mode) { + FILE *f; + OutStream *outStr; + int res; + + if (!(f = fopen(name->getCString(), "wb"))) { + error(-1, "Couldn't open file '%s'", name->getCString()); + return errOpenFile; + } + outStr = new FileOutStream(f,0); + res = saveAs(outStr, mode); + delete outStr; + fclose(f); + return res; +} + +int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) { + + // find if we have updated objects + GBool updated = gFalse; + for(int i=0; i<xref->getNumObjects(); i++) { + if (xref->getEntry(i)->updated) { + updated = gTrue; + break; + } + } + + // we don't support rewriting files with Encrypt at the moment + Object obj; + xref->getTrailerDict()->getDict()->lookupNF("Encrypt", &obj); + if (!obj.isNull()) + { + obj.free(); + if (!updated && mode == writeStandard) { + // simply copy the original file + saveWithoutChangesAs (outStr); + } else { + return errEncrypted; + } + } + else + { + obj.free(); + + if (mode == writeForceRewrite) { + saveCompleteRewrite(outStr); + } else if (mode == writeForceIncremental) { + saveIncrementalUpdate(outStr); + } else { // let poppler decide + if(updated) { + saveIncrementalUpdate(outStr); + } else { + // simply copy the original file + saveWithoutChangesAs (outStr); + } + } + } + + return errNone; +} + +int PDFDoc::saveWithoutChangesAs(GooString *name) { + FILE *f; + OutStream *outStr; + int res; + + if (!(f = fopen(name->getCString(), "wb"))) { + error(-1, "Couldn't open file '%s'", name->getCString()); + return errOpenFile; + } + + outStr = new FileOutStream(f,0); + res = saveWithoutChangesAs(outStr); + delete outStr; + + fclose(f); + + return res; +} + +int PDFDoc::saveWithoutChangesAs(OutStream *outStr) { + int c; + + str->reset(); + while ((c = str->getChar()) != EOF) { + outStr->put(c); + } + str->close(); + + return errNone; +} + +void PDFDoc::saveIncrementalUpdate (OutStream* outStr) +{ + XRef *uxref; + int c; + //copy the original file + str->reset(); + while ((c = str->getChar()) != EOF) { + outStr->put(c); + } + str->close(); + + uxref = new XRef(); + uxref->add(0, 65535, 0, gFalse); + for(int i=0; i<xref->getNumObjects(); i++) { + if ((xref->getEntry(i)->type == xrefEntryFree) && + (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects + continue; + + if (xref->getEntry(i)->updated) { //we have an updated object + Object obj1; + Ref ref; + ref.num = i; + ref.gen = xref->getEntry(i)->type == xrefEntryCompressed ? 0 : xref->getEntry(i)->gen; + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, outStr); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } + } + if (uxref->getSize() == 0) { //we have nothing to update + delete uxref; + return; + } + + Guint uxrefOffset = outStr->getPos(); + uxref->writeToFile(outStr, gFalse /* do not write unnecessary entries */); + + writeTrailer(uxrefOffset, xref->getSize(), outStr, gTrue); + + delete uxref; +} + +void PDFDoc::saveCompleteRewrite (OutStream* outStr) +{ + outStr->printf("%%PDF-%d.%d\r\n",pdfMajorVersion,pdfMinorVersion); + XRef *uxref = new XRef(); + uxref->add(0, 65535, 0, gFalse); + for(int i=0; i<xref->getNumObjects(); i++) { + Object obj1; + Ref ref; + XRefEntryType type = xref->getEntry(i)->type; + if (type == xrefEntryFree) { + ref.num = i; + ref.gen = xref->getEntry(i)->gen; + /* the XRef class adds a lot of irrelevant free entries, we only want the significant one + and we don't want the one with num=0 because it has already been added (gen = 65535)*/ + if (ref.gen > 0 && ref.num > 0) + uxref->add(ref.num, ref.gen, 0, gFalse); + } else if (type == xrefEntryUncompressed){ + ref.num = i; + ref.gen = xref->getEntry(i)->gen; + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, outStr); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } else if (type == xrefEntryCompressed) { + ref.num = i; + ref.gen = 0; //compressed entries have gen == 0 + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, outStr); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } + } + Guint uxrefOffset = outStr->getPos(); + uxref->writeToFile(outStr, gTrue /* write all entries */); + + writeTrailer(uxrefOffset, uxref->getSize(), outStr, gFalse); + + + delete uxref; + +} + +void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr, XRef *xRef, Guint numOffset) +{ + Object obj1; + outStr->printf("<<"); + for (int i=0; i<dict->getLength(); i++) { + GooString keyName(dict->getKey(i)); + GooString *keyNameToPrint = keyName.sanitizedName(gFalse /* non ps mode */); + outStr->printf("/%s ", keyNameToPrint->getCString()); + delete keyNameToPrint; + writeObject(dict->getValNF(i, &obj1), NULL, outStr, xRef, numOffset); + obj1.free(); + } + outStr->printf(">> "); +} + +void PDFDoc::writeStream (Stream* str, OutStream* outStr) +{ + outStr->printf("stream\r\n"); + str->reset(); + for (int c=str->getChar(); c!= EOF; c=str->getChar()) { + outStr->printf("%c", c); + } + outStr->printf("\r\nendstream\r\n"); +} + +void PDFDoc::writeRawStream (Stream* str, OutStream* outStr) +{ + Object obj1; + str->getDict()->lookup("Length", &obj1); + if (!obj1.isInt()) { + error (-1, "PDFDoc::writeRawStream, no Length in stream dict"); + return; + } + + const int length = obj1.getInt(); + obj1.free(); + + outStr->printf("stream\r\n"); + str->unfilteredReset(); + for (int i=0; i<length; i++) { + int c = str->getUnfilteredChar(); + outStr->printf("%c", c); + } + str->reset(); + outStr->printf("\r\nendstream\r\n"); +} + +void PDFDoc::writeString (GooString* s, OutStream* outStr) +{ + if (s->hasUnicodeMarker()) { + //unicode string don't necessary end with \0 + const char* c = s->getCString(); + outStr->printf("("); + for(int i=0; i<s->getLength(); i++) { + char unescaped = *(c+i)&0x000000ff; + //escape if needed + if (unescaped == '(' || unescaped == ')' || unescaped == '\\') + outStr->printf("%c", '\\'); + outStr->printf("%c", unescaped); + } + outStr->printf(") "); + } else { + const char* c = s->getCString(); + outStr->printf("("); + for(int i=0; i<s->getLength(); i++) { + char unescaped = *(c+i)&0x000000ff; + //escape if needed + if (unescaped == '\r') + outStr->printf("\\r"); + else if (unescaped == '\n') + outStr->printf("\\n"); + else { + if (unescaped == '(' || unescaped == ')' || unescaped == '\\') { + outStr->printf("%c", '\\'); + } + outStr->printf("%c", unescaped); + } + } + outStr->printf(") "); + } +} + +Guint PDFDoc::writeObject (Object* obj, Ref* ref, OutStream* outStr, XRef *xRef, Guint numOffset) +{ + Array *array; + Object obj1; + Guint offset = outStr->getPos(); + int tmp; + + if(ref) + outStr->printf("%i %i obj ", ref->num, ref->gen); + + switch (obj->getType()) { + case objBool: + outStr->printf("%s ", obj->getBool()?"true":"false"); + break; + case objInt: + outStr->printf("%i ", obj->getInt()); + break; + case objReal: + { + GooString s; + s.appendf("{0:.10g}", obj->getReal()); + outStr->printf("%s ", s.getCString()); + break; + } + case objString: + writeString(obj->getString(), outStr); + break; + case objName: + { + GooString name(obj->getName()); + GooString *nameToPrint = name.sanitizedName(gFalse /* non ps mode */); + outStr->printf("/%s ", nameToPrint->getCString()); + delete nameToPrint; + break; + } + case objNull: + outStr->printf( "null "); + break; + case objArray: + array = obj->getArray(); + outStr->printf("["); + for (int i=0; i<array->getLength(); i++) { + writeObject(array->getNF(i, &obj1), NULL,outStr, xRef, numOffset); + obj1.free(); + } + outStr->printf("] "); + break; + case objDict: + writeDictionnary (obj->getDict(),outStr, xRef, numOffset); + break; + case objStream: + { + //We can't modify stream with the current implementation (no write functions in Stream API) + // => the only type of streams which that have been modified are internal streams (=strWeird) + Stream *stream = obj->getStream(); + if (stream->getKind() == strWeird) { + //we write the stream unencoded => TODO: write stream encoder + stream->reset(); + //recalculate stream length + tmp = 0; + for (int c=stream->getChar(); c!=EOF; c=stream->getChar()) { + tmp++; + } + obj1.initInt(tmp); + stream->getDict()->set("Length", &obj1); + + //Remove Stream encoding + stream->getDict()->remove("Filter"); + stream->getDict()->remove("DecodeParms"); + + writeDictionnary (stream->getDict(),outStr, xRef, numOffset); + writeStream (stream,outStr); + obj1.free(); + } else { + //raw stream copy + FilterStream *fs = dynamic_cast<FilterStream*>(stream); + if (fs) { + BaseStream *bs = fs->getBaseStream(); + if (bs) { + Guint streamEnd; + if (xRef->getStreamEnd(bs->getStart(), &streamEnd)) { + Object val; + val.initInt(streamEnd - bs->getStart()); + stream->getDict()->set("Length", &val); + } + } + } + writeDictionnary (stream->getDict(), outStr, xRef, numOffset); + writeRawStream (stream, outStr); + } + break; + } + case objRef: + outStr->printf("%i %i R ", obj->getRef().num + numOffset, obj->getRef().gen); + break; + case objCmd: + outStr->printf("%s\n", obj->getCmd()); + break; + case objError: + outStr->printf("error\r\n"); + break; + case objEOF: + outStr->printf("eof\r\n"); + break; + case objNone: + outStr->printf("none\r\n"); + break; + default: + error(-1,"Unhandled objType : %i, please report a bug with a testcase\r\n", obj->getType()); + break; + } + if (ref) + outStr->printf("endobj\r\n"); + return offset; +} + +void PDFDoc::writeTrailer(Guint uxrefOffset, int uxrefSize, + OutStream* outStr, GBool incrUpdate, + Guint startxRef, Ref *root, XRef *xRef, const char *fileName, + Guint fileSize) +{ + Dict *trailerDict = new Dict(xRef); + Object obj1; + obj1.initInt(uxrefSize); + trailerDict->set("Size", &obj1); + obj1.free(); + + + //build a new ID, as recommended in the reference, uses: + // - current time + // - file name + // - file size + // - values of entry in information dictionnary + GooString message; + char buffer[256]; + sprintf(buffer, "%i", (int)time(NULL)); + message.append(buffer); + message.append(fileName); + + sprintf(buffer, "%i", fileSize); + message.append(buffer); + + //info dict -- only use text string + if (!xRef->getTrailerDict()->isNone() && xRef->getDocInfo(&obj1)->isDict()) { + for(int i=0; i<obj1.getDict()->getLength(); i++) { + Object obj2; + obj1.getDict()->getVal(i, &obj2); + if (obj2.isString()) { + message.append(obj2.getString()); + } + obj2.free(); + } + } + obj1.free(); + + //calculate md5 digest + Guchar digest[16]; + Decrypt::md5((Guchar*)message.getCString(), message.getLength(), digest); + obj1.initString(new GooString((const char*)digest, 16)); + + //create ID array + Object obj2,obj3,obj5; + obj2.initArray(xRef); + + if (incrUpdate) { + Object obj4; + //only update the second part of the array + xRef->getTrailerDict()->getDict()->lookup("ID", &obj4); + if (!obj4.isArray()) { + error(-1, "PDFDoc::writeTrailer original file's ID entry isn't an array. Trying to continue"); + } else { + //Get the first part of the ID + obj4.arrayGet(0,&obj3); + + obj2.arrayAdd(&obj3); + obj2.arrayAdd(&obj1); + trailerDict->set("ID", &obj2); + } + obj4.free(); + } else { + //new file => same values for the two identifiers + obj2.arrayAdd(&obj1); + obj1.initString(new GooString((const char*)digest, 16)); + obj2.arrayAdd(&obj1); + trailerDict->set("ID", &obj2); + } + + obj1.initRef(root->num, root->gen); + trailerDict->set("Root", &obj1); + + if (incrUpdate) { + obj1.initInt(startxRef); + trailerDict->set("Prev", &obj1); + } + + if (!xRef->getTrailerDict()->isNone()) { + xRef->getDocInfoNF(&obj5); + if (!obj5.isNull()) { + trailerDict->set("Info", &obj5); + } + } + + outStr->printf( "trailer\r\n"); + writeDictionnary(trailerDict, outStr, xRef, 0); + outStr->printf( "\r\nstartxref\r\n"); + outStr->printf( "%i\r\n", uxrefOffset); + outStr->printf( "%%%%EOF\r\n"); + + delete trailerDict; +} + +void PDFDoc::writeTrailer(Guint uxrefOffset, int uxrefSize, OutStream* outStr, GBool incrUpdate) +{ + char *fileNameA; + if (fileName) + fileNameA = fileName->getCString(); + else + fileNameA = "streamwithoutfilename.pdf"; + // file size + unsigned int fileSize = 0; + int c; + str->reset(); + while ((c = str->getChar()) != EOF) { + fileSize++; + } + str->close(); + Ref ref; + ref.num = getXRef()->getRootNum(); + ref.gen = getXRef()->getRootGen(); + writeTrailer(uxrefOffset, uxrefSize, outStr, incrUpdate, getStartXRef(), &ref, getXRef(), fileNameA, fileSize); +} + +void PDFDoc::writeHeader(OutStream *outStr, int major, int minor) +{ + outStr->printf("%%PDF-%d.%d\n", major, minor); + outStr->printf("%%\xE2\xE3\xCF\xD3\n"); +} + +void PDFDoc::markDictionnary (Dict* dict, XRef * xRef, XRef *countRef, Guint numOffset) +{ + Object obj1; + for (int i=0; i<dict->getLength(); i++) { + markObject(dict->getValNF(i, &obj1), xRef, countRef, numOffset); + obj1.free(); + } +} + +void PDFDoc::markObject (Object* obj, XRef *xRef, XRef *countRef, Guint numOffset) +{ + Array *array; + Object obj1; + + switch (obj->getType()) { + case objArray: + array = obj->getArray(); + for (int i=0; i<array->getLength(); i++) { + markObject(array->getNF(i, &obj1), xRef, countRef, numOffset); + obj1.free(); + } + break; + case objDict: + markDictionnary (obj->getDict(), xRef, countRef, numOffset); + break; + case objStream: + { + Stream *stream = obj->getStream(); + markDictionnary (stream->getDict(), xRef, countRef, numOffset); + } + break; + case objRef: + { + if (obj->getRef().num + (int) numOffset >= xRef->getNumObjects() || xRef->getEntry(obj->getRef().num + numOffset)->type == xrefEntryFree) { + if (getXRef()->getEntry(obj->getRef().num)->type == xrefEntryFree) { + return; // already marked as free => should be replaced + } + xRef->add(obj->getRef().num + numOffset, obj->getRef().gen, 0, gTrue); + if (getXRef()->getEntry(obj->getRef().num)->type == xrefEntryCompressed) { + xRef->getEntry(obj->getRef().num + numOffset)->type = xrefEntryCompressed; + } + } + if (obj->getRef().num + (int) numOffset >= countRef->getNumObjects() || + countRef->getEntry(obj->getRef().num + numOffset)->type == xrefEntryFree) + { + countRef->add(obj->getRef().num + numOffset, 1, 0, gTrue); + } else { + XRefEntry *entry = countRef->getEntry(obj->getRef().num + numOffset); + entry->gen++; + if (entry->gen > 9) + break; + } + Object obj1; + getXRef()->fetch(obj->getRef().num, obj->getRef().gen, &obj1); + markObject(&obj1, xRef, countRef, numOffset); + obj1.free(); + } + break; + default: + break; + } +} + +void PDFDoc::replacePageDict(int pageNo, int rotate, + PDFRectangle *mediaBox, + PDFRectangle *cropBox, Object *pageCTM) +{ + Ref *refPage = getCatalog()->getPageRef(pageNo); + Object page; + getXRef()->fetch(refPage->num, refPage->gen, &page); + Dict *pageDict = page.getDict(); + pageDict->remove("MediaBox"); + pageDict->remove("CropBox"); + pageDict->remove("ArtBox"); + pageDict->remove("BleedBox"); + pageDict->remove("TrimBox"); + pageDict->remove("Rotate"); + Object *mediaBoxObj = new Object(); + mediaBoxObj->initArray(getXRef()); + Object *murx = new Object(); + murx->initReal(mediaBox->x1); + Object *mury = new Object(); + mury->initReal(mediaBox->y1); + Object *mllx = new Object(); + mllx->initReal(mediaBox->x2); + Object *mlly = new Object(); + mlly->initReal(mediaBox->y2); + mediaBoxObj->arrayAdd(murx); + mediaBoxObj->arrayAdd(mury); + mediaBoxObj->arrayAdd(mllx); + mediaBoxObj->arrayAdd(mlly); + pageDict->add(copyString("MediaBox"), mediaBoxObj); + if (cropBox != NULL) { + Object *cropBoxObj = new Object(); + cropBoxObj->initArray(getXRef()); + Object *curx = new Object(); + curx->initReal(cropBox->x1); + Object *cury = new Object(); + cury->initReal(cropBox->y1); + Object *cllx = new Object(); + cllx->initReal(cropBox->x2); + Object *clly = new Object(); + clly->initReal(cropBox->y2); + cropBoxObj->arrayAdd(curx); + cropBoxObj->arrayAdd(cury); + cropBoxObj->arrayAdd(cllx); + cropBoxObj->arrayAdd(clly); + pageDict->add(copyString("CropBox"), cropBoxObj); + } + Object *rotateObj = new Object(); + rotateObj->initInt(rotate); + pageDict->add(copyString("Rotate"), rotateObj); + if (pageCTM != NULL) { + Object *contents = new Object(); + Ref cmRef = getXRef()->addIndirectObject(pageCTM); + Object *ref = new Object(); + ref->initRef(cmRef.num, cmRef.gen); + pageDict->lookupNF("Contents", contents); + Object *newContents = new Object(); + newContents->initArray(getXRef()); + if (contents->getType() == objRef) { + newContents->arrayAdd(ref); + newContents->arrayAdd(contents); + } else { + newContents->arrayAdd(ref); + for (int i = 0; i < contents->arrayGetLength(); i++) { + Object *contentEle = new Object(); + contents->arrayGetNF(i, contentEle); + newContents->arrayAdd(contentEle); + } + } + pageDict->remove("Contents"); + pageDict->add(copyString("Contents"), newContents); + } + getXRef()->setModifiedObject(&page, *refPage); + page.free(); +} + +void PDFDoc::markPageObjects(Dict *pageDict, XRef *xRef, XRef *countRef, Guint numOffset) +{ + for (int n = 0; n < pageDict->getLength(); n++) { + const char *key = pageDict->getKey(n); + Object value; pageDict->getValNF(n, &value); + if (strcmp(key, "Parent") != 0) { + markObject(&value, xRef, countRef, numOffset); + } + value.free(); + } +} + +Guint PDFDoc::writePageObjects(OutStream *outStr, XRef *xRef, Guint numOffset) +{ + Guint objectsCount = 0; //count the number of objects in the XRef(s) + + for (int n = numOffset; n < xRef->getNumObjects(); n++) { + if (xRef->getEntry(n)->type != xrefEntryFree) { + Object obj; + Ref ref; + ref.num = n; + ref.gen = xRef->getEntry(n)->gen; + objectsCount++; + getXRef()->fetch(ref.num - numOffset, ref.gen, &obj); + Guint offset = writeObject(&obj, &ref, outStr, xRef, numOffset); + xRef->add(ref.num, ref.gen, offset, gTrue); + obj.free(); + } + } + return objectsCount; +} + +#ifndef DISABLE_OUTLINE +Outline *PDFDoc::getOutline() +{ + if (!outline) { + // read outline + outline = new Outline(catalog->getOutline(), xref); + } + + return outline; +} +#endif + +PDFDoc *PDFDoc::ErrorPDFDoc(int errorCode, GooString *fileNameA) +{ + PDFDoc *doc = new PDFDoc(); + doc->errCode = errorCode; + doc->fileName = fileNameA; + + return doc; +} + +Guint PDFDoc::strToUnsigned(char *s) { + Guint x; + char *p; + int i; + + x = 0; + for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) { + x = 10 * x + (*p - '0'); + } + return x; +} + +// Read the 'startxref' position. +Guint PDFDoc::getStartXRef() +{ + if (startXRefPos == ~(Guint)0) { + + if (isLinearized()) { + char buf[linearizationSearchSize+1]; + int c, n, i; + + str->setPos(0); + for (n = 0; n < linearizationSearchSize; ++n) { + if ((c = str->getChar()) == EOF) { + break; + } + buf[n] = c; + } + buf[n] = '\0'; + + // find end of first obj (linearization dictionary) + startXRefPos = 0; + for (i = 0; i < n; i++) { + if (!strncmp("endobj", &buf[i], 6)) { + i += 6; + //skip whitespace + while (buf[i] && Lexer::isSpace(buf[i])) ++i; + startXRefPos = i; + break; + } + } + } else { + char buf[xrefSearchSize+1]; + char *p; + int c, n, i; + + // read last xrefSearchSize bytes + str->setPos(xrefSearchSize, -1); + for (n = 0; n < xrefSearchSize; ++n) { + if ((c = str->getChar()) == EOF) { + break; + } + buf[n] = c; + } + buf[n] = '\0'; + + // find startxref + for (i = n - 9; i >= 0; --i) { + if (!strncmp(&buf[i], "startxref", 9)) { + break; + } + } + if (i < 0) { + startXRefPos = 0; + } else { + for (p = &buf[i+9]; isspace(*p); ++p) ; + startXRefPos = strToUnsigned(p); + } + } + + } + + return startXRefPos; +} + +Guint PDFDoc::getMainXRefEntriesOffset() +{ + Guint mainXRefEntriesOffset = 0; + + if (isLinearized()) { + mainXRefEntriesOffset = getLinearization()->getMainXRefEntriesOffset(); + } + + return mainXRefEntriesOffset; +} + +int PDFDoc::getNumPages() +{ + if (isLinearized()) { + int n; + if ((n = getLinearization()->getNumPages())) { + return n; + } + } + + return catalog->getNumPages(); +} + +Page *PDFDoc::parsePage(int page) +{ + Page *p = NULL; + Object obj; + Ref pageRef; + Dict *pageDict; + + pageRef.num = getHints()->getPageObjectNum(page); + if (!pageRef.num) { + error(-1, "Failed to get object num from hint tables for page %d", page); + return NULL; + } + + // check for bogus ref - this can happen in corrupted PDF files + if (pageRef.num < 0 || pageRef.num >= xref->getNumObjects()) { + error(-1, "Invalid object num (%d) for page %d", pageRef.num, page); + return NULL; + } + + pageRef.gen = xref->getEntry(pageRef.num)->gen; + xref->fetch(pageRef.num, pageRef.gen, &obj); + if (!obj.isDict("Page")) { + obj.free(); + error(-1, "Object (%d %d) is not a pageDict", pageRef.num, pageRef.gen); + return NULL; + } + pageDict = obj.getDict(); + + p = new Page(xref, page, pageDict, pageRef, + new PageAttrs(NULL, pageDict), catalog->getForm()); + obj.free(); + + return p; +} + +Page *PDFDoc::getPage(int page) +{ + if ((page < 1) || page > getNumPages()) return NULL; + + if (isLinearized()) { + if (!pageCache) { + pageCache = (Page **) gmallocn(getNumPages(), sizeof(Page *)); + for (int i = 0; i < getNumPages(); i++) { + pageCache[i] = NULL; + } + } + if (!pageCache[page-1]) { + pageCache[page-1] = parsePage(page); + } + if (pageCache[page-1]) { + return pageCache[page-1]; + } else { + error(-1, "Failed parsing page %d using hint tables", page); + } + } + + return catalog->getPage(page); +} |