[poppler] poppler/poppler: Annot.cc, 1.6, 1.7 Annot.h, 1.3, 1.4 Catalog.cc, 1.18, 1.19 Catalog.h, 1.10, 1.11 CharCodeToUnicode.cc, 1.4, 1.5 CharCodeToUnicode.h, 1.2, 1.3 Dict.cc, 1.5, 1.6 Dict.h, 1.4, 1.5 Form.cc, NONE, 1.1 Form.h, NONE, 1.1 GfxFont.cc, 1.9, 1.10 GfxFont.h, 1.4, 1.5 Makefile.am, 1.27, 1.28 Object.h, 1.3, 1.4 Page.cc, 1.15, 1.16 Page.h, 1.7, 1.8 XRef.cc, 1.13, 1.14 XRef.h, 1.6, 1.7

Albert Astals Cid aacid at kemper.freedesktop.org
Sat Feb 24 15:32:26 PST 2007


Update of /cvs/poppler/poppler/poppler
In directory kemper:/tmp/cvs-serv6254/poppler

Modified Files:
	Annot.cc Annot.h Catalog.cc Catalog.h CharCodeToUnicode.cc 
	CharCodeToUnicode.h Dict.cc Dict.h GfxFont.cc GfxFont.h 
	Makefile.am Object.h Page.cc Page.h XRef.cc XRef.h 
Added Files:
	Form.cc Form.h 
Log Message:
2007-02-25  Julien Rebetez  <julienr at svn.gnome.org>
            reviewed by: <aacid at kde.org>

        * glib/poppler-document.cc:
        * glib/poppler-document.h:
        * glib/poppler-page.cc:
        * glib/poppler-page.h:
        * glib/poppler-private.h:
        * glib/poppler.h:
        * poppler/Annot.cc:
        * poppler/Annot.h:
        * poppler/Catalog.cc:
        * poppler/Catalog.h:
        * poppler/CharCodeToUnicode.cc:
        * poppler/CharCodeToUnicode.h:
        * poppler/Dict.cc:
        * poppler/Dict.h:
        * poppler/Form.cc:
        * poppler/Form.h:
        * poppler/GfxFont.cc:
        * poppler/GfxFont.h:
        * poppler/Makefile.am:
        * poppler/Object.h:
        * poppler/Page.cc:
        * poppler/Page.h:
        * poppler/XRef.cc:
        * poppler/XRef.h:
                Beginning of Interactive Form support:
                Add a bunch of new classes (FormWidget / FormField) to deal with form
                fields.
                Add support for object modification through XRef::setModifiedObject, as
                well as a function to write the Xref to a file, which will be used
                to implement PDF writing.
                Add some functions to glib wrapper to expose the new form features.



Index: Annot.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Annot.cc,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- Annot.cc	27 Dec 2006 23:15:06 -0000	1.6
+++ Annot.cc	24 Feb 2007 23:32:23 -0000	1.7
@@ -20,19 +20,30 @@
 #include "Lexer.h"
 #include "UGooString.h"
 #include "Annot.h"
+#include "GfxFont.h"
+#include "CharCodeToUnicode.h"
+#include "Form.h"
+#include "Error.h"
 
 //------------------------------------------------------------------------
 // Annot
 //------------------------------------------------------------------------
 
-Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) {
+Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref* aref, Catalog* catalog) {
   Object apObj, asObj, obj1, obj2;
-  GBool regen, isTextField;
   double t;
 
   ok = gTrue;
   xref = xrefA;
   appearBuf = NULL;
+  fontSize = 0;
+  widget = NULL;
+
+  if (aref) {
+    hasRef = true;
+    ref = *aref;
+  } else 
+    hasRef = false;
 
   if (dict->lookup("Rect", &obj1)->isArray() &&
       obj1.arrayGetLength() == 4) {
@@ -59,6 +70,16 @@
   }
   obj1.free();
 
+  //check for hidden annot
+  hidden = false;
+  Object obj3;
+  if (dict->lookup("F", &obj3)->isInt()) {
+    int flags = obj3.getInt();
+    if (flags & 0x2) 
+      hidden = true;
+  }
+  obj3.free();
+
   // check if field apperances need to be regenerated
   regen = gFalse;
   if (acroForm) {
@@ -68,16 +89,38 @@
     }
     obj1.free();
   }
+  regen = gTrue;
 
-  // check for a text-type field
-  isTextField = dict->lookup("FT", &obj1)->isName("Tx");
-  obj1.free();
+  /*TODO: appearance generation isn't complete :
+   non-comprehensive list of missings things by Leonard Rosenthol :
+   * Doesn't support walking up the parents of the field for /DA information
+   * Doesn't support field attributes
+         border style (width, color, etc.)
+         background color
+         flags (password, comb, etc.)
+         etc.
+   * Only works for text fields, basic support for combos
+  */
+
+  //try to find the corresponding FormWidget
+  if (hasRef && catalog->getForm()) {
+    widget = catalog->getForm()->findWidgetByRef(ref); 
+  }
+
+  isMultiline = isListbox = false;
+  if (widget) { 
+    if (widget->getType() == formText) {
+      isTextField = true;
+      isMultiline = static_cast<FormWidgetText*>(widget)->isMultiline();
+    } else if (widget->getType() == formChoice) {
+      isTextField = true;
+      isListbox = static_cast<FormWidgetChoice*>(widget)->isListBox();
+    }
+  }
 
-#if 0 //~ appearance stream generation is not finished yet
   if (regen && isTextField) {
     generateAppearance(acroForm, dict);
   } else {
-#endif
     if (dict->lookup("AP", &apObj)->isDict()) {
       if (dict->lookup("AS", &asObj)->isName()) {
 	if (apObj.dictLookup("N", &obj1)->isDict()) {
@@ -104,9 +147,7 @@
       asObj.free();
     }
     apObj.free();
-#if 0 //~ appearance stream generation is not finished yet
   }
-#endif
 }
 
 void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
@@ -129,14 +170,81 @@
   }
 }
 
