[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