[poppler] 3 commits - poppler/Annot.cc poppler/Annot.h poppler/Form.cc poppler/Form.h poppler/Page.cc

Carlos Garcia Campos carlosgc at kemper.freedesktop.org
Mon Feb 11 12:59:12 PST 2008


 poppler/Annot.cc | 2345 ++++++++++++++++++++++++++-----------------------------
 poppler/Annot.h  |   87 +-
 poppler/Form.cc  |  209 ++--
 poppler/Form.h   |   45 -
 poppler/Page.cc  |    9 
 5 files changed, 1337 insertions(+), 1358 deletions(-)

New commits:
commit e0eff92c7067d43faa8e93baed1f061863111251
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Mon Feb 11 20:32:41 2008 +0100

    Update the field dict instead of the annot dict for non composed dicts
    
    We were always updating the annot dictionary, adding or updating
    the V field. While this is correct for composed dicts (annot + field)
    it isn't for non composed ones since the annot dictionary doesn't
    contain any V field because it's a field of the form field dictionary.
    In these cases the form field is not correctly updated and if the
    document is saved it will be wrong.

diff --git a/poppler/Form.cc b/poppler/Form.cc
index c60e127..f356477 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -119,7 +119,6 @@ FormWidget::FormWidget(FormWidget *dest)
 
   type = dest->type;
   field = dest->field;
-
 }
 
 FormWidget::~FormWidget()
@@ -143,6 +142,28 @@ void FormWidget::decodeID (unsigned id, unsigned* pageNum, unsigned* fieldNum)
   *fieldNum = (id << 4*sizeof(unsigned)) >> 4*sizeof(unsigned);
 }
 
+void FormWidget::updateField (const char *key, Object *value)
+{
+  Object *obj1;
+  Ref ref1;
+  Object obj2;
+
+  if (obj.getDict()->lookup ("FT", &obj2)->isName ()) {
+    // It's a composed (annot + field) dict
+    obj1 = &obj;
+    ref1 = ref;
+  } else {
+    // It's an annot dict, we have to modify the Field (parent) dict
+    obj1 = field->getObj ();
+    ref1 = field->getRef ();
+  }
+  obj2.free ();
+
+  obj1->getDict ()->set ("V", value);
+  //notify the xref about the update
+  xref->setModifiedObject(obj1, ref1);
+}
+
 FormWidgetButton::FormWidgetButton (XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormField *p) :
 	FormWidget(xrefA, aobj, num, ref, p)
 {
@@ -178,15 +199,16 @@ void FormWidgetButton::setState (GBool astate, GBool calledByParent)
     }
   }
   state = astate;
+  
   //update appearance
   char *offStr = "Off";
   Object obj1;
   obj1.initName(state?getOnStr():offStr);
-  obj.getDict()->set("V", &obj1);
+  updateField ("V", &obj1);
+
   obj1.initName(state?getOnStr():offStr);
   //modify the Appearance State entry as well
   obj.getDict()->set("AS", &obj1);
-
   //notify the xref about the update
   xref->setModifiedObject(&obj, ref);
 }
@@ -375,11 +397,10 @@ void FormWidgetText::setContent(GooString* new_content)
     
     GooString *cont = new GooString(new_content);
     parent->setContentCopy(cont);
+    
     Object obj1;
     obj1.initString(cont);
-    obj.getDict()->set("V", &obj1);
-    //notify the xref about the update
-    xref->setModifiedObject(&obj, ref);
+    updateField ("V", &obj1);
   }
 }
 
@@ -485,8 +506,7 @@ void FormWidgetChoice::loadDefaults ()
       parent->select(i);
   }
 #ifdef UPDATE_OPT
-  obj.getDict()->set("Opt", objOpt);
-  xref->setModifiedObject(&obj, ref); 
+  updateField ("Opt", objOpt);
 #endif
   delete [] tmpCurrentChoice;
 }
@@ -523,9 +543,7 @@ void FormWidgetChoice::_updateV ()
       }
     }
   }
-  obj.getDict()->set("V", &obj1);
-  //notify the xref about the update
-  xref->setModifiedObject(&obj, ref);
+  updateField ("V", &obj1);
   modified = gTrue;
 }
 
@@ -900,8 +918,8 @@ GBool FormFieldButton::setState (int num, GBool s)
         if (actChild->getOnStr()) {
           Object obj1;
           obj1.initName(actChild->getOnStr());
-          obj.getDict()->set("V", &obj1);
-          xref->setModifiedObject(&obj, ref);
+	  obj.getDict()->set("V", &obj1);
+	  xref->setModifiedObject(&obj, ref);
         }
       }
     } else {
diff --git a/poppler/Form.h b/poppler/Form.h
index 39938bb..64df280 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -92,6 +92,8 @@ protected:
   FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref, FormField *fieldA);
   FormWidget(FormWidget *dest);
 
+  void updateField (const char *key, Object *value);
+
   FormField* field;
   FormFieldType type;
   Object obj;
commit 905e6da9795fee94329022c0cafbc229055fd4f6
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Feb 10 19:22:50 2008 +0100

    Move forms specific draw methods from Annot to AnnotWidget class

diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 9e866aa..7dc8537 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -670,14 +670,14 @@ AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
 // Annot
 //------------------------------------------------------------------------
 
-Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog* catalog) {
+Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog) {
   hasRef = false;
   flags = flagUnknown;
   type = typeUnknown;
-  initialize (xrefA, acroForm, dict, catalog);
+  initialize (xrefA, dict, catalog);
 }
 
-Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog* catalog, Object *obj) {
+Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog, Object *obj) {
   if (obj->isRef()) {
     hasRef = gTrue;
     ref = obj->getRef();
@@ -686,10 +686,10 @@ Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog* catalog, Object *
   }
   flags = flagUnknown;
   type = typeUnknown;
-  initialize (xrefA, acroForm, dict, catalog);
+  initialize (xrefA, dict, catalog);
 }
 
-void Annot::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog) {
+void Annot::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
   Object apObj, asObj, obj1, obj2, obj3;
 
   appRef.num = 0;
@@ -698,15 +698,6 @@ void Annot::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog
   xref = xrefA;
   appearBuf = NULL;
   fontSize = 0;
-  widget = NULL;
-
-  //----- get the FormWidget
-  if (hasRef) {
-    Form *form = catalog->getForm ();
-    
-    if (form)
-      widget = form->findWidgetByRef (ref);
-  }
 
   //----- parse the rectangle
   rect = new PDFRectangle();
@@ -779,22 +770,6 @@ void Annot::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog
   }
   obj1.free();
 
-  // check if field apperances need to be regenerated
-  // Only text or choice fields needs to have appearance regenerated
-  // see section 8.6.2 "Variable Text" of PDFReference
-  regen = gFalse;
-  Form::fieldLookup(dict, "FT", &obj3);
-  if (obj3.isName("Tx") || obj3.isName("Ch")) {
-    if (acroForm) {
-      acroForm->lookup("NeedAppearances", &obj1);
-      if (obj1.isBool() && obj1.getBool()) {
-        regen = gTrue;
-      }
-      obj1.free();
-    }
-  }
-  obj3.free();
-
   if (dict->lookup("AP", &obj1)->isDict()) {
     Object obj2;
 
@@ -811,8 +786,7 @@ void Annot::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog
           obj4.free();
           if (obj3.dictLookupNF("Off", &obj4)->isRef()) {
             obj4.copy(&appearance);
-          } else
-            regen = gTrue;
+          }
         } 
         obj4.free();
       }
@@ -823,14 +797,11 @@ void Annot::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog
       appearState = NULL;
       if (obj1.dictLookupNF("N", &obj2)->isRef()) {
         obj2.copy(&appearance);
-      } else
-        regen = gTrue;
+      }
     }
     obj2.free();
   } else {
     appearState = NULL;
-    // If field doesn't have an AP we'll have to generate it
-    regen = gTrue;
   }
   obj1.free();
 
@@ -926,423 +897,6 @@ Annot::~Annot() {
     delete optionalContent;
 }
 