+void Annot::writeTextString (GooString* vStr, CharCodeToUnicode* ccToUnicode, GooString* appearBuf, GfxFont* font)
+{
+  int i0,i1; //i0 = line begin index, i1 = line end index
+  int charSize;
+  CharCode c;
+  char buf[256];
+  double currentLineWidth= 0.0; //width filled by displayed chars (used for multilines)
+
+  if (vStr->hasUnicodeMarker()) {
+    //skip unicode marker and go one char forward because we read character by pairs
+    i0 = 3;
+    charSize = 2;
+  } else {
+    i0 = 0;
+    charSize = 1;
+  }
+  while (i0 < vStr->getLength()) {
+    //find next end of line
+    for (i1 = i0; i1 < vStr->getLength(); i1++) {
+      if (vStr->getChar(i1) == '\n' || vStr->getChar(i1) == '\r')
+        break;
+    }
+    appearBuf->append('(');
+
+    for(; i0 < i1; i0 += charSize) {
+      c = vStr->getChar(i0);
+      if (ccToUnicode && vStr->hasUnicodeMarker()) {
+        char ctmp[2];
+        ctmp[0] = vStr->getChar(i0-1);
+        ctmp[1] = vStr->getChar(i0);
+        ccToUnicode->mapToCharCode((Unicode*)ctmp, &c, 2);
+        appearBuf->append(c);
+      } else {
+        c &= 0xff;
+        if (c == '(' || c == ')' || c == '\\') {
+          appearBuf->append('\\');
+          appearBuf->append(c);
+        } else if (c < 0x20 || c >= 0x80) {
+          sprintf(buf, "\\%03o", c);
+          appearBuf->append(buf);
+        } else {
+          appearBuf->append(c);
+        }
+      }
+      if (font) {
+        if (font->isCIDFont()) {
+          currentLineWidth += fontSize*static_cast<GfxCIDFont*>(font)->getWidth((char*)&c, 1);
+        } else { //Gfx8Bit
+          currentLineWidth += fontSize*static_cast<Gfx8BitFont*>(font)->getWidth((char)c);
+        }
+
+        if (isMultiline && (currentLineWidth >= xMax - xMin)) {
+          currentLineWidth = 0;
+          break;
+        }
+      } else {
+      
+      }
+    }
+    appearBuf->append(") Tj\n");
+    appearBuf->append("T*\n");
+    i0 = i0 + charSize;
+  }
+}
+
 void Annot::generateAppearance(Dict *acroForm, Dict *dict) {
   MemStream *appearStream;
-  Object daObj, vObj, drObj, appearDict, obj1, obj2;
+  Object daObj, vObj, drObj, appearDict, obj1, obj2, resObj;
   GooString *daStr, *daStr1, *vStr, *s;
+  GooString *fontName=NULL;
   char buf[256];
-  double fontSize;
   int c;
   int i0, i1;
+  GfxFont *font = NULL;
+
 
   //~ DA can be inherited
   if (dict->lookup("DA", &daObj)->isString()) {
@@ -167,68 +275,24 @@
 			   daStr->getLength() - i1);
 	  }
 	}
-	break;
-      }
-    }
 
-    // build the appearance stream contents
-    appearBuf = new GooString();
-    appearBuf->append("/Tx BMC\n");
-    appearBuf->append("q BT\n");
-    appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n");
-    if (dict->lookup("V", &vObj)->isString()) {
-      //~ handle quadding -- this requires finding the font and using
-      //~   the encoding and char widths
-      sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize);
-      appearBuf->append(buf);
-      sprintf(buf, "%g TL\n", fontSize);
-      appearBuf->append(buf);
-      vStr = vObj.getString();
-      i0 = 0;
-      while (i0 < vStr->getLength()) {
-	for (i1 = i0;
-	     i1 < vStr->getLength() &&
-	       vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r';
-	     ++i1) ;
-	if (i0 > 0) {
-	  appearBuf->append("T*\n");
-	}
-	appearBuf->append('(');
-	for (; i0 < i1; ++i0) {
-	  c = vStr->getChar(i0);
-	  if (c == '(' || c == ')' || c == '\\') {
-	    appearBuf->append('\\');
-	    appearBuf->append(c);
-	  } else if (c < 0x20 || c >= 0x80) {
-	    sprintf(buf, "\\%03o", c);
-	    appearBuf->append(buf);
-	  } else {
-	    appearBuf->append(c);
-	  }
-	}
-	appearBuf->append(") Tj\n");
-	if (i1 + 1 < vStr->getLength() &&
-	    vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') {
-	  i0 = i1 + 2;
-	} else {
-	  i0 = i1 + 1;
+        //find the font name
+        i1=i0;
+        for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ;
+        for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ;
+        if (i0<0) i0=0;
+        if (i0 != i1) {
+          ++i0;
+          ++i1;
+          fontName = new GooString(daStr, i0, i1 - i0);
 	}
+
+        break;
       }
     }
-    vObj.free();
-    appearBuf->append("ET Q\n");
-    appearBuf->append("EMC\n");
 
-    // build the appearance stream dictionary
+    //init appearance dictionnary
     appearDict.initDict(xref);
-    appearDict.dictAdd("Length", obj1.initInt(appearBuf->getLength()));
-    appearDict.dictAdd("Subtype", obj1.initName("Form"));
-    obj1.initArray(xref);
-    obj1.arrayAdd(obj2.initReal(0));
-    obj1.arrayAdd(obj2.initReal(0));
-    obj1.arrayAdd(obj2.initReal(xMax - xMin));
-    obj1.arrayAdd(obj2.initReal(yMax - yMin));
-    appearDict.dictAdd("BBox", &obj1);
 
     // find the resource dictionary
     dict->lookup("DR", &drObj);
@@ -252,14 +316,141 @@
 	}
       }
     }
+    CharCodeToUnicode* ccToUnicode = NULL;
     if (drObj.isDict()) {
-      appearDict.dictAdd("Resources", drObj.copy(&obj1));
+      //Lookup for the font name in the font entry of the ressource dictionnary
+      //also lookup in BaseFont/Name of each DR entry
+      //without that second lookup, forms-scribus.pdf doesn't find font 
+      if (fontName && drObj.dictLookup("Font", &obj1)->isDict()) {
+        Object obj3, obj4;
+        //--
+        bool found = false;
+        if (obj1.dictLookup(*fontName, &obj3)->isDict() && obj3.dictLookup("Type", &obj4)->isName("Font")) {
+          found = true;
+        }
+        if (!found) { //font not found in DR, try to lookup in each entry BaseFont/Name
+          error(-1, "Can't find font name in DR, trying to lookup in BaseFont/Name");
+          for(int i=0; i<obj1.dictGetLength(); i++) {
+            if (!found && obj1.dictGetVal(i, &obj3)->isDict()) {
+              if (obj3.dictLookup("Type", &obj4)->isName("Font")) {
+                obj4.free();
+                if (obj3.dictLookup("Name", &obj4)->isName()) {
+                  if (fontName->cmp(obj4.getName()) == 0) {
+                    found = true;
+                    break;
+                  }
+                  obj4.free();
+                }
+                if (obj3.dictLookup("BaseFont", &obj4)->isName()) {
+                  if (fontName->cmp(obj4.getName()) == 0) {
+                    found = true;
+                    break;
+                  }
+                  obj4.free();
+                }
+              }
+              obj3.free();
+            }
+          }
+        }
+        if (found) {
+          obj4.free();
+          obj1.dictLookupNF(*fontName, &obj4);
+          Ref r;
+          if (obj4.isRef()) r = obj4.getRef();
+          else r.gen = r.num = 0;
+          font = GfxFont::makeFont(xref, "temp", r, obj3.getDict());
+          ccToUnicode = font->getToUnicode();
+        }
+        obj3.free();
+      }
+      obj1.free();
+      appearDict.dictAdd("Resources", drObj.copy(&resObj));
     }
     drObj.free();
 
