summaryrefslogtreecommitdiff
path: root/Build/source/libs/poppler/poppler-src/poppler/Form.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/poppler/poppler-src/poppler/Form.cc')
-rw-r--r--Build/source/libs/poppler/poppler-src/poppler/Form.cc651
1 files changed, 407 insertions, 244 deletions
diff --git a/Build/source/libs/poppler/poppler-src/poppler/Form.cc b/Build/source/libs/poppler/poppler-src/poppler/Form.cc
index 8f8d14c4ebd..3c23dd3f651 100644
--- a/Build/source/libs/poppler/poppler-src/poppler/Form.cc
+++ b/Build/source/libs/poppler/poppler-src/poppler/Form.cc
@@ -5,7 +5,7 @@
// This file is licensed under the GPLv2 or later
//
// Copyright 2006-2008 Julien Rebetez <julienr@svn.gnome.org>
-// Copyright 2007-2012, 2015, 2016 Albert Astals Cid <aacid@kde.org>
+// Copyright 2007-2012, 2015-2017 Albert Astals Cid <aacid@kde.org>
// Copyright 2007-2008, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright 2007, 2013, 2016 Adrian Johnson <ajohnson@redneon.com>
// Copyright 2007 Iñigo Martínez <inigomartinez@gmail.com>
@@ -17,6 +17,8 @@
// Copyright 2012 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
// Copyright 2015 André Esser <bepandre@hotmail.com>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
+// Copyright 2017 Bernd Kuhls <berndkuhls@hotmail.com>
//
//========================================================================
@@ -27,8 +29,11 @@
#endif
#include <set>
+#include <limits>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#include "goo/gmem.h"
#include "goo/GooString.h"
#include "Error.h"
@@ -47,6 +52,7 @@
#include "PDFDocEncoding.h"
#include "Annot.h"
#include "Link.h"
+#include "Lexer.h"
//return a newly allocated char* containing an UTF16BE string of size length
char* pdfDocEncodingToUTF16 (GooString* orig, int* length)
@@ -56,8 +62,8 @@ char* pdfDocEncodingToUTF16 (GooString* orig, int* length)
char *result = new char[(*length)];
char *cstring = orig->getCString();
//unicode marker
- result[0] = 0xfe;
- result[1] = 0xff;
+ result[0] = (char)0xfe;
+ result[1] = (char)0xff;
//convert to utf16
for(int i=2,j=0; i<(*length); i+=2,j++) {
Unicode u = pdfDocEncoding[(unsigned int)((unsigned char)cstring[j])]&0xffff;
@@ -85,7 +91,7 @@ FormWidget::FormWidget(PDFDoc *docA, Object *aobj, unsigned num, Ref aref, FormF
childNum = num;
doc = docA;
xref = doc->getXRef();
- aobj->copy(&obj);
+ obj = aobj->copy();
type = formUndef;
field = fieldA;
widget = NULL;
@@ -95,7 +101,6 @@ FormWidget::~FormWidget()
{
if (widget)
widget->decRefCnt();
- obj.free ();
}
#ifdef DEBUG_FORMS
@@ -108,10 +113,8 @@ void FormWidget::createWidgetAnnotation() {
if (widget)
return;
- Object obj1;
- obj1.initRef(ref.num, ref.gen);
- widget = new AnnotWidget(doc, obj.getDict(), &obj1, field);
- obj1.free();
+ Object obj1(ref.num, ref.gen);
+ widget = new AnnotWidget(doc, &obj, &obj1, field);
}
GBool FormWidget::inRect(double x, double y) const {
@@ -178,13 +181,13 @@ FormWidgetButton::FormWidgetButton (PDFDoc *docA, Object *aobj, unsigned num, Re
type = formButton;
onStr = NULL;
- Object obj1, obj2;
-
// Find the name of the ON state in the AP dictionnary
// The reference say the Off state, if it existe, _must_ be stored in the AP dict under the name /Off
// The "on" state may be stored under any other name
- if (obj.dictLookup("AP", &obj1)->isDict()) {
- if (obj1.dictLookup("N", &obj2)->isDict()) {
+ Object obj1 = obj.dictLookup("AP");
+ if (obj1.isDict()) {
+ Object obj2 = obj1.dictLookup("N");
+ if (obj2.isDict()) {
for (int i = 0; i < obj2.dictGetLength(); i++) {
char *key = obj2.dictGetKey(i);
if (strcmp (key, "Off") != 0) {
@@ -193,9 +196,7 @@ FormWidgetButton::FormWidgetButton (PDFDoc *docA, Object *aobj, unsigned num, Re
}
}
}
- obj2.free();
}
- obj1.free();
}
char *FormWidgetButton::getOnStr() {
@@ -315,13 +316,18 @@ int FormWidgetText::getMaxLen () const
return parent()->getMaxLen ();
}
-void FormWidgetText::setContent(GooString* new_content)
+double FormWidgetText::getTextFontSize()
{
- if (isReadOnly()) {
- error(errInternal, -1, "FormWidgetText::setContentCopy called on a read only field\n");
- return;
- }
+ return parent()->getTextFontSize();
+}
+void FormWidgetText::setTextFontSize(int fontSize)
+{
+ parent()->setTextFontSize(fontSize);
+}
+
+void FormWidgetText::setContent(GooString* new_content)
+{
parent()->setContentCopy(new_content);
}
@@ -351,30 +357,18 @@ bool FormWidgetChoice::_checkRange (int i)
void FormWidgetChoice::select (int i)
{
- if (isReadOnly()) {
- error(errInternal, -1, "FormWidgetChoice::select called on a read only field\n");
- return;
- }
if (!_checkRange(i)) return;
parent()->select(i);
}
void FormWidgetChoice::toggle (int i)
{
- if (isReadOnly()) {
- error(errInternal, -1, "FormWidgetChoice::toggle called on a read only field\n");
- return;
- }
if (!_checkRange(i)) return;
parent()->toggle(i);
}
void FormWidgetChoice::deselectAll ()
{
- if (isReadOnly()) {
- error(errInternal, -1, "FormWidgetChoice::deselectAll called on a read only field\n");
- return;
- }
parent()->deselectAll();
}
@@ -401,10 +395,6 @@ bool FormWidgetChoice::isSelected (int i)
void FormWidgetChoice::setEditChoice (GooString* new_content)
{
- if (isReadOnly()) {
- error(errInternal, -1, "FormWidgetText::setEditChoice called on a read only field\n");
- return;
- }
if (!hasEdit()) {
error(errInternal, -1, "FormFieldChoice::setEditChoice : trying to edit an non-editable choice\n");
return;
@@ -464,9 +454,148 @@ FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *aobj, unsigned nu
type = formSignature;
}
-SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation)
+SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime)
+{
+ return static_cast<FormFieldSignature*>(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime);
+}
+
+std::vector<Goffset> FormWidgetSignature::getSignedRangeBounds()
+{
+ Object* obj = static_cast<FormFieldSignature*>(field)->getByteRange();
+ std::vector<Goffset> range_vec;
+ if (obj->isArray())
+ {
+ if (obj->arrayGetLength() == 4)
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ Object offsetObj(obj->arrayGet(2*i));
+ Object lenObj(obj->arrayGet(2*i+1));
+ if (offsetObj.isIntOrInt64() && lenObj.isIntOrInt64())
+ {
+ Goffset offset = offsetObj.getIntOrInt64();
+ Goffset len = lenObj.getIntOrInt64();
+ range_vec.push_back(offset);
+ range_vec.push_back(offset+len);
+ }
+ }
+ }
+ }
+ return range_vec;
+}
+
+GooString* FormWidgetSignature::getCheckedSignature(Goffset *checkedFileSize)
{
- return static_cast<FormFieldSignature*>(field)->validateSignature(doVerifyCert, forceRevalidation);
+ Goffset start = 0;
+ Goffset end = 0;
+ const std::vector<Goffset> ranges = getSignedRangeBounds();
+ if (ranges.size() == 4)
+ {
+ start = ranges[1];
+ end = ranges[2];
+ }
+ if (end >= start+6)
+ {
+ BaseStream* stream = doc->getBaseStream();
+ *checkedFileSize = stream->getLength();
+ Goffset len = end-start;
+ stream->setPos(end-1);
+ int c2 = stream->lookChar();
+ stream->setPos(start);
+ int c1 = stream->getChar();
+ // PDF signatures are first ASN1 DER, then hex encoded PKCS#7 structures,
+ // possibly padded with 0 characters and enclosed in '<' and '>'.
+ // The ASN1 DER encoding of a PKCS#7 structure must start with the tag 0x30
+ // for SEQUENCE. The next byte must be 0x80 for ASN1 DER indefinite length
+ // encoding or (0x80 + n) for ASN1 DER definite length encoding
+ // where n is the number of subsequent "length bytes" which big-endian
+ // encode the length of the content of the SEQUENCE following them.
+ if (len <= std::numeric_limits<int>::max() && *checkedFileSize > end && c1 == '<' && c2 == '>')
+ {
+ GooString gstr;
+ ++start;
+ --end;
+ len = end-start;
+ Goffset pos = 0;
+ do
+ {
+ c1 = stream->getChar();
+ if (c1 == EOF)
+ return nullptr;
+ gstr.append(static_cast<char>(c1));
+ } while (++pos < len);
+ if (gstr.getChar(0) == '3' && gstr.getChar(1) == '0')
+ {
+ if (gstr.getChar(2) == '8' && gstr.getChar(3) == '0')
+ {
+ // ASN1 DER indefinite length encoding:
+ // We only check that all characters up to the enclosing '>'
+ // are hex characters and that there are two hex encoded 0 bytes
+ // just before the enclosing '>' marking the end of the indefinite
+ // length encoding.
+ int paddingCount = 0;
+ while (gstr.getChar(len-1) == '0' && gstr.getChar(len-2) == '0')
+ {
+ ++paddingCount;
+ len -= 2;
+ }
+ if (paddingCount < 2 || len%2 == 1)
+ len = 0;
+ }
+ else if (gstr.getChar(2) == '8')
+ {
+ // ASN1 DER definite length encoding:
+ // We calculate the length of the following bytes from the length bytes and
+ // check that after the length bytes and the following calculated number of
+ // bytes all bytes up to the enclosing '>' character are hex encoded 0 bytes.
+ int lenBytes = gstr.getChar(3) - '0';
+ if (lenBytes > 0 && lenBytes <= 4)
+ {
+ int sigLen = 0;
+ for (int i = 0; i < 2*lenBytes; ++i)
+ {
+ sigLen <<= 4;
+ char c = gstr.getChar(i+4);
+ if (isdigit(c))
+ sigLen += c - '0';
+ else if (isxdigit(c) && c >= 'a')
+ sigLen += c - 'a' + 10;
+ else if (isxdigit(c) && c >= 'A')
+ sigLen += c - 'A' + 10;
+ else
+ {
+ len = 0;
+ break;
+ }
+ }
+ if (sigLen > 0 && 2*(sigLen+lenBytes) <= len-4)
+ {
+ for (int i = 2*(sigLen+lenBytes)+4; i < len; ++i)
+ {
+ if (gstr.getChar(i) != '0')
+ {
+ len = 0;
+ break;
+ }
+ }
+ }
+ else
+ len = 0;
+ }
+ }
+ for (int i = 0; i < len; ++i)
+ {
+ if (!isxdigit(gstr.getChar(i)))
+ len = 0;
+ }
+ if (len > 0)
+ {
+ return new GooString(&gstr, 0, len);
+ }
+ }
+ }
+ }
+ return nullptr;
}
void FormWidgetSignature::updateWidgetAppearance()
@@ -483,7 +612,7 @@ FormField::FormField(PDFDoc *docA, Object *aobj, const Ref& aref, FormField *par
{
doc = docA;
xref = doc->getXRef();
- aobj->copy(&obj);
+ obj = aobj->copy();
Dict* dict = obj.getDict();
ref.num = ref.gen = 0;
type = ty;
@@ -500,76 +629,65 @@ FormField::FormField(PDFDoc *docA, Object *aobj, const Ref& aref, FormField *par
ref = aref;
- Object obj1;
//childs
- if (dict->lookup("Kids", &obj1)->isArray()) {
+ Object obj1 = dict->lookup("Kids");
+ if (obj1.isArray()) {
// Load children
for (int i = 0 ; i < obj1.arrayGetLength(); i++) {
- Object childRef, childObj;
-
- if (!obj1.arrayGetNF(i, &childRef)->isRef()) {
+ Object childRef = obj1.arrayGetNF(i);
+ if (!childRef.isRef()) {
error (errSyntaxError, -1, "Invalid form field renference");
- childRef.free();
continue;
}
- if (!obj1.arrayGet(i, &childObj)->isDict()) {
+ Object childObj = obj1.arrayGet(i);
+ if (!childObj.isDict()) {
error (errSyntaxError, -1, "Form field child is not a dictionary");
- childObj.free();
- childRef.free();
continue;
}
const Ref ref = childRef.getRef();
if (usedParents->find(ref.num) == usedParents->end()) {
- Object obj2, obj3;
// Field child: it could be a form field or a widget or composed dict
- if (childObj.dictLookupNF("Parent", &obj2)->isRef() || childObj.dictLookup("Parent", &obj3)->isDict()) {
+ Object obj2 = childObj.dictLookupNF("Parent");
+ Object obj3 = childObj.dictLookup("Parent");
+ if (obj2.isRef() || obj3.isDict()) {
// Child is a form field or composed dict
// We create the field, if it's composed
// it will create the widget as a child
std::set<int> usedParentsAux = *usedParents;
usedParentsAux.insert(ref.num);
- obj2.free();
- obj3.free();
if (terminal) {
error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids\n");
- childObj.free();
- childRef.free();
continue;
}
numChildren++;
children = (FormField**)greallocn(children, numChildren, sizeof(FormField*));
children[numChildren - 1] = Form::createFieldFromDict(&childObj, doc, ref, this, &usedParentsAux);
- } else if (childObj.dictLookup("Subtype", &obj2)->isName("Widget")) {
- // Child is a widget annotation
- if (!terminal && numChildren > 0) {
- error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids\n");
- obj2.free();
- obj3.free();
- childObj.free();
- childRef.free();
- continue;
- }
- _createWidget(&childObj, ref);
- }
- obj2.free();
- obj3.free();
+ } else {
+ obj2 = childObj.dictLookup("Subtype");
+ if (obj2.isName("Widget")) {
+ // Child is a widget annotation
+ if (!terminal && numChildren > 0) {
+ error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids\n");
+ continue;
+ }
+ _createWidget(&childObj, ref);
+ }
+ }
}
- childObj.free();
- childRef.free();
}
} else {
// No children, if it's a composed dict, create the child widget
- obj1.free();
- if (dict->lookup("Subtype", &obj1)->isName("Widget"))
+ obj1 = dict->lookup("Subtype");
+ if (obj1.isName("Widget"))
_createWidget(&obj, ref);
}
- obj1.free();
//flags
- if (Form::fieldLookup(dict, "Ff", &obj1)->isInt()) {
+ obj1 = Form::fieldLookup(dict, "Ff");
+ if (obj1.isInt()) {
int flags = obj1.getInt();
if (flags & 0x1) { // 1 -> ReadOnly
readOnly = true;
@@ -581,39 +699,38 @@ FormField::FormField(PDFDoc *docA, Object *aobj, const Ref& aref, FormField *par
//TODO
}
}
- obj1.free();
// Variable Text
- if (Form::fieldLookup(dict, "DA", &obj1)->isString())
+ obj1 = Form::fieldLookup(dict, "DA");
+ if (obj1.isString())
defaultAppearance = obj1.getString()->copy();
- obj1.free();
- if (Form::fieldLookup(dict, "Q", &obj1)->isInt()) {
+ obj1 = Form::fieldLookup(dict, "Q");
+ if (obj1.isInt()) {
quadding = static_cast<VariableTextQuadding>(obj1.getInt());
hasQuadding = gTrue;
}
- obj1.free();
- if (dict->lookup("T", &obj1)->isString()) {
+ obj1 = dict->lookup("T");
+ if (obj1.isString()) {
partialName = obj1.getString()->copy();
} else {
partialName = NULL;
}
- obj1.free();
- if (dict->lookup("TU", &obj1)->isString()) {
+ obj1 = dict->lookup("TU");
+ if (obj1.isString()) {
alternateUiName = obj1.getString()->copy();
} else {
alternateUiName = NULL;
}
- obj1.free();
- if(dict->lookup("TM", &obj1)->isString()) {
+ obj1 = dict->lookup("TM");
+ if(obj1.isString()) {
mappingName = obj1.getString()->copy();
} else {
mappingName = NULL;
}
- obj1.free();
}
void FormField::setPartialName(const GooString &name)
@@ -621,9 +738,7 @@ void FormField::setPartialName(const GooString &name)
delete partialName;
partialName = name.copy();
- Object obj1;
- obj1.initString(name.copy());
- obj.getDict()->set("T", &obj1);
+ obj.getDict()->set("T", Object(name.copy()));
xref->setModifiedObject(&obj, ref);
}
@@ -640,7 +755,6 @@ FormField::~FormField()
delete widgets[i];
gfree (widgets);
}
- obj.free();
delete defaultAppearance;
delete partialName;
@@ -710,7 +824,6 @@ void FormField::_createWidget (Object *obj, Ref aref)
default:
error(errSyntaxWarning, -1, "SubType on non-terminal field, invalid document?");
numChildren--;
- terminal = false;
}
}
@@ -732,7 +845,7 @@ FormWidget* FormField::findWidgetByRef (Ref aref)
}
GooString* FormField::getFullyQualifiedName() {
- Object obj1, obj2;
+ Object obj1;
Object parent;
GooString *parent_name;
GooString *full_name;
@@ -743,9 +856,10 @@ GooString* FormField::getFullyQualifiedName() {
full_name = new GooString();
- obj.copy(&obj1);
- while (obj1.dictLookup("Parent", &parent)->isDict()) {
- if (parent.dictLookup("T", &obj2)->isString()) {
+ obj1 = obj.copy();
+ while (parent = obj1.dictLookup("Parent"), parent.isDict()) {
+ Object obj2 = parent.dictLookup("T");
+ if (obj2.isString()) {
parent_name = obj2.getString();
if (unicode_encoded) {
@@ -768,14 +882,9 @@ GooString* FormField::getFullyQualifiedName() {
full_name->insert(0, parent_name);
}
}
- obj2.free();
}
- obj1.free();
- parent.copy(&obj1);
- parent.free();
+ obj1 = parent.copy();
}
- obj1.free();
- parent.free();
if (partialName) {
if (unicode_encoded) {
@@ -811,8 +920,7 @@ GooString* FormField::getFullyQualifiedName() {
}
if (unicode_encoded) {
- full_name->insert(0, 0xff);
- full_name->insert(0, 0xfe);
+ full_name->prependUnicodeMarker();
}
fullyQualifiedName = full_name;
@@ -842,11 +950,11 @@ FormFieldButton::FormFieldButton(PDFDoc *docA, Object *aobj, const Ref& ref, For
noAllOff = false;
siblings = NULL;
numSiblings = 0;
- appearanceState.initNull();
+ appearanceState.setToNull();
- Object obj1;
- btype = formButtonCheck;
- if (Form::fieldLookup(dict, "Ff", &obj1)->isInt()) {
+ btype = formButtonCheck;
+ Object obj1 = Form::fieldLookup(dict, "Ff");
+ if (obj1.isInt()) {
int flags = obj1.getInt();
if (flags & 0x10000) { // 17 -> push button
@@ -865,7 +973,7 @@ FormFieldButton::FormFieldButton(PDFDoc *docA, Object *aobj, const Ref& ref, For
if (btype != formButtonPush) {
// Even though V is inheritable we are interested in the value of this
// field, if not present it's probably because it's a button in a set.
- dict->lookup("V", &appearanceState);
+ appearanceState = dict->lookup("V");
}
}
@@ -924,11 +1032,6 @@ void FormFieldButton::fillChildrenSiblingsID()
GBool FormFieldButton::setState(char *state)
{
- if (readOnly) {
- error(errInternal, -1, "FormFieldButton::setState called on a readOnly field\n");
- return gFalse;
- }
-
// A check button could behave as a radio button
// when it's in a set of more than 1 buttons
if (btype != formButtonRadio && btype != formButtonCheck)
@@ -994,19 +1097,13 @@ GBool FormFieldButton::getState(char *state) {
}
void FormFieldButton::updateState(char *state) {
- Object obj1;
-
- appearanceState.free();
- appearanceState.initName(state);
-
- appearanceState.copy(&obj1);
- obj.getDict()->set("V", &obj1);
+ appearanceState = Object(objName, state);
+ obj.getDict()->set("V", appearanceState.copy());
xref->setModifiedObject(&obj, ref);
}
FormFieldButton::~FormFieldButton()
{
- appearanceState.free();
if (siblings)
gfree(siblings);
}
@@ -1023,7 +1120,8 @@ FormFieldText::FormFieldText(PDFDoc *docA, Object *aobj, const Ref& ref, FormFie
multiline = password = fileSelect = doNotSpellCheck = doNotScroll = comb = richText = false;
maxLen = 0;
- if (Form::fieldLookup(dict, "Ff", &obj1)->isInt()) {
+ obj1 = Form::fieldLookup(dict, "Ff");
+ if (obj1.isInt()) {
int flags = obj1.getInt();
if (flags & 0x1000) // 13 -> Multiline
multiline = true;
@@ -1040,14 +1138,14 @@ FormFieldText::FormFieldText(PDFDoc *docA, Object *aobj, const Ref& ref, FormFie
if (flags & 0x2000000)// 26 -> RichText
richText = true;
}
- obj1.free();
- if (Form::fieldLookup(dict, "MaxLen", &obj1)->isInt()) {
+ obj1 = Form::fieldLookup(dict, "MaxLen");
+ if (obj1.isInt()) {
maxLen = obj1.getInt();
}
- obj1.free();
- if (Form::fieldLookup(dict, "V", &obj1)->isString()) {
+ obj1 = Form::fieldLookup(dict, "V");
+ if (obj1.isString()) {
if (obj1.getString()->hasUnicodeMarker()) {
if (obj1.getString()->getLength() > 2)
content = obj1.getString()->copy();
@@ -1059,7 +1157,6 @@ FormFieldText::FormFieldText(PDFDoc *docA, Object *aobj, const Ref& ref, FormFie
delete [] tmp_str;
}
}
- obj1.free();
}
#ifdef DEBUG_FORMS
@@ -1086,14 +1183,11 @@ void FormFieldText::setContentCopy (GooString* new_content)
//append the unicode marker <FE FF> if needed
if (!content->hasUnicodeMarker()) {
- content->insert(0, 0xff);
- content->insert(0, 0xfe);
+ content->prependUnicodeMarker();
}
}
- Object obj1;
- obj1.initString(content ? content->copy() : new GooString(""));
- obj.getDict()->set("V", &obj1);
+ obj.getDict()->set("V", Object(content ? content->copy() : new GooString("")));
xref->setModifiedObject(&obj, ref);
updateChildrenAppearance();
}
@@ -1103,6 +1197,87 @@ FormFieldText::~FormFieldText()
delete content;
}
+double FormFieldText::getTextFontSize()
+{
+ GooList* daToks = new GooList();
+ int idx = parseDA(daToks);
+ double fontSize = -1;
+ if (idx >= 0) {
+ char* p = nullptr;
+ fontSize = strtod(static_cast<GooString*>(daToks->get(idx))->getCString(), &p);
+ if (!p || *p)
+ fontSize = -1;
+ }
+ deleteGooList(daToks, GooString);
+ return fontSize;
+}
+
+void FormFieldText::setTextFontSize(int fontSize)
+{
+ if (fontSize > 0 && obj.isDict()) {
+ GooList* daToks = new GooList();
+ int idx = parseDA(daToks);
+ if (idx == -1) {
+ error(errSyntaxError, -1, "FormFieldText:: invalid DA object\n");
+ deleteGooList(daToks, GooString);
+ return;
+ }
+ if (defaultAppearance)
+ delete defaultAppearance;
+ defaultAppearance = new GooString;
+ for (int i = 0; i < daToks->getLength(); ++i) {
+ if (i > 0)
+ defaultAppearance->append(' ');
+ if (i == idx) {
+ defaultAppearance->appendf("{0:d}", fontSize);
+ } else {
+ defaultAppearance->append(static_cast<GooString*>(daToks->get(i)));
+ }
+ }
+ deleteGooList(daToks, GooString);
+ obj.dictSet("DA", Object(defaultAppearance->copy()));
+ xref->setModifiedObject(&obj, ref);
+ updateChildrenAppearance();
+ }
+}
+
+int FormFieldText::tokenizeDA(GooString* da, GooList* daToks, const char* searchTok)
+{
+ int idx = -1;
+ if(da && daToks) {
+ int i = 0;
+ int j = 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) {
+ }
+ GooString* tok = new GooString(da, i, j - i);
+ if (searchTok && !tok->cmp(searchTok))
+ idx = daToks->getLength();
+ daToks->append(tok);
+ i = j;
+ }
+ }
+ }
+ return idx;
+}
+
+int FormFieldText::parseDA(GooList* daToks)
+{
+ int idx = -1;
+ if (obj.isDict()) {
+ Object objDA(obj.dictLookup("DA"));
+ if (objDA.isString()) {
+ GooString* da = objDA.getString();
+ idx = tokenizeDA(da, daToks, "Tf") - 1;
+ }
+ }
+ return idx;
+}
+
//------------------------------------------------------------------------
// FormFieldChoice
@@ -1120,7 +1295,8 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For
combo = edit = multiselect = doNotSpellCheck = doCommitOnSelChange = false;
- if (Form::fieldLookup(dict, "Ff", &obj1)->isInt()) {
+ obj1 = Form::fieldLookup(dict, "Ff");
+ if (obj1.isInt()) {
int flags = obj1.getInt();
if (flags & 0x20000) // 18 -> Combo
combo = true;
@@ -1133,67 +1309,62 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For
if (flags & 0x4000000) // 27 -> CommitOnSelChange
doCommitOnSelChange = true;
}
- obj1.free();
- if (dict->lookup("TI", &obj1)->isInt())
+ obj1 = dict->lookup("TI");
+ if (obj1.isInt())
topIdx = obj1.getInt();
- obj1.free();
-
- if (dict->lookup("Opt", &obj1)->isArray()) {
- Object obj2;
+ obj1 = dict->lookup("Opt");
+ if (obj1.isArray()) {
numChoices = obj1.arrayGetLength();
choices = new ChoiceOpt[numChoices];
memset(choices, 0, sizeof(ChoiceOpt) * numChoices);
for (int i = 0; i < numChoices; i++) {
- if (obj1.arrayGet(i, &obj2)->isString()) {
+ Object obj2 = obj1.arrayGet(i);
+ if (obj2.isString()) {
choices[i].optionName = obj2.getString()->copy();
} else if (obj2.isArray()) { // [Export_value, Displayed_text]
- Object obj3;
-
if (obj2.arrayGetLength() < 2) {
error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- array's length < 2\n");
continue;
}
- if (obj2.arrayGet(0, &obj3)->isString())
+ Object obj3 = obj2.arrayGet(0);
+ if (obj3.isString())
choices[i].exportVal = obj3.getString()->copy();
else
error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- exported value not a string\n");
- obj3.free();
- if (obj2.arrayGet(1, &obj3)->isString())
+ obj3 = obj2.arrayGet(1);
+ if (obj3.isString())
choices[i].optionName = obj3.getString()->copy();
else
error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- choice name not a string\n");
- obj3.free();
} else {
error(errSyntaxError, -1, "FormWidgetChoice:: invalid {0:d} Opt entry\n", i);
}
- obj2.free();
}
} else {
//empty choice
}
- obj1.free();
// Find selected items
// Note: PDF specs say that /V has precedence over /I, but acroread seems to
// do the opposite. We do the same.
- if (Form::fieldLookup(dict, "I", &obj1)->isArray()) {
+ obj1 = Form::fieldLookup(dict, "I");
+ if (obj1.isArray()) {
for (int i = 0; i < obj1.arrayGetLength(); i++) {
- Object obj2;
- if (obj1.arrayGet(i, &obj2)->isInt() && obj2.getInt() >= 0 && obj2.getInt() < numChoices) {
+ Object obj2 = obj1.arrayGet(i);
+ if (obj2.isInt() && obj2.getInt() >= 0 && obj2.getInt() < numChoices) {
choices[obj2.getInt()].selected = true;
}
- obj2.free();
}
} else {
- obj1.free();
// Note: According to PDF specs, /V should *never* contain the exportVal.
// However, if /Opt is an array of (exportVal,optionName) pairs, acroread
// seems to expect the exportVal instead of the optionName and so we do too.
- if (Form::fieldLookup(dict, "V", &obj1)->isString()) {
+ obj1 = Form::fieldLookup(dict, "V");
+ if (obj1.isString()) {
GBool optionFound = gFalse;
for (int i = 0; i < numChoices; i++) {
@@ -1220,8 +1391,7 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For
} else if (obj1.isArray()) {
for (int i = 0; i < numChoices; i++) {
for (int j = 0; j < obj1.arrayGetLength(); j++) {
- Object obj2;
- obj1.arrayGet(j, &obj2);
+ Object obj2 = obj1.arrayGet(j);
GBool matches = gFalse;
if (choices[i].exportVal) {
@@ -1234,8 +1404,6 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For
}
}
- obj2.free();
-
if (matches) {
choices[i].selected = true;
break; // We've determined that this option is selected. No need to keep on scanning
@@ -1244,7 +1412,6 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For
}
}
}
- obj1.free();
}
FormFieldChoice::~FormFieldChoice()
@@ -1266,35 +1433,35 @@ void FormFieldChoice::print(int indent)
#endif
void FormFieldChoice::updateSelection() {
- Object objV, objI, obj1;
- objI.initNull();
+ Object objV, obj1;
+ Object objI(objNull);
if (edit && editedChoice) {
// This is an editable combo-box with user-entered text
- objV.initString(editedChoice->copy());
+ objV = Object(editedChoice->copy());
} else {
const int numSelected = getNumSelected();
// Create /I array only if multiple selection is allowed (as per PDF spec)
if (multiselect) {
- objI.initArray(xref);
+ objI = Object(new Array(xref));
}
if (numSelected == 0) {
// No options are selected
- objV.initString(new GooString(""));
+ objV = Object(new GooString(""));
} else if (numSelected == 1) {
// Only one option is selected
for (int i = 0; i < numChoices; i++) {
if (choices[i].selected) {
if (multiselect) {
- objI.arrayAdd(obj1.initInt(i));
+ objI.arrayAdd(Object(i));
}
if (choices[i].exportVal) {
- objV.initString(choices[i].exportVal->copy());
+ objV = Object(choices[i].exportVal->copy());
} else if (choices[i].optionName) {
- objV.initString(choices[i].optionName->copy());
+ objV = Object(choices[i].optionName->copy());
}
break; // We've just written the selected option. No need to keep on scanning
@@ -1302,25 +1469,25 @@ void FormFieldChoice::updateSelection() {
}
} else {
// More than one option is selected
- objV.initArray(xref);
+ objV = Object(new Array(xref));
for (int i = 0; i < numChoices; i++) {
if (choices[i].selected) {
if (multiselect) {
- objI.arrayAdd(obj1.initInt(i));
+ objI.arrayAdd(Object(i));
}
if (choices[i].exportVal) {
- objV.arrayAdd(obj1.initString(choices[i].exportVal->copy()));
+ objV.arrayAdd(Object(choices[i].exportVal->copy()));
} else if (choices[i].optionName) {
- objV.arrayAdd(obj1.initString(choices[i].optionName->copy()));
+ objV.arrayAdd(Object(choices[i].optionName->copy()));
}
}
}
}
}
- obj.getDict()->set("V", &objV);
- obj.getDict()->set("I", &objI);
+ obj.getDict()->set("V", std::move(objV));
+ obj.getDict()->set("I", std::move(objI));
xref->setModifiedObject(&obj, ref);
updateChildrenAppearance();
}
@@ -1373,8 +1540,7 @@ void FormFieldChoice::setEditChoice (GooString* new_content)
//append the unicode marker <FE FF> if needed
if (!editedChoice->hasUnicodeMarker()) {
- editedChoice->insert(0, 0xff);
- editedChoice->insert(0, 0xfe);
+ editedChoice->prependUnicodeMarker();
}
}
updateSelection();
@@ -1411,7 +1577,9 @@ GooString *FormFieldChoice::getSelectedChoice() {
// FormFieldSignature
//------------------------------------------------------------------------
FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& ref, FormField *parent, std::set<int> *usedParents)
- : FormField(docA, dict, ref, parent, usedParents, formSignature)
+ : FormField(docA, dict, ref, parent, usedParents, formSignature),
+ signature_type(adbe_pkcs7_detached),
+ signature(nullptr), signature_info(nullptr)
{
signature = NULL;
@@ -1421,7 +1589,6 @@ FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& re
FormFieldSignature::~FormFieldSignature()
{
- byte_range.free();
delete signature_info;
delete signature;
}
@@ -1431,39 +1598,40 @@ void FormFieldSignature::parseInfo()
if (!obj.isDict())
return;
- Object sig_dict, contents_obj, time_of_signing, subfilterName;
-
// retrieve PKCS#7
- obj.dictLookup("V", &sig_dict);
+ Object sig_dict = obj.dictLookup("V");
if (!sig_dict.isDict()) {
- sig_dict.free();
return;
}
- sig_dict.dictLookup("Contents", &contents_obj);
+ Object contents_obj = sig_dict.dictLookup("Contents");
if (contents_obj.isString()) {
signature = contents_obj.getString()->copy();
}
- contents_obj.free();
- sig_dict.dictLookup("ByteRange", &byte_range);
+ byte_range = sig_dict.dictLookup("ByteRange");
// retrieve SigningTime
- sig_dict.dictLookup("M", &time_of_signing);
+ Object time_of_signing = sig_dict.dictLookup("M");
if (time_of_signing.isString()) {
GooString *time_str = time_of_signing.getString();
signature_info->setSigningTime(dateStringToTime(time_str)); // Put this information directly in SignatureInfo object
- time_of_signing.free();
}
// check if subfilter is supported for signature validation, only detached signatures work for now
- sig_dict.dictLookup("SubFilter", &subfilterName);
- if (subfilterName.isName("adbe.pkcs7.detached") || subfilterName.isName("adbe.pkcs7.sha1")) {
+ Object subfilterName = sig_dict.dictLookup("SubFilter");
+ if (subfilterName.isName("adbe.pkcs7.sha1")) {
+ signature_type = adbe_pkcs7_sha1;
+ signature_info->setSubFilterSupport(true);
+ }
+ else if (subfilterName.isName("adbe.pkcs7.detached")) {
+ signature_type = adbe_pkcs7_detached;
+ signature_info->setSubFilterSupport(true);
+ }
+ else if (subfilterName.isName("ETSI.CAdES.detached")) {
+ signature_type = ETSI_CAdES_detached;
signature_info->setSubFilterSupport(true);
}
-
- subfilterName.free();
- sig_dict.free();
}
void FormFieldSignature::hashSignedDataBlock(SignatureHandler *handler, Goffset block_len)
@@ -1492,8 +1660,12 @@ void FormFieldSignature::hashSignedDataBlock(SignatureHandler *handler, Goffset
#endif
}
+FormSignatureType FormWidgetSignature::signatureType()
+{
+ return static_cast<FormFieldSignature*>(field)->signature_type;
+}
-SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation)
+SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime)
{
#ifdef ENABLE_NSS3
if (!signature_info->isSubfilterSupported()) {
@@ -1530,9 +1702,8 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
Goffset fileLength = doc->getBaseStream()->getLength();
for (int i = 0; i < arrayLen/2; i++) {
- Object offsetObj, lenObj;
- byte_range.arrayGet(i*2, &offsetObj);
- byte_range.arrayGet(i*2+1, &lenObj);
+ Object offsetObj = byte_range.arrayGet(i*2);
+ Object lenObj = byte_range.arrayGet(i*2+1);
if (!offsetObj.isIntOrInt64() || !lenObj.isIntOrInt64()) {
error(errSyntaxError, 0, "Illegal values in ByteRange array");
@@ -1554,6 +1725,8 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
sig_val_state = signature_handler.validateSignature();
signature_info->setSignatureValStatus(SignatureHandler::NSS_SigTranslate(sig_val_state));
signature_info->setSignerName(signature_handler.getSignerName());
+ signature_info->setSubjectDN(signature_handler.getSignerSubjectDN());
+ signature_info->setHashAlgorithm(signature_handler.getHashAlgorithm());
// verify if signature contains a 'signing time' attribute
if (signature_handler.getSigningTime() != 0) {
@@ -1564,7 +1737,7 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
return signature_info;
}
- cert_val_state = signature_handler.validateCertificate();
+ cert_val_state = signature_handler.validateCertificate(validationTime);
signature_info->setCertificateValStatus(SignatureHandler::NSS_CertTranslate(cert_val_state));
#endif
@@ -1598,50 +1771,41 @@ Form::Form(PDFDoc *docA, Object* acroFormA)
defaultAppearance = NULL;
defaultResources = NULL;
- acroForm->dictLookup("NeedAppearances", &obj1);
+ obj1 = acroForm->dictLookup("NeedAppearances");
needAppearances = (obj1.isBool() && obj1.getBool());
- obj1.free();
- if (acroForm->dictLookup("DA", &obj1)->isString())
+ obj1 = acroForm->dictLookup("DA");
+ if (obj1.isString())
defaultAppearance = obj1.getString()->copy();
- obj1.free();
- if (acroForm->dictLookup("Q", &obj1)->isInt())
+ obj1 = acroForm->dictLookup("Q");
+ if (obj1.isInt())
quadding = static_cast<VariableTextQuadding>(obj1.getInt());
- obj1.free();
- acroForm->dictLookup("DR", &resDict);
+ resDict = acroForm->dictLookup("DR");
if (resDict.isDict()) {
// At a minimum, this dictionary shall contain a Font entry
- if (resDict.dictLookup("Font", &obj1)->isDict())
+ obj1 = resDict.dictLookup("Font");
+ if (obj1.isDict())
defaultResources = new GfxResources(xref, resDict.getDict(), NULL);
- obj1.free();
}
if (!defaultResources) {
- resDict.free();
- resDict.initNull();
+ resDict.setToNull();
}
- acroForm->dictLookup("Fields", &obj1);
+ obj1 = acroForm->dictLookup("Fields");
if (obj1.isArray()) {
Array *array = obj1.getArray();
- Object obj2;
-
for(int i=0; i<array->getLength(); i++) {
- Object oref;
- array->get(i, &obj2);
- array->getNF(i, &oref);
+ Object obj2 = array->get(i);
+ Object oref = array->getNF(i);
if (!oref.isRef()) {
error(errSyntaxWarning, -1, "Direct object in rootFields");
- obj2.free();
- oref.free();
continue;
}
if (!obj2.isDict()) {
error(errSyntaxWarning, -1, "Reference in Fields array to an invalid or non existent object");
- obj2.free();
- oref.free();
continue;
}
@@ -1653,13 +1817,24 @@ Form::Form(PDFDoc *docA, Object* acroFormA)
std::set<int> usedParents;
rootFields[numFields++] = createFieldFromDict (&obj2, doc, oref.getRef(), NULL, &usedParents);
- obj2.free();
- oref.free();
}
} else {
error(errSyntaxError, -1, "Can't get Fields array\n");
}
- obj1.free ();
+
+ obj1 = acroForm->dictLookup("CO");
+ if (obj1.isArray()) {
+ Array *array = obj1.getArray();
+ calculateOrder.reserve(array->getLength());
+ for(int i=0; i<array->getLength(); i++) {
+ Object oref = array->getNF(i);
+ if (!oref.isRef()) {
+ error(errSyntaxWarning, -1, "Direct object in CO");
+ continue;
+ }
+ calculateOrder.push_back(oref.getRef());
+ }
+ }
#ifdef DEBUG_FORMS
for (int i = 0; i < numFields; i++)
@@ -1674,54 +1849,43 @@ Form::~Form() {
gfree (rootFields);
delete defaultAppearance;
delete defaultResources;
- resDict.free();
}
// Look up an inheritable field dictionary entry.
-static Object *fieldLookup(Dict *field, const char *key, Object *obj, std::set<int> *usedParents) {
- Dict *dict;
- Object parent;
-
- dict = field;
- if (!dict->lookup(key, obj)->isNull()) {
+static Object fieldLookup(Dict *field, const char *key, std::set<int> *usedParents) {
+ Dict *dict = field;
+ Object obj = dict->lookup(key);
+ if (!obj.isNull()) {
return obj;
}
- obj->free();
- dict->lookupNF("Parent", &parent);
+ Object parent = dict->lookupNF("Parent");
if (parent.isRef()) {
const Ref ref = parent.getRef();
if (usedParents->find(ref.num) == usedParents->end()) {
usedParents->insert(ref.num);
- Object obj2;
- parent.fetch(dict->getXRef(), &obj2);
+ Object obj2 = parent.fetch(dict->getXRef());
if (obj2.isDict()) {
- fieldLookup(obj2.getDict(), key, obj, usedParents);
- } else {
- obj->initNull();
+ return fieldLookup(obj2.getDict(), key, usedParents);
}
- obj2.free();
}
} else if (parent.isDict()) {
- fieldLookup(parent.getDict(), key, obj, usedParents);
- } else {
- obj->initNull();
+ return fieldLookup(parent.getDict(), key, usedParents);
}
- parent.free();
- return obj;
+ return Object(objNull);
}
-Object *Form::fieldLookup(Dict *field, const char *key, Object *obj) {
+Object Form::fieldLookup(Dict *field, const char *key) {
std::set<int> usedParents;
- return ::fieldLookup(field, key, obj, &usedParents);
+ return ::fieldLookup(field, key, &usedParents);
}
FormField *Form::createFieldFromDict (Object* obj, PDFDoc *docA, const Ref& pref, FormField *parent, std::set<int> *usedParents)
{
- Object obj2;
FormField *field;
- if (Form::fieldLookup(obj->getDict (), "FT", &obj2)->isName("Btn")) {
+ Object obj2 = Form::fieldLookup(obj->getDict (), "FT");
+ if (obj2.isName("Btn")) {
field = new FormFieldButton(docA, obj, pref, parent, usedParents);
} else if (obj2.isName("Tx")) {
field = new FormFieldText(docA, obj, pref, parent, usedParents);
@@ -1732,7 +1896,6 @@ FormField *Form::createFieldFromDict (Object* obj, PDFDoc *docA, const Ref& pref
} else { //we don't have an FT entry => non-terminal field
field = new FormField(docA, obj, pref, parent, usedParents);
}
- obj2.free();
return field;
}