I promised on IRC earlier today (or yesterday, UTC) that I'd send a
patch off with some work to make checkboxes use the appearance streams
provided in a PDF.
Widgets with variable text need to have their appearance streams
completely rewritten when modified. However, for checkboxes and radio
boxes, the document may come with appearance streams for all the states
the widget may be in. Nonetheless, the current code looks like it will
completely regenerate the appearance streams when the state of a
checkbox is changed. The attached patch tries to fix this by factoring
out the code that loads the appropriate appearance stream from the PDF
file, and re-executes it when the state of checkbox widget changes.
Please don't apply this patch as-is. If the general idea looks sound, I
will try to clean it up some. This patch also touches the core of the
annotation code again, so feedback would be good.
I saw mentioned on the mailing list the possibility of a merger of
FormWidget and AnnotWidget classes. If so, that might be useful work to
have, since it would be good to have the FormWidget code more directly
update the appearState field in the annotation (instead of currently,
where FormWidget saves the information into the annotation dictionary,
and the annotation code reads it back out).
I'm not in a big hurry with this patch--the original motivation for me
to start working on this was the checkbox problem I posted a short fix
for earlier; that short patch makes things work well enough for me for
now. I have another bug that I'd like to spend some time working on
first, but I'll come back to this later.
--Michael Vrable
[DRAFT] Split appearance stream loading out of annotation initialization.
Instead of finding the appropriate appearance stream for an annotation
directly in the Annot::initialize method, split it out into a separate
method (Annot::loadAppearance). This work is still done in initialization,
but can now also be re-done when necessary, such as when the appearance
state of a widget annotation changes as a result of interaction with the
document.
Previously, when a checkbox was modified, the appearance stream was
generated from scratch. Now, if there is an appropriate appearance stream
in the document, simply switch to displaying it. Still fall back to the
old behavior if an appearance stream does not exist.
---
poppler/Annot.cc | 95 ++++++++++++++++++++++++++++++++++-------------------
poppler/Annot.h | 1 +
2 files changed, 62 insertions(+), 34 deletions(-)
diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 2f7d8fc..4634d07 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -771,40 +771,8 @@ void Annot::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
}
obj1.free();
- if (dict->lookup("AP", &obj1)->isDict()) {
- Object obj2;
-
- if (dict->lookup("AS", &obj2)->isName()) {
- Object obj3;
-
- appearState = new GooString(obj2.getName());
- if (obj1.dictLookup("N", &obj3)->isDict()) {
- Object obj4;
-
- if (obj3.dictLookupNF(appearState->getCString(), &obj4)->isRef()) {
- obj4.copy(&appearance);
- } else {
- obj4.free();
- if (obj3.dictLookupNF("Off", &obj4)->isRef()) {
- obj4.copy(&appearance);
- }
- }
- obj4.free();
- }
- obj3.free();
- } else {
- obj2.free();
-
- appearState = NULL;
- if (obj1.dictLookupNF("N", &obj2)->isRef()) {
- obj2.copy(&appearance);
- }
- }
- obj2.free();
- } else {
- appearState = NULL;
- }
- obj1.free();
+ appearState = NULL;
+ loadAppearance(dict);
//----- parse the border style
if (dict->lookup("BS", &obj1)->isDict()) {
@@ -898,6 +866,56 @@ Annot::~Annot() {
delete optionalContent;
}
+// Look in the appearance dictionary for an appearance stream for the current
+// state of this annotation, and store it in the appearance member variable.
+// Returns gTrue if an appearance stream was found, and gFalse if none could be
+// found.
+GBool Annot::loadAppearance(Dict *dict)
+{
+ Object obj1, obj2, obj3;
+
+ appearance.free();
+
+ if (!dict->lookup("AP", &obj1)->isDict()) {
+ obj1.free();
+ return gFalse;
+ }
+
+ // TODO: Perhaps expect other code to update appearState, rather than
+ // re-reading it from the AS field in the annotation dictionary?
+ if (appearState)
+ delete appearState;
+ if (dict->lookup("AS", &obj2)->isName()) {
+ appearState = new GooString(obj2.getName());
+ } else {
+ appearState = NULL;
+ }
+ obj2.free();
+
+ // TODO: Support states other than "N"?
+ if (appearState) {
+ if (obj1.dictLookup("N", &obj2)->isDict()) {
+ if (obj2.dictLookupNF(appearState->getCString(), &obj3)->isRef()) {
+ obj3.copy(&appearance);
+ } else {
+ obj3.free();
+ if (obj2.dictLookupNF("Off", &obj3)->isRef()) {
+ obj3.copy(&appearance);
+ }
+ }
+ obj3.free();
+ }
+ } else {
+ if (obj1.dictLookupNF("N", &obj2)->isRef()) {
+ obj2.copy(&appearance);
+ }
+ }
+ obj2.free();
+ obj1.free();
+
+ return gTrue;
+}
+
// Set the current fill or stroke color, based on <a> (which should
// have 1, 3, or 4 elements). If <adjust> is +1, color is brightened;
// if <adjust> is -1, color is darkened; otherwise color is not
@@ -2591,6 +2609,15 @@ void AnnotWidget::generateFieldAppearance() {
return;
}
+ // Only annotations with variable text need to be regenerated from scratch if
+ // modified. For other types, try to simply re-read appropriate data from
+ // the appearances dictionary.
+ if (widget->getType() != formText && widget->getType() != formChoice) {
+ if (loadAppearance(annot)) {
+ return;
+ }
+ }
+
appearBuf = new GooString ();
// get the appearance characteristics (MK) dictionary
if (annot->lookup("MK", &mkObj)->isDict()) {
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 3098502..3755b40 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -477,6 +477,7 @@ private:
void initialize (XRef *xrefA, Dict *dict, Catalog *catalog);
protected:
+ GBool loadAppearance(Dict *dict);
void setColor(Array *a, GBool fill, int adjust);
void drawCircle(double cx, double cy, double r, GBool fill);
void drawCircleTopLeft(double cx, double cy, double r);
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler