poppler/Annot.cc | 207 ++++++++++++++++++++++++++++++++++++++++++------------- poppler/Annot.h | 30 ++++++- poppler/Page.cc | 36 +++++++++ poppler/Page.h | 3 4 files changed, 225 insertions(+), 51 deletions(-)
New commits: commit 631224dc0c721119c91984f1940c9e51edf17eca Author: Fabio D'Urso <[email protected]> Date: Tue Mar 20 00:56:50 2012 +0100 Annotation removal diff --git a/poppler/Annot.cc b/poppler/Annot.cc index 79add84..caa13c5 100644 --- a/poppler/Annot.cc +++ b/poppler/Annot.cc @@ -5726,6 +5726,23 @@ void Annots::appendAnnot(Annot *annot) { } } +GBool Annots::removeAnnot(Annot *annot) { + int idx = -1; + // Search annot and remove it by swapping with last element + for (int i = 0; idx == -1 && i < nAnnots; i++) { + if (annots[i] == annot) { + idx = i; + } + } + if (idx == -1) { + return gFalse; + } else { + annots[idx] = annots[--nAnnots]; + annot->decRefCnt(); + return gTrue; + } +} + Annot *Annots::createAnnot(Dict* dict, Object *obj) { Annot *annot = NULL; Object obj1; diff --git a/poppler/Annot.h b/poppler/Annot.h index b506df9..b196e51 100644 --- a/poppler/Annot.h +++ b/poppler/Annot.h @@ -1330,6 +1330,7 @@ public: int getNumAnnots() { return nAnnots; } Annot *getAnnot(int i) { return annots[i]; } void appendAnnot(Annot *annot); + GBool removeAnnot(Annot *annot); private: Annot* createAnnot(Dict* dict, Object *obj); diff --git a/poppler/Page.cc b/poppler/Page.cc index 48ed647..b9d25ff 100644 --- a/poppler/Page.cc +++ b/poppler/Page.cc @@ -24,6 +24,7 @@ // Copyright (C) 2008 Iñigo MartÃnez <[email protected]> // Copyright (C) 2008 Brad Hards <[email protected]> // Copyright (C) 2008 Ilya Gorenbein <[email protected]> +// Copyright (C) 2012 Fabio D'Urso <[email protected]> // // 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 @@ -394,6 +395,41 @@ void Page::addAnnot(Annot *annot) { annot->setPage(&pageRef, num); } +void Page::removeAnnot(Annot *annot) { + Ref annotRef = annot->getRef(); + Object annArray; + + getAnnots(&annArray); + if (annArray.isArray()) { + int idx = -1; + // Get annotation position + for (int i = 0; idx == -1 && i < annArray.arrayGetLength(); ++i) { + Object tmp; + Ref currAnnot = annArray.arrayGetNF(i, &tmp)->getRef(); + tmp.free(); + if (currAnnot.num == annotRef.num && currAnnot.gen == annotRef.gen) { + idx = i; + } + } + + if (idx == -1) { + error(errInternal, -1, "Annotation doesn't belong to this page"); + annArray.free(); + return; + } + annots->removeAnnot(annot); // Gracefully fails on popup windows + annArray.arrayRemove(idx); + xref->removeIndirectObject(annotRef); + + if (annotsObj.isRef()) { + xref->setModifiedObject (&annArray, annotsObj.getRef()); + } else { + xref->setModifiedObject (&pageObj, pageRef); + } + } + annArray.free(); +} + Links *Page::getLinks() { return new Links(getAnnots()); } diff --git a/poppler/Page.h b/poppler/Page.h index 70141d0..a1722a4 100644 --- a/poppler/Page.h +++ b/poppler/Page.h @@ -171,6 +171,9 @@ public: Object *getAnnots(Object *obj) { return annotsObj.fetch(xref, obj); } // Add a new annotation to the page void addAnnot(Annot *annot); + // Remove an existing annotation from the page + // Note: Caller is responsible for deleting popup and appearance streams too + void removeAnnot(Annot *annot); // Return a list of links. Links *getLinks(); commit 20476370a445a26f1fae9db6ad58727ee3c63550 Author: Fabio D'Urso <[email protected]> Date: Wed Mar 28 23:16:37 2012 +0200 Basic support for Annot appearance stream removal and invalidation diff --git a/poppler/Annot.cc b/poppler/Annot.cc index 405f5f6..79add84 100644 --- a/poppler/Annot.cc +++ b/poppler/Annot.cc @@ -776,6 +776,111 @@ AnnotIconFit::AnnotIconFit(Dict* dict) { } //------------------------------------------------------------------------ +// AnnotAppearance +//------------------------------------------------------------------------ + +AnnotAppearance::AnnotAppearance(PDFDoc *docA, Object *dict) { + assert(dict->isDict()); + xref = docA->getXRef(); + dict->copy(&appearDict); +} + +AnnotAppearance::~AnnotAppearance() { + appearDict.free(); +} + +void AnnotAppearance::getAppearanceStream(AnnotAppearance::AnnotAppearanceType type, const char *state, Object *dest) { + Object apData, stream; + apData.initNull(); + + // Obtain dictionary or stream associated to appearance type + if (type == appearRollover) { + appearDict.dictLookupNF("R", &apData); + } else if (type == appearDown) { + appearDict.dictLookupNF("D", &apData); + } + if (apData.isNull()) { // Normal appearance, also used as fallback + appearDict.dictLookupNF("N", &apData); + } + + // Search state if it's a subdictionary + if (apData.isDict() && state) { + Object obj1; + apData.dictLookupNF(state, &obj1); + apData.free(); + obj1.copy(&apData); + obj1.free(); + } + + dest->initNull(); + // Sanity check on the value we are about to return: it must be a ref to stream + if (apData.isRef()) { + apData.fetch(xref, &stream); + if (stream.isStream()) { + apData.copy(dest); + } else { + error(errSyntaxWarning, -1, "AP points to a non-stream object"); + } + stream.free(); + } + apData.free(); +} + +GooString * AnnotAppearance::getStateKey(int i) { + Object obj1; + GooString * res = NULL; + appearDict.dictLookupNF("N", &obj1); + if (obj1.isDict()) { + res = new GooString(obj1.dictGetKey(i)); + } + obj1.free(); + return res; +} + +int AnnotAppearance::getNumStates() { + Object obj1; + int res = 0; + appearDict.dictLookupNF("N", &obj1); + if (obj1.isDict()) { + res = obj1.dictGetLength(); + } + obj1.free(); + return res; +} + +// Removes stream if obj is a Ref, or removes pointed streams if obj is a Dict +void AnnotAppearance::removeStateStreams(Object *obj1) { + // TODO: Remove XObject resources, check for XObjects shared by multiple + // annotations, delete streams from name table (if any) + if (obj1->isRef()) { + xref->removeIndirectObject(obj1->getRef()); + } else if (obj1->isDict()) { + const int size = obj1->dictGetLength(); + for (int i = 0; i < size; ++i) { + Object obj2; + obj1->dictGetValNF(i, &obj2); + if (obj2.isRef()) { + xref->removeIndirectObject(obj2.getRef()); + } + obj2.free(); + } + } +} + +void AnnotAppearance::removeAllStreams() { + Object obj1; + appearDict.dictLookupNF("N", &obj1); + removeStateStreams(&obj1); + obj1.free(); + appearDict.dictLookupNF("R", &obj1); + removeStateStreams(&obj1); + obj1.free(); + appearDict.dictLookupNF("D", &obj1); + removeStateStreams(&obj1); + obj1.free(); +} + +//------------------------------------------------------------------------ // AnnotAppearanceCharacs //------------------------------------------------------------------------ @@ -912,13 +1017,12 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Object *obj) { } void Annot::initialize(PDFDoc *docA, Dict *dict) { - Object apObj, asObj, obj1, obj2, obj3; + Object apObj, asObj, obj1, obj2; - appRef.num = 0; - appRef.gen = 65535; ok = gTrue; doc = docA; xref = doc->getXRef(); + appearStreams = NULL; appearState = NULL; appearBuf = NULL; fontSize = 0; @@ -995,22 +1099,25 @@ void Annot::initialize(PDFDoc *docA, Dict *dict) { } obj1.free(); - //----- get the appearance state - + //----- get the annotation appearance dictionary dict->lookup("AP", &apObj); + if (apObj.isDict()) { + appearStreams = new AnnotAppearance(doc, &apObj); + } + apObj.free(); + + //----- get the appearance state dict->lookup("AS", &asObj); if (asObj.isName()) { appearState = new GooString(asObj.getName()); - } else if (apObj.isDict()) { - if (apObj.dictLookup("N", &obj1)->isDict()) { - error (errSyntaxError, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries"); - // AS value is required in this case, but if the - // N dictionary contains only one entry - // take it as default appearance. - if (obj1.dictGetLength() == 1) - appearState = new GooString(obj1.dictGetKey(0)); + } else if (appearStreams && appearStreams->getNumStates() != 0) { + error (errSyntaxError, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries"); + // AS value is required in this case, but if the + // N dictionary contains only one entry + // take it as default appearance. + if (appearStreams->getNumStates() == 1) { + appearState = appearStreams->getStateKey(0); } - obj1.free(); } if (!appearState) { appearState = new GooString("Off"); @@ -1018,22 +1125,9 @@ void Annot::initialize(PDFDoc *docA, Dict *dict) { asObj.free(); //----- get the annotation appearance - - if (apObj.isDict()) { - apObj.dictLookup("N", &obj1); - apObj.dictLookupNF("N", &obj2); - if (obj1.isDict()) { - if (obj1.dictLookupNF(appearState->getCString(), &obj3)->isRef()) { - obj3.copy(&appearance); - } - obj3.free(); - } else if (obj2.isRef()) { - obj2.copy(&appearance); - } - obj1.free(); - obj2.free(); + if (appearStreams) { + appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->getCString(), &appearance); } - apObj.free(); //----- parse the border style if (dict->lookup("BS", &obj1)->isDict()) { @@ -1218,9 +1312,6 @@ void Annot::setAppearanceState(const char *state) { if (!state) return; - if (appearState && appearState->cmp(state) == 0) - return; - delete appearState; appearState = new GooString(state); @@ -1229,23 +1320,27 @@ void Annot::setAppearanceState(const char *state) { update ("AS", &obj1); // The appearance state determines the current appearance stream - Object obj2; - if (annotObj.dictLookup("AP", &obj2)->isDict()) { - Object obj3; - - if (obj2.dictLookup("N", &obj3)->isDict()) { - Object obj4; + appearance.free(); + if (appearStreams) { + appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->getCString(), &appearance); + } else { + appearance.initNull(); + } +} - appearance.free(); - if (obj3.dictLookupNF(state, &obj4)->isRef()) - obj4.copy(&appearance); - else - appearance.initNull(); - obj4.free(); - } - obj3.free(); +void Annot::invalidateAppearance() { + if (appearStreams) { // Remove existing appearance streams + appearStreams->removeAllStreams(); } - obj2.free(); + delete appearStreams; + appearStreams = NULL; + + setAppearanceState("Off"); // Default appearance state + + Object obj1; + obj1.initNull(); + update ("AP", &obj1); // Remove AP + update ("AS", &obj1); // Remove AS } double Annot::getXMin() { @@ -1292,6 +1387,7 @@ Annot::~Annot() { if (modified) delete modified; + delete appearStreams; appearance.free(); if (appearState) diff --git a/poppler/Annot.h b/poppler/Annot.h index 8d40a8a..b506df9 100644 --- a/poppler/Annot.h +++ b/poppler/Annot.h @@ -368,7 +368,26 @@ public: appearDown }; - AnnotAppearance(Dict *dict); + AnnotAppearance(PDFDoc *docA, Object *dict); + ~AnnotAppearance(); + + // State is ignored if no subdictionary is present + void getAppearanceStream(AnnotAppearanceType type, const char *state, Object *dest); + + // Access keys in normal appearance subdictionary (N) + GooString * getStateKey(int i); + int getNumStates(); + + // Removes all associated streams in the xref table. Caller is required to + // reset parent annotation's AP and AS after this call. + void removeAllStreams(); + +private: + void removeStateStreams(Object *state); + +protected: + XRef *xref; // the xref table for this PDF file + Object appearDict; // Annotation's AP }; //------------------------------------------------------------------------ @@ -504,6 +523,9 @@ public: void setAppearanceState(const char *state); + // Delete appearance streams and reset appearance state + void invalidateAppearance(); + // getters PDFDoc *getDoc() const { return doc; } XRef *getXRef() const { return xref; } @@ -517,7 +539,7 @@ public: GooString *getName() const { return name; } GooString *getModified() const { return modified; } Guint getFlags() const { return flags; } - /*Dict *getAppearDict() const { return appearDict; }*/ + AnnotAppearance *getAppearStreams() const { return appearStreams; } GooString *getAppearState() const { return appearState; } AnnotBorder *getBorder() const { return border; } AnnotColor *getColor() const { return color; } @@ -564,8 +586,7 @@ protected: GooString *name; // NM GooString *modified; // M Guint flags; // F (must be a 32 bit unsigned int) - //Dict *appearDict; // AP (should be correctly parsed) - Ref appRef; //the reference to the indirect appearance object in XRef + AnnotAppearance *appearStreams; // AP Object appearance; // a reference to the Form XObject stream // for the normal appearance GooString *appearState; // AS
_______________________________________________ poppler mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/poppler
