upgraded to xpdf-3.01pl1
[swftools.git] / pdf2swf / xpdf / Annot.cc
index 245780d..68bfb6d 100644 (file)
 #pragma implementation
 #endif
 
+#include <stdlib.h>
 #include "gmem.h"
 #include "Object.h"
+#include "Catalog.h"
 #include "Gfx.h"
+#include "Lexer.h"
 #include "Annot.h"
 
 //------------------------------------------------------------------------
 // Annot
 //------------------------------------------------------------------------
 
-Annot::Annot(XRef *xrefA, Dict *dict) {
+Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) {
   Object apObj, asObj, obj1, obj2;
+  GBool regen, isTextField;
   double t;
 
   ok = gFalse;
   xref = xrefA;
-
-  if (dict->lookup("AP", &apObj)->isDict()) {
-    if (dict->lookup("AS", &asObj)->isName()) {
-      if (apObj.dictLookup("N", &obj1)->isDict()) {
-       if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
-         obj2.copy(&appearance);
-         ok = gTrue;
-       }
-       obj2.free();
-      }
-      obj1.free();
-    } else {
-      if (apObj.dictLookupNF("N", &obj1)->isRef()) {
-       obj1.copy(&appearance);
-       ok = gTrue;
-      }
-      obj1.free();
-    }
-    asObj.free();
-  }
-  apObj.free();
+  appearBuf = NULL;
 
   if (dict->lookup("Rect", &obj1)->isArray() &&
       obj1.arrayGetLength() == 4) {
@@ -76,10 +60,204 @@ Annot::Annot(XRef *xrefA, Dict *dict) {
     xMax = yMax = 1;
   }
   obj1.free();
+
+  // check if field apperances need to be regenerated
+  regen = gFalse;
+  if (acroForm) {
+    acroForm->lookup("NeedAppearances", &obj1);
+    if (obj1.isBool() && obj1.getBool()) {
+      regen = gTrue;
+    }
+    obj1.free();
+  }
+
+  // check for a text-type field
+  isTextField = dict->lookup("FT", &obj1)->isName("Tx");
+  obj1.free();
+
+#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()) {
+         if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
+           obj2.copy(&appearance);
+           ok = gTrue;
+         } else {
+           obj2.free();
+           if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
+             obj2.copy(&appearance);
+             ok = gTrue;
+           }
+         }
+         obj2.free();
+       }
+       obj1.free();
+      } else {
+       if (apObj.dictLookupNF("N", &obj1)->isRef()) {
+         obj1.copy(&appearance);
+         ok = gTrue;
+       }
+       obj1.free();
+      }
+      asObj.free();
+    }
+    apObj.free();
+#if 0 //~ appearance stream generation is not finished yet
+  }
+#endif
 }
 
 Annot::~Annot() {
   appearance.free();
+  if (appearBuf) {
+    delete appearBuf;
+  }
+}
+
+void Annot::generateAppearance(Dict *acroForm, Dict *dict) {
+  MemStream *appearStream;
+  Object daObj, vObj, drObj, appearDict, obj1, obj2;
+  GString *daStr, *daStr1, *vStr, *s;
+  char buf[256];
+  double fontSize;
+  int c;
+  int i0, i1;
+
+  //~ DA can be inherited
+  if (dict->lookup("DA", &daObj)->isString()) {
+    daStr = daObj.getString();
+
+    // look for a font size
+    //~ may want to parse the DS entry in place of this (if it exists)
+    daStr1 = NULL;
+    fontSize = 10;
+    for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) {
+      if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') {
+       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;
+         ++i1;
+         s = new GString(daStr, i0, i1 - i0);
+         fontSize = atof(s->getCString());
+         delete s;
+
+         // autosize the font
+         if (fontSize == 0) {
+           fontSize = 0.67 * (yMax - yMin);
+           daStr1 = new GString(daStr, 0, i0);
+           sprintf(buf, "%.2f", fontSize);
+           daStr1->append(buf);
+           daStr1->append(daStr->getCString() + i1,
+                          daStr->getLength() - i1);
+         }
+       }
+       break;
+      }
+    }
+
+    // build the appearance stream contents
+    appearBuf = new GString();
+    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;
+       }
+      }
+    }
+    vObj.free();
+    appearBuf->append("ET Q\n");
+    appearBuf->append("EMC\n");
+
+    // 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(xMax - xMin));
+    obj1.arrayAdd(obj2.initReal(yMax - yMin));
+    appearDict.dictAdd(copyString("BBox"), &obj1);
+
+    // find the resource dictionary
+    dict->lookup("DR", &drObj);
+    if (!drObj.isDict()) {
+      dict->lookup("Parent", &obj1);
+      while (obj1.isDict()) {
+       drObj.free();
+       obj1.dictLookup("DR", &drObj);
+       if (drObj.isDict()) {
+         break;
+       }
+       obj1.dictLookup("Parent", &obj2);
+       obj1.free();
+       obj1 = obj2;
+      }
+      obj1.free();
+      if (!drObj.isDict()) {
+       if (acroForm) {
+         drObj.free();
+         acroForm->lookup("DR", &drObj);
+       }
+      }
+    }
+    if (drObj.isDict()) {
+      appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+    }
+    drObj.free();
+
+    // build the appearance stream
+    appearStream = new MemStream(appearBuf->getCString(), 0,
+                                appearBuf->getLength(), &appearDict);
+    appearance.initStream(appearStream);
+    ok = gTrue;
+
+    if (daStr1) {
+      delete daStr1;
+    }
+  }
+  daObj.free();
 }
 
 void Annot::draw(Gfx *gfx) {
@@ -95,7 +273,8 @@ void Annot::draw(Gfx *gfx) {
 // Annots
 //------------------------------------------------------------------------
 
-Annots::Annots(XRef *xref, Object *annotsObj) {
+Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
+  Dict *acroForm;
   Annot *annot;
   Object obj1;
   int size;
@@ -105,14 +284,16 @@ Annots::Annots(XRef *xref, Object *annotsObj) {
   size = 0;
   nAnnots = 0;
 
+  acroForm = catalog->getAcroForm()->isDict() ?
+               catalog->getAcroForm()->getDict() : NULL;
   if (annotsObj->isArray()) {
     for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
-       annot = new Annot(xref, obj1.getDict());
+       annot = new Annot(xref, acroForm, obj1.getDict());
        if (annot->isOk()) {
          if (nAnnots >= size) {
            size += 16;
-           annots = (Annot **)grealloc(annots, size * sizeof(Annot *));
+           annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
          }
          annots[nAnnots++] = annot;
        } else {