[poppler] 2 commits - poppler/Annot.cc poppler/Annot.h poppler/Page.cc poppler/Page.h

Albert Astals Cid aacid at kemper.freedesktop.org
Wed Mar 28 14:54:12 PDT 2012


 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 <fabiodurso at hotmail.it>
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 <inigomartinez at gmail.com>
 // Copyright (C) 2008 Brad Hards <bradh at kde.org>
 // Copyright (C) 2008 Ilya Gorenbein <igorenbein at finjan.com>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso at hotmail.it>
 //
 // 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 <fabiodurso at hotmail.it>
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


More information about the poppler mailing list