? test/out.pdf ? test/pdf-fullrewrite Index: poppler/Decrypt.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/Decrypt.cc,v retrieving revision 1.3 diff -u -8 -p -r1.3 Decrypt.cc --- poppler/Decrypt.cc 16 Sep 2005 18:29:18 -0000 1.3 +++ poppler/Decrypt.cc 28 Feb 2007 10:50:00 -0000 @@ -13,17 +13,16 @@ #endif #include #include "goo/gmem.h" #include "Decrypt.h" static void rc4InitKey(Guchar *key, int keyLen, Guchar *state); static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c); -static void md5(Guchar *msg, int msgLen, Guchar *digest); static Guchar passwordPad[32] = { 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a }; @@ -263,17 +262,17 @@ static inline Gulong md5Round3(Gulong a, return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s); } static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d, Gulong Xk, Gulong s, Gulong Ti) { return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s); } -static void md5(Guchar *msg, int msgLen, Guchar *digest) { +void Decrypt::md5(Guchar *msg, int msgLen, Guchar *digest) { Gulong x[16]; Gulong a, b, c, d, aa, bb, cc, dd; int n64; int i, j, k; // compute number of 64-byte blocks // (length + pad byte (0x80) + 8 bytes for length) n64 = (msgLen + 1 + 8 + 63) / 64; Index: poppler/Decrypt.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/Decrypt.h,v retrieving revision 1.2 diff -u -8 -p -r1.2 Decrypt.h --- poppler/Decrypt.h 16 Sep 2005 18:29:18 -0000 1.2 +++ poppler/Decrypt.h 28 Feb 2007 10:50:00 -0000 @@ -27,16 +27,18 @@ public: Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen); // Reset decryption. void reset(); // Decrypt one byte. Guchar decryptByte(Guchar c); + static void md5(Guchar *msg, int msgLen, Guchar *digest); + // Generate a file key. The buffer must have space for at // least 16 bytes. Checks and then // and returns true if either is correct. Sets if // the owner password was correct. Either or both of the passwords // may be NULL, which is treated as an empty string. static GBool makeFileKey(int encVersion, int encRevision, int keyLength, GooString *ownerKey, GooString *userKey, int permissions, GooString *fileID, Index: poppler/PDFDoc.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/PDFDoc.cc,v retrieving revision 1.10 diff -u -8 -p -r1.10 PDFDoc.cc --- poppler/PDFDoc.cc 18 Jan 2006 22:32:13 -0000 1.10 +++ poppler/PDFDoc.cc 28 Feb 2007 10:50:01 -0000 @@ -29,16 +29,17 @@ #include "XRef.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 "UGooString.h" //------------------------------------------------------------------------ @@ -456,31 +457,395 @@ GBool PDFDoc::isLinearized() { obj4.free(); obj3.free(); obj2.free(); obj1.free(); delete parser; return lin; } -GBool PDFDoc::saveAs(GooString *name) { +GBool PDFDoc::saveAs(GooString *name, PDFWriteMode mode) { FILE *f; - int c; if (!(f = fopen(name->getCString(), "wb"))) { error(-1, "Couldn't open file '%s'", name->getCString()); return gFalse; } + + if (mode == writeForceRewrite) { + saveCompleteRewrite(f); + } else if (mode == writeForceIncremental) { + saveIncrementalUpdate(f); + } else { // let poppler decide + // find if we have updated objects + GBool updated = gFalse; + for(int i=0; igetNumObjects(); i++) { + if (xref->getEntry(i)->obj) { + updated = gTrue; + break; + } + } + if(updated) { + saveIncrementalUpdate(f); + } else { + // simply copy the original file + int c; + str->reset(); + while ((c = str->getChar()) != EOF) { + fputc(c, f); + } + str->close(); + } + } + + fclose(f); + return gTrue; +} + +void PDFDoc::saveIncrementalUpdate (FILE *f) +{ + XRef *uxref; + int c; + //copy the original file str->reset(); while ((c = str->getChar()) != EOF) { fputc(c, f); } str->close(); - fclose(f); - return gTrue; + + uxref = new XRef(); + uxref->add(0, 65535, 0, gFalse); + int objectsCount = 0; //count the number of objects in the XRef(s) + for(int i=0; igetNumObjects(); i++) { + if ((xref->getEntry(i)->type == xrefEntryFree) && + (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects + continue; + objectsCount++; + if (xref->getEntry(i)->obj) { //we have an updated object + Object obj1; + Ref ref; + ref.num = i; + ref.gen = xref->getEntry(i)->gen; + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, f); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } + } + if (uxref->getSize() == 0) { //we have nothing to update + delete uxref; + return; + } + + Guint uxrefOffset = ftell(f); + uxref->writeToFile(f); + + writeTrailer(uxrefOffset, objectsCount, f, gTrue); + + delete uxref; +} + +void PDFDoc::saveCompleteRewrite (FILE *f) +{ + fprintf(f, "%%PDF-%.1f\r\n",pdfVersion); + XRef *uxref = new XRef(); + uxref->add(0, 65535, 0, gFalse); + for(int i=0; igetNumObjects(); 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 add 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, f); + 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, f); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } + } + Guint uxrefOffset = ftell(f); + uxref->writeToFile(f); + + writeTrailer(uxrefOffset, uxref->getSize(), f, gFalse); + + + delete uxref; + +} + +void PDFDoc::writeDictionnary (Dict* dict, FILE *f) +{ + Object obj1; + fprintf(f,"<<"); + for (int i=0; igetLength(); i++) { + fprintf(f,"/%s ", dict->getKey(i)->getCString()); + writeObject(dict->getValNF(i, &obj1), NULL, f); + obj1.free(); + } + fprintf(f, ">>"); +} + +void PDFDoc::writeStream (Stream* str, FILE *f) +{ + int c; + fprintf(f,"stream\r\n"); + str->reset(); + for (int c=str->getChar(); c!= EOF; c=str->getChar()) { + fprintf(f,"%c", c); + } + fprintf(f,"\r\nendstream\r\n"); +} + +void PDFDoc::writeRawStream (Stream* str, FILE *f) +{ + 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(); + + fprintf(f,"stream\r\n"); + str->unfilteredReset(); + for (int i=0; igetUnfilteredChar(); + fprintf(f,"%c", c); + } + str->reset(); + fprintf(f,"\r\nendstream\r\n"); +} + +void PDFDoc::writeString (GooString* s, FILE* f) +{ + if (s->hasUnicodeMarker()) { + //unicode string don't necessary end with \0 + const char* c = s->getCString(); + fprintf(f, "("); + for(int i=0; igetLength(); i++) { + char unescaped = *(c+i)&0x000000ff; + //escape if needed + if (unescaped == '(' || unescaped == ')' || unescaped == '\\') + fprintf(f, "%c", '\\'); + fprintf(f, "%c", unescaped); + } + fprintf(f, ") "); + } else { + const char* c = s->getCString(); + fprintf(f, "("); + while(*c!='\0') { + char unescaped = (*c)&0x000000ff; + //escape if needed + if (unescaped == '(' || unescaped == ')' || unescaped == '\\') + fprintf(f, "%c", '\\'); + fprintf(f, "%c", unescaped); + c++; + } + fprintf(f, ") "); + } +} + +Guint PDFDoc::writeObject (Object* obj, Ref* ref, FILE *f) +{ + Array *array; + Object obj1; + Guint offset = ftell(f); + int tmp; + + if(ref) + fprintf(f,"%i %i obj", ref->num, ref->gen); + + switch (obj->getType()) { + case objBool: + fprintf(f,"%s ", obj->getBool()?"true":"false"); + break; + case objInt: + fprintf(f,"%i ", obj->getInt()); + break; + case objReal: + fprintf(f,"%g ", obj->getReal()); + break; + case objString: + writeString(obj->getString(), f); + break; + case objName: + fprintf(f,"/%s ", obj->getName()); + break; + case objNull: + fprintf(f, "null"); + break; + case objArray: + array = obj->getArray(); + fprintf(f,"["); + for (int i=0; igetLength(); i++) { + writeObject(array->getNF(i, &obj1), NULL,f); + obj1.free(); + } + fprintf(f,"]"); + break; + case objDict: + writeDictionnary (obj->getDict(),f); + break; + case objStream: + { + //We can't modify stream with the current implementation (no write functions in Stream API) + // => the only type of streams for which we can't copy the raw data 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(),f); + writeStream (stream,f); + obj1.free(); + } else { + //raw stream copy + writeDictionnary (stream->getDict(), f); + writeRawStream (stream, f); + } + break; + } + case objRef: + fprintf(f,"%i %i R ", obj->getRef().num, obj->getRef().gen); + break; + case objCmd: + fprintf(f,"cmd\r\n"); + break; + case objError: + fprintf(f,"error\r\n"); + break; + case objEOF: + fprintf(f,"eof\r\n"); + break; + case objNone: + fprintf(f,"none\r\n"); + break; + default: + error(-1,"Unhandled objType : %i, please report a bug with a testcase\r\n", obj->getType()); + break; + } + if (ref) + fprintf(f,"endobj\r\n"); + return offset; +} + +void PDFDoc::writeTrailer (Guint uxrefOffset, int uxrefSize, FILE* f, GBool incrUpdate) +{ + 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); + // file size + unsigned int fileSize = 0; + int c; + str->reset(); + while ((c = str->getChar()) != EOF) { + fileSize++; + } + str->close(); + sprintf(buffer, "%i", fileSize); + message.append(buffer); + + //info dict -- only use text string + if (xref->getDocInfo(&obj1)->isDict()) { + for(int i=0; igetLength(); 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; + obj2.initArray(xref); + + if (incrUpdate) { + //only update the second part of the array + if(xref->getTrailerDict()->getDict()->lookup("ID", &obj1) != NULL) { + if (!obj1.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 + obj1.arrayGet(0,&obj3); + obj2.arrayAdd(&obj3); + obj2.arrayAdd(&obj1); + trailerDict->set("ID", &obj2); + } + } + } else { + //new file => same values for the two identifiers + obj2.arrayAdd(&obj1); + obj2.arrayAdd(&obj1); + trailerDict->set("ID", &obj2); + } + + + obj1.initRef(xref->getRootNum(), xref->getRootGen()); + trailerDict->set("Root", &obj1); + obj1.free(); + + if (incrUpdate) { + obj1.initInt(xref->getLastXRefPos()); + trailerDict->set("Prev", &obj1); + obj1.free(); + } + fprintf(f, "trailer\r\n"); + writeDictionnary(trailerDict, f); + fprintf(f, "\r\nstartxref\r\n"); + fprintf(f, "%i\r\n", uxrefOffset); + fprintf(f, "%%%%EOF\r\n"); } void PDFDoc::getLinks(Page *page) { Object obj; links = new Links(page->getAnnots(&obj), catalog->getBaseURI()); obj.free(); } Index: poppler/PDFDoc.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/PDFDoc.h,v retrieving revision 1.7 diff -u -8 -p -r1.7 PDFDoc.h --- poppler/PDFDoc.h 18 Jan 2006 22:32:13 -0000 1.7 +++ poppler/PDFDoc.h 28 Feb 2007 10:50:01 -0000 @@ -22,16 +22,22 @@ class GooString; class BaseStream; class OutputDev; class Links; class LinkAction; class LinkDest; class Outline; +enum PDFWriteMode { + writeStandard, + writeForceRewrite, + writeForceIncremental +}; + //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ class PDFDoc { public: PDFDoc(GooString *fileNameA, GooString *ownerPassword = NULL, @@ -158,23 +164,32 @@ public: // Return the document's Info dictionary (if any). Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); } Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); } // Return the PDF version specified by the file. double getPDFVersion() { return pdfVersion; } - // Save this file with another name. - GBool saveAs(GooString *name); + // Save this file with another name + GBool saveAs(GooString *name, PDFWriteMode mode=writeStandard); // Return a pointer to the GUI (XPDFCore or WinPDFCore object). void *getGUIData() { return guiData; } private: + // Add object to current file stream and return the offset of the beginning of the object + Guint writeObject (Object *obj, Ref *ref, FILE* f); + void writeDictionnary (Dict* dict, FILE* f); + void writeStream (Stream* str, FILE* f); + void writeRawStream (Stream* str, FILE* f); + void writeTrailer (Guint uxrefOffset, int uxrefSize, FILE* f, GBool incrUpdate); + void writeString (GooString* s, FILE* f); + void saveIncrementalUpdate (FILE* f); + void saveCompleteRewrite (FILE* f); GBool setup(GooString *ownerPassword, GooString *userPassword); GBool checkFooter(); void checkHeader(); GBool checkEncryption(GooString *ownerPassword, GooString *userPassword); void getLinks(Page *page); GooString *fileName; Index: poppler/Stream.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/Stream.cc,v retrieving revision 1.15 diff -u -8 -p -r1.15 Stream.cc --- poppler/Stream.cc 13 Jan 2007 23:19:21 -0000 1.15 +++ poppler/Stream.cc 28 Feb 2007 10:50:04 -0000 @@ -627,16 +627,17 @@ void FileStream::reset() { #endif saved = gTrue; bufPtr = bufEnd = buf; bufPos = start; if (decrypt) decrypt->reset(); } + void FileStream::close() { if (saved) { #if HAVE_FSEEKO fseeko(f, savePos, SEEK_SET); #elif HAVE_FSEEK64 fseek64(f, savePos, SEEK_SET); #else fseek(f, savePos, SEEK_SET); @@ -1312,29 +1313,31 @@ CCITTFaxStream::CCITTFaxStream(Stream *s } CCITTFaxStream::~CCITTFaxStream() { delete str; gfree(refLine); gfree(codingLine); } -void CCITTFaxStream::reset() { - short code1; - +void CCITTFaxStream::unfilteredReset() { str->reset(); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = 0; codingLine[1] = refLine[2] = columns; a0 = 1; buf = EOF; +} +void CCITTFaxStream::reset() { + short code1; + unfilteredReset(); // skip any initial zero bits and end-of-line marker, and get the 2D // encoding tag while ((code1 = lookBits(12)) == 0) { eatBits(1); } if (code1 == 0x001) { eatBits(12); } @@ -1912,31 +1915,35 @@ DCTStream::~DCTStream() { for (i = 0; i < numComps; ++i) { for (j = 0; j < mcuHeight; ++j) { gfree(rowBuf[i][j]); } } } } -void DCTStream::reset() { - int i, j; - +void DCTStream::unfilteredReset() { str->reset(); progressive = interleaved = gFalse; width = height = 0; numComps = 0; numQuantTables = 0; numDCHuffTables = 0; numACHuffTables = 0; colorXform = 0; gotJFIFMarker = gFalse; gotAdobeMarker = gFalse; restartInterval = 0; +} + +void DCTStream::reset() { + int i, j; + + unfilteredReset(); if (!readHeader()) { y = height; return; } // compute MCU size if (numComps == 1) { @@ -3897,28 +3904,32 @@ FlateStream::~FlateStream() { gfree(distCodeTab.codes); } if (pred) { delete pred; } delete str; } -void FlateStream::reset() { - int cmf, flg; - +void FlateStream::unfilteredReset() { index = 0; remain = 0; codeBuf = 0; codeSize = 0; compressedBlock = gFalse; endOfBlock = gTrue; eof = gTrue; str->reset(); +} + +void FlateStream::reset() { + int cmf, flg; + + unfilteredReset(); // read header //~ need to look at window size? endOfBlock = eof = gTrue; cmf = str->getChar(); flg = str->getChar(); if (cmf == EOF || flg == EOF) return; Index: poppler/Stream.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/Stream.h,v retrieving revision 1.11 diff -u -8 -p -r1.11 Stream.h --- poppler/Stream.h 13 Jan 2007 23:19:21 -0000 1.11 +++ poppler/Stream.h 28 Feb 2007 10:50:04 -0000 @@ -74,16 +74,24 @@ public: // Peek at next char in stream. virtual int lookChar() = 0; // Get next char from stream without using the predictor. // This is only used by StreamPredictor. virtual int getRawChar(); + // Get next char directly from stream source, without filtering it + virtual int getUnfilteredChar () = 0; + + // Resets the stream without reading anything (even not the headers) + // WARNING: Reading the stream with something else than getUnfilteredChar + // may lead to unexcepted behaviour until you call reset () + virtual void unfilteredReset () = 0; + // Get next line from stream. virtual char *getLine(char *buf, int size); // Get current position in file. virtual int getPos() = 0; // Go to a position in the stream. If is negative, the // position is from the end of the file; otherwise the position is @@ -166,17 +174,19 @@ public: FilterStream(Stream *strA); virtual ~FilterStream(); virtual void close(); virtual int getPos() { return str->getPos(); } virtual void setPos(Guint pos, int dir = 0); virtual BaseStream *getBaseStream() { return str->getBaseStream(); } virtual Dict *getDict() { return str->getDict(); } - + + virtual int getUnfilteredChar () { return str->getUnfilteredChar(); } + virtual void unfilteredReset () { str->unfilteredReset(); } protected: Stream *str; }; //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ @@ -272,17 +282,19 @@ public: virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getPos() { return bufPos + (bufPtr - buf); } virtual void setPos(Guint pos, int dir = 0); virtual Guint getStart() { return start; } virtual void moveStart(int delta); - + + virtual int getUnfilteredChar () { return getChar(); } + virtual void unfilteredReset () { reset(); } private: GBool fillBuf(); FILE *f; Guint start; GBool limited; Guint length; @@ -314,16 +326,18 @@ public: { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } virtual int getPos() { return (int)(bufPtr - buf); } virtual void setPos(Guint pos, int dir = 0); virtual Guint getStart() { return start; } virtual void moveStart(int delta); virtual void doDecryption(Guchar *fileKey, int keyLength, int objNum, int objGen); + virtual int getUnfilteredChar () { return getChar(); } + virtual void unfilteredReset () { reset (); } private: char *buf; Guint start; Guint length; char *bufEnd; char *bufPtr; GBool needFree; @@ -350,16 +364,18 @@ public: virtual void reset() {} virtual int getChar(); virtual int lookChar(); virtual int getPos() { return str->getPos(); } virtual void setPos(Guint pos, int dir = 0); virtual Guint getStart(); virtual void moveStart(int delta); + virtual int getUnfilteredChar () { return str->getUnfilteredChar(); } + virtual void unfilteredReset () { str->unfilteredReset(); } private: Stream *str; GBool limited; Guint length; }; //------------------------------------------------------------------------ @@ -498,16 +514,17 @@ public: virtual StreamKind getKind() { return strCCITTFax; } virtual void reset(); virtual int getChar() { int c = lookChar(); buf = EOF; return c; } virtual int lookChar(); virtual GooString *getPSFilter(int psLevel, char *indent); virtual GBool isBinary(GBool last = gTrue); + virtual void unfilteredReset (); private: int encoding; // 'K' parameter GBool endOfLine; // 'EndOfLine' parameter GBool byteAlign; // 'EncodedByteAlign' parameter int columns; // 'Columns' parameter int rows; // 'Rows' parameter GBool endOfBlock; // 'EndOfBlock' parameter @@ -569,16 +586,17 @@ public: virtual ~DCTStream(); virtual StreamKind getKind() { return strDCT; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GooString *getPSFilter(int psLevel, char *indent); virtual GBool isBinary(GBool last = gTrue); Stream *getRawStream() { return str; } + virtual void unfilteredReset(); private: GBool progressive; // set if in progressive mode GBool interleaved; // set if in interleaved mode int width, height; // image size int mcuWidth, mcuHeight; // size of min coding unit, in data units int bufWidth, bufHeight; // frameBuf size @@ -672,16 +690,18 @@ public: virtual StreamKind getKind() { return strFlate; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual GooString *getPSFilter(int psLevel, char *indent); virtual GBool isBinary(GBool last = gTrue); + virtual void unfilteredReset (); + private: StreamPredictor *pred; // predictor Guchar buf[flateWindow]; // output data buffer int index; // current index into output buffer int remain; // number valid bytes in output buffer int codeBuf; // input buffer int codeSize; // number of bits in input buffer Index: test/Makefile.am =================================================================== RCS file: /cvs/poppler/poppler/test/Makefile.am,v retrieving revision 1.8 diff -u -8 -p -r1.8 Makefile.am --- test/Makefile.am 25 Sep 2006 20:43:18 -0000 1.8 +++ test/Makefile.am 28 Feb 2007 10:50:04 -0000 @@ -21,27 +21,34 @@ pdf_inspector = \ cairo_includes = \ $(CAIRO_CFLAGS) \ $(FREETYPE_CFLAGS) endif endif +pdf_fullrewrite = \ + pdf-fullrewrite INCLUDES = \ -I$(top_srcdir) \ -I$(top_srcdir)/poppler \ -I$(top_srcdir)/glib \ -I$(top_builddir)/glib \ $(cairo_includes) \ $(GTK_TEST_CFLAGS) \ $(FONTCONFIG_CFLAGS) -noinst_PROGRAMS = $(gtk_splash_test) $(gtk_cairo_test) $(pdf_inspector) +noinst_PROGRAMS = $(gtk_splash_test) $(gtk_cairo_test) $(pdf_inspector) $(pdf_fullrewrite) + +pdf_fullrewrite_SOURCES = \ + pdf-fullrewrite.cc +pdf_fullrewrite_LDADD = \ + $(top_builddir)/poppler/libpoppler.la gtk_splash_test_SOURCES = \ gtk-splash-test.cc gtk_splash_test_LDADD = \ $(top_builddir)/poppler/libpoppler.la \ $(GTK_TEST_LIBS) Index: test/pdf-fullrewrite.cc =================================================================== RCS file: test/pdf-fullrewrite.cc diff -N test/pdf-fullrewrite.cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/pdf-fullrewrite.cc 28 Feb 2007 10:50:05 -0000 @@ -0,0 +1,45 @@ +//======================================================================== +// +// pdf-fullrewrite.cc +// +// Copyright 2007 Julien Rebetez +// +//======================================================================== +#include "config.h" +#include +#include "GlobalParams.h" +#include "Error.h" +#include "PDFDoc.h" +#include "goo/GooString.h" + +int main (int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *inputName, *outputName; + GBool ok; + + // parse args + if (argc < 3) { + fprintf(stderr, "usage: %s INPUT-FILE OUTPUT-FILE\n", argv[0]); + return 1; + } + + inputName = new GooString(argv[1]); + outputName = new GooString(argv[2]); + + globalParams = new GlobalParams(); + + doc = new PDFDoc(inputName); + + if (!doc->isOk()) { + delete doc; + fprintf(stderr, "Error loading document !\n"); + return 1; + } + + + doc->saveAs(outputName, writeForceRewrite); + + delete doc; + delete globalParams; +}