-void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {
-  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
-  Dict *mkDict;
-  MemStream *appearStream;
-  GfxFontDict *fontDict;
-  GBool hasCaption;
-  double w, dx, dy, r;
-  double *dash;
-  GooString *caption, *da;
-  GooString **text;
-  GBool *selection;
-  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
-  GBool modified;
-
-  // must be a Widget annotation
-  if (type != typeWidget) {
-    return;
-  }
-
-  // do not regenerate appearence if widget has not changed
-  if (widget && widget->isModified ()) {
-    modified = gTrue;
-  } else {
-    modified = gFalse;
-  }
-
-  // only regenerate when it doesn't have an AP or
-  // it already has an AP but widget has been modified
-  if (!regen && !modified) {
-    return;
-  }
-
-  appearBuf = new GooString ();
-  // get the appearance characteristics (MK) dictionary
-  if (annot->lookup("MK", &mkObj)->isDict()) {
-    mkDict = mkObj.getDict();
-  } else {
-    mkDict = NULL;
-  }
-  // draw the background
-  if (mkDict) {
-    if (mkDict->lookup("BG", &obj1)->isArray() &&
-        obj1.arrayGetLength() > 0) {
-      setColor(obj1.getArray(), gTrue, 0);
-      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
-          rect->x2 - rect->x1, rect->y2 - rect->y1);
-    }
-    obj1.free();
-  }
-
-  // get the field type
-  Form::fieldLookup(field, "FT", &ftObj);
-
-  // get the field flags (Ff) value
-  if (Form::fieldLookup(field, "Ff", &obj1)->isInt()) {
-    ff = obj1.getInt();
-  } else {
-    ff = 0;
-  }
-  obj1.free();
-
-  // draw the border
-  if (mkDict && border) {
-    w = border->getWidth();
-    if (w > 0) {
-      mkDict->lookup("BC", &obj1);
-      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
-        mkDict->lookup("BG", &obj1);
-      }
-      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
-        dx = rect->x2 - rect->x1;
-        dy = rect->y2 - rect->y1;
-
-        // radio buttons with no caption have a round border
-        hasCaption = mkDict->lookup("CA", &obj2)->isString();
-        obj2.free();
-        if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
-          r = 0.5 * (dx < dy ? dx : dy);
-          switch (border->getStyle()) {
-            case AnnotBorder::borderDashed:
-              appearBuf->append("[");
-              dashLength = border->getDashLength();
-              dash = border->getDash();
-              for (i = 0; i < dashLength; ++i) {
-                appearBuf->appendf(" {0:.2f}", dash[i]);
-              }
-              appearBuf->append("] 0 d\n");
-              // fall through to the solid case
-            case AnnotBorder::borderSolid:
-            case AnnotBorder::borderUnderlined:
-              appearBuf->appendf("{0:.2f} w\n", w);
-              setColor(obj1.getArray(), gFalse, 0);
-              drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
-              break;
-            case AnnotBorder::borderBeveled:
-            case AnnotBorder::borderInset:
-              appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
-              setColor(obj1.getArray(), gFalse, 0);
-              drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
-              setColor(obj1.getArray(), gFalse,
-                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
-              drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
-              setColor(obj1.getArray(), gFalse,
-                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
-              drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
-              break;
-          }
-
-        } else {
-          switch (border->getStyle()) {
-            case AnnotBorder::borderDashed:
-              appearBuf->append("[");
-              dashLength = border->getDashLength();
-              dash = border->getDash();
-              for (i = 0; i < dashLength; ++i) {
-                appearBuf->appendf(" {0:.2f}", dash[i]);
-              }
-              appearBuf->append("] 0 d\n");
-              // fall through to the solid case
-            case AnnotBorder::borderSolid:
-              appearBuf->appendf("{0:.2f} w\n", w);
-              setColor(obj1.getArray(), gFalse, 0);
-              appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
-                  0.5 * w, dx - w, dy - w);
-              break;
-            case AnnotBorder::borderBeveled:
-            case AnnotBorder::borderInset:
-              setColor(obj1.getArray(), gTrue,
-                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
-              appearBuf->append("0 0 m\n");
-              appearBuf->appendf("0 {0:.2f} l\n", dy);
-              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
-              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
-              appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
-              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
-              appearBuf->append("f\n");
-              setColor(obj1.getArray(), gTrue,
-                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
-              appearBuf->append("0 0 m\n");
-              appearBuf->appendf("{0:.2f} 0 l\n", dx);
-              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
-              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
-              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
-              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
-              appearBuf->append("f\n");
-              break;
-            case AnnotBorder::borderUnderlined:
-              appearBuf->appendf("{0:.2f} w\n", w);
-              setColor(obj1.getArray(), gFalse, 0);
-              appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
-              break;
-          }
-
-          // clip to the inside of the border
-          appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
-              w, dx - 2 * w, dy - 2 * w);
-        }
-      }
-      obj1.free();
-    }
-  }
-
-  // get the resource dictionary
-  acroForm->lookup("DR", &drObj);
-
-  // build the font dictionary
-  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
-    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
-  } else {
-    fontDict = NULL;
-  }
-  obj1.free();
-
-  // get the default appearance string
-  if (Form::fieldLookup(field, "DA", &obj1)->isNull()) {
-    obj1.free();
-    acroForm->lookup("DA", &obj1);
-  }
-  if (obj1.isString()) {
-    da = obj1.getString()->copy();
-    //TODO: look for a font size / name HERE
-    // => create a function
-  } else {
-    da = NULL;
-  }
-  obj1.free();
-
-  // draw the field contents
-  if (ftObj.isName("Btn")) {
-    caption = NULL;
-    if (mkDict) {
-      if (mkDict->lookup("CA", &obj1)->isString()) {
-        caption = obj1.getString()->copy();
-      }
-      obj1.free();
-    }
-    // radio button
-    if (ff & fieldFlagRadio) {
-      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
-      if (Form::fieldLookup(field, "V", &obj1)->isName()) {
-        if (annot->lookup("AS", &obj2)->isName(obj1.getName()) &&
-	    strcmp (obj1.getName(), "Off") != 0) {
-          if (caption) {
-            drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
-                gFalse, gTrue);
-          } else {
-            if (mkDict) {
-              if (mkDict->lookup("BC", &obj3)->isArray() &&
-                  obj3.arrayGetLength() > 0) {
-                dx = rect->x2 - rect->x1;
-                dy = rect->y2 - rect->y1;
-                setColor(obj3.getArray(), gTrue, 0);
-                drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
-                    gTrue);
-              }
-              obj3.free();
-            }
-          }
-        }
-        obj2.free();
-      }
-      obj1.free();
-      // pushbutton
-    } else if (ff & fieldFlagPushbutton) {
-      if (caption) {
-        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
-            gFalse, gFalse);
-      }
-      // checkbox
-    } else {
-      // According to the PDF spec the off state must be named "Off",
-      // and the on state can be named anything, but Acrobat apparently
-      // looks for "Yes" and treats anything else as off.
-      if (Form::fieldLookup(field, "V", &obj1)->isName("Yes")) {
-        if (!caption) {
-          caption = new GooString("3"); // ZapfDingbats checkmark
-        }
-        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
-            gFalse, gTrue);
-      }
-      obj1.free();
-    }
-    if (caption) {
-      delete caption;
-    }
-  } else if (ftObj.isName("Tx")) {
-    //~ value strings can be Unicode
-    if (Form::fieldLookup(field, "V", &obj1)->isString()) {
-      if (Form::fieldLookup(field, "Q", &obj2)->isInt()) {
-        quadding = obj2.getInt();
-      } else {
-        quadding = fieldQuadLeft;
-      }
-      obj2.free();
-      comb = 0;
-      if (ff & fieldFlagComb) {
-        if (Form::fieldLookup(field, "MaxLen", &obj2)->isInt()) {
-          comb = obj2.getInt();
-        }
-        obj2.free();
-      }
-      drawText(obj1.getString(), da, fontDict,
-          ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, ff & fieldFlagPassword);
-    }
-    obj1.free();
-  } else if (ftObj.isName("Ch")) {
-    //~ value/option strings can be Unicode
-    if (Form::fieldLookup(field, "Q", &obj1)->isInt()) {
-      quadding = obj1.getInt();
-    } else {
-      quadding = fieldQuadLeft;
-    }
-    obj1.free();
-    // combo box
-    if (ff & fieldFlagCombo) {
-      if (Form::fieldLookup(field, "V", &obj1)->isString()) {
-        drawText(obj1.getString(), da, fontDict,
-            gFalse, 0, quadding, gTrue, gFalse);
-        //~ Acrobat draws a popup icon on the right side
-      }
-      obj1.free();
-      // list box
-    } else {
-      if (field->lookup("Opt", &obj1)->isArray()) {
-        nOptions = obj1.arrayGetLength();
-        // get the option text
-        text = (GooString **)gmallocn(nOptions, sizeof(GooString *));
-        for (i = 0; i < nOptions; ++i) {
-          text[i] = NULL;
-          obj1.arrayGet(i, &obj2);
-          if (obj2.isString()) {
-            text[i] = obj2.getString()->copy();
-          } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
-            if (obj2.arrayGet(1, &obj3)->isString()) {
-              text[i] = obj3.getString()->copy();
-            }
-            obj3.free();
-          }
-          obj2.free();
-          if (!text[i]) {
-            text[i] = new GooString();
-          }
-        }
-        // get the selected option(s)
-        selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
-        //~ need to use the I field in addition to the V field
-	Form::fieldLookup(field, "V", &obj2);
-        for (i = 0; i < nOptions; ++i) {
-          selection[i] = gFalse;
-          if (obj2.isString()) {
-            if (!obj2.getString()->cmp(text[i])) {
-              selection[i] = gTrue;
-            }
-          } else if (obj2.isArray()) {
-            for (j = 0; j < obj2.arrayGetLength(); ++j) {
-              if (obj2.arrayGet(j, &obj3)->isString() &&
-                  !obj3.getString()->cmp(text[i])) {
-                selection[i] = gTrue;
-              }
-              obj3.free();
-            }
-          }
-        }
-        obj2.free();
-        // get the top index
-        if (field->lookup("TI", &obj2)->isInt()) {
-          topIdx = obj2.getInt();
-        } else {
-          topIdx = 0;
-        }
-        obj2.free();
-        // draw the text
-        drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
-        for (i = 0; i < nOptions; ++i) {
-          delete text[i];
-        }
-        gfree(text);
-        gfree(selection);
-      }
-      obj1.free();
-    }
-  } else if (ftObj.isName("Sig")) {
-    //~unimp
-  } else {
-    error(-1, "Unknown field type");
-  }
-
-  if (da) {
-    delete da;
-  }
-
-  // build the appearance stream dictionary
-  appearDict.initDict(xref);
-  appearDict.dictAdd(copyString("Length"),
-      obj1.initInt(appearBuf->getLength()));
-  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
-  obj1.initArray(xref);
-  obj1.arrayAdd(obj2.initReal(0));
-  obj1.arrayAdd(obj2.initReal(0));
-  obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
-  obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
-  appearDict.dictAdd(copyString("BBox"), &obj1);
-
-  // set the resource dictionary
-  if (drObj.isDict()) {
-    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
-  }
-  drObj.free();
-
-  // build the appearance stream
-  appearStream = new MemStream(strdup(appearBuf->getCString()), 0,
-      appearBuf->getLength(), &appearDict);
-  appearance.free();
-  appearance.initStream(appearStream);
-  delete appearBuf;
-
-  appearStream->setNeedFree(gTrue);
-
-  if (widget->isModified()) {
-    //create a new object that will contains the new appearance
-    
-    //if we already have a N entry in our AP dict, reuse it
-    if (annot->lookup("AP", &obj1)->isDict() &&
-        obj1.dictLookupNF("N", &obj2)->isRef()) {
-      appRef = obj2.getRef();
-    }
-
-    // this annot doesn't have an AP yet, create one
-    if (appRef.num == 0)
-      appRef = xref->addIndirectObject(&appearance);
-    else // since we reuse the already existing AP, we have to notify the xref about this update
-      xref->setModifiedObject(&appearance, appRef);
-
-    // update object's AP and AS
-    Object apObj;
-    apObj.initDict(xref);
-
-    Object oaRef;
-    oaRef.initRef(appRef.num, appRef.gen);
-
-    apObj.dictSet("N", &oaRef);
-    annot->set("AP", &apObj);
-    Dict* d = new Dict(annot);
-    Object dictObj;
-    dictObj.initDict(d);
-
-    xref->setModifiedObject(&dictObj, ref);
-  }
-
-  if (fontDict) {
-    delete fontDict;
-  }
-  ftObj.free();
-  mkObj.free();
-}
-
-
 // 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
@@ -1391,636 +945,6 @@ void Annot::setColor(Array *a, GBool fill, int adjust) {
   }
 }
 