+    // build the appearance stream contents
+    appearBuf = new GooString();
+   
+    //TODO: support for dash and style options from BS dict
+    bool drawBorder = false;
+    if (dict->lookup("BS", &obj1)->isDict()) {
+      Object obj3,obj4;
+      Dict *bsDict = obj1.getDict();
+      if (bsDict->lookup("Type", &obj3)->isName("Border")) {
+        //width
+        if (bsDict->lookup("W", &obj4)->isInt()) {
+          sprintf(buf, "%i w\n", obj4.getInt());
+          appearBuf->append(buf);
+          drawBorder = true;
+        }
+        obj4.free();
+      }
+      obj3.free();
+    }
+    obj1.free();
+    //border rendering
+    if (drawBorder && !hidden) {
+      sprintf(buf, "0 0 %f %f re\n", xMax-xMin, yMax-yMin);
+      appearBuf->append(buf);
+      appearBuf->append("S\n");
+    }
+
+    appearBuf->append("/Tx BMC\n");
+    appearBuf->append("q BT\n");
+    appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n");
+
+    sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize);
+    appearBuf->append(buf);
+    sprintf(buf, "%g TL\n", fontSize);
+    appearBuf->append(buf);
+    
+    if (hidden) {
+      //don't display hidden annot
+    } else if (isListbox) {
+      FormWidgetChoice* choice = static_cast<FormWidgetChoice*>(widget);
+      for(int i=0; i<choice->getNumChoices(); i++) {
+        if (choice->isSelected(i)) { //selected choice
+          //TODO: The color of the highlighting rect should depend on the color of the font. 
+          //highlight with a black background rect
+          appearBuf->append("q\n");
+          sprintf(buf, "0 %f %f %f re\n", yMax-yMin-(i+1)*fontSize, xMax-xMin, fontSize);
+          appearBuf->append(buf);
+          appearBuf->append("f\n");
+          appearBuf->append("Q\n");
+          //draw the text in white
+          appearBuf->append("1.0 1.0 1.0 rg\n");
+          writeTextString(choice->getChoice(i), ccToUnicode, appearBuf, font);
+          appearBuf->append("0.0 0.0 0.0 rg\n");
+        } else
+          writeTextString(choice->getChoice(i), ccToUnicode, appearBuf, font);
+      }
+      vObj.free();
+    } else if (dict->lookup("V", &vObj)->isString()) {
+      //~ handle quadding -- this requires finding the font and using
+      //~   the encoding and char widths
+      vStr = vObj.getString();
+      writeTextString(vStr, ccToUnicode, appearBuf, font);
+      vObj.free();
+    }
+
+    appearBuf->append("ET Q\n");
+    appearBuf->append("EMC\n");
+
+    // build the appearance stream dictionary
+    appearDict.dictAdd("Length", obj1.initInt(appearBuf->getLength()));
+    appearDict.dictAdd("Subtype", obj1.initName("Form"));
+    obj1.initArray(xref);
+    obj1.arrayAdd(obj2.initReal(0));
+    obj1.arrayAdd(obj2.initReal(0));
+    obj1.arrayAdd(obj2.initReal(xMax - xMin));
+    obj1.arrayAdd(obj2.initReal(yMax - yMin));
+    appearDict.dictAdd("BBox", &obj1);
+
     // build the appearance stream
     appearStream = new MemStream(appearBuf->getCString(), 0,
 				 appearBuf->getLength(), &appearDict);
+    appearance.free();
     appearance.initStream(appearStream);
     ok = gTrue;
 
@@ -268,11 +459,13 @@
     }
   }
   daObj.free();
+
+  if (fontName) delete fontName;
+  if (font) delete font;
 }
 
 void Annot::draw(Gfx *gfx) {
   Object obj;
-
   if (appearance.fetch(xref, &obj)->isStream()) {
     gfx->doAnnot(&obj, xMin, yMin, xMax, yMax);
   }
@@ -298,8 +491,18 @@
                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 
+      //this way, it'll be possible for the annot to retrieve the corresponding
+      //form widget
+      Object obj2;
+      Ref* pref;
+      if (annotsObj->arrayGetNF(i, &obj2)->isRef())
+        pref = &obj2.getRef();
+      else 
+        pref = NULL;
+
       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
-	annot = new Annot(xref, acroForm, obj1.getDict());
+        annot = new Annot(xref, acroForm, obj1.getDict(), pref, catalog);
 	if (annot->isOk()) {
 	  if (nAnnots >= size) {
 	    size += 16;
@@ -310,6 +513,7 @@
 	  delete annot;
 	}
       }
+      obj2.free();
       obj1.free();
     }
   }

Index: Annot.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Annot.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- Annot.h	27 Dec 2006 23:15:06 -0000	1.3
+++ Annot.h	24 Feb 2007 23:32:23 -0000	1.4
@@ -16,6 +16,9 @@
 class XRef;
 class Gfx;
 class Catalog;
+class CharCodeToUnicode;
+class GfxFont;
+class FormWidget;
 
 //------------------------------------------------------------------------
 // Annot
@@ -24,7 +27,7 @@
 class Annot {
 public:
 
-  Annot(XRef *xrefA, Dict *acroForm, Dict *dict);
+  Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref* aref, Catalog* catalog);
   ~Annot();
   GBool isOk() { return ok; }
 
@@ -32,9 +35,15 @@
 
   // Get appearance object.
   Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
+  GBool textField() { return isTextField; }
 
-private:
+  double getXMin() { return xMin; }
+  double getYMin() { return yMin; }
+
+  double getFontSize() { return fontSize; }
 
