summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorFabio D'Urso <fabiodurso@hotmail.it>2012-08-09 13:18:22 +0200
committerAlbert Astals Cid <aacid@kde.org>2012-09-06 22:06:48 +0200
commitbe88963a5955ac033e7a7d224bdcc4049085a9dc (patch)
treeeea2e3d69facf13655952d2bdf8a6487de451a26 /test
parent381be58e9e0d0e323acbd975a2334eca6d9018fd (diff)
pdf-fullrewrite: Added support for encrypted documents, checks on output documents, incremental update mode
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/Makefile.am3
-rw-r--r--test/pdf-fullrewrite.cc354
3 files changed, 342 insertions, 16 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 178b15e1..a89a4cfb 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,7 @@ endif (GTK_FOUND)
set (pdf_fullrewrite_SRCS
pdf-fullrewrite.cc
+ ../utils/parseargs.cc
)
add_executable(pdf-fullrewrite ${pdf_fullrewrite_SRCS})
target_link_libraries(pdf-fullrewrite poppler)
diff --git a/test/Makefile.am b/test/Makefile.am
index 619d6718..b3289c72 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -76,7 +76,8 @@ perf_test_LDADD = \
$(X_EXTRA_LIBS)
pdf_fullrewrite_SOURCES = \
- pdf-fullrewrite.cc
+ pdf-fullrewrite.cc \
+ ../utils/parseargs.cc
pdf_fullrewrite_LDADD = \
$(top_builddir)/poppler/libpoppler.la
diff --git a/test/pdf-fullrewrite.cc b/test/pdf-fullrewrite.cc
index 9658e55a..2b912f7e 100644
--- a/test/pdf-fullrewrite.cc
+++ b/test/pdf-fullrewrite.cc
@@ -3,44 +3,368 @@
// pdf-fullrewrite.cc
//
// Copyright 2007 Julien Rebetez
+// Copyright 2012 Fabio D'Urso
//
//========================================================================
-#include "config.h"
-#include <poppler-config.h>
+
#include "GlobalParams.h"
#include "Error.h"
+#include "Object.h"
#include "PDFDoc.h"
+#include "XRef.h"
#include "goo/GooString.h"
+#include "utils/parseargs.h"
+
+static GBool compareDocuments(PDFDoc *origDoc, PDFDoc *newDoc);
+static GBool compareObjects(Object *objA, Object *objB);
+
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool forceIncremental = gFalse;
+static GBool checkOutput = gFalse;
+static GBool printHelp = gFalse;
+
+static const ArgDesc argDesc[] = {
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-i", argFlag, &forceIncremental,0,
+ "incremental update mode"},
+ {"-check", argFlag, &checkOutput, 0,
+ "verify the generated document"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
int main (int argc, char *argv[])
{
- PDFDoc *doc;
- GooString *inputName, *outputName;
+ PDFDoc *doc = NULL;
+ PDFDoc *docOut = NULL;
+ GooString *inputName = NULL;
+ GooString *outputName = NULL;
+ GooString *ownerPW = NULL;
+ GooString *userPW = NULL;
+ int res = 0;
// parse args
- if (argc < 3) {
- fprintf(stderr, "usage: %s INPUT-FILE OUTPUT-FILE\n", argv[0]);
- return 1;
+ GBool ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || (argc < 3) || printHelp) {
+ printUsage(argv[0], "INPUT-FILE OUTPUT-FILE", argDesc);
+ if (!printHelp) {
+ res = 1;
+ }
+ goto done;
}
inputName = new GooString(argv[1]);
outputName = new GooString(argv[2]);
- globalParams = new GlobalParams();
-
- doc = new PDFDoc(inputName);
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GooString(ownerPassword);
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GooString(userPassword);
+ }
+ // load input document
+ globalParams = new GlobalParams();
+ doc = new PDFDoc(inputName, ownerPW, userPW);
if (!doc->isOk()) {
- delete doc;
- fprintf(stderr, "Error loading document !\n");
- return 1;
+ fprintf(stderr, "Error loading input document\n");
+ res = 1;
+ goto done;
}
+ // save it back (in rewrite or incremental update mode)
+ if (doc->saveAs(outputName, forceIncremental ? writeForceIncremental : writeForceRewrite) != 0) {
+ fprintf(stderr, "Error saving document\n");
+ res = 1;
+ goto done;
+ }
- int res = doc->saveAs(outputName, writeForceRewrite);
+ if (checkOutput) {
+ // open the generated document to verify it
+ docOut = new PDFDoc(outputName, ownerPW, userPW);
+ if (!docOut->isOk()) {
+ fprintf(stderr, "Error loading generated document\n");
+ res = 1;
+ } else if (!compareDocuments(doc, docOut)) {
+ fprintf(stderr, "Verification failed\n");
+ res = 1;
+ }
+ } else {
+ delete outputName;
+ }
+done:
+ delete docOut;
delete doc;
delete globalParams;
- delete outputName;
+ delete userPW;
+ delete ownerPW;
return res;
}
+
+static GBool compareDictionaries(Dict *dictA, Dict *dictB)
+{
+ const int length = dictA->getLength();
+ if (dictB->getLength() != length)
+ return gFalse;
+
+ /* Check that every key in dictA is contained in dictB.
+ * Since keys are unique and we've already checked that dictA and dictB
+ * contain the same number of entries, we don't need to check that every key
+ * in dictB is also contained in dictA */
+ for (int i = 0; i < length; ++i) {
+ Object valA, valB;
+ const char *key = dictA->getKey(i);
+ dictA->getValNF(i, &valA);
+ dictB->lookupNF(key, &valB);
+ if (!compareObjects(&valA, &valB))
+ return gFalse;
+ valA.free();
+ valB.free();
+ }
+
+ return gTrue;
+}
+
+static GBool compareObjects(Object *objA, Object *objB)
+{
+ switch (objA->getType()) {
+ case objBool:
+ {
+ if (objB->getType() != objBool) {
+ return gFalse;
+ } else {
+ return (objA->getBool() == objB->getBool());
+ }
+ }
+ case objInt:
+ case objReal:
+ {
+ if (!objB->isNum()) {
+ return gFalse;
+ } else {
+ // Fuzzy comparison
+ const double diff = objA->getNum() - objB->getNum();
+ return (-0.01 < diff) && (diff < 0.01);
+ }
+ }
+ case objUint:
+ {
+ if (objB->getType() != objUint) {
+ return gFalse;
+ } else {
+ return (objA->getUint() == objB->getUint());
+ }
+ }
+ case objString:
+ {
+ if (objB->getType() != objString) {
+ return gFalse;
+ } else {
+ GooString *strA = objA->getString();
+ GooString *strB = objB->getString();
+ return (strA->cmp(strB) == 0);
+ }
+ }
+ case objName:
+ {
+ if (objB->getType() != objName) {
+ return gFalse;
+ } else {
+ GooString nameA(objA->getName());
+ GooString nameB(objB->getName());
+ return (nameA.cmp(&nameB) == 0);
+ }
+ }
+ case objNull:
+ {
+ if (objB->getType() != objNull) {
+ return gFalse;
+ } else {
+ return gTrue;
+ }
+ }
+ case objArray:
+ {
+ if (objB->getType() != objArray) {
+ return gFalse;
+ } else {
+ Array *arrayA = objA->getArray();
+ Array *arrayB = objB->getArray();
+ const int length = arrayA->getLength();
+ if (arrayB->getLength() != length) {
+ return gFalse;
+ } else {
+ for (int i = 0; i < length; ++i) {
+ Object elemA, elemB;
+ arrayA->getNF(i, &elemA);
+ arrayB->getNF(i, &elemB);
+ if (!compareObjects(&elemA, &elemB)) {
+ return gFalse;
+ }
+ elemA.free();
+ elemB.free();
+ }
+ return gTrue;
+ }
+ }
+ }
+ case objDict:
+ {
+ if (objB->getType() != objDict) {
+ return gFalse;
+ } else {
+ Dict *dictA = objA->getDict();
+ Dict *dictB = objB->getDict();
+ return compareDictionaries(dictA, dictB);
+ }
+ }
+ case objStream:
+ {
+ if (objB->getType() != objStream) {
+ return gFalse;
+ } else {
+ Stream *streamA = objA->getStream();
+ Stream *streamB = objB->getStream();
+ if (!compareDictionaries(streamA->getDict(), streamB->getDict())) {
+ return gFalse;
+ } else {
+ int c;
+ streamA->reset();
+ streamB->reset();
+ do
+ {
+ c = streamA->getChar();
+ if (c != streamB->getChar()) {
+ return gFalse;
+ }
+ } while (c != EOF);
+ return gTrue;
+ }
+ }
+ return gTrue;
+ }
+ case objRef:
+ {
+ if (objB->getType() != objRef) {
+ return gFalse;
+ } else {
+ Ref refA = objA->getRef();
+ Ref refB = objB->getRef();
+ return (refA.num == refB.num) && (refA.gen == refB.gen);
+ }
+ }
+ default:
+ {
+ fprintf(stderr, "compareObjects failed: unexpected object type %u\n", objA->getType());
+ return gFalse;
+ }
+ }
+}
+
+static GBool compareDocuments(PDFDoc *origDoc, PDFDoc *newDoc)
+{
+ GBool result = gTrue;
+ XRef *origXRef = origDoc->getXRef();
+ XRef *newXRef = newDoc->getXRef();
+
+ // Make sure that special flags are set in both documents
+ origXRef->scanSpecialFlags();
+ newXRef->scanSpecialFlags();
+
+ // Compare XRef tables' size
+ const int origNumObjects = origXRef->getNumObjects();
+ const int newNumObjects = newXRef->getNumObjects();
+ if (forceIncremental && origXRef->isXRefStream()) {
+ // In case of incremental update, expect a new entry to be appended to store the new XRef stream
+ if (origNumObjects+1 != newNumObjects) {
+ fprintf(stderr, "XRef table: Unexpected number of entries (%d+1 != %d)\n", origNumObjects, newNumObjects);
+ result = gFalse;
+ }
+ } else {
+ // In all other cases the number of entries must be the same
+ if (origNumObjects != newNumObjects) {
+ fprintf(stderr, "XRef table: Different number of entries (%d != %d)\n", origNumObjects, newNumObjects);
+ result = gFalse;
+ }
+ }
+
+ // Compare each XRef entry
+ const int numObjects = (origNumObjects < newNumObjects) ? origNumObjects : newNumObjects;
+ for (int i = 0; i < numObjects; ++i) {
+ XRefEntryType origType = origXRef->getEntry(i)->type;
+ XRefEntryType newType = newXRef->getEntry(i)->type;
+ const int origGenNum = (origType != xrefEntryCompressed) ? origXRef->getEntry(i)->gen : 0;
+ const int newGenNum = (newType != xrefEntryCompressed) ? newXRef->getEntry(i)->gen : 0;
+
+ // Check that DontRewrite entries are freed in full rewrite mode
+ if (!forceIncremental && origXRef->getEntry(i)->getFlag(XRefEntry::DontRewrite)) {
+ if (newType != xrefEntryFree || origGenNum+1 != newGenNum) {
+ fprintf(stderr, "XRef entry %u: DontRewrite entry was not freed correctly\n", i);
+ result = gFalse;
+ }
+ continue; // There's nothing left to check for this entry
+ }
+
+ // Compare generation numbers
+ // Object num 0 should always have gen 65535 according to specs, but some
+ // documents have it set to 0. We always write 65535 in output
+ if (i != 0) {
+ if (origGenNum != newGenNum) {
+ fprintf(stderr, "XRef entry %u: generation numbers differ (%d != %d)\n", i, origGenNum, newGenNum);
+ result = gFalse;
+ continue;
+ }
+ } else {
+ if (newGenNum != 65535) {
+ fprintf(stderr, "XRef entry %u: generation number was expected to be 65535 (%d != 65535)\n", i, newGenNum);
+ result = gFalse;
+ continue;
+ }
+ }
+
+ // Compare object flags. A failure shows that there's some error in XRef::scanSpecialFlags()
+ if (origXRef->getEntry(i)->flags != newXRef->getEntry(i)->flags) {
+ fprintf(stderr, "XRef entry %u: flags detected by scanSpecialFlags differ (%d != %d)\n", i, origXRef->getEntry(i)->flags, newXRef->getEntry(i)->flags);
+ result = gFalse;
+ }
+
+ // Check that either both are free or both are in use
+ if ((origType == xrefEntryFree) != (newType == xrefEntryFree)) {
+ const char *origStatus = (origType == xrefEntryFree) ? "free" : "in use";
+ const char *newStatus = (newType == xrefEntryFree) ? "free" : "in use";
+ fprintf(stderr, "XRef entry %u: usage status differs (%s != %s)\n", i, origStatus, newStatus);
+ result = gFalse;
+ continue;
+ }
+
+ // Skip free entries
+ if (origType == xrefEntryFree) {
+ continue;
+ }
+
+ // Compare contents
+ Object origObj, newObj;
+ origXRef->fetch(i, origGenNum, &origObj);
+ newXRef->fetch(i, newGenNum, &newObj);
+ if (!compareObjects(&origObj, &newObj)) {
+ fprintf(stderr, "XRef entry %u: contents differ\n", i);
+ result = gFalse;
+ }
+ origObj.free();
+ newObj.free();
+ }
+
+ return result;
+}