-void Annot::writeTextString (GooString *text, GooString *appearBuf, int *i, int j,
-                             CharCodeToUnicode *ccToUnicode, GBool password)
-{
-  CharCode c;
-  int charSize;
-
-  if (*i == 0 && text->hasUnicodeMarker()) {
-    //we need to have an even number of chars
-    if (text->getLength () % 2 != 0) {
-      error(-1, "Annot::writeTextString, bad unicode string");
-      return;
-    }
-    //skip unicode marker an go one char forward because we read character by pairs
-    (*i) += 3;
-    charSize = 2;
-  } else
-    charSize = 1;
-
-  for (; (*i) < j; (*i)+=charSize) {
-    // Render '*' instead of characters for password
-    if (password)
-      appearBuf->append('*');
-    else {
-      c = text->getChar(*i);
-      if (ccToUnicode && text->hasUnicodeMarker()) {
-        char ctmp[2];
-        ctmp[0] = text->getChar((*i)-1);
-        ctmp[1] = text->getChar((*i));
-        ccToUnicode->mapToCharCode((Unicode*)ctmp, &c, 2);
-        if (c == '(' || c == ')' || c == '\\')
-          appearBuf->append('\\');
-        appearBuf->append(c);
-      } else {
-        c &= 0xff;
-        if (c == '(' || c == ')' || c == '\\') {
-          appearBuf->append('\\');
-          appearBuf->append(c);
-        } else if (c < 0x20 || c >= 0x80) {
-          appearBuf->appendf("\\{0:03o}", c);
-        } else {
-          appearBuf->append(c);
-        }
-      }
-    }
-  }
-}
-
-// Draw the variable text or caption for a field.
-void Annot::drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
-    GBool multiline, int comb, int quadding,
-    GBool txField, GBool forceZapfDingbats,
-    GBool password) {
-  GooList *daToks;
-  GooString *tok;
-  GfxFont *font;
-  double fontSize, fontSize2, borderWidth, x, xPrev, y, w, w2, wMax;
-  int tfPos, tmPos, i, j, k;
-
-  //~ if there is no MK entry, this should use the existing content stream,
-  //~ and only replace the marked content portion of it
-  //~ (this is only relevant for Tx fields)
-  
-  // parse the default appearance string
-  tfPos = tmPos = -1;
-  if (da) {
-    daToks = new GooList();
-    i = 0;
-    while (i < da->getLength()) {
-      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
-        ++i;
-      }
-      if (i < da->getLength()) {
-        for (j = i + 1;
-            j < da->getLength() && !Lexer::isSpace(da->getChar(j));
-            ++j) ;
-        daToks->append(new GooString(da, i, j - i));
-        i = j;
-      }
-    }
-    for (i = 2; i < daToks->getLength(); ++i) {
-      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
-        tfPos = i - 2;
-      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
-        tmPos = i - 6;
-      }
-    }
-  } else {
-    daToks = NULL;
-  }
-
-  // force ZapfDingbats
-  //~ this should create the font if needed (?)
-  if (forceZapfDingbats) {
-    if (tfPos >= 0) {
-      tok = (GooString *)daToks->get(tfPos);
-      if (tok->cmp("/ZaDb")) {
-        tok->clear();
-        tok->append("/ZaDb");
-      }
-    }
-  }
-  // get the font and font size
-  font = NULL;
-  fontSize = 0;
-  if (tfPos >= 0) {
-    tok = (GooString *)daToks->get(tfPos);
-    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
-      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
-        error(-1, "Unknown font in field's DA string");
-      }
-    } else {
-      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
-    }
-    tok = (GooString *)daToks->get(tfPos + 1);
-    fontSize = atof(tok->getCString());
-  } else {
-    error(-1, "Missing 'Tf' operator in field's DA string");
-  }
-  if (!font) {
-    return;
-  }
-
-  // get the border width
-  borderWidth = border ? border->getWidth() : 0;
-
-  // setup
-  if (txField) {
-    appearBuf->append("/Tx BMC\n");
-  }
-  appearBuf->append("q\n");
-  appearBuf->append("BT\n");
-  // multi-line text
-  if (multiline) {
-    // note: the comb flag is ignored in multiline mode
-
-    wMax = rect->x2 - rect->x1 - 2 * borderWidth - 4;
-
-    // compute font autosize
-    if (fontSize == 0) {
-      for (fontSize = 20; fontSize > 1; --fontSize) {
-        y = rect->y2 - rect->y1;
-        w2 = 0;
-        i = 0;
-        while (i < text->getLength()) {
-          getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
-          if (w > w2) {
-            w2 = w;
-          }
-          i = k;
-          y -= fontSize;
-        }
-        // approximate the descender for the last line
-        if (y >= 0.33 * fontSize) {
-          break;
-        }
-      }
-      if (tfPos >= 0) {
-        tok = (GooString *)daToks->get(tfPos + 1);
-        tok->clear();
-        tok->appendf("{0:.2f}", fontSize);
-      }
-    }
-
-    // starting y coordinate
-    // (note: each line of text starts with a Td operator that moves
-    // down a line)
-    y = rect->y2 - rect->y1;
-
-    // set the font matrix
-    if (tmPos >= 0) {
-      tok = (GooString *)daToks->get(tmPos + 4);
-      tok->clear();
-      tok->append('0');
-      tok = (GooString *)daToks->get(tmPos + 5);
-      tok->clear();
-      tok->appendf("{0:.2f}", y);
-    }
-
-    // write the DA string
-    if (daToks) {
-      for (i = 0; i < daToks->getLength(); ++i) {
-        appearBuf->append((GooString *)daToks->get(i))->append(' ');
-      }
-    }
-
-    // write the font matrix (if not part of the DA string)
-    if (tmPos < 0) {
-      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
-    }
-
-    // write a series of lines of text
-    i = 0;
-    xPrev = 0;
-    while (i < text->getLength()) {
-
-      getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
-
-      // compute text start position
-      switch (quadding) {
-        case fieldQuadLeft:
-        default:
-          x = borderWidth + 2;
-          break;
-        case fieldQuadCenter:
-          x = (rect->x2 - rect->x1 - w) / 2;
-          break;
-        case fieldQuadRight:
-          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
-          break;
-      }
-
-      // draw the line
-      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
-      appearBuf->append('(');
-      writeTextString (text, appearBuf, &i, j, font->getToUnicode(), password);
-      appearBuf->append(") Tj\n");
-
-      // next line
-      i = k;
-      xPrev = x;
-    }
-
-    // single-line text
-  } else {
-    //~ replace newlines with spaces? - what does Acrobat do?
-
-    // comb formatting
-    if (comb > 0) {
-      // compute comb spacing
-      w = (rect->x2 - rect->x1 - 2 * borderWidth) / comb;
-
-      // compute font autosize
-      if (fontSize == 0) {
-        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
-        if (w < fontSize) {
-          fontSize = w;
-        }
-        fontSize = floor(fontSize);
-        if (tfPos >= 0) {
-          tok = (GooString *)daToks->get(tfPos + 1);
-          tok->clear();
-          tok->appendf("{0:.2f}", fontSize);
-        }
-      }
-
-      // compute text start position
-      switch (quadding) {
-        case fieldQuadLeft:
-        default:
-          x = borderWidth + 2;
-          break;
-        case fieldQuadCenter:
-          x = borderWidth + 2 + 0.5 * (comb - text->getLength()) * w;
-          break;
-        case fieldQuadRight:
-          x = borderWidth + 2 + (comb - text->getLength()) * w;
-          break;
-      }
-      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
-
-      // set the font matrix
-      if (tmPos >= 0) {
-        tok = (GooString *)daToks->get(tmPos + 4);
-        tok->clear();
-        tok->appendf("{0:.2f}", x);
-        tok = (GooString *)daToks->get(tmPos + 5);
-        tok->clear();
-        tok->appendf("{0:.2f}", y);
-      }
-
-      // write the DA string
-      if (daToks) {
-        for (i = 0; i < daToks->getLength(); ++i) {
-          appearBuf->append((GooString *)daToks->get(i))->append(' ');
-        }
-      }
-
-      // write the font matrix (if not part of the DA string)
-      if (tmPos < 0) {
-        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
-      }
-      // write the text string
-      //~ this should center (instead of left-justify) each character within
-      //~     its comb cell
-      for (i = 0; i < text->getLength(); ++i) {
-        if (i > 0) {
-          appearBuf->appendf("{0:.2f} 0 Td\n", w);
-        }
-        appearBuf->append('(');
-        //~ it would be better to call it only once for the whole string instead of once for
-        //each character => but we need to handle centering in writeTextString
-        writeTextString (text, appearBuf, &i, i+1, font->getToUnicode(), password);
-        appearBuf->append(") Tj\n");
-      }
-
-      // regular (non-comb) formatting
-    } else {
-      // compute string width
-      if (font && !font->isCIDFont()) {
-        w = 0;
-        for (i = 0; i < text->getLength(); ++i) {
-          w += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
-        }
-      } else {
-        // otherwise, make a crude estimate
-        w = text->getLength() * 0.5;
-      }
-
-      // compute font autosize
-      if (fontSize == 0) {
-        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
-        fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / w;
-        if (fontSize2 < fontSize) {
-          fontSize = fontSize2;
-        }
-        fontSize = floor(fontSize);
-        if (tfPos >= 0) {
-          tok = (GooString *)daToks->get(tfPos + 1);
-          tok->clear();
-          tok->appendf("{0:.2f}", fontSize);
-        }
-      }
-
-      // compute text start position
-      w *= fontSize;
-      switch (quadding) {
-        case fieldQuadLeft:
-        default:
-          x = borderWidth + 2;
-          break;
-        case fieldQuadCenter:
-          x = (rect->x2 - rect->x1 - w) / 2;
-          break;
-        case fieldQuadRight:
-          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
-          break;
-      }
-      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
-
-      // set the font matrix
-      if (tmPos >= 0) {
-        tok = (GooString *)daToks->get(tmPos + 4);
-        tok->clear();
-        tok->appendf("{0:.2f}", x);
-        tok = (GooString *)daToks->get(tmPos + 5);
-        tok->clear();
-        tok->appendf("{0:.2f}", y);
-      }
-
-      // write the DA string
-      if (daToks) {
-        for (i = 0; i < daToks->getLength(); ++i) {
-          appearBuf->append((GooString *)daToks->get(i))->append(' ');
-        }
-      }
-
-      // write the font matrix (if not part of the DA string)
-      if (tmPos < 0) {
-        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
-      }
-      // write the text string
-      appearBuf->append('(');
-      i=0;
-      writeTextString (text, appearBuf, &i, text->getLength(), font->getToUnicode(), password);
-      appearBuf->append(") Tj\n");
-    }
-  }
-  // cleanup
-  appearBuf->append("ET\n");
-  appearBuf->append("Q\n");
-  if (txField) {
-    appearBuf->append("EMC\n");
-  }
-  if (daToks) {
-    deleteGooList(daToks, GooString);
-  }
-}
-
-// Draw the variable text or caption for a field.
-void Annot::drawListBox(GooString **text, GBool *selection,
-			int nOptions, int topIdx,
-			GooString *da, GfxFontDict *fontDict, GBool quadding) {
-  GooList *daToks;
-  GooString *tok;
-  GfxFont *font;
-  double fontSize, fontSize2, borderWidth, x, y, w, wMax;
-  int tfPos, tmPos, i, j;
-
-  //~ if there is no MK entry, this should use the existing content stream,
-  //~ and only replace the marked content portion of it
-  //~ (this is only relevant for Tx fields)
-
-  // parse the default appearance string
-  tfPos = tmPos = -1;
-  if (da) {
-    daToks = new GooList();
-    i = 0;
-    while (i < da->getLength()) {
-      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
-	++i;
-       }
-      if (i < da->getLength()) {
-	for (j = i + 1;
-	     j < da->getLength() && !Lexer::isSpace(da->getChar(j));
-	     ++j) ;
-	daToks->append(new GooString(da, i, j - i));
-	i = j;
-      }
-    }
-    for (i = 2; i < daToks->getLength(); ++i) {
-      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
-	tfPos = i - 2;
-      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
-	tmPos = i - 6;
-      }
-    }
-  } else {
-    daToks = NULL;
-  }
-
-  // get the font and font size
-  font = NULL;
-  fontSize = 0;
-  if (tfPos >= 0) {
-    tok = (GooString *)daToks->get(tfPos);
-    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
-      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
-        error(-1, "Unknown font in field's DA string");
-      }
-    } else {
-      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
-    }
-    tok = (GooString *)daToks->get(tfPos + 1);
-    fontSize = atof(tok->getCString());
-  } else {
-    error(-1, "Missing 'Tf' operator in field's DA string");
-  }
-  if (!font) {
-    return;
-  }
-
-  // get the border width
-  borderWidth = border ? border->getWidth() : 0;
-
-  // compute font autosize
-  if (fontSize == 0) {
-    wMax = 0;
-    for (i = 0; i < nOptions; ++i) {
-      if (font && !font->isCIDFont()) {
-        w = 0;
-        for (j = 0; j < text[i]->getLength(); ++j) {
-          w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
-        }
-      } else {
-        // otherwise, make a crude estimate
-        w = text[i]->getLength() * 0.5;
-      }
-      if (w > wMax) {
-        wMax = w;
-      }
-    }
-    fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
-    fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
-    if (fontSize2 < fontSize) {
-      fontSize = fontSize2;
-    }
-    fontSize = floor(fontSize);
-    if (tfPos >= 0) {
-      tok = (GooString *)daToks->get(tfPos + 1);
-      tok->clear();
-      tok->appendf("{0:.2f}", fontSize);
-    }
-  }
-  // draw the text
-  y = rect->y2 - rect->y1 - 1.1 * fontSize;
-  for (i = topIdx; i < nOptions; ++i) {
-    // setup
-    appearBuf->append("q\n");
-
-    // draw the background if selected
-    if (selection[i]) {
-      appearBuf->append("0 g f\n");
-      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
-          borderWidth,
-          y - 0.2 * fontSize,
-          rect->x2 - rect->x1 - 2 * borderWidth,
-          1.1 * fontSize);
-    }
-
-    // setup
-    appearBuf->append("BT\n");
-
-    // compute string width
-    if (font && !font->isCIDFont()) {
-      w = 0;
-      for (j = 0; j < text[i]->getLength(); ++j) {
-        w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
-      }
-    } else {
-      // otherwise, make a crude estimate
-      w = text[i]->getLength() * 0.5;
-    }
-
-    // compute text start position
-    w *= fontSize;
-    switch (quadding) {
-      case fieldQuadLeft:
-      default:
-        x = borderWidth + 2;
-        break;
-      case fieldQuadCenter:
-        x = (rect->x2 - rect->x1 - w) / 2;
-        break;
-      case fieldQuadRight:
-        x = rect->x2 - rect->x1 - borderWidth - 2 - w;
-        break;
-    }
-
-    // set the font matrix
-    if (tmPos >= 0) {
-      tok = (GooString *)daToks->get(tmPos + 4);
-      tok->clear();
-      tok->appendf("{0:.2f}", x);
-      tok = (GooString *)daToks->get(tmPos + 5);
-      tok->clear();
-      tok->appendf("{0:.2f}", y);
-    }
-
-    // write the DA string
-    if (daToks) {
-      for (j = 0; j < daToks->getLength(); ++j) {
-        appearBuf->append((GooString *)daToks->get(j))->append(' ');
-      }
-    }
-
-    // write the font matrix (if not part of the DA string)
-    if (tmPos < 0) {
-      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
-    }
-
-    // change the text color if selected
-    if (selection[i]) {
-      appearBuf->append("1 g\n");
-    }
-
-    // write the text string
-    appearBuf->append('(');
-    j = 0;
-    writeTextString (text[i], appearBuf, &j, text[i]->getLength(), font->getToUnicode(), false);
-    appearBuf->append(") Tj\n");
-
-    // cleanup
-    appearBuf->append("ET\n");
-    appearBuf->append("Q\n");
-
-    // next line
-    y -= 1.1 * fontSize;
-  }
-
-  if (daToks) {
-    deleteGooList(daToks, GooString);
-  }
-}
-
-// Figure out how much text will fit on the next line.  Returns:
-// *end = one past the last character to be included
-// *width = width of the characters start .. end-1
-// *next = index of first character on the following line
-void Annot::getNextLine(GooString *text, int start,
-    GfxFont *font, double fontSize, double wMax,
-    int *end, double *width, int *next) {
-  double w, dw;
-  int j, k, c;
-
-  // figure out how much text will fit on the line
-  //~ what does Adobe do with tabs?
-  w = 0;
-  for (j = start; j < text->getLength() && w <= wMax; ++j) {
-    c = text->getChar(j) & 0xff;
-    if (c == 0x0a || c == 0x0d) {
-      break;
-    }
-    if (font && !font->isCIDFont()) {
-      dw = ((Gfx8BitFont *)font)->getWidth(c) * fontSize;
-    } else {
-      // otherwise, make a crude estimate
-      dw = 0.5 * fontSize;
-    }
-    w += dw;
-  }
-  if (w > wMax) {
-    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
-    for (; k > start && text->getChar(k-1) == ' '; --k) ;
-    if (k > start) {
-      j = k;
-    }
-    if (j == start) {
-      // handle the pathological case where the first character is
-      // too wide to fit on the line all by itself
-      j = start + 1;
-    }
-  }
-  *end = j;
-
-  // compute the width
-  w = 0;
-  for (k = start; k < j; ++k) {
-    if (font && !font->isCIDFont()) {
-      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
-    } else {
-      // otherwise, make a crude estimate
-      dw = 0.5 * fontSize;
-    }
-    w += dw;
-  }
-  *width = w;
-
-  // next line
-  while (j < text->getLength() && text->getChar(j) == ' ') {
-    ++j;
-  }
-  if (j < text->getLength() && text->getChar(j) == 0x0d) {
-    ++j;
-  }
-  if (j < text->getLength() && text->getChar(j) == 0x0a) {
-    ++j;
-  }
-  *next = j;
-}
-
 // Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
 // If <fill> is true, the circle is filled; otherwise it is stroked.
 void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