+private:
+  void writeTextString (GooString* vStr, CharCodeToUnicode* ccToUnicode, GooString* appearBuf, GfxFont* font);
   void generateAppearance(Dict *acroForm, Dict *dict);
   void readArrayNum(Object *pdfArray, int key, double *value);
 
@@ -44,7 +53,15 @@
   GooString *appearBuf;
   double xMin, yMin,		// annotation rectangle
          xMax, yMax;
+  double fontSize; 
   GBool ok;
+  GBool regen, isTextField;
+  GBool isMultiline, isListbox;
+  
+  bool hasRef;
+  bool hidden;
+  Ref ref;
+  FormWidget* widget;
 };
 
 //------------------------------------------------------------------------

Index: Catalog.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Catalog.cc,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- Catalog.cc	11 Jan 2007 22:12:11 -0000	1.18
+++ Catalog.cc	24 Feb 2007 23:32:23 -0000	1.19
@@ -25,6 +25,7 @@
 #include "PageLabelInfo.h"
 #include "UGooString.h"
 #include "Catalog.h"
+#include "Form.h"
 
 // This define is used to limit the depth of recursive readPageTree calls
 // This is needed because the page tree nodes can reference their parents
@@ -49,12 +50,21 @@
   numPages = pagesSize = 0;
   baseURI = NULL;
   pageLabelInfo = NULL;
+  form = NULL;
 
   xref->getCatalog(&catDict);
   if (!catDict.isDict()) {
     error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
     goto err1;
   }
+  // get the AcroForm dictionary
+  catDict.dictLookup("AcroForm", &acroForm);
+
+  // load Forms
+  if (acroForm.isDict()) {
+    form = new Form(xref,&acroForm);
+  }
+
 
   // read page tree
   catDict.dictLookup("Pages", &pagesDict);
@@ -158,8 +168,9 @@
   // get the outline dictionary
   catDict.dictLookup("Outlines", &outline);
 
-  // get the AcroForm dictionary
-  catDict.dictLookup("AcroForm", &acroForm);
+  // perform form-related loading after all widgets have been loaded
+  if (form) 
+    form->postWidgetsLoad();
 
   catDict.free();
   return;
@@ -242,7 +253,7 @@
     kids.arrayGet(i, &kid);
     if (kid.isDict("Page")) {
       attrs2 = new PageAttrs(attrs1, kid.getDict());
-      page = new Page(xref, start+1, kid.getDict(), attrs2);
+      page = new Page(xref, start+1, kid.getDict(), attrs2, form);
       if (!page->isOk()) {
 	++start;
 	goto err3;

Index: Catalog.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Catalog.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- Catalog.h	11 Jan 2007 22:12:11 -0000	1.10
+++ Catalog.h	24 Feb 2007 23:32:23 -0000	1.11
@@ -21,6 +21,7 @@
 class LinkDest;
 class UGooString;
 class PageLabelInfo;
+class Form;
 
 //------------------------------------------------------------------------
 // NameTree
@@ -151,6 +152,8 @@
 
   Object *getAcroForm() { return &acroForm; }
 
+  Form* getForm() { return form; }
+
   enum PageMode {
     pageModeNone,
     pageModeOutlines,
@@ -178,6 +181,7 @@
   XRef *xref;			// the xref table for this PDF file
   Page **pages;			// array of pages
   Ref *pageRefs;		// object ID for each page
+  Form *form;
   int numPages;			// number of pages
   int pagesSize;		// size of pages array
   Object dests;			// named destination dictionary

Index: CharCodeToUnicode.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/CharCodeToUnicode.cc,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- CharCodeToUnicode.cc	18 Jan 2006 18:54:12 -0000	1.4
+++ CharCodeToUnicode.cc	24 Feb 2007 23:32:23 -0000	1.5
@@ -504,6 +504,40 @@
   return 0;
 }
 
+int CharCodeToUnicode::mapToCharCode(Unicode* u, CharCode *c, int usize) {
+  //look for charcode in map
+  if (usize == 1) {
+    for (int i=0; i<mapLen; i++) {
+      if (map[i] == ((*u)&0xff)) {
+        *c = (char)map[i];
+        return 1;
+      }
+    }
+    *c = 'x';
+  } else {
+    int i, j;
+    //for each entry in the sMap
+    for (i=0; i<sMapLen; i++) {
+      //if the entry's unicode length isn't the same are usize, the strings 
+      // are obviously differents
+      if (sMap[i].len != usize) continue;
+      //compare the string char by char
+      for (j=0; j<sMap[i].len; j++) {
+        if (sMap[i].u[j] != u[j]) {
+          continue;
+        }
+      }
+
+      //we have the same strings
+      if (j==sMap[i].len) { 
+        *c = sMap[i].c;
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
 //------------------------------------------------------------------------
 
 CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {

Index: CharCodeToUnicode.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/CharCodeToUnicode.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- CharCodeToUnicode.h	16 Sep 2005 18:38:58 -0000	1.2
+++ CharCodeToUnicode.h	24 Feb 2007 23:32:23 -0000	1.3
@@ -27,6 +27,7 @@
 //------------------------------------------------------------------------
 
 class CharCodeToUnicode {
+friend class UnicodeToCharCode;
 public:
 
   // Read the CID-to-Unicode mapping for <collection> from the file
@@ -66,6 +67,8 @@
   // Map a CharCode to Unicode.
   int mapToUnicode(CharCode c, Unicode *u, int size);
 
+  int mapToCharCode(Unicode* u, CharCode *c, int usize);
+
   // Return the mapping's length, i.e., one more than the max char
   // code supported by the mapping.
   CharCode getLength() { return mapLen; }

Index: Dict.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Dict.cc,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- Dict.cc	3 Sep 2006 09:27:21 -0000	1.5
+++ Dict.cc	24 Feb 2007 23:32:23 -0000	1.6
@@ -65,6 +65,38 @@
   return NULL;
 }
 
+void Dict::remove(const UGooString &key) {
+  int i; 
+  bool found = false;
+  DictEntry tmp;
+  if(length == 0) return;
+
+  for(i=0; i<length; i++) {
+    if (!key.cmp(entries[i].key)) {
+      found = true;
+      break;
+    }
+  }
+  if(!found) return;
+  //replace the deleted entry with the last entry
+  length -= 1;
+  tmp = entries[length];
+  if (i!=length) //don't copy the last entry if it is deleted 
+    entries[i] = tmp;
+}
+
+void Dict::set(const UGooString &key, Object *val) {
+  DictEntry *e;
+  e = find (key);
+  if (e) {
+    e->val.free();
+    e->val = *val;
+  } else {
+    add (key, val);
+  }
+}
+
+
 GBool Dict::is(char *type) {
   DictEntry *e;
 

Index: Dict.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Dict.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- Dict.h	3 Sep 2006 09:27:21 -0000	1.4
+++ Dict.h	24 Feb 2007 23:32:23 -0000	1.5
@@ -50,6 +50,10 @@
   void addOwnVal(const char *key, Object *val) {
     addOwnKeyVal(new UGooString(key), val);
   }
+  // Update the value of an existing entry, otherwise create it
+  void set(const UGooString &key, Object *val);
+  // Remove an entry. This invalidate indexes
+  void remove(const UGooString &key);
 
   // Check if dictionary is of specified type.
   GBool is(char *type);

--- NEW FILE: Form.cc ---
//========================================================================
//
// Form.cc
//
// Copyright 2006 Julien Rebetez
//
//========================================================================

#include <config.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include <string.h>
#include "goo/gmem.h"
#include "goo/GooString.h"
#include "Error.h"
[...1148 lines suppressed...]
      FormWidget* tmp = form->findWidgetByRef(r);
      if(tmp) {
        // We've found a corresponding form field, link it
        tmp->setID(FormWidget::encodeID(page, numWidgets));
        widgets[numWidgets++] = tmp;
        //create a temporary Annot to get the font size
        Object obj2;
        if (annots->arrayGet(i, &obj2)->isDict()) {
          Annot* ann = new Annot(xref, NULL ,obj2.getDict(), NULL, NULL);
          tmp->setFontSize(ann->getFontSize());
          delete ann;
        }
        obj2.free();
      } 
      
      obj1.free();
    }
  } 
}


--- NEW FILE: Form.h ---
//========================================================================
//
// Form.h
//
// Copyright 2006 Julien Rebetez
//
//========================================================================

#ifndef FORM_H
#define FORM_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"
#include "goo/GooVector.h"

class GooString;
class UGooString;
class Array;
class Dict;
class Annot;
class Catalog;

enum FormFieldType {
  formButton,
  formText,
  formChoice,
  formSignature,
  formUndef,
};

enum FormButtonType {
  formButtonCheck,
  formButtonPush,
  formButtonRadio
};

class Form;
class FormField;
class FormFieldButton;
class FormFieldText;
class FormFieldSignature;
class FormFieldChoice;

//------------------------------------------------------------------------
// FormWidget
// A FormWidget represents the graphical part of a field and is "attached"
// to a page.
//------------------------------------------------------------------------

class FormWidget {
public:
  virtual ~FormWidget();

  // see the description of FormField::LoadChildrenDefaults
  virtual void loadDefaults () {}
  
  // Check if point is inside the field bounding rect
  GBool inRect(double x, double y)
    { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }

  // Get the field bounding rect
  void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }

  unsigned getID () { return ID; }
  void setID (unsigned int i) { ID=i; }

  FormFieldType getType() { return type; }

  Object* getObj() { return &obj; }
  Ref getRef() { return ref; }

  void setChildNum (unsigned i) { childNum = i; }
  unsigned getChildNum () { return childNum; }

  void setFontSize(double f) { fontSize = f; }
  double getFontSize () { return fontSize; }

  virtual bool isReadOnly() const = 0;

  // return the unique ID corresponding to pageNum/fieldNum
  static int encodeID (unsigned pageNum, unsigned fieldNum);
  // decode id and retrieve pageNum and fieldNum
  static void decodeID (unsigned id, unsigned* pageNum, unsigned* fieldNum);

protected:
  FormWidget(XRef *xrefA, Object *aobj, unsigned num, Ref aref);
  FormWidget(FormWidget *dest);

  FormField* field;
  FormFieldType type;
  Object obj;
  Ref ref;
  XRef *xref;
  //index of this field in the parent's child list
  unsigned childNum;

  /*
  Field ID is an (unsigned) integer, calculated as follow :
  the first sizeof/2 bits are the field number, relative to the page
  the last sizeof/2 bits are the page number
  [page number | field number]
  (encoding) id = (pageNum << 4*sizeof(unsigned)) + fieldNum;
  (decoding) pageNum = id >> 4*sizeof(unsigned); fieldNum = (id << 4*sizeof(unsigned)) >> 4*sizeof(unsigned);
  */
  unsigned ID; 

  double x1, y1;                // lower left corner
  double x2, y2;                // upper right corner
  double fontSize; //font size if this widget has text

};

//------------------------------------------------------------------------
// FormWidgetButton
//------------------------------------------------------------------------

class FormWidgetButton: public FormWidget {
public:
  FormWidgetButton(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldButton *p);
  ~FormWidgetButton ();

  void setState (GBool state, GBool calledByParent=gFalse);
  GBool getState ();

  char* getOnStr () { return onStr; }

  void loadDefaults();

  bool isReadOnly () const;

  void setNumSiblingsID (int i);
  void setSiblingsID (int i, unsigned id) { siblingsID[i] = id; }

  //For radio buttons, return the IDs of the other radio buttons in the same group
  unsigned* getSiblingsID () const { return siblingsID; }
  int getNumSiblingsID () const { return numSiblingsID; }

protected:
  unsigned* siblingsID; // IDs of dependent buttons (each button of a radio field has all the others buttons
                        // of the same field in this array)
  int numSiblingsID;
  char *onStr;
  FormFieldButton *parent;
  GBool state;
};

//------------------------------------------------------------------------
// FormWidgetText
//------------------------------------------------------------------------

class FormWidgetText: public FormWidget {
public:
  FormWidgetText(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldText *p);
  //return the field's content (UTF16BE)
  GooString* getContent() ;
  //return a copy of the field's content (UTF16BE)
  GooString* getContentCopy();

  //except a UTF16BE string
  void setContent(GooString* new_content);

  void loadDefaults ();

  bool isMultiline () const; 
  bool isPassword () const; 
  bool isFileSelect () const; 
  bool noSpellCheck () const; 
  bool noScroll () const; 
  bool isComb () const; 
  bool isRichText () const;
  bool isReadOnly () const;
protected:
  FormFieldText *parent;
};

//------------------------------------------------------------------------
// FormWidgetChoice
//------------------------------------------------------------------------

class FormWidgetChoice: public FormWidget {
public:
  FormWidgetChoice(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldChoice *p);
  ~FormWidgetChoice();

  void loadDefaults ();
  int getNumChoices();
  //return the display name of the i-th choice (UTF16BE)
  GooString* getChoice(int i);
  //select the i-th choice
  void select (int i); 

  //toggle selection of the i-th choice
  void toggle (int i);

  //deselect everything
  void deselectAll ();

  //except a UTF16BE string
  //only work for editable combo box, set the user-entered text as the current choice
  void setEditChoice(GooString* new_content);

  GooString* getEditChoice ();

  bool isSelected (int i);

  bool isCombo () const; 
  bool hasEdit () const; 
  bool isMultiSelect () const; 
  bool noSpellCheck () const; 
  bool commitOnSelChange () const; 
  bool isReadOnly () const;
  bool isListBox () const;
protected:
  void _updateV ();
  bool _checkRange (int i);
  FormFieldChoice *parent;
};

//------------------------------------------------------------------------
// FormWidgetSignature
//------------------------------------------------------------------------

class FormWidgetSignature: public FormWidget {
public:
  FormWidgetSignature(XRef *xrefA, Object *dict, unsigned num, Ref ref, FormFieldSignature *p);
  bool isReadOnly () const;
protected:
  FormFieldSignature *parent;
};

//------------------------------------------------------------------------
// FormField
// A FormField implements the logical side of a field and is "attached" to
// the Catalog. This is an internal class and client applications should
// only interact with FormWidgets.
//------------------------------------------------------------------------

class FormField {
public:
  FormField(XRef* xrefa, Object *aobj, Ref *aref, Form* aform, FormFieldType t=formUndef);

  virtual ~FormField();

  FormField *copy() { return new FormField(this); }

  // Accessors.
  FormFieldType getType() { return type; }
  Object* getObj() { return &obj; }
  Ref getRef() { return ref; }

  void setReadOnly (bool b) { readOnly = b; }
  bool isReadOnly () const { return readOnly; }

  FormWidget* findWidgetByRef (Ref aref);
  // Since while loading their defaults, children may call parents methods, it's better
  // to do that when parents are completly constructed
  void loadChildrenDefaults();

  // only implemented in FormFieldButton
  virtual void fillChildrenSiblingsID ();


 protected:
  void _createWidget (Object *obj, Ref aref);

  FormFieldType type;           // field type
  Ref ref;
  bool direct;
  bool terminal;
  Object obj;
  XRef *xref;
  FormField **children;
  int numChildren;
  FormWidget **widgets;
  Form* form;
  bool readOnly;

private:
  FormField() {}
  FormField(FormField *dest);
};


//------------------------------------------------------------------------
// FormFieldButton
//------------------------------------------------------------------------

class FormFieldButton: public FormField {
public:
  FormFieldButton(XRef *xrefA, Object *dict, Ref *ref, Form* form);

  FormButtonType getButtonType () { return btype; }

  bool noToggleToOff () const { return noAllOff; }

  // returns gTrue if the state modification is accepted
  GBool setState (int num, GBool s);
  
  void fillChildrenSiblingsID ();

  virtual ~FormFieldButton();
protected:
  FormButtonType btype;
  int size;
  int active_child; //only used for combo box
  bool noAllOff;
};

//------------------------------------------------------------------------
// FormFieldText
//------------------------------------------------------------------------

class FormFieldText: public FormField {
public:
  FormFieldText(XRef *xrefA, Object *dict, Ref *ref, Form* form);
  
  GooString* getContent () { return content; }
  GooString* getContentCopy ();
  void setContentCopy (GooString* new_content);
  virtual ~FormFieldText();

  bool isMultiline () const { return multiline; }
  bool isPassword () const { return password; }
  bool isFileSelect () const { return fileSelect; }
  bool noSpellCheck () const { return doNotSpellCheck; }
  bool noScroll () const { return doNotScroll; }
  bool isComb () const { return comb; }
  bool isRichText () const { return richText; }
protected:
  GooString* content;
  bool multiline;
  bool password;
  bool fileSelect;
  bool doNotSpellCheck;
  bool doNotScroll;
  bool comb;
  bool richText;
};

//------------------------------------------------------------------------
// FormFieldChoice
//------------------------------------------------------------------------

class FormFieldChoice: public FormField {
public:
  FormFieldChoice(XRef *xrefA, Object *aobj, Ref *ref, Form* form);

  virtual ~FormFieldChoice();

  int getNumChoices() { return numChoices; }
  GooString* getChoice(int i) { return choices[i].optionName; }
  GooString* getExportVal (int i) { return choices[i].exportVal; }

  //select the i-th choice
  void select (int i); 

  //toggle selection of the i-th choice
  void toggle (int i);

  //deselect everything
  void deselectAll ();

  //only work for editable combo box, set the user-entered text as the current choice
  void setEditChoice(GooString* new_content);

  GooString* getEditChoice ();

  bool isSelected (int i) { return choices[i].selected; }

  int getNumSelected ();

  bool isCombo () const { return combo; }
  bool hasEdit () const { return edit; }
  bool isMultiSelect () const { return multiselect; }
  bool noSpellCheck () const { return doNotSpellCheck; }
  bool commitOnSelChange () const { return doCommitOnSelChange; }
  bool isListBox () const { return !combo; }

  /* these functions _must_ only be used by FormWidgetChoice */
  void _setNumChoices (int i) { numChoices = i; }
  void _createChoicesTab ();
  void _setChoiceExportVal (int i, GooString* str) { choices[i].exportVal = str; }
  void _setChoiceOptionName (int i, GooString* str) { choices[i].optionName = str; }

protected:
  bool combo;
  bool edit;
  bool multiselect;
  bool doNotSpellCheck;
  bool doCommitOnSelChange;

  struct ChoiceOpt {
    GooString* exportVal; //the export value ("internal" name)
    GooString* optionName; //displayed name
    bool selected; //if this choice is selected
  };

  int numChoices;
  ChoiceOpt* choices;
  GooString* editedChoice; 
};

//------------------------------------------------------------------------
// FormFieldSignature
//------------------------------------------------------------------------

class FormFieldSignature: public FormField {
public:
  FormFieldSignature(XRef *xrefA, Object *dict, Ref *ref, Form* form);

  virtual ~FormFieldSignature();
};

//------------------------------------------------------------------------
// Form
// This class handle the document-wide part of Form (things in the acroForm
// Catalog entry).
//------------------------------------------------------------------------

class Form {
public:
  Form(XRef *xrefA, Object* acroForm);

  ~Form();

  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, Ref* pref);

  void postWidgetsLoad();
  void checkForNeedAppearances ();
private:
  FormField** rootFields;
  int numFields;
  int size;
  XRef* xref;
  Catalog* catalog;
};

//------------------------------------------------------------------------
// FormPageWidgets
//------------------------------------------------------------------------

class FormPageWidgets {
public:
  FormPageWidgets (XRef *xrefA, Object* annots, unsigned int page, Form *form);
  ~FormPageWidgets();
  
  int getNumWidgets() const { return numWidgets; }
  FormWidget* getWidget(int i) const { return widgets[i]; }

private:
  FormWidget** widgets;
  int numWidgets;
  int size;
  unsigned pageNum;
  XRef* xref;
};

#endif


Index: GfxFont.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/GfxFont.cc,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- GfxFont.cc	2 May 2006 04:38:39 -0000	1.9
+++ GfxFont.cc	24 Feb 2007 23:32:23 -0000	1.10
@@ -1582,6 +1582,33 @@
   return map;
 }
 
+double GfxCIDFont::getWidth (char* s, int len) {
+  int nUsed;
+  double w, h, vx, vy;
+  int a, b, m;
+
+  CID cid = cMap->getCID(s, len, &nUsed);
+
+  w = widths.defWidth;
+  if (widths.nExceps > 0 && cid >= widths.exceps[0].first) {
+    a = 0;
+    b = widths.nExceps;
+    // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      if (widths.exceps[m].first <= cid) {
+        a = m;
+      } else {
+        b = m;
+      }
+    }
+    if (cid <= widths.exceps[a].last) {
+      w = widths.exceps[a].width;
+    }
+  }
+  return w;
+}
+
 //------------------------------------------------------------------------
 // GfxFontDict
 //------------------------------------------------------------------------