@@ -2116,10 +1040,10 @@ void Annot::draw(Gfx *gfx, GBool printing) {
 // AnnotPopup
 //------------------------------------------------------------------------
 
-AnnotPopup::AnnotPopup(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) :
-    Annot(xrefA, acroForm, dict, catalog, obj) {
+AnnotPopup::AnnotPopup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+    Annot(xrefA, dict, catalog, obj) {
   type = typePopup;
-  initialize(xrefA, acroForm, dict, catalog);
+  initialize(xrefA, dict, catalog);
 }
 
 AnnotPopup::~AnnotPopup() {
@@ -2129,7 +1053,7 @@ AnnotPopup::~AnnotPopup() {
   */
 }
 
-void AnnotPopup::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog) {
+void AnnotPopup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
   Object obj1;
   /*
   if (dict->lookup("Parent", &obj1)->isDict()) {
@@ -2151,8 +1075,8 @@ void AnnotPopup::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *ca
 // AnnotMarkup
 //------------------------------------------------------------------------
  
-AnnotMarkup::AnnotMarkup(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) {
-  initialize(xrefA, acroForm, dict, catalog, obj);
+AnnotMarkup::AnnotMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) {
+  initialize(xrefA, dict, catalog, obj);
 }
 
 AnnotMarkup::~AnnotMarkup() {
@@ -2172,7 +1096,7 @@ AnnotMarkup::~AnnotMarkup() {
     delete subject;
 }
 
-void AnnotMarkup::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) {
+void AnnotMarkup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) {
   Object obj1;
 
   if (dict->lookup("T", &obj1)->isString()) {
@@ -2183,7 +1107,7 @@ void AnnotMarkup::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *c
   obj1.free();
 
   if (dict->lookup("Popup", &obj1)->isDict()) {
-    popup = new AnnotPopup(xrefA, acroForm, obj1.getDict(), catalog, obj);
+    popup = new AnnotPopup(xrefA, obj1.getDict(), catalog, obj);
   } else {
     popup = NULL;
   }
@@ -2245,8 +1169,8 @@ void AnnotMarkup::initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *c
 // AnnotText
 //------------------------------------------------------------------------
 
-AnnotText::AnnotText(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) :
-    Annot(xrefA, acroForm, dict, catalog, obj), AnnotMarkup(xref, acroForm, dict, catalog, obj) {
+AnnotText::AnnotText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+    Annot(xrefA, dict, catalog, obj), AnnotMarkup(xref, dict, catalog, obj) {
 
   type = typeText;
   flags |= flagNoZoom | flagNoRotate;
@@ -2366,8 +1290,8 @@ void AnnotText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
 // AnnotLink
 //------------------------------------------------------------------------
 
-AnnotLink::AnnotLink(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) :
-    Annot(xrefA, acroForm, dict, catalog, obj) {
+AnnotLink::AnnotLink(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+    Annot(xrefA, dict, catalog, obj) {
 
   type = typeLink;
   initialize (xrefA, catalog, dict);
@@ -2430,12 +1354,29 @@ void AnnotLink::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
   obj1.free();
 }
 
+void AnnotLink::draw(Gfx *gfx, GBool printing) {
+  Object obj;
+
+  // check the flags
+  if ((flags & annotFlagHidden) ||
+      (printing && !(flags & annotFlagPrint)) ||
+      (!printing && (flags & annotFlagNoView))) {
+    return;
+  }
+
+  // draw the appearance stream
+  appearance.fetch(xref, &obj);
+  gfx->drawAnnot(&obj, border, color,
+		 rect->x1, rect->y1, rect->x2, rect->y2);
+  obj.free();
+}
+
 //------------------------------------------------------------------------
 // AnnotFreeText
 //------------------------------------------------------------------------
 
-AnnotFreeText::AnnotFreeText(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) :
-    Annot(xrefA, acroForm, dict, catalog, obj), AnnotMarkup(xref, acroForm, dict, catalog, obj) {
+AnnotFreeText::AnnotFreeText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+    Annot(xrefA, dict, catalog, obj), AnnotMarkup(xref, dict, catalog, obj) {
   type = typeFreeText;
   initialize(xrefA, catalog, dict);
 }
@@ -2587,8 +1528,8 @@ void AnnotFreeText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
 // AnnotLine
 //------------------------------------------------------------------------
 
-AnnotLine::AnnotLine(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) :
-    Annot(xrefA, acroForm, dict, catalog, obj), AnnotMarkup(xref, acroForm, dict, catalog, obj) {
+AnnotLine::AnnotLine(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+    Annot(xrefA, dict, catalog, obj), AnnotMarkup(xref, dict, catalog, obj) {
   type = typeLine;
   initialize(xrefA, catalog, dict);
 }
@@ -2761,8 +1702,8 @@ AnnotTextMarkup::~AnnotTextMarkup() {
 // AnnotWidget
 //------------------------------------------------------------------------
 
-AnnotWidget::AnnotWidget(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj) :
-    Annot(xrefA, acroForm, dict, catalog, obj) {
+AnnotWidget::AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+    Annot(xrefA, dict, catalog, obj) {
   type = typeWidget;
   initialize(xrefA, catalog, dict);
 }
@@ -2784,6 +1725,21 @@ AnnotWidget::~AnnotWidget() {
 void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
   Object obj1;
 
+  form = catalog->getForm ();
+  widget = form->findWidgetByRef (ref);
+
+  // check if field apperances need to be regenerated
+  // Only text or choice fields needs to have appearance regenerated
+  // see section 8.6.2 "Variable Text" of PDFReference
+  regen = gFalse;
+  if (widget->getType () == formText || widget->getType () == formChoice) {
+    regen = form->getNeedAppearances ();
+  }
+
+  // If field doesn't have an AP we'll have to generate it
+  if (appearance.isNone () || appearance.isNull ())
+    regen = gTrue;
+
   if(dict->lookup("H", &obj1)->isName()) {
     GooString *modeName = new GooString(obj1.getName());
 
@@ -2831,12 +1787,1078 @@ void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
   obj1.free();
 }
 
+// Draw the variable text or caption for a field.
+void AnnotWidget::drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
+    GBool multiline, int comb, int quadding,
+    GBool txField, GBool forceZapfDingbats,
+    GBool password) {
+  GooList *daToks;
+  GooString *tok;
+  GfxFont *font;
+  double fontSize, fontSize2, borderWidth, x, xPrev, y, w, w2, wMax;
+  int tfPos, tmPos, i, j, k;
+
+  //~ if there is no MK entry, this should use the existing content stream,
+  //~ and only replace the marked content portion of it
+  //~ (this is only relevant for Tx fields)
+  
+  // parse the default appearance string
+  tfPos = tmPos = -1;
+  if (da) {
+    daToks = new GooList();
+    i = 0;
+    while (i < da->getLength()) {
+      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
+        ++i;
+      }
+      if (i < da->getLength()) {
+        for (j = i + 1;
+            j < da->getLength() && !Lexer::isSpace(da->getChar(j));
+            ++j) ;
+        daToks->append(new GooString(da, i, j - i));
+        i = j;
+      }
+    }
+    for (i = 2; i < daToks->getLength(); ++i) {
+      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
+        tfPos = i - 2;
+      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
+        tmPos = i - 6;
+      }
+    }
+  } else {
+    daToks = NULL;
+  }
+
+  // force ZapfDingbats
+  //~ this should create the font if needed (?)
+  if (forceZapfDingbats) {
+    if (tfPos >= 0) {
+      tok = (GooString *)daToks->get(tfPos);
+      if (tok->cmp("/ZaDb")) {
+        tok->clear();
+        tok->append("/ZaDb");
+      }
+    }
+  }
+  // get the font and font size
+  font = NULL;
+  fontSize = 0;
+  if (tfPos >= 0) {
+    tok = (GooString *)daToks->get(tfPos);
+    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
+      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
+        error(-1, "Unknown font in field's DA string");
+      }
+    } else {
+      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
+    }
+    tok = (GooString *)daToks->get(tfPos + 1);
+    fontSize = atof(tok->getCString());
+  } else {
+    error(-1, "Missing 'Tf' operator in field's DA string");
+  }
+  if (!font) {
+    return;
+  }
+
+  // get the border width
+  borderWidth = border ? border->getWidth() : 0;
+
+  // setup
+  if (txField) {
+    appearBuf->append("/Tx BMC\n");
+  }
+  appearBuf->append("q\n");
+  appearBuf->append("BT\n");
+  // multi-line text
+  if (multiline) {
+    // note: the comb flag is ignored in multiline mode
+
+    wMax = rect->x2 - rect->x1 - 2 * borderWidth - 4;
+
+    // compute font autosize
+    if (fontSize == 0) {
+      for (fontSize = 20; fontSize > 1; --fontSize) {
+        y = rect->y2 - rect->y1;
+        w2 = 0;
+        i = 0;
+        while (i < text->getLength()) {
+          getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
+          if (w > w2) {
+            w2 = w;
+          }
+          i = k;
+          y -= fontSize;
+        }
+        // approximate the descender for the last line
+        if (y >= 0.33 * fontSize) {
+          break;
+        }
+      }
+      if (tfPos >= 0) {
+        tok = (GooString *)daToks->get(tfPos + 1);
+        tok->clear();
+        tok->appendf("{0:.2f}", fontSize);
+      }
+    }
+
+    // starting y coordinate
+    // (note: each line of text starts with a Td operator that moves
+    // down a line)
+    y = rect->y2 - rect->y1;
+
+    // set the font matrix
+    if (tmPos >= 0) {
+      tok = (GooString *)daToks->get(tmPos + 4);
+      tok->clear();
+      tok->append('0');
+      tok = (GooString *)daToks->get(tmPos + 5);
+      tok->clear();
+      tok->appendf("{0:.2f}", y);
+    }
+
+    // write the DA string
+    if (daToks) {
+      for (i = 0; i < daToks->getLength(); ++i) {
+        appearBuf->append((GooString *)daToks->get(i))->append(' ');
+      }
+    }
+
+    // write the font matrix (if not part of the DA string)
+    if (tmPos < 0) {
+      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
+    }
+
+    // write a series of lines of text
+    i = 0;
+    xPrev = 0;
+    while (i < text->getLength()) {
+
+      getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
+
+      // compute text start position
+      switch (quadding) {
+        case fieldQuadLeft:
+        default:
+          x = borderWidth + 2;
+          break;
+        case fieldQuadCenter:
+          x = (rect->x2 - rect->x1 - w) / 2;
+          break;
+        case fieldQuadRight:
+          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
+          break;
+      }
+
+      // draw the line
+      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
+      appearBuf->append('(');
+      writeTextString (text, appearBuf, &i, j, font->getToUnicode(), password);
+      appearBuf->append(") Tj\n");
+
+      // next line
+      i = k;
+      xPrev = x;
+    }
+
+    // single-line text
+  } else {
+    //~ replace newlines with spaces? - what does Acrobat do?
+
+    // comb formatting
+    if (comb > 0) {
+      // compute comb spacing
+      w = (rect->x2 - rect->x1 - 2 * borderWidth) / comb;
+
+      // compute font autosize
+      if (fontSize == 0) {
+        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
+        if (w < fontSize) {
+          fontSize = w;
+        }
+        fontSize = floor(fontSize);
+        if (tfPos >= 0) {
+          tok = (GooString *)daToks->get(tfPos + 1);
+          tok->clear();
+          tok->appendf("{0:.2f}", fontSize);
+        }
+      }
+
+      // compute text start position
+      switch (quadding) {
+        case fieldQuadLeft:
+        default:
+          x = borderWidth + 2;
+          break;
+        case fieldQuadCenter:
+          x = borderWidth + 2 + 0.5 * (comb - text->getLength()) * w;
+          break;
+        case fieldQuadRight:
+          x = borderWidth + 2 + (comb - text->getLength()) * w;
+          break;
+      }
+      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
+
+      // set the font matrix
+      if (tmPos >= 0) {
+        tok = (GooString *)daToks->get(tmPos + 4);
+        tok->clear();
+        tok->appendf("{0:.2f}", x);
+        tok = (GooString *)daToks->get(tmPos + 5);
+        tok->clear();
+        tok->appendf("{0:.2f}", y);
+      }
+
+      // write the DA string
+      if (daToks) {
+        for (i = 0; i < daToks->getLength(); ++i) {
+          appearBuf->append((GooString *)daToks->get(i))->append(' ');
+        }
+      }
+
+      // write the font matrix (if not part of the DA string)
+      if (tmPos < 0) {
+        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
+      }
+      // write the text string
+      //~ this should center (instead of left-justify) each character within
+      //~     its comb cell
+      for (i = 0; i < text->getLength(); ++i) {
+        if (i > 0) {
+          appearBuf->appendf("{0:.2f} 0 Td\n", w);
+        }
+        appearBuf->append('(');
+        //~ it would be better to call it only once for the whole string instead of once for
+        //each character => but we need to handle centering in writeTextString
+        writeTextString (text, appearBuf, &i, i+1, font->getToUnicode(), password);
+        appearBuf->append(") Tj\n");
+      }
+
+      // regular (non-comb) formatting
+    } else {
+      // compute string width
+      if (font && !font->isCIDFont()) {
+        w = 0;
+        for (i = 0; i < text->getLength(); ++i) {
+          w += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
+        }
+      } else {
+        // otherwise, make a crude estimate
+        w = text->getLength() * 0.5;
+      }
+
+      // compute font autosize
+      if (fontSize == 0) {
+        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
+        fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / w;
+        if (fontSize2 < fontSize) {
+          fontSize = fontSize2;
+        }
+        fontSize = floor(fontSize);
+        if (tfPos >= 0) {
+          tok = (GooString *)daToks->get(tfPos + 1);
+          tok->clear();
+          tok->appendf("{0:.2f}", fontSize);
+        }
+      }
+
+      // compute text start position
+      w *= fontSize;
+      switch (quadding) {
+        case fieldQuadLeft:
+        default:
+          x = borderWidth + 2;
+          break;
+        case fieldQuadCenter:
+          x = (rect->x2 - rect->x1 - w) / 2;
+          break;
+        case fieldQuadRight:
+          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
+          break;
+      }
+      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
+
+      // set the font matrix
+      if (tmPos >= 0) {
+        tok = (GooString *)daToks->get(tmPos + 4);
+        tok->clear();
+        tok->appendf("{0:.2f}", x);
+        tok = (GooString *)daToks->get(tmPos + 5);
+        tok->clear();
+        tok->appendf("{0:.2f}", y);
+      }
+
+      // write the DA string
+      if (daToks) {
+        for (i = 0; i < daToks->getLength(); ++i) {
+          appearBuf->append((GooString *)daToks->get(i))->append(' ');
+        }
+      }
+
+      // write the font matrix (if not part of the DA string)
+      if (tmPos < 0) {
+        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
+      }
+      // write the text string
+      appearBuf->append('(');
+      i=0;
+      writeTextString (text, appearBuf, &i, text->getLength(), font->getToUnicode(), password);
+      appearBuf->append(") Tj\n");
+    }
+  }
+  // cleanup
+  appearBuf->append("ET\n");
+  appearBuf->append("Q\n");
+  if (txField) {
+    appearBuf->append("EMC\n");
+  }
+  if (daToks) {
+    deleteGooList(daToks, GooString);
+  }
+}
+
+// Draw the variable text or caption for a field.
+void AnnotWidget::drawListBox(GooString **text, GBool *selection,
+			      int nOptions, int topIdx,
+			      GooString *da, GfxFontDict *fontDict, GBool quadding) {
+  GooList *daToks;
+  GooString *tok;
+  GfxFont *font;
+  double fontSize, fontSize2, borderWidth, x, y, w, wMax;
+  int tfPos, tmPos, i, j;
+
+  //~ if there is no MK entry, this should use the existing content stream,
+  //~ and only replace the marked content portion of it
+  //~ (this is only relevant for Tx fields)
+
+  // parse the default appearance string
+  tfPos = tmPos = -1;
+  if (da) {
+    daToks = new GooList();
+    i = 0;
+    while (i < da->getLength()) {
+      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
+	++i;
+       }
+      if (i < da->getLength()) {
+	for (j = i + 1;
+	     j < da->getLength() && !Lexer::isSpace(da->getChar(j));
+	     ++j) ;
+	daToks->append(new GooString(da, i, j - i));
+	i = j;
+      }
+    }
+    for (i = 2; i < daToks->getLength(); ++i) {
+      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
+	tfPos = i - 2;
+      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
+	tmPos = i - 6;
+      }
+    }
+  } else {
+    daToks = NULL;
+  }
+
+  // get the font and font size
+  font = NULL;
+  fontSize = 0;
+  if (tfPos >= 0) {
+    tok = (GooString *)daToks->get(tfPos);
+    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
+      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
+        error(-1, "Unknown font in field's DA string");
+      }
+    } else {
+      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
+    }
+    tok = (GooString *)daToks->get(tfPos + 1);
+    fontSize = atof(tok->getCString());
+  } else {
+    error(-1, "Missing 'Tf' operator in field's DA string");
+  }
+  if (!font) {
+    return;
+  }
+
+  // get the border width
+  borderWidth = border ? border->getWidth() : 0;
+
+  // compute font autosize
+  if (fontSize == 0) {
+    wMax = 0;
+    for (i = 0; i < nOptions; ++i) {
+      if (font && !font->isCIDFont()) {
+        w = 0;
+        for (j = 0; j < text[i]->getLength(); ++j) {
+          w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
+        }
+      } else {
+        // otherwise, make a crude estimate
+        w = text[i]->getLength() * 0.5;
+      }
+      if (w > wMax) {
+        wMax = w;
+      }
+    }
+    fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
+    fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
+    if (fontSize2 < fontSize) {
+      fontSize = fontSize2;
+    }
+    fontSize = floor(fontSize);
+    if (tfPos >= 0) {
+      tok = (GooString *)daToks->get(tfPos + 1);
+      tok->clear();
+      tok->appendf("{0:.2f}", fontSize);
+    }
+  }
+  // draw the text
+  y = rect->y2 - rect->y1 - 1.1 * fontSize;
+  for (i = topIdx; i < nOptions; ++i) {
+    // setup
+    appearBuf->append("q\n");
+
+    // draw the background if selected
+    if (selection[i]) {
+      appearBuf->append("0 g f\n");
+      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
+          borderWidth,
+          y - 0.2 * fontSize,
+          rect->x2 - rect->x1 - 2 * borderWidth,
+          1.1 * fontSize);
+    }
+
+    // setup
+    appearBuf->append("BT\n");
+
+    // compute string width
+    if (font && !font->isCIDFont()) {
+      w = 0;
+      for (j = 0; j < text[i]->getLength(); ++j) {
+        w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
+      }
+    } else {
+      // otherwise, make a crude estimate
+      w = text[i]->getLength() * 0.5;
+    }
+
+    // compute text start position
+    w *= fontSize;
+    switch (quadding) {
+      case fieldQuadLeft:
+      default:
+        x = borderWidth + 2;
+        break;
+      case fieldQuadCenter:
+        x = (rect->x2 - rect->x1 - w) / 2;
+        break;
+      case fieldQuadRight:
+        x = rect->x2 - rect->x1 - borderWidth - 2 - w;
+        break;
+    }
+
+    // set the font matrix
+    if (tmPos >= 0) {
+      tok = (GooString *)daToks->get(tmPos + 4);
+      tok->clear();
+      tok->appendf("{0:.2f}", x);
+      tok = (GooString *)daToks->get(tmPos + 5);
+      tok->clear();
+      tok->appendf("{0:.2f}", y);
+    }
+
+    // write the DA string
+    if (daToks) {
+      for (j = 0; j < daToks->getLength(); ++j) {
+        appearBuf->append((GooString *)daToks->get(j))->append(' ');
+      }
+    }
+
+    // write the font matrix (if not part of the DA string)
+    if (tmPos < 0) {
+      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
+    }
+
+    // change the text color if selected
+    if (selection[i]) {
+      appearBuf->append("1 g\n");
+    }
+
+    // write the text string
+    appearBuf->append('(');
+    j = 0;
+    writeTextString (text[i], appearBuf, &j, text[i]->getLength(), font->getToUnicode(), false);
+    appearBuf->append(") Tj\n");
+
+    // cleanup
+    appearBuf->append("ET\n");
+    appearBuf->append("Q\n");
+
+    // next line
+    y -= 1.1 * fontSize;
+  }
+
+  if (daToks) {
+    deleteGooList(daToks, GooString);
+  }
+}
+
+// Figure out how much text will fit on the next line.  Returns:
+// *end = one past the last character to be included
+// *width = width of the characters start .. end-1
+// *next = index of first character on the following line
+void AnnotWidget::getNextLine(GooString *text, int start,
+			      GfxFont *font, double fontSize, double wMax,
+			      int *end, double *width, int *next) {
+  double w, dw;
+  int j, k, c;
+
+  // figure out how much text will fit on the line
+  //~ what does Adobe do with tabs?
+  w = 0;
+  for (j = start; j < text->getLength() && w <= wMax; ++j) {
+    c = text->getChar(j) & 0xff;
+    if (c == 0x0a || c == 0x0d) {
+      break;
+    }
+    if (font && !font->isCIDFont()) {
+      dw = ((Gfx8BitFont *)font)->getWidth(c) * fontSize;
+    } else {
+      // otherwise, make a crude estimate
+      dw = 0.5 * fontSize;
+    }
+    w += dw;
+  }
+  if (w > wMax) {
+    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
+    for (; k > start && text->getChar(k-1) == ' '; --k) ;
+    if (k > start) {
+      j = k;
+    }
+    if (j == start) {
+      // handle the pathological case where the first character is
+      // too wide to fit on the line all by itself
+      j = start + 1;
+    }
+  }
+  *end = j;
+
+  // compute the width
+  w = 0;
+  for (k = start; k < j; ++k) {
+    if (font && !font->isCIDFont()) {
+      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
+    } else {
+      // otherwise, make a crude estimate
+      dw = 0.5 * fontSize;
+    }
+    w += dw;
+  }
+  *width = w;
+
+  // next line
+  while (j < text->getLength() && text->getChar(j) == ' ') {
+    ++j;
+  }
+  if (j < text->getLength() && text->getChar(j) == 0x0d) {
+    ++j;
+  }
+  if (j < text->getLength() && text->getChar(j) == 0x0a) {
+    ++j;
+  }
+  *next = j;
+}
+
+void AnnotWidget::writeTextString (GooString *text, GooString *appearBuf, int *i, int j,
+				   CharCodeToUnicode *ccToUnicode, GBool password)
+{
+  CharCode c;
+  int charSize;
+
+  if (*i == 0 && text->hasUnicodeMarker()) {
+    //we need to have an even number of chars
+    if (text->getLength () % 2 != 0) {
+      error(-1, "Annot::writeTextString, bad unicode string");
+      return;
+    }
+    //skip unicode marker an go one char forward because we read character by pairs
+    (*i) += 3;
+    charSize = 2;
+  } else
+    charSize = 1;
+
+  for (; (*i) < j; (*i)+=charSize) {
+    // Render '*' instead of characters for password
+    if (password)
+      appearBuf->append('*');
+    else {
+      c = text->getChar(*i);
+      if (ccToUnicode && text->hasUnicodeMarker()) {
+        char ctmp[2];
+        ctmp[0] = text->getChar((*i)-1);
+        ctmp[1] = text->getChar((*i));
+        ccToUnicode->mapToCharCode((Unicode*)ctmp, &c, 2);
+        if (c == '(' || c == ')' || c == '\\')
+          appearBuf->append('\\');
+        appearBuf->append(c);
+      } else {
+        c &= 0xff;
+        if (c == '(' || c == ')' || c == '\\') {
+          appearBuf->append('\\');
+          appearBuf->append(c);
+        } else if (c < 0x20 || c >= 0x80) {
+          appearBuf->appendf("\\{0:03o}", c);
+        } else {
+          appearBuf->append(c);
+        }
+      }
+    }
+  }
+}
+
+void AnnotWidget::generateFieldAppearance() {
+  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
+  Dict *field;
+  Dict *annot;
+  Dict *acroForm;
+  Dict *mkDict;
+  MemStream *appearStream;
+  GfxFontDict *fontDict;
+  GBool hasCaption;
+  double w, dx, dy, r;
+  double *dash;
+  GooString *caption, *da;
+  GooString **text;
+  GBool *selection;
+  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
+  GBool modified;
+
+  if (!widget->getField () || !widget->getField ()->getObj ()->isDict ())
+    return;
+
+  field = widget->getField ()->getObj ()->getDict ();
+  annot = widget->getObj ()->getDict ();
+  acroForm = form->getObj ()->getDict ();
+  
+  // do not regenerate appearence if widget has not changed
+  modified = widget->isModified ();
+
+  // only regenerate when it doesn't have an AP or
+  // it already has an AP but widget has been modified
+  if (!regen && !modified) {
+    return;
+  }
+
+  appearBuf = new GooString ();
+  // get the appearance characteristics (MK) dictionary
+  if (annot->lookup("MK", &mkObj)->isDict()) {
+    mkDict = mkObj.getDict();
+  } else {
+    mkDict = NULL;
+  }
+  // draw the background
+  if (mkDict) {
+    if (mkDict->lookup("BG", &obj1)->isArray() &&
+        obj1.arrayGetLength() > 0) {
+      setColor(obj1.getArray(), gTrue, 0);
+      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
+          rect->x2 - rect->x1, rect->y2 - rect->y1);
+    }
+    obj1.free();
+  }
+
+  // get the field type
+  Form::fieldLookup(field, "FT", &ftObj);
+
+  // get the field flags (Ff) value
+  if (Form::fieldLookup(field, "Ff", &obj1)->isInt()) {
+    ff = obj1.getInt();
+  } else {
+    ff = 0;
+  }
+  obj1.free();
+
+  // draw the border
+  if (mkDict && border) {
+    w = border->getWidth();
+    if (w > 0) {
+      mkDict->lookup("BC", &obj1);
+      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
+        mkDict->lookup("BG", &obj1);
+      }
+      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
+        dx = rect->x2 - rect->x1;
+        dy = rect->y2 - rect->y1;
+
+        // radio buttons with no caption have a round border
+        hasCaption = mkDict->lookup("CA", &obj2)->isString();
+        obj2.free();
+        if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
+          r = 0.5 * (dx < dy ? dx : dy);
+          switch (border->getStyle()) {
+            case AnnotBorder::borderDashed:
+              appearBuf->append("[");
+              dashLength = border->getDashLength();
+              dash = border->getDash();
+              for (i = 0; i < dashLength; ++i) {
+                appearBuf->appendf(" {0:.2f}", dash[i]);
+              }
+              appearBuf->append("] 0 d\n");
+              // fall through to the solid case
+            case AnnotBorder::borderSolid:
+            case AnnotBorder::borderUnderlined:
+              appearBuf->appendf("{0:.2f} w\n", w);
+              setColor(obj1.getArray(), gFalse, 0);
+              drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
+              break;
+            case AnnotBorder::borderBeveled:
+            case AnnotBorder::borderInset:
+              appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
+              setColor(obj1.getArray(), gFalse, 0);
+              drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
+              setColor(obj1.getArray(), gFalse,
+                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
+              drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
+              setColor(obj1.getArray(), gFalse,
+                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
+              drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
+              break;
+          }
+
+        } else {
+          switch (border->getStyle()) {
+            case AnnotBorder::borderDashed:
+              appearBuf->append("[");
+              dashLength = border->getDashLength();
+              dash = border->getDash();
+              for (i = 0; i < dashLength; ++i) {
+                appearBuf->appendf(" {0:.2f}", dash[i]);
+              }
+              appearBuf->append("] 0 d\n");
+              // fall through to the solid case
+            case AnnotBorder::borderSolid:
+              appearBuf->appendf("{0:.2f} w\n", w);
+              setColor(obj1.getArray(), gFalse, 0);
+              appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
+                  0.5 * w, dx - w, dy - w);
+              break;
+            case AnnotBorder::borderBeveled:
+            case AnnotBorder::borderInset:
+              setColor(obj1.getArray(), gTrue,
+                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
+              appearBuf->append("0 0 m\n");
+              appearBuf->appendf("0 {0:.2f} l\n", dy);
+              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
+              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
+              appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
+              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
+              appearBuf->append("f\n");
+              setColor(obj1.getArray(), gTrue,
+                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
+              appearBuf->append("0 0 m\n");
+              appearBuf->appendf("{0:.2f} 0 l\n", dx);
+              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
+              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
+              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
+              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
+              appearBuf->append("f\n");
+              break;
+            case AnnotBorder::borderUnderlined:
+              appearBuf->appendf("{0:.2f} w\n", w);
+              setColor(obj1.getArray(), gFalse, 0);
+              appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
+              break;
+          }
+
+          // clip to the inside of the border
+          appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
+              w, dx - 2 * w, dy - 2 * w);
+        }
+      }
+      obj1.free();
+    }
+  }
+
+  // get the resource dictionary
+  acroForm->lookup("DR", &drObj);
+
+  // build the font dictionary
+  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
+    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
+  } else {
+    fontDict = NULL;
+  }
+  obj1.free();
+
+  // get the default appearance string
+  if (Form::fieldLookup(field, "DA", &obj1)->isNull()) {
+    obj1.free();
+    acroForm->lookup("DA", &obj1);
+  }
+  if (obj1.isString()) {
+    da = obj1.getString()->copy();
+    //TODO: look for a font size / name HERE
+    // => create a function
+  } else {
+    da = NULL;
+  }
+  obj1.free();
+
+  // draw the field contents
+  if (ftObj.isName("Btn")) {
+    caption = NULL;
+    if (mkDict) {
+      if (mkDict->lookup("CA", &obj1)->isString()) {
+        caption = obj1.getString()->copy();
+      }
+      obj1.free();
+    }
+    // radio button
+    if (ff & fieldFlagRadio) {
+      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
+      if (Form::fieldLookup(field, "V", &obj1)->isName()) {
+        if (annot->lookup("AS", &obj2)->isName(obj1.getName()) &&
+	    strcmp (obj1.getName(), "Off") != 0) {
+          if (caption) {
+            drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
+                gFalse, gTrue);
+          } else {
+            if (mkDict) {
+              if (mkDict->lookup("BC", &obj3)->isArray() &&
+                  obj3.arrayGetLength() > 0) {
+                dx = rect->x2 - rect->x1;
+                dy = rect->y2 - rect->y1;
+                setColor(obj3.getArray(), gTrue, 0);
+                drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
+                    gTrue);
+              }
+              obj3.free();
+            }
+          }
+        }
+        obj2.free();
+      }
+      obj1.free();
+      // pushbutton
+    } else if (ff & fieldFlagPushbutton) {
+      if (caption) {
+        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
+            gFalse, gFalse);
+      }
+      // checkbox
+    } else {
+      // According to the PDF spec the off state must be named "Off",
+      // and the on state can be named anything, but Acrobat apparently
+      // looks for "Yes" and treats anything else as off.
+      if (Form::fieldLookup(field, "V", &obj1)->isName("Yes")) {
+        if (!caption) {
+          caption = new GooString("3"); // ZapfDingbats checkmark
+        }
+        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
+            gFalse, gTrue);
+      }
+      obj1.free();
+    }
+    if (caption) {
+      delete caption;
+    }
+  } else if (ftObj.isName("Tx")) {
+    //~ value strings can be Unicode
+    if (Form::fieldLookup(field, "V", &obj1)->isString()) {
+      if (Form::fieldLookup(field, "Q", &obj2)->isInt()) {
+        quadding = obj2.getInt();
+      } else {
+        quadding = fieldQuadLeft;
+      }
+      obj2.free();
+      comb = 0;
+      if (ff & fieldFlagComb) {
+        if (Form::fieldLookup(field, "MaxLen", &obj2)->isInt()) {
+          comb = obj2.getInt();
+        }
+        obj2.free();
+      }
+      drawText(obj1.getString(), da, fontDict,
+          ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, ff & fieldFlagPassword);
+    }
+    obj1.free();
+  } else if (ftObj.isName("Ch")) {
+    //~ value/option strings can be Unicode
+    if (Form::fieldLookup(field, "Q", &obj1)->isInt()) {
+      quadding = obj1.getInt();
+    } else {
+      quadding = fieldQuadLeft;
+    }
+    obj1.free();
+    // combo box
+    if (ff & fieldFlagCombo) {
+      if (Form::fieldLookup(field, "V", &obj1)->isString()) {
+        drawText(obj1.getString(), da, fontDict,
+            gFalse, 0, quadding, gTrue, gFalse);
+        //~ Acrobat draws a popup icon on the right side
+      }
+      obj1.free();
+      // list box
+    } else {
+      if (field->lookup("Opt", &obj1)->isArray()) {
+        nOptions = obj1.arrayGetLength();
+        // get the option text
+        text = (GooString **)gmallocn(nOptions, sizeof(GooString *));
+        for (i = 0; i < nOptions; ++i) {
+          text[i] = NULL;
+          obj1.arrayGet(i, &obj2);
+          if (obj2.isString()) {
+            text[i] = obj2.getString()->copy();
+          } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
+            if (obj2.arrayGet(1, &obj3)->isString()) {
+              text[i] = obj3.getString()->copy();
+            }
+            obj3.free();
+          }
+          obj2.free();
+          if (!text[i]) {
+            text[i] = new GooString();
+          }
+        }
+        // get the selected option(s)
+        selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
+        //~ need to use the I field in addition to the V field
+	Form::fieldLookup(field, "V", &obj2);
+        for (i = 0; i < nOptions; ++i) {
+          selection[i] = gFalse;
+          if (obj2.isString()) {
+            if (!obj2.getString()->cmp(text[i])) {
+              selection[i] = gTrue;
+            }
+          } else if (obj2.isArray()) {
+            for (j = 0; j < obj2.arrayGetLength(); ++j) {
+              if (obj2.arrayGet(j, &obj3)->isString() &&
+                  !obj3.getString()->cmp(text[i])) {
+                selection[i] = gTrue;
+              }
+              obj3.free();
+            }
+          }
+        }
+        obj2.free();
+        // get the top index
+        if (field->lookup("TI", &obj2)->isInt()) {
+          topIdx = obj2.getInt();
+        } else {
+          topIdx = 0;
+        }
+        obj2.free();
+        // draw the text
+        drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
+        for (i = 0; i < nOptions; ++i) {
+          delete text[i];
+        }
+        gfree(text);
+        gfree(selection);
+      }
+      obj1.free();
+    }
+  } else if (ftObj.isName("Sig")) {
+    //~unimp
+  } else {
+    error(-1, "Unknown field type");
+  }
+
+  if (da) {
+    delete da;
+  }
+
+  // build the appearance stream dictionary
+  appearDict.initDict(xref);
+  appearDict.dictAdd(copyString("Length"),
+      obj1.initInt(appearBuf->getLength()));
+  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+  obj1.initArray(xref);
+  obj1.arrayAdd(obj2.initReal(0));
+  obj1.arrayAdd(obj2.initReal(0));
+  obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
+  obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
+  appearDict.dictAdd(copyString("BBox"), &obj1);
+
+  // set the resource dictionary
+  if (drObj.isDict()) {
+    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+  }
+  drObj.free();
+
+  // build the appearance stream
+  appearStream = new MemStream(strdup(appearBuf->getCString()), 0,
+      appearBuf->getLength(), &appearDict);
+  appearance.free();
+  appearance.initStream(appearStream);
+  delete appearBuf;
+
+  appearStream->setNeedFree(gTrue);
+
+  if (widget->isModified()) {
+    //create a new object that will contains the new appearance
+    
+    //if we already have a N entry in our AP dict, reuse it
+    if (annot->lookup("AP", &obj1)->isDict() &&
+        obj1.dictLookupNF("N", &obj2)->isRef()) {
+      appRef = obj2.getRef();
+    }
+
+    // this annot doesn't have an AP yet, create one
+    if (appRef.num == 0)
+      appRef = xref->addIndirectObject(&appearance);
+    else // since we reuse the already existing AP, we have to notify the xref about this update
+      xref->setModifiedObject(&appearance, appRef);
+
+    // update object's AP and AS
+    Object apObj;
+    apObj.initDict(xref);
+
+    Object oaRef;
+    oaRef.initRef(appRef.num, appRef.gen);
+
+    apObj.dictSet("N", &oaRef);
+    annot->set("AP", &apObj);
+    Dict* d = new Dict(annot);
+    Object dictObj;
+    dictObj.initDict(d);
+
+    xref->setModifiedObject(&dictObj, ref);
+  }
+
+  if (fontDict) {
+    delete fontDict;
+  }
+  ftObj.free();
+  mkObj.free();
+}
+
+
+void AnnotWidget::draw(Gfx *gfx, GBool printing) {
+  Object obj;
+
+  // check the flags
+  if ((flags & annotFlagHidden) ||
+      (printing && !(flags & annotFlagPrint)) ||
+      (!printing && (flags & annotFlagNoView))) {
+    return;
+  }
+
+  generateFieldAppearance ();
+
+  // draw the appearance stream
+  appearance.fetch(xref, &obj);
+  gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
+		 rect->x1, rect->y1, rect->x2, rect->y2);
+  obj.free();
+}
+
 //------------------------------------------------------------------------
 // Annots
 //------------------------------------------------------------------------
 
 Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
-  Dict *acroForm;
   Annot *annot;
   Object obj1;
   int size;
@@ -2846,8 +2868,6 @@ Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
   size = 0;
   nAnnots = 0;
 
-  acroForm = catalog->getAcroForm()->isDict() ?
-    catalog->getAcroForm()->getDict() : NULL;
   if (annotsObj->isArray()) {
     for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
       //get the Ref to this annot and pass it to Annot constructor 
@@ -2856,7 +2876,7 @@ Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
       Object obj2;
       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
         annotsObj->arrayGetNF(i, &obj2);
-        annot = createAnnot (xref, acroForm, obj1.getDict(), catalog, &obj2);
+        annot = createAnnot (xref, obj1.getDict(), catalog, &obj2);
         if (annot && annot->isOk()) {
           if (nAnnots >= size) {
             size += 16;
@@ -2873,7 +2893,7 @@ Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
   }
 }
 
-Annot *Annots::createAnnot(XRef *xref, Dict *acroForm, Dict* dict, Catalog *catalog, Object *obj) {
+Annot *Annots::createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj) {
   Annot *annot;
   Object obj1;
 
@@ -2881,55 +2901,55 @@ Annot *Annots::createAnnot(XRef *xref, Dict *acroForm, Dict* dict, Catalog *cata
     GooString *typeName = new GooString(obj1.getName());
 
     if (!typeName->cmp("Text")) {
-      annot = new AnnotText(xref, acroForm, dict, catalog, obj);
+      annot = new AnnotText(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Link")) {
-      annot = new AnnotLink(xref, acroForm, dict, catalog, obj);
+      annot = new AnnotLink(xref, dict, catalog, obj);
     } else if (!typeName->cmp("FreeText")) {
-      annot = new AnnotFreeText(xref, acroForm, dict, catalog, obj);
+      annot = new AnnotFreeText(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Line")) {
-      annot = new AnnotLine(xref, acroForm, dict, catalog, obj);
+      annot = new AnnotLine(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Square")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Circle")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Polygon")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("PolyLine")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Highlight")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Underline")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Squiggly")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("StrikeOut")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Stamp")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Caret")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Ink")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("FileAttachment")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Sound")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Movie")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Widget")) {
-      annot = new AnnotWidget(xref, acroForm, dict, catalog, obj);
+      annot = new AnnotWidget(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Screen")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("PrinterMark")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("TrapNet")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("Watermark")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else if (!typeName->cmp("3D")) {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     } else {
-      annot = new Annot(xref, acroForm, dict, catalog, obj);
+      annot = new Annot(xref, dict, catalog, obj);
     }
 
     delete typeName;
@@ -2941,69 +2961,6 @@ Annot *Annots::createAnnot(XRef *xref, Dict *acroForm, Dict* dict, Catalog *cata
   return annot;
 }
 
-void Annots::generateAppearances(Dict *acroForm) {
-  Object obj1, obj2;
-  Ref ref;
-  int i;
-  
-  if (acroForm->lookup("Fields", &obj1)->isArray()) {
-    for (i = 0; i < obj1.arrayGetLength(); ++i) {
-      if (obj1.arrayGetNF(i, &obj2)->isRef()) {
-        ref = obj2.getRef();
-        obj2.free();
-        obj1.arrayGet(i, &obj2);
-      } else {
-        ref.num = ref.gen = -1;
-      }
-      if (obj2.isDict()) {
-        scanFieldAppearances(obj2.getDict(), &ref, NULL, acroForm);
-      }
-      obj2.free();
-    }
-  }
-  obj1.free();
-}
-
-void Annots::scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
-    Dict *acroForm) {
-  Annot *annot;
-  Object obj1, obj2;
-  Ref ref2;
-  int i;
-
-  // non-terminal node: scan the children
-  if (node->lookup("Kids", &obj1)->isArray()) {
-    for (i = 0; i < obj1.arrayGetLength(); ++i) {
-      if (obj1.arrayGetNF(i, &obj2)->isRef()) {
-        ref2 = obj2.getRef();
-        obj2.free();
-        obj1.arrayGet(i, &obj2);
-      } else {
-        ref2.num = ref2.gen = -1;
-      }
-      if (obj2.isDict()) {
-        scanFieldAppearances(obj2.getDict(), &ref2, node, acroForm);
-      }
-      obj2.free();
-    }
-    obj1.free();
-    return;
-  }
-  obj1.free();
-
-  // terminal node: this is either a combined annot/field dict, or an
-  // annot dict whose parent is a field
-  if ((annot = findAnnot(ref))) {
-    node->lookupNF("Parent", &obj1);
-    if (!parent || !obj1.isNull()) {
-      annot->generateFieldAppearance(node, node, acroForm);
-    } else {
-      annot->generateFieldAppearance(parent, node, acroForm);
-    }
-    obj1.free();
-  }
-}
-
 Annot *Annots::findAnnot(Ref *ref) {
   int i;
 
diff --git a/poppler/Annot.h b/poppler/Annot.h
index d9cc039..fecb5bd 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -19,6 +19,7 @@ class Catalog;
 class CharCodeToUnicode;
 class GfxFont;
 class GfxFontDict;
+class Form;
 class FormWidget;
 class PDFRectangle;
 
@@ -437,21 +438,18 @@ public:
     type3D              // 3D             25
   };
 
-  Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog* catalog);
-  Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  Annot(XRef *xrefA, Dict *dict, Catalog* catalog);
+  Annot(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~Annot();
   GBool isOk() { return ok; }
 
-  void draw(Gfx *gfx, GBool printing);
+  virtual void draw(Gfx *gfx, GBool printing);
   // Get appearance object.
   Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
-  GBool textField() { return isTextField; }
 
   GBool match(Ref *refA)
     { return ref.num == refA->num && ref.gen == refA->gen; }
 
-  void generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm);
-
   double getXMin();
   double getYMin();
 
@@ -473,27 +471,17 @@ public:
   Dict *getOptionalContent() const { return optionalContent; }
 
 private:
-  void setColor(Array *a, GBool fill, int adjust);
-  void drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
-		GBool multiline, int comb, int quadding,
-		GBool txField, GBool forceZapfDingbats,
-    GBool password=false);
-  void drawListBox(GooString **text, GBool *selection,
-		   int nOptions, int topIdx,
-		   GooString *da, GfxFontDict *fontDict, GBool quadding);
-  void getNextLine(GooString *text, int start,
-		   GfxFont *font, double fontSize, double wMax,
-		   int *end, double *width, int *next);
-  void drawCircle(double cx, double cy, double r, GBool fill);
-  void drawCircleTopLeft(double cx, double cy, double r);
-  void drawCircleBottomRight(double cx, double cy, double r);
   void readArrayNum(Object *pdfArray, int key, double *value);
   // write vStr[i:j[ in appearBuf
-  void writeTextString (GooString *text, GooString *appearBuf, int *i, int j, CharCodeToUnicode *ccToUnicode, GBool password); 
 
-  void initialize (XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog);
+  void initialize (XRef *xrefA, Dict *dict, Catalog *catalog);
 
 protected:
+  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);
+  void drawCircleBottomRight(double cx, double cy, double r);
+  
   // required data
   AnnotSubtype type;                // Annotation type
   PDFRectangle *rect;               // Rect
@@ -514,14 +502,11 @@ protected:
 
   XRef *xref;			// the xref table for this PDF file
   Ref ref;                      // object ref identifying this annotation
-  FormWidget *widget;           // FormWidget object for this annotation
   GooString *appearBuf;
   AnnotBorder *border;          // Border, BS
   AnnotColor *color;            // C
   double fontSize; 
   GBool ok;
-  GBool regen, isTextField;
-  GBool isMultiline, isListbox;
 
   bool hasRef;
 };
@@ -532,14 +517,14 @@ protected:
 
 class AnnotPopup: public Annot {
 public:
-  AnnotPopup(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotPopup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~AnnotPopup();
 
   Dict *getParent() const { return parent; }
   GBool getOpen() const { return open; }
 
 protected:
-  void initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog);
+  void initialize(XRef *xrefA, Dict *dict, Catalog *catalog);
 
   Dict *parent; // Parent
   GBool open;   // Open
@@ -556,7 +541,7 @@ public:
     replyTypeGroup  // Group
   };
 
-  AnnotMarkup(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~AnnotMarkup();
 
   // getters
@@ -585,7 +570,7 @@ protected:
   AnnotExternalDataType exData; // ExData
 
 private:
-  void initialize(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  void initialize(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
 };
 
 //------------------------------------------------------------------------
@@ -617,7 +602,7 @@ public:
     stateNone       // None
   };
 
-  AnnotText(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
 
   // getters
   GBool getOpen() const { return open; }
@@ -652,9 +637,11 @@ public:
     effectPush      // P
   };
 
-  AnnotLink(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotLink(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~AnnotLink();
 
+  virtual void draw(Gfx *gfx, GBool printing);
+
   // getters
   Dict *getActionDict() const { return actionDict; }
   // getDest
@@ -693,7 +680,7 @@ public:
     intentFreeTextTypeWriter  // FreeTextTypeWriter
   };
 
-  AnnotFreeText(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotFreeText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~AnnotFreeText();
 
   // getters
@@ -744,7 +731,7 @@ public:
     captionPosTop     // Top
   };
 
-  AnnotLine(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotLine(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~AnnotLine();
 
   // getters
@@ -818,19 +805,38 @@ public:
     highlightModePush     // P,T
   };
 
-  AnnotWidget(XRef *xrefA, Dict *acroForm, Dict *dict, Catalog *catalog, Object *obj);
+  AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
   virtual ~AnnotWidget();
 
+  virtual void draw(Gfx *gfx, GBool printing);
+
+  void generateFieldAppearance ();
+
   AnnotWidgetHighlightMode getMode() { return mode; }
   AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
   Dict *getAction() { return action; }
   Dict *getAdditionActions() { return additionActions; }
   Dict *getParent() { return parent; }
 
-protected:
+private:
 
   void initialize(XRef *xrefA, Catalog *catalog, Dict *dict);
-  
+
+  void drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
+		GBool multiline, int comb, int quadding,
+		GBool txField, GBool forceZapfDingbats,
+		GBool password=false);
+  void drawListBox(GooString **text, GBool *selection,
+		   int nOptions, int topIdx,
+		   GooString *da, GfxFontDict *fontDict, GBool quadding);
+  void getNextLine(GooString *text, int start,
+		   GfxFont *font, double fontSize, double wMax,
+		   int *end, double *width, int *next);
+  void writeTextString (GooString *text, GooString *appearBuf, int *i, int j,
+			CharCodeToUnicode *ccToUnicode, GBool password); 
+
+  Form *form;
+  FormWidget *widget;                     // FormWidget object for this annotation
   AnnotWidgetHighlightMode mode;          // H  (Default I)
   AnnotAppearanceCharacs *appearCharacs;  // MK
   Dict *action;                           // A
@@ -838,6 +844,7 @@ protected:
   // inherited  from Annot
   // AnnotBorderBS border;                // BS
   Dict *parent;                           // Parent
+  GBool regen;
 };
 
 //------------------------------------------------------------------------
@@ -856,14 +863,8 @@ public:
   int getNumAnnots() { return nAnnots; }
   Annot *getAnnot(int i) { return annots[i]; }
 
-  // (Re)generate the appearance streams for all annotations belonging
-  // to a form field.
-  void generateAppearances(Dict *acroForm);
-
 private:
-  Annot* createAnnot(XRef *xref, Dict *acroForm, Dict* dict, Catalog *catalog, Object *obj);
-  void scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
-			    Dict *acroForm);
+  Annot* createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj);
   Annot *findAnnot(Ref *ref);
 
   Annot **annots;
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 0c0e2df..c60e127 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -1089,34 +1089,38 @@ FormFieldSignature::~FormFieldSignature()
 
 Form::Form(XRef *xrefA, Object* acroFormA)
 {
-  Array *array = NULL;
   Object obj1;
+
   xref = xrefA;
   acroForm = acroFormA;
-  acroForm->dictLookup("Fields",&obj1);
-  if (obj1.isArray()) array = obj1.getArray();
-  obj1.free();
-  if(!array) {
-    error(-1, "Can't get Fields array\n");
-  }
+  
   size = 0;
   numFields = 0;
   rootFields = NULL;
-  if (array) {
+
+  acroForm->dictLookup("NeedAppearances", &obj1);
+  needAppearances = (obj1.isBool() && obj1.getBool());
+  obj1.free();
+
+  acroForm->dictLookup("Fields", &obj1);
+  if (obj1.isArray()) {
+    Array *array = obj1.getArray();
+    Object obj2;
+    
     for(int i=0; i<array->getLength(); i++) {
       Object oref;
-      array->get(i, &obj1);
+      array->get(i, &obj2);
       array->getNF(i, &oref);
       if (!oref.isRef()) {
         error(-1, "Direct object in rootFields");
-	obj1.free();
+	obj2.free();
 	oref.free();
         continue;
       }
 
-      if (!obj1.isDict()) {
+      if (!obj2.isDict()) {
         error(-1, "Reference in Fields array to an invalid or non existant object");
-	obj1.free();
+	obj2.free();
 	oref.free();
 	continue;
       }
@@ -1126,21 +1130,24 @@ Form::Form(XRef *xrefA, Object* acroFormA)
         rootFields = (FormField**)greallocn(rootFields,size,sizeof(FormField*));
       }
 
-      rootFields[numFields++] = createFieldFromDict (&obj1, xrefA, oref.getRef());
+      rootFields[numFields++] = createFieldFromDict (&obj2, xrefA, oref.getRef());
 
       //Mark readonly field
       Object obj3;
-      if (Form::fieldLookup(obj1.getDict (), "Ff", &obj3)->isInt()) {
+      if (Form::fieldLookup(obj2.getDict (), "Ff", &obj3)->isInt()) {
         int flags = obj3.getInt();
         if (flags & 0x1)
           rootFields[numFields-1]->setReadOnly(true);
       }
       obj3.free();
 
-      obj1.free();
+      obj2.free();
       oref.free();
     }
+  } else {
+    error(-1, "Can't get Fields array\n");
   }
+  obj1.free ();
 }
 
 Form::~Form() {
@@ -1246,7 +1253,7 @@ FormPageWidgets::FormPageWidgets (XRef *xrefA, Object* annots, unsigned int page
         if (annots->arrayGet(i, &obj2)->isDict()) {
           Annot *ann;
 	  
-          ann = new Annot(xref, NULL ,obj2.getDict(), NULL);
+          ann = new Annot(xref, obj2.getDict(), NULL);
           tmp->setFontSize(ann->getFontSize());
           delete ann;
         }
diff --git a/poppler/Form.h b/poppler/Form.h
index 2f70c20..39938bb 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -435,6 +435,7 @@ public:
   static FormField *createFieldFromDict (Object* obj, XRef *xref, const Ref& aref);
 
   Object *getObj () const { return acroForm; }
+  GBool getNeedAppearances () const { return needAppearances; }
   int getNumFields() const { return numFields; }
   FormField* getRootField(int i) const { return rootFields[i]; }
 
@@ -447,6 +448,7 @@ private:
   int size;
   XRef* xref;
   Object *acroForm;
+  GBool needAppearances;
 };
 
 //------------------------------------------------------------------------
diff --git a/poppler/Page.cc b/poppler/Page.cc
index ddb0b60..906d18e 100644
--- a/poppler/Page.cc
+++ b/poppler/Page.cc
@@ -393,7 +393,6 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
   Gfx *gfx;
   Object obj;
   Annots *annotList;
-  Dict *acroForm;
   int i;
   
   if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
@@ -417,15 +416,9 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
   }
   obj.free();
 
-
   // draw annotations
   annotList = new Annots(xref, catalog, getAnnots(&obj));
   obj.free();
-  acroForm = catalog->getAcroForm()->isDict() ?
-               catalog->getAcroForm()->getDict() : NULL;
-  if (acroForm) {
-    annotList->generateAppearances(acroForm);
-  }
   
   if (annotList->getNumAnnots() > 0) {
     if (globalParams->getPrintCommands()) {
@@ -437,7 +430,7 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
              (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || 
             !annotDisplayDecideCbk) {
              annotList->getAnnot(i)->draw(gfx, printing);
-    }
+	}
     }
     out->dump();
   }
commit 78afbc4d7819654e8742d4457c9847ad02b73bf1
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Feb 10 17:12:59 2008 +0100

    Some code refactoring and cleanup
    
     - Unused 'field' attribute in FormWidget class is now used and
       getField() method has been added
     - Method isReadOnly() in FormWidget class is not virtual anymore
       but implemented in the base class
     - Unused attribute 'catalog' in Form class has been removed and
       'acroForm' has been added instead with a getObj() method to get it.
     - createFieldFromDict() method is now static

diff --git a/poppler/Form.cc b/poppler/Form.cc
index 4695600..0c0e2df 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -47,7 +47,7 @@ char* pdfDocEncodingToUTF16 (GooString* orig, int* length)
 
 
 
-FormWidget::FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref) 
+FormWidget::FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref, FormField *fieldA) 
 {
   Object obj1, obj2;
   ref = aref;
@@ -60,6 +60,7 @@ FormWidget::FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref)
   xref = xrefA;
   aobj->copy(&obj);
   type = formUndef;
+  field = fieldA;
   Dict *dict = obj.getDict();
 
   if (!dict->lookup("Rect", &obj1)->isArray()) {
@@ -117,6 +118,7 @@ FormWidget::FormWidget(FormWidget *dest)
   y2 = dest->x2;
 
   type = dest->type;
+  field = dest->field;
 
 }
 
@@ -125,6 +127,11 @@ FormWidget::~FormWidget()
   obj.free ();
 }
 
+bool FormWidget::isReadOnly() const
+{
+  return field->isReadOnly();
+}
+
 int FormWidget::encodeID (unsigned pageNum, unsigned fieldNum)
 {
   return (pageNum << 4*sizeof(unsigned)) + fieldNum;
@@ -136,10 +143,11 @@ void FormWidget::decodeID (unsigned id, unsigned* pageNum, unsigned* fieldNum)
   *fieldNum = (id << 4*sizeof(unsigned)) >> 4*sizeof(unsigned);
 }
 
-FormWidgetButton::FormWidgetButton (XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormFieldButton *p) : FormWidget(xrefA, aobj, num, ref)
+FormWidgetButton::FormWidgetButton (XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormField *p) :
+	FormWidget(xrefA, aobj, num, ref, p)
 {
-  parent = p;
   type = formButton;
+  parent = static_cast<FormFieldButton*>(field);
   onStr = NULL;
   state = gFalse;
   siblingsID = NULL;
@@ -183,18 +191,13 @@ void FormWidgetButton::setState (GBool astate, GBool calledByParent)
   xref->setModifiedObject(&obj, ref);
 }
 
-bool FormWidgetButton::isReadOnly() const
-{
-  return parent->isReadOnly();
-}
-
 void FormWidgetButton::loadDefaults ()
 {
   if (defaultsLoaded)
     return;
 
   defaultsLoaded = gTrue;
-  
+
   Dict *dict = obj.getDict();
   Object obj1;
 
@@ -264,10 +267,11 @@ void FormWidgetButton::setNumSiblingsID (int i)
 }
 
 
-FormWidgetText::FormWidgetText (XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormFieldText *p) : FormWidget(xrefA, aobj, num, ref)
+FormWidgetText::FormWidgetText (XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormField *p) :
+	FormWidget(xrefA, aobj, num, ref, p)
 {
-  parent = p;
   type = formText;
+  parent = static_cast<FormFieldText*>(field);
 }
 
 void FormWidgetText::loadDefaults ()
@@ -276,7 +280,7 @@ void FormWidgetText::loadDefaults ()
     return;
 
   defaultsLoaded = gTrue;
-  
+
   Dict *dict = obj.getDict();
   Object obj1;
 
@@ -347,11 +351,6 @@ bool FormWidgetText::isRichText () const
   return parent->isRichText(); 
 }
 
-bool FormWidgetText::isReadOnly () const
-{
-  return parent->isReadOnly();
-}
-
 int FormWidgetText::getMaxLen () const
 {
   return parent->getMaxLen ();
@@ -384,10 +383,11 @@ void FormWidgetText::setContent(GooString* new_content)
   }
 }
 
-FormWidgetChoice::FormWidgetChoice(XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormFieldChoice *p) : FormWidget(xrefA, aobj, num, ref)
+FormWidgetChoice::FormWidgetChoice(XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormField *p) :
+	FormWidget(xrefA, aobj, num, ref, p)
 {
-  parent = p;
   type = formChoice;
+  parent = static_cast<FormFieldChoice*>(field);
 }
 
 void FormWidgetChoice::loadDefaults ()
@@ -396,7 +396,7 @@ void FormWidgetChoice::loadDefaults ()
     return;
 
   defaultsLoaded = gTrue;
-  
+
   Dict *dict = obj.getDict();
   Object obj1;
   if (dict->lookup("Opt", &obj1)->isArray()) {
@@ -460,10 +460,10 @@ void FormWidgetChoice::loadDefaults ()
 
   //convert choice's human readable strings to UTF16
   //and update the /Opt dict entry to reflect this change
-  #ifdef UPDATE_OPT
+#ifdef UPDATE_OPT
   Object *objOpt = new Object();
   objOpt->initArray(xref);
-  #endif
+#endif
   for(int i=0; i<parent->getNumChoices(); i++) {
         if (parent->getChoice(i)->hasUnicodeMarker()) { //string already in UTF16, do nothing
 
@@ -484,10 +484,10 @@ void FormWidgetChoice::loadDefaults ()
     if (tmpCurrentChoice[i])
       parent->select(i);
   }
-  #ifdef UPDATE_OPT
+#ifdef UPDATE_OPT
   obj.getDict()->set("Opt", objOpt);
   xref->setModifiedObject(&obj, ref); 
-  #endif
+#endif
   delete [] tmpCurrentChoice;
 }
 
@@ -623,7 +623,6 @@ GooString* FormWidgetChoice::getChoice(int i)
   return parent->getChoice(i); 
 }
 
-
 bool FormWidgetChoice::isCombo () const 
 { 
   return parent->isCombo(); 
@@ -649,32 +648,24 @@ bool FormWidgetChoice::commitOnSelChange () const
   return parent->commitOnSelChange(); 
 }
 
-bool FormWidgetChoice::isReadOnly () const
-{
-  return parent->isReadOnly();
-}
-
 bool FormWidgetChoice::isListBox () const
 {
   return parent->isListBox();
 }
 
-FormWidgetSignature::FormWidgetSignature(XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormFieldSignature *p) : FormWidget(xrefA, aobj, num, ref)
+FormWidgetSignature::FormWidgetSignature(XRef *xrefA, Object *aobj, unsigned num, Ref ref, FormField *p) :
+	FormWidget(xrefA, aobj, num, ref, p)
 {
-  parent = p;
   type = formSignature;
+  parent = static_cast<FormFieldSignature*>(field);
 }
 
-bool FormWidgetSignature::isReadOnly () const
-{
-  return parent->isReadOnly();
-}
 
 //========================================================================
 // FormField
 //========================================================================
 
-FormField::FormField(XRef* xrefA, Object *aobj, const Ref& aref, Form* aform, FormFieldType ty) 
+FormField::FormField(XRef* xrefA, Object *aobj, const Ref& aref, FormFieldType ty) 
 {
   xref = xrefA;
   aobj->copy(&obj);
@@ -686,7 +677,6 @@ FormField::FormField(XRef* xrefA, Object *aobj, const Ref& aref, Form* aform, Fo
   terminal = false;
   widgets = NULL;
   readOnly = false;
-  form = aform;
   ref = aref;
 
   Object obj1;
@@ -721,7 +711,7 @@ FormField::FormField(XRef* xrefA, Object *aobj, const Ref& aref, Form* aform, Fo
         children = (FormField**)greallocn(children, numChildren, sizeof(FormField*));
 
         obj3.free();
-        form->createFieldFromDict (&obj2, &children[numChildren-1], xrefA, childRef.getRef());
+	children[numChildren-1] = Form::createFieldFromDict (&obj2, xrefA, childRef.getRef());
       }
       // 1 - we will handle 'collapsed' fields (field + annot in the same dict)
       // as if the annot was in the Kids array of the field
@@ -797,11 +787,20 @@ void FormField::_createWidget (Object *obj, Ref aref)
   numChildren++;
   widgets = (FormWidget**)greallocn(widgets, numChildren, sizeof(FormWidget*));
   //ID = index in "widgets" table
-  if(type==formButton) widgets[numChildren-1] = new FormWidgetButton(xref, obj, numChildren-1, aref, static_cast<FormFieldButton*>(this));
-  else if (type==formText) widgets[numChildren-1] = new FormWidgetText(xref, obj, numChildren-1, aref, static_cast<FormFieldText*>(this));
-  else if (type==formChoice) widgets[numChildren-1] = new FormWidgetChoice(xref, obj, numChildren-1, aref, static_cast<FormFieldChoice*>(this));
-  else if (type==formSignature) widgets[numChildren-1] = new FormWidgetSignature(xref, obj, numChildren-1, aref, static_cast<FormFieldSignature*>(this));
-  else  {
+  switch (type) {
+  case formButton:
+    widgets[numChildren-1] = new FormWidgetButton(xref, obj, numChildren-1, aref, this);
+    break;
+  case formText:
+    widgets[numChildren-1] = new FormWidgetText(xref, obj, numChildren-1, aref, this);
+    break;
+  case formChoice:
+    widgets[numChildren-1] = new FormWidgetChoice(xref, obj, numChildren-1, aref, this);
+    break;
+  case formSignature:
+    widgets[numChildren-1] = new FormWidgetSignature(xref, obj, numChildren-1, aref, this);
+    break;
+  default:
     error(-1, "SubType on non-terminal field, invalid document?");
     numChildren--;
     terminal = false;
@@ -829,10 +828,9 @@ FormWidget* FormField::findWidgetByRef (Ref aref)
 //------------------------------------------------------------------------
 // FormFieldButton
 //------------------------------------------------------------------------
-FormFieldButton::FormFieldButton(XRef *xrefA, Object *aobj, const Ref& ref, Form* form) 
-                                 : FormField(xrefA, aobj, ref, form, formButton)
+FormFieldButton::FormFieldButton(XRef *xrefA, Object *aobj, const Ref& ref) 
+	: FormField(xrefA, aobj, ref, formButton)
 {
-  type = formButton;
   Dict* dict = obj.getDict();
   active_child = -1;
   noAllOff = false;
@@ -924,10 +922,9 @@ FormFieldButton::~FormFieldButton()
 //------------------------------------------------------------------------
 // FormFieldText
 //------------------------------------------------------------------------
-FormFieldText::FormFieldText(XRef *xrefA, Object *aobj, const Ref& ref, Form* form) 
-                             : FormField(xrefA, aobj, ref, form, formText)
+FormFieldText::FormFieldText(XRef *xrefA, Object *aobj, const Ref& ref) 
+	: FormField(xrefA, aobj, ref, formText)
 {
-  type = formText;
   Dict* dict = obj.getDict();
   Object obj1;
   content = NULL;
@@ -982,8 +979,8 @@ FormFieldText::~FormFieldText()
 //------------------------------------------------------------------------
 // FormFieldChoice
 //------------------------------------------------------------------------
-FormFieldChoice::FormFieldChoice(XRef *xrefA, Object *aobj, const Ref& ref, Form* form) 
-                                 : FormField(xrefA, aobj, ref, form, formChoice)
+FormFieldChoice::FormFieldChoice(XRef *xrefA, Object *aobj, const Ref& ref) 
+	: FormField(xrefA, aobj, ref, formChoice)
 {
   numChoices = 0;
   choices = NULL;
@@ -1076,8 +1073,8 @@ void FormFieldChoice::_createChoicesTab ()
 //------------------------------------------------------------------------
 // FormFieldSignature
 //------------------------------------------------------------------------
-FormFieldSignature::FormFieldSignature(XRef *xrefA, Object *dict, const Ref& ref, Form* form)
-                                      : FormField(xrefA, dict, ref, form, formSignature)
+FormFieldSignature::FormFieldSignature(XRef *xrefA, Object *dict, const Ref& ref)
+	: FormField(xrefA, dict, ref, formSignature)
 {
 }
 
@@ -1090,11 +1087,12 @@ FormFieldSignature::~FormFieldSignature()
 // Form
 //------------------------------------------------------------------------
 
-Form::Form(XRef *xrefA, Object* acroForm)
+Form::Form(XRef *xrefA, Object* acroFormA)
 {
   Array *array = NULL;
   Object obj1;
   xref = xrefA;
+  acroForm = acroFormA;
   acroForm->dictLookup("Fields",&obj1);
   if (obj1.isArray()) array = obj1.getArray();
   obj1.free();
@@ -1128,7 +1126,7 @@ Form::Form(XRef *xrefA, Object* acroForm)
         rootFields = (FormField**)greallocn(rootFields,size,sizeof(FormField*));
       }
 
-      createFieldFromDict (&obj1, &rootFields[numFields++], xrefA, oref.getRef());
+      rootFields[numFields++] = createFieldFromDict (&obj1, xrefA, oref.getRef());
 
       //Mark readonly field
       Object obj3;
@@ -1171,23 +1169,27 @@ Object *Form::fieldLookup(Dict *field, char *key, Object *obj) {
   return obj;
 }
 
-void Form::createFieldFromDict (Object* obj, FormField** ptr, XRef *xrefA, const Ref& pref)
+FormField *Form::createFieldFromDict (Object* obj, XRef *xrefA, const Ref& pref)
 {
     Object obj2;
+    FormField *field;
 
     if (Form::fieldLookup(obj->getDict (), "FT", &obj2)->isName("Btn")) {
-      (*ptr) = new FormFieldButton(xrefA, obj, pref, this);
+      field = new FormFieldButton(xrefA, obj, pref);
     } else if (obj2.isName("Tx")) {
-      (*ptr) = new FormFieldText(xrefA, obj, pref, this);
+      field = new FormFieldText(xrefA, obj, pref);
     } else if (obj2.isName("Ch")) {
-      (*ptr) = new FormFieldChoice(xrefA, obj, pref, this);
+      field = new FormFieldChoice(xrefA, obj, pref);
     } else if (obj2.isName("Sig")) {
-      (*ptr) = new FormFieldSignature(xrefA, obj, pref, this);
+      field = new FormFieldSignature(xrefA, obj, pref);
     } else { //we don't have an FT entry => non-terminal field
-      (*ptr) = new FormField(xrefA, obj, pref, this);
+      field = new FormField(xrefA, obj, pref);
     }
     obj2.free();
-    (*ptr)->loadChildrenDefaults();
+    
+    field->loadChildrenDefaults();
+
+    return field;
 }
 
 void Form::postWidgetsLoad ()
diff --git a/poppler/Form.h b/poppler/Form.h
index 4cac8d8..2f70c20 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -67,6 +67,7 @@ public:
   unsigned getID () { return ID; }
   void setID (unsigned int i) { ID=i; }
 
+  FormField *getField () { return field; }
   FormFieldType getType() { return type; }
 
   Object* getObj() { return &obj; }
@@ -80,7 +81,7 @@ public:
 
   GBool isModified () { return modified; }
 
-  virtual bool isReadOnly() const = 0;
+  bool isReadOnly() const;
 
   // return the unique ID corresponding to pageNum/fieldNum
   static int encodeID (unsigned pageNum, unsigned fieldNum);
@@ -88,7 +89,7 @@ public:
   static void decodeID (unsigned id, unsigned* pageNum, unsigned* fieldNum);
 
 protected:
-  FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref);
+  FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref, FormField *fieldA);
   FormWidget(FormWidget *dest);
 
   FormField* field;
@@ -123,7 +124,7 @@ protected:
 
 class FormWidgetButton: public FormWidget {
 public:
-  FormWidgetButton(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldButton *p);
+  FormWidgetButton(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormField *p);
   ~FormWidgetButton ();
 
   FormButtonType getButtonType() const;
@@ -135,8 +136,6 @@ public:
 
   void loadDefaults();
 
-  bool isReadOnly () const;
-
   void setNumSiblingsID (int i);
   void setSiblingsID (int i, unsigned id) { siblingsID[i] = id; }
 
@@ -159,7 +158,7 @@ protected:
 
 class FormWidgetText: public FormWidget {
 public:
-  FormWidgetText(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldText *p);
+  FormWidgetText(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormField *p);
   //return the field's content (UTF16BE)
   GooString* getContent() ;
   //return a copy of the field's content (UTF16BE)
@@ -177,7 +176,6 @@ public:
   bool noScroll () const; 
   bool isComb () const; 
   bool isRichText () const;
-  bool isReadOnly () const;
   int getMaxLen () const;
 protected:
   FormFieldText *parent;
@@ -189,7 +187,7 @@ protected:
 
 class FormWidgetChoice: public FormWidget {
 public:
-  FormWidgetChoice(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldChoice *p);
+  FormWidgetChoice(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormField *p);
   ~FormWidgetChoice();
 
   void loadDefaults ();
@@ -218,7 +216,6 @@ public:
   bool isMultiSelect () const; 
   bool noSpellCheck () const; 
   bool commitOnSelChange () const; 
-  bool isReadOnly () const;
   bool isListBox () const;
 protected:
   void _updateV ();
@@ -232,8 +229,7 @@ protected:
 
 class FormWidgetSignature: public FormWidget {
 public:
-  FormWidgetSignature(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldSignature *p);
-  bool isReadOnly () const;
+  FormWidgetSignature(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormField *p);
 protected:
   FormFieldSignature *parent;
 };
@@ -247,7 +243,7 @@ protected:
 
 class FormField {
 public:
-  FormField(XRef* xrefa, Object *aobj, const Ref& aref, Form* aform, FormFieldType t=formUndef);
+  FormField(XRef* xrefa, Object *aobj, const Ref& aref, FormFieldType t=formUndef);
 
   virtual ~FormField();
 
@@ -279,7 +275,6 @@ public:
   FormField **children;
   int numChildren;
   FormWidget **widgets;
-  Form* form;
   bool readOnly;
 
 private:
@@ -293,7 +288,7 @@ private:
 
 class FormFieldButton: public FormField {
 public:
-  FormFieldButton(XRef *xrefA, Object *dict, const Ref& ref, Form* form);
+  FormFieldButton(XRef *xrefA, Object *dict, const Ref& ref);
 
   FormButtonType getButtonType () { return btype; }
 
@@ -318,7 +313,7 @@ protected:
 
 class FormFieldText: public FormField {
 public:
-  FormFieldText(XRef *xrefA, Object *dict, const Ref& ref, Form* form);
+  FormFieldText(XRef *xrefA, Object *dict, const Ref& ref);
   
   GooString* getContent () { return content; }
   GooString* getContentCopy ();
@@ -352,7 +347,7 @@ protected:
 
 class FormFieldChoice: public FormField {
 public:
-  FormFieldChoice(XRef *xrefA, Object *aobj, const Ref& ref, Form* form);
+  FormFieldChoice(XRef *xrefA, Object *aobj, const Ref& ref);
 
   virtual ~FormFieldChoice();
 
@@ -415,7 +410,7 @@ protected:
 
 class FormFieldSignature: public FormField {
 public:
-  FormFieldSignature(XRef *xrefA, Object *dict, const Ref& ref, Form* form);
+  FormFieldSignature(XRef *xrefA, Object *dict, const Ref& ref);
 
   virtual ~FormFieldSignature();
 };
@@ -432,24 +427,26 @@ public:
 
   ~Form();
 
+  // Look up an inheritable field dictionary entry.
   static Object *fieldLookup(Dict *field, char *key, Object *obj);
   
+  /* Creates a new Field of the type specified in obj's dict.
+     used in Form::Form and FormField::FormField */
+  static FormField *createFieldFromDict (Object* obj, XRef *xref, const Ref& aref);
+
+  Object *getObj () const { return acroForm; }
   int getNumFields() const { return numFields; }
   FormField* getRootField(int i) const { return rootFields[i]; }
 
   FormWidget* findWidgetByRef (Ref aref);
 
-  /* Creates a new Field of the type specified in obj's dict. 
-     used in Form::Form and FormField::FormField */
-  void createFieldFromDict (Object* obj, FormField** ptr, XRef *xref, const Ref& aref);
-
   void postWidgetsLoad();
 private:
   FormField** rootFields;
   int numFields;
   int size;
   XRef* xref;
-  Catalog* catalog;
+  Object *acroForm;
 };
 
 //------------------------------------------------------------------------


More information about the poppler mailing list