Index: GfxFont.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/GfxFont.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- GfxFont.h	2 May 2006 04:38:39 -0000	1.4
+++ GfxFont.h	24 Feb 2007 23:32:23 -0000	1.5
@@ -167,6 +167,9 @@
   GBool isItalic() { return flags & fontItalic; }
   GBool isBold() { return flags & fontBold; }
 
+  // Return the Unicode map.
+  virtual CharCodeToUnicode *getToUnicode() = 0;
+
   // Return the font matrix.
   double *getFontMatrix() { return fontMat; }
 
@@ -316,6 +319,8 @@
 
   Gushort *getCodeToGIDMap(FoFiTrueType *ff, int *length);
 
+  double getWidth(char* s, int len);
+
 private:
 
   CMap *cMap;			// char code --> CID

Index: Makefile.am
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Makefile.am,v
retrieving revision 1.27
retrieving revision 1.28
diff -u -d -r1.27 -r1.28
--- Makefile.am	13 Jan 2007 18:29:39 -0000	1.27
+++ Makefile.am	24 Feb 2007 23:32:23 -0000	1.28
@@ -117,6 +117,7 @@
 	Error.h			\
 	FontEncodingTables.h	\
 	FontInfo.h		\
+	Form.h 			\
 	Function.cc		\
 	Function.h		\
 	Gfx.h			\
@@ -179,6 +180,7 @@
 	Dict.cc 		\
 	Error.cc 		\
 	FontEncodingTables.cc	\
+	Form.cc 		\
 	FontInfo.cc		\
 	Function.cc		\
 	Gfx.cc 			\

Index: Object.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Object.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- Object.h	3 Sep 2006 09:27:21 -0000	1.3
+++ Object.h	24 Feb 2007 23:32:23 -0000	1.4
@@ -171,6 +171,7 @@
   void dictAddOwnKeyVal(UGooString *key, Object *val);
   void dictAdd(const UGooString &key, Object *val);
   void dictAddOwnVal(const char *key, Object *val);
+  void dictSet(const UGooString &key, Object *val);
   GBool dictIs(char *dictType);
   Object *dictLookup(const UGooString &key, Object *obj);
   Object *dictLookupNF(const UGooString &key, Object *obj);
@@ -254,6 +255,9 @@
 inline void Object::dictAddOwnKeyVal(UGooString *key, Object *val)
   { dict->addOwnKeyVal(key, val); }
 
+inline void Object::dictSet(const UGooString &key, Object *val)
+ 	{ dict->set(key, val); }
+
 inline GBool Object::dictIs(char *dictType)
   { return dict->is(dictType); }
 

Index: Page.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Page.cc,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- Page.cc	26 Dec 2006 19:56:29 -0000	1.15
+++ Page.cc	24 Feb 2007 23:32:23 -0000	1.16
@@ -26,10 +26,12 @@
 #include "GfxState.h"
 #include "Annot.h"
 #include "TextOutputDev.h"
+#include "Form.h"
 #endif
 #include "Error.h"
 #include "Page.h"
 #include "UGooString.h"
+#include "Form.h"
 
 //------------------------------------------------------------------------
 // PageAttrs
@@ -188,7 +190,7 @@
 // Page
 //------------------------------------------------------------------------
 
-Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
+Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA, Form *form) {
   Object tmp;
 	
   ok = gTrue;
@@ -226,6 +228,10 @@
     goto err2;
   }
 
+  // forms
+  pageWidgets = new FormPageWidgets(xrefA, this->getAnnots(&tmp),num,form);
+  tmp.free();
+
   // contents
   pageDict->lookupNF("Contents", &contents);
   if (!(contents.isRef() || contents.isArray() ||
@@ -420,9 +426,10 @@
         Annot *annot = annotList->getAnnot(i);
         if ((annotDisplayDecideCbk &&
              (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || 
-            !annotDisplayDecideCbk)
+            !annotDisplayDecideCbk) {
           annot->draw(gfx); 
     }
+    }
     out->dump();
   }
   delete annotList;

Index: Page.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/Page.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- Page.h	26 Dec 2006 19:56:29 -0000	1.7
+++ Page.h	24 Feb 2007 23:32:23 -0000	1.8
@@ -23,6 +23,8 @@
 class Annots;
 class Annot;
 class Gfx;
+class FormPageWidgets;
+class Form;
 
 //------------------------------------------------------------------------
 
@@ -104,7 +106,7 @@
 public:
 
   // Constructor.
-  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);
+  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA, Form *form);
 
   // Destructor.
   ~Page();
@@ -151,6 +153,9 @@
   // Get transition.
   Object *getTrans(Object *obj) { return trans.fetch(xref, obj); }
 
+  // Get form.
+  FormPageWidgets *getPageWidgets() { return pageWidgets; }
+
   // Get duration, the maximum length of time, in seconds,
   // that the page is displayed before the presentation automatically
   // advances to the next page
@@ -200,6 +205,7 @@
   PageAttrs *attrs;		// page attributes
   Object annots;		// annotations array
   Object contents;		// page contents
+  FormPageWidgets *pageWidgets; 			// the form for that page
   Object thumb;			// page thumbnail
   Object trans;			// page transition
   Object actions;		// page addiction actions

Index: XRef.cc
===================================================================
RCS file: /cvs/poppler/poppler/poppler/XRef.cc,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- XRef.cc	1 Apr 2006 11:25:57 -0000	1.13
+++ XRef.cc	24 Feb 2007 23:32:23 -0000	1.14
@@ -201,6 +201,16 @@
 // XRef
 //------------------------------------------------------------------------
 
+XRef::XRef() {
+  ok = gTrue;
+  errCode = errNone;
+  entries = NULL;
+  size = 0;
+  streamEnds = NULL;
+  streamEndsLen = 0;
+  objStr = NULL;
+}
+
 XRef::XRef(BaseStream *strA) {
   Guint pos;
   Object obj;
@@ -264,7 +274,12 @@
 }
 
 XRef::~XRef() {
+  for(int i=0; i<size; i++) {
+    if (entries[i].obj)
+            delete entries[i].obj;
+  }
   gfree(entries);
+
   trailerDict.free();
   if (streamEnds) {
     gfree(streamEnds);
@@ -397,6 +412,7 @@
       for (i = size; i < newSize; ++i) {
 	entries[i].offset = 0xffffffff;
 	entries[i].type = xrefEntryFree;
+	entries[i].obj = NULL;
       }
       size = newSize;
     }
@@ -410,6 +426,7 @@
 	goto err1;
       }
       entry.gen = obj.getInt();
+      entry.obj = NULL;
       obj.free();
       parser->getObj(&obj);
       if (obj.isCmd("n")) {
@@ -507,6 +524,7 @@
     for (i = size; i < newSize; ++i) {
       entries[i].offset = 0xffffffff;
       entries[i].type = xrefEntryFree;
+      entries[i].obj = NULL;
     }
     size = newSize;
   }
@@ -601,6 +619,7 @@
     for (i = size; i < newSize; ++i) {
       entries[i].offset = 0xffffffff;
       entries[i].type = xrefEntryFree;
+      entries[i].obj = NULL;
     }
     size = newSize;
   }
@@ -741,6 +760,7 @@
 		  for (i = size; i < newSize; ++i) {
 		    entries[i].offset = 0xffffffff;
 		    entries[i].type = xrefEntryFree;
+		    entries[i].obj = NULL;
 		  }
 		  size = newSize;
 		}
@@ -850,6 +870,10 @@
   }
 
   e = &entries[num];
+  if(e->obj) { //check for updated object
+    obj = e->obj->copy(obj);
+    return obj;
+  }
   switch (e->type) {
 
   case xrefEntryUncompressed:
@@ -967,3 +991,66 @@
   }
   return x;
 }
+
+void XRef::add(int num, int gen, Guint offs, GBool used) {
+  size += 1;
+  entries = (XRefEntry *)greallocn(entries, size, sizeof(XRefEntry));
+  XRefEntry *e = &entries[size-1];
+
+  e->gen = gen;
+  e->num = num;
+  e->obj = NULL;
+  if (used) {
+    e->type = xrefEntryUncompressed;
+    e->offset = offs;
+  } else {
+    e->type = xrefEntryFree;
+    e->offset = 0;
+  }
+}
+
+void XRef::setModifiedObject (Object* o, Ref r) {
+  if (r.num < 0 || r.num >= size) {
+    error(-1,"XRef::setModifiedObject on unknown ref: %i, %i\n", r.num, r.gen);
+    return;
+  }
+  entries[r.num].obj = new Object();
+  o->copy(entries[r.num].obj);
+}
+
+//used to sort the entries
+int compare (const void* a, const void* b)
+{
+  return (((XRefEntry*)a)->num - ((XRefEntry*)b)->num);
+}
+
+void XRef::writeToFile(FILE* file) {
+  qsort(entries, size, sizeof(XRefEntry), compare);
+  //create free entries linked-list
+  if (entries[0].gen != 65535) {
+    error(-1, "XRef::writeToFile, entry 0 of the XRef is invalid (gen != 65535)\n");
+  }
+  int lastFreeEntry = 0; 
+  for (int i=0; i<size; i++) {
+    if (entries[i].type == xrefEntryFree) {
+      entries[lastFreeEntry].offset = entries[i].num;
+      lastFreeEntry = i;
+    }
+  }
+  //write the new xref
+  int j;
+  fprintf(file,"xref\r\n");
+  for (int i=0; i<size; i++) {
+    for(j=i; j<size; j++) { //look for consecutive entry
+      if (j!=i && entries[j].num != entries[j-1].num+1) 
+              break;
+    }
+    fprintf(file,"%i %i\r\n", entries[i].num, j-i);
+    for (int k=i; k<j; k++) {
+      if(entries[k].gen > 65535) entries[k].gen = 65535; //cap generation number to 65535 (required by PDFReference)
+      fprintf(file,"%010i %05i %c\r\n", entries[k].offset, entries[k].gen, (entries[k].type==xrefEntryFree)?'f':'n');
+    }
+    i = j-1;
+  }
+}
+

Index: XRef.h
===================================================================
RCS file: /cvs/poppler/poppler/poppler/XRef.h,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- XRef.h	17 Jan 2006 21:35:31 -0000	1.6
+++ XRef.h	24 Feb 2007 23:32:23 -0000	1.7
@@ -34,12 +34,16 @@
 struct XRefEntry {
   Guint offset;
   int gen;
+  int num;
   XRefEntryType type;
+  Object* obj;
 };
 
 class XRef {
 public:
 
+  // Constructor, create an empty XRef, used for PDF writing
+  XRef();
   // Constructor.  Read xref table from stream.
   XRef(BaseStream *strA);
 
@@ -102,6 +106,11 @@
   XRefEntry *getEntry(int i) { return &entries[i]; }
   Object *getTrailerDict() { return &trailerDict; }
 
+  // Write access
+  void setModifiedObject(Object* o, Ref r);
+  void add(int num, int gen,  Guint offs, GBool used);
+  void writeToFile(FILE* f);
+
 private:
 
   BaseStream *str;		// input stream



More information about the poppler mailing list