2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 02:49:00 +00:00

Implement helper class for interactive forms

This commit is contained in:
Jay Berkenbilt 2018-06-19 09:26:41 -04:00
parent 0dadf17ab7
commit 0b05111db8
27 changed files with 14524 additions and 0 deletions

View File

@ -1,5 +1,22 @@
2018-06-20 Jay Berkenbilt <ejb@ql.org>
* Added new classes QPDFAcroFormDocumentHelper,
QPDFFormFieldObjectHelper, and QPDFAnnotationObjectHelper to
assist with working with interactive forms in PDF files. At
present, API methods for reading forms, form fields, and widget
annotations have been added. It is likely that some additional
methods for modifying forms will be added in the future. Note that
qpdf remains a library whose function is primarily focused around
document structure and metadata rather than content. As such, it
is not expected that qpdf will have higher level APIs for
generating form contents, but qpdf will hopefully gain the
capability to deal with the bookkeeping aspects of wiring up all
the objects, which could make it a useful library for other
software that works with PDF interactive forms. PDF forms are
complex, and the terminology around them is confusing. Please see
comments at the top of QPDFAcroFormDocumentHelper.hh for
additional discussion.
* Added new classes QPDFPageDocumentHelper and QPDFPageObjctHelper
for page-level API functions. These classes introduce a new API
pattern of document helpers and object helpers in qpdf. The helper

View File

@ -0,0 +1,165 @@
// Copyright (c) 2005-2018 Jay Berkenbilt
//
// This file is part of qpdf.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Versions of qpdf prior to version 7 were released under the terms
// of version 2.0 of the Artistic License. At your option, you may
// continue to consider qpdf to be licensed under those terms. Please
// see the manual for additional information.
#ifndef __QPDFACROFORMDOCUMENTHELPER_HH__
#define __QPDFACROFORMDOCUMENTHELPER_HH__
// This document helper is intended to help with operations on
// interactive forms. Here are the key things to know:
// * The PDF specification talks about interactive forms and also
// about form XObjects. While form XObjects appear in parts of
// interactive forms, this class is concerned about interactive
// forms, not form XObjects.
//
// * Interactive forms are discussed in the PDF Specification (ISO PDF
// 32000-1:2008) section 12.7. Also relevant is the section about
// Widget annotations. Annotations are discussed in section 12.5
// with annotation dictionaries discussed in 12.5.1. Widget
// annotations are discussed specifically in section 12.5.6.19.
//
// * What you need to know about the structure of interactive forms in
// PDF files:
//
// - The document catalog contains the key "/AcroForm" which
// contains a list of fields. Fields are represented as a tree
// structure much like pages. Nodes in the fields tree may contain
// other fields. Fields may inherit values of many of their
// attributes from ancestors in the tree.
//
// - Fields may also have children that are widget annotations. As a
// special case, and a cause of considerable confusion, if a field
// has a single annotation as a child, the annotation dictionary
// may be merged with the field dictionary. In that case, the
// field and the annotation are in the same object. Note that,
// while field dictionary attributes are inherited, annotation
// dictionary attributes are not.
//
// - A page dictionary contains a key called "/Annots" which
// contains a simple list of annotations. For any given annotation
// of subtype "/Widget", you should encounter that annotation in
// the "/Annots" dictionary of a page, and you should also be able
// to reach it by traversing through the "/AcroForm" dictionary
// from the document catalog. In the simplest case (and also a
// very common case), a form field's widget annotation will be
// merged with the field object, and the object will appear
// directly both under "/Annots" in the page dictionary and under
// "/Fields" in the "/AcroForm" dictionary. In a more complex
// case, you may have to trace through various "/Kids" elements in
// the "/AcroForm" field entry until you find the annotation
// dictionary.
#include <qpdf/QPDFDocumentHelper.hh>
#include <qpdf/DLL.h>
#include <qpdf/QPDFAnnotationObjectHelper.hh>
#include <qpdf/QPDFFormFieldObjectHelper.hh>
#include <qpdf/QPDFPageObjectHelper.hh>
#include <map>
#include <set>
#include <vector>
class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
{
public:
QPDFAcroFormDocumentHelper(QPDF&);
// This class lazily creates an internal cache of the mapping
// among form fields, annotations, and pages. Methods within this
// class preserve the validity of this cache. However, if you
// modify pages' annotation dictionaries, the document's /AcroForm
// dictionary, or any form fields manually in a way that alters
// the association between forms, fields, annotations, and pages,
// it may cause this cache to become invalid. This method marks
// the cache invalid and forces it to be regenerated the next time
// it is needed.
QPDF_DLL
void invalidateCache();
QPDF_DLL
bool
hasAcroForm();
// Return a vector of all terminal fields in a document. Terminal
// fields are fields that have no children that are also fields.
// Terminal fields may still have children that are annotations.
// Intermediate nodes in the fields tree are not included in this
// list, but you can still reach them through the getParent method
// of the field object helper.
QPDF_DLL
std::vector<QPDFFormFieldObjectHelper>
getFormFields();
// Return the annotations associated with a terminal field. Note
// that in the case of a field having a single annotation, the
// underlying object will typically be the same as the underlying
// object for the field.
QPDF_DLL
std::vector<QPDFAnnotationObjectHelper>
getAnnotationsForField(QPDFFormFieldObjectHelper);
// Return annotations of subtype /Widget for a page.
QPDF_DLL
std::vector<QPDFAnnotationObjectHelper>
getWidgetAnnotationsForPage(QPDFPageObjectHelper);
// Return the terminal field that is associated with this
// annotation. If the annotation dictionary is merged with the
// field dictionary, the underlying object will be the same, but
// this is not always the case. Note that if you call this method
// with an annotation that is not a widget annotation, there will
// not be an associated field, and this method will raise an
// exception.
QPDF_DLL
QPDFFormFieldObjectHelper
getFieldForAnnotation(QPDFAnnotationObjectHelper);
private:
void analyze();
void traverseField(QPDFObjectHandle field,
QPDFObjectHandle parent,
int depth, std::set<QPDFObjGen>& visited);
class Members
{
friend class QPDFAcroFormDocumentHelper;
public:
~Members();
private:
Members();
Members(Members const&);
bool cache_valid;
std::map<QPDFObjGen,
std::vector<QPDFAnnotationObjectHelper>
> field_to_annotations;
std::map<QPDFObjGen, QPDFFormFieldObjectHelper> annotation_to_field;
};
PointerHolder<Members> m;
};
#endif // __QPDFACROFORMDOCUMENTHELPER_HH__

View File

@ -0,0 +1,86 @@
// Copyright (c) 2005-2018 Jay Berkenbilt
//
// This file is part of qpdf.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Versions of qpdf prior to version 7 were released under the terms
// of version 2.0 of the Artistic License. At your option, you may
// continue to consider qpdf to be licensed under those terms. Please
// see the manual for additional information.
#ifndef __QPDFANNOTATIONOBJECTHELPER_HH__
#define __QPDFANNOTATIONOBJECTHELPER_HH__
#include <qpdf/QPDFObjectHelper.hh>
#include <qpdf/DLL.h>
class QPDFAnnotationObjectHelper: public QPDFObjectHelper
{
public:
QPDFAnnotationObjectHelper(QPDFObjectHandle);
// This class provides helper methods for certain types of
// annotations. At its introduction, it only supports Widget
// annotations, but other types of annotations may be supported in
// the future. For additional information about interactive forms,
// please see the comments at the top of
// QPDFAcroFormDocumentHelper.hh.
// Return the subtype of the annotation as a string (e.g.
// "/Widget"). Returns the empty string if the subtype (which is
// required by the spec) is missing.
QPDF_DLL
std::string getSubtype();
QPDF_DLL
QPDFObjectHandle::Rectangle getRect();
QPDF_DLL
QPDFObjectHandle getAppearanceDictionary();
// Return the appearance state as given in "/AS", or the empty
// string if none is given.
QPDF_DLL
std::string getAppearanceState();
// Return a specific stream. "which" may be one of "/N", "/R", or
// "/D" to indicate the normal, rollover, or down appearance
// stream. (Any value may be passed to "which"; if an appearance
// stream of that name exists, it will be returned.) If the value
// associated with "which" in the appearance dictionary is a
// subdictionary, an appearance state may be specified to select
// which appearance stream is desired. If not specified, the
// appearance state in "/AS" will used.
QPDF_DLL
QPDFObjectHandle getAppearanceStream(std::string const& which,
std::string const& state = "");
private:
class Members
{
friend class QPDFPageObjectHelper;
public:
~Members();
private:
Members();
Members(Members const&);
};
PointerHolder<Members> m;
};
#endif // __QPDFANNOTATIONOBJECTHELPER_HH__

View File

@ -0,0 +1,133 @@
// Copyright (c) 2005-2018 Jay Berkenbilt
//
// This file is part of qpdf.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Versions of qpdf prior to version 7 were released under the terms
// of version 2.0 of the Artistic License. At your option, you may
// continue to consider qpdf to be licensed under those terms. Please
// see the manual for additional information.
#ifndef __QPDFFORMFIELDOBJECTHELPER_HH__
#define __QPDFFORMFIELDOBJECTHELPER_HH__
// This object helper helps with form fields for interactive forms.
// Please see comments in QPDFAcroFormDocumentHelper.hh for additional
// details.
#include <qpdf/QPDFObjectHelper.hh>
#include <qpdf/DLL.h>
class QPDFFormFieldObjectHelper: public QPDFObjectHelper
{
public:
QPDFFormFieldObjectHelper();
QPDFFormFieldObjectHelper(QPDFObjectHandle);
QPDF_DLL
bool isNull();
// Return the field's parent. A form field object helper whose
// underlying object is null is returned if there is no parent.
// This condition may be tested by calling isNull().
QPDF_DLL
QPDFFormFieldObjectHelper getParent();
// Get a field value, possibly inheriting the value from an
// ancestor node.
QPDF_DLL
QPDFObjectHandle getInheritableFieldValue(std::string const& name);
// Get an inherited field value as a string. If it is not a
// string, silently return the empty string.
QPDF_DLL
std::string getInheritableFieldValueAsString(std::string const& name);
// Get an inherited field value of type name as a string
// representing the name. If it is not a name, silently return
// the empty string.
QPDF_DLL
std::string getInheritableFieldValueAsName(std::string const& name);
// Returns the value of /FT if present, otherwise returns the
// empty string.
QPDF_DLL
std::string getFieldType();
QPDF_DLL
std::string getFullyQualifiedName();
QPDF_DLL
std::string getPartialName();
// Return the alternative field name (/TU), which is the field
// name intended to be presented to users. If not present, fall
// back to the fully qualified name.
QPDF_DLL
std::string getAlternativeName();
// Return the mapping field name (/TM). If not present, fall back
// to the alternative name, then to the partial name.
QPDF_DLL
std::string getMappingName();
QPDF_DLL
QPDFObjectHandle getValue();
// Return the field's value as a string. If this is called with a
// field whose value is not a string, the empty string will be
// silently returned.
QPDF_DLL
std::string getValueAsString();
QPDF_DLL
QPDFObjectHandle getDefaultValue();
// Return the field's default value as a string. If this is called
// with a field whose value is not a string, the empty string will
// be silently returned.
QPDF_DLL
std::string getDefaultValueAsString();
// Return the default appearance string, taking inheritance from
// the field tree into account. Returns the empty string if the
// default appearance string is not available (because it's
// erroneously absent or because this is not a variable text
// field).
QPDF_DLL
std::string getDefaultAppearance();
// Return the quadding value, taking inheritance from the field
// tree into account. Returns 0 if quadding is not specified.
QPDF_DLL
int getQuadding();
private:
class Members
{
friend class QPDFFormFieldObjectHelper;
public:
~Members();
private:
Members();
Members(Members const&);
};
PointerHolder<Members> m;
};
#endif // __QPDFFORMFIELDOBJECTHELPER_HH__

View File

@ -23,6 +23,7 @@
#define __QPDFPAGEOBJECTHELPER_HH__
#include <qpdf/QPDFObjectHelper.hh>
#include <qpdf/QPDFAnnotationObjectHelper.hh>
#include <qpdf/DLL.h>
@ -43,6 +44,13 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
QPDF_DLL
std::map<std::string, QPDFObjectHandle> getPageImages();
// Return the annotations in the page's "/Annots" list, if any. If
// only_subtype is non-empty, only include annotations of the
// given subtype.
QPDF_DLL
std::vector<QPDFAnnotationObjectHelper> getAnnotations(
std::string const& only_subtype = "");
// Returns a vector of stream objects representing the content
// streams for the given page. This routine allows the caller to
// not care whether there are one or more than one content streams

View File

@ -0,0 +1,252 @@
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QPDFPageDocumentHelper.hh>
QPDFAcroFormDocumentHelper::Members::~Members()
{
}
QPDFAcroFormDocumentHelper::Members::Members() :
cache_valid(false)
{
}
QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) :
QPDFDocumentHelper(qpdf),
m(new Members())
{
}
void
QPDFAcroFormDocumentHelper::invalidateCache()
{
this->m->cache_valid = false;
this->m->field_to_annotations.clear();
this->m->annotation_to_field.clear();
}
bool
QPDFAcroFormDocumentHelper::hasAcroForm()
{
return this->qpdf.getRoot().hasKey("/AcroForm");
}
std::vector<QPDFFormFieldObjectHelper>
QPDFAcroFormDocumentHelper::getFormFields()
{
analyze();
std::vector<QPDFFormFieldObjectHelper> result;
for (std::map<QPDFObjGen,
std::vector<QPDFAnnotationObjectHelper> >::iterator iter =
this->m->field_to_annotations.begin();
iter != this->m->field_to_annotations.end(); ++iter)
{
result.push_back(this->qpdf.getObjectByObjGen((*iter).first));
}
return result;
}
std::vector<QPDFAnnotationObjectHelper>
QPDFAcroFormDocumentHelper::getAnnotationsForField(QPDFFormFieldObjectHelper h)
{
analyze();
std::vector<QPDFAnnotationObjectHelper> result;
QPDFObjGen og(h.getObjectHandle().getObjGen());
if (this->m->field_to_annotations.count(og))
{
result = this->m->field_to_annotations[og];
}
return result;
}
std::vector<QPDFAnnotationObjectHelper>
QPDFAcroFormDocumentHelper::getWidgetAnnotationsForPage(QPDFPageObjectHelper h)
{
return h.getAnnotations("/Widget");
}
QPDFFormFieldObjectHelper
QPDFAcroFormDocumentHelper::getFieldForAnnotation(QPDFAnnotationObjectHelper h)
{
QPDFObjectHandle oh = h.getObjectHandle();
if (! (oh.isDictionary() &&
oh.getKey("/Subtype").isName() &&
(oh.getKey("/Subtype").getName() == "/Widget")))
{
throw std::logic_error(
"QPDFAnnotationObjectHelper::getFieldForAnnotation called for"
" non-/Widget annotation");
}
analyze();
QPDFFormFieldObjectHelper result(QPDFObjectHandle::newNull());
QPDFObjGen og(oh.getObjGen());
if (this->m->annotation_to_field.count(og))
{
result = this->m->annotation_to_field[og];
}
return result;
}
void
QPDFAcroFormDocumentHelper::analyze()
{
if (this->m->cache_valid)
{
return;
}
this->m->cache_valid = true;
QPDFObjectHandle acroform = this->qpdf.getRoot().getKey("/AcroForm");
if (! (acroform.isDictionary() && acroform.hasKey("/Fields")))
{
return;
}
QPDFObjectHandle fields = acroform.getKey("/Fields");
if (! fields.isArray())
{
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array");
acroform.warnIfPossible(
"/Fields key of /AcroForm dictionary is not an array; ignoring");
fields = QPDFObjectHandle::newArray();
}
// Traverse /AcroForm to find annotations and map them
// bidirectionally to fields.
std::set<QPDFObjGen> visited;
size_t nfields = fields.getArrayNItems();
QPDFObjectHandle null(QPDFObjectHandle::newNull());
for (size_t i = 0; i < nfields; ++i)
{
traverseField(fields.getArrayItem(i), null, 0, visited);
}
// All Widget annotations should have been encountered by
// traversing /AcroForm, but in case any weren't, find them by
// walking through pages, and treat any widget annotation that is
// not associated with a field as its own field. This just ensures
// that requesting the field for any annotation we find through a
// page's /Annots list will have some associated field. Note that
// a file that contains this kind of error will probably not
// actually work with most viewers.
QPDFPageDocumentHelper dh(this->qpdf);
std::vector<QPDFPageObjectHelper> pages = dh.getAllPages();
for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin();
iter != pages.end(); ++iter)
{
QPDFPageObjectHelper ph(*iter);
std::vector<QPDFAnnotationObjectHelper> annots =
getWidgetAnnotationsForPage(ph);
for (std::vector<QPDFAnnotationObjectHelper>::iterator i2 =
annots.begin();
i2 != annots.end(); ++i2)
{
QPDFObjectHandle annot((*i2).getObjectHandle());
QPDFObjGen og(annot.getObjGen());
if (this->m->annotation_to_field.count(og) == 0)
{
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper orphaned widget");
// This is not supposed to happen, but it's easy
// enough for us to handle this case. Treat the
// annotation as its own field. This could allow qpdf
// to sensibly handle a case such as a PDF creator
// adding a self-contained annotation (merged with the
// field dictionary) to the page's /Annots array and
// forgetting to also put it in /AcroForm.
annot.warnIfPossible(
"this widget annotation is not"
" reachable from /AcroForm in the document catalog");
this->m->annotation_to_field[og] =
QPDFFormFieldObjectHelper(annot);
this->m->field_to_annotations[og].push_back(
QPDFAnnotationObjectHelper(annot));
}
}
}
}
void
QPDFAcroFormDocumentHelper::traverseField(
QPDFObjectHandle field, QPDFObjectHandle parent, int depth,
std::set<QPDFObjGen>& visited)
{
if (depth > 100)
{
// Arbitrarily cut off recursion at a fixed depth to avoid
// specially crafted files that could cause stack overflow.
return;
}
if (! field.isIndirect())
{
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper direct field");
field.warnIfPossible(
"encountered a direct object as a field or annotation while"
" traversing /AcroForm; ignoring field or annotation");
return;
}
if (! field.isDictionary())
{
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper non-dictionary field");
field.warnIfPossible(
"encountered a non-dictionary as a field or annotation while"
" traversing /AcroForm; ignoring field or annotation");
return;
}
QPDFObjGen og(field.getObjGen());
if (visited.count(og) != 0)
{
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper loop");
field.warnIfPossible("loop detected while traversing /AcroForm");
return;
}
visited.insert(og);
// A dictionary encountered while traversing the /AcroForm field
// may be a form field, an annotation, or the merger of the two. A
// field that has no fields below it is a terminal. If a terminal
// field looks like an annotation, it is an annotation because
// annotation dictionary fields can be merged with terminal field
// dictionaries. Otherwise, the annotation fields might be there
// to be inherited by annotations below it.
bool is_annotation = false;
bool is_field = (0 == depth);
QPDFObjectHandle kids = field.getKey("/Kids");
if (kids.isArray())
{
is_field = true;
size_t nkids = kids.getArrayNItems();
for (size_t k = 0; k < nkids; ++k)
{
traverseField(kids.getArrayItem(k), field, 1 + depth, visited);
}
}
else
{
if (field.hasKey("/Parent"))
{
is_field = true;
}
if (field.hasKey("/Subtype") ||
field.hasKey("/Rect") ||
field.hasKey("/AP"))
{
is_annotation = true;
}
}
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper field found",
(depth == 0) ? 0 : 1);
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper annotation found",
(is_field ? 0 : 1));
if (is_annotation)
{
QPDFObjectHandle our_field = (is_field ? field : parent);
this->m->field_to_annotations[our_field.getObjGen()].push_back(
QPDFAnnotationObjectHelper(field));
this->m->annotation_to_field[og] =
QPDFFormFieldObjectHelper(our_field);
}
}

View File

@ -0,0 +1,75 @@
#include <qpdf/QPDFAnnotationObjectHelper.hh>
#include <qpdf/QTC.hh>
QPDFAnnotationObjectHelper::Members::~Members()
{
}
QPDFAnnotationObjectHelper::Members::Members()
{
}
QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) :
QPDFObjectHelper(oh)
{
}
std::string
QPDFAnnotationObjectHelper::getSubtype()
{
return this->oh.getKey("/Subtype").getName();
}
QPDFObjectHandle::Rectangle
QPDFAnnotationObjectHelper::getRect()
{
return this->oh.getKey("/Rect").getArrayAsRectangle();
}
QPDFObjectHandle
QPDFAnnotationObjectHelper::getAppearanceDictionary()
{
return this->oh.getKey("/AP");
}
std::string
QPDFAnnotationObjectHelper::getAppearanceState()
{
if (this->oh.getKey("/AS").isName())
{
QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS present");
return this->oh.getKey("/AS").getName();
}
QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS absent");
return "";
}
QPDFObjectHandle
QPDFAnnotationObjectHelper::getAppearanceStream(
std::string const& which,
std::string const& state)
{
QPDFObjectHandle ap = getAppearanceDictionary();
std::string desired_state = state.empty() ? getAppearanceState() : state;
if (ap.isDictionary())
{
QPDFObjectHandle ap_sub = ap.getKey(which);
if (ap_sub.isStream() && desired_state.empty())
{
QTC::TC("qpdf", "QPDFAnnotationObjectHelper AP stream");
return ap_sub;
}
if (ap_sub.isDictionary() && (! desired_state.empty()))
{
QTC::TC("qpdf", "QPDFAnnotationObjectHelper AP dictionary");
QPDFObjectHandle ap_sub_val = ap_sub.getKey(desired_state);
if (ap_sub_val.isStream())
{
QTC::TC("qpdf", "QPDFAnnotationObjectHelper AN sub stream");
return ap_sub_val;
}
}
}
QTC::TC("qpdf", "QPDFAnnotationObjectHelper AN null");
return QPDFObjectHandle::newNull();
}

View File

@ -0,0 +1,190 @@
#include <qpdf/QPDFFormFieldObjectHelper.hh>
#include <qpdf/QTC.hh>
QPDFFormFieldObjectHelper::Members::~Members()
{
}
QPDFFormFieldObjectHelper::Members::Members()
{
}
QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) :
QPDFObjectHelper(oh),
m(new Members())
{
}
QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() :
QPDFObjectHelper(QPDFObjectHandle::newNull()),
m(new Members())
{
}
bool
QPDFFormFieldObjectHelper::isNull()
{
return this->oh.isNull();
}
QPDFFormFieldObjectHelper
QPDFFormFieldObjectHelper::getParent()
{
return this->oh.getKey("/Parent"); // may be null
}
QPDFObjectHandle
QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
{
QPDFObjectHandle node = this->oh;
QPDFObjectHandle result(node.getKey(name));
std::set<QPDFObjGen> seen;
while (result.isNull() && node.hasKey("/Parent"))
{
seen.insert(node.getObjGen());
node = node.getKey("/Parent");
if (seen.count(node.getObjGen()))
{
break;
}
result = node.getKey(name);
if (! result.isNull())
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper non-trivial inheritance");
}
}
return result;
}
std::string
QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(
std::string const& name)
{
QPDFObjectHandle fv = getInheritableFieldValue(name);
std::string result;
if (fv.isString())
{
result = fv.getUTF8Value();
}
return result;
}
std::string
QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(
std::string const& name)
{
QPDFObjectHandle fv = getInheritableFieldValue(name);
std::string result;
if (fv.isName())
{
result = fv.getName();
}
return result;
}
std::string
QPDFFormFieldObjectHelper::getFieldType()
{
return getInheritableFieldValueAsName("/FT");
}
std::string
QPDFFormFieldObjectHelper::getFullyQualifiedName()
{
std::string result;
QPDFObjectHandle node = this->oh;
std::set<QPDFObjGen> seen;
while ((! node.isNull()) && (seen.count(node.getObjGen()) == 0))
{
if (node.getKey("/T").isString())
{
if (! result.empty())
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper non-trivial qualified name");
result = "." + result;
}
result = node.getKey("/T").getUTF8Value() + result;
}
seen.insert(node.getObjGen());
node = node.getKey("/Parent");
}
return result;
}
std::string
QPDFFormFieldObjectHelper::getPartialName()
{
std::string result;
if (this->oh.getKey("/T").isString())
{
result = this->oh.getKey("/T").getUTF8Value();
}
return result;
}
std::string
QPDFFormFieldObjectHelper::getAlternativeName()
{
if (this->oh.getKey("/TU").isString())
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present");
return this->oh.getKey("/TU").getUTF8Value();
}
QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent");
return getFullyQualifiedName();
}
std::string
QPDFFormFieldObjectHelper::getMappingName()
{
if (this->oh.getKey("/TM").isString())
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present");
return this->oh.getKey("/TM").getUTF8Value();
}
QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent");
return getAlternativeName();
}
QPDFObjectHandle
QPDFFormFieldObjectHelper::getValue()
{
return getInheritableFieldValue("/V");
}
std::string
QPDFFormFieldObjectHelper::getValueAsString()
{
return getInheritableFieldValueAsString("/V");
}
QPDFObjectHandle
QPDFFormFieldObjectHelper::getDefaultValue()
{
return getInheritableFieldValue("/DV");
}
std::string
QPDFFormFieldObjectHelper::getDefaultValueAsString()
{
return getInheritableFieldValueAsString("/DV");
}
std::string
QPDFFormFieldObjectHelper::getDefaultAppearance()
{
return getInheritableFieldValueAsString("/DA");
}
int
QPDFFormFieldObjectHelper::getQuadding()
{
int result = 0;
QPDFObjectHandle fv = getInheritableFieldValue("/Q");
if (fv.isInteger())
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper Q present");
result = static_cast<int>(fv.getIntValue());
}
return result;
}

View File

@ -19,6 +19,29 @@ QPDFPageObjectHelper::getPageImages()
return this->oh.getPageImages();
}
std::vector<QPDFAnnotationObjectHelper>
QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype)
{
std::vector<QPDFAnnotationObjectHelper> result;
QPDFObjectHandle annots = this->oh.getKey("/Annots");
if (annots.isArray())
{
size_t nannots = annots.getArrayNItems();
for (size_t i = 0; i < nannots; ++i)
{
QPDFObjectHandle annot = annots.getArrayItem(i);
if (only_subtype.empty() ||
(annot.isDictionary() &&
annot.getKey("/Subtype").isName() &&
(only_subtype == annot.getKey("/Subtype").getName())))
{
result.push_back(QPDFAnnotationObjectHelper(annot));
}
}
}
return result;
}
std::vector<QPDFObjectHandle>
QPDFPageObjectHelper::getPageContents()
{

View File

@ -35,7 +35,10 @@ SRCS_libqpdf = \
libqpdf/Pl_StdioFile.cc \
libqpdf/Pl_TIFFPredictor.cc \
libqpdf/QPDF.cc \
libqpdf/QPDFAcroFormDocumentHelper.cc \
libqpdf/QPDFAnnotationObjectHelper.cc \
libqpdf/QPDFExc.cc \
libqpdf/QPDFFormFieldObjectHelper.cc \
libqpdf/QPDFObjGen.cc \
libqpdf/QPDFObject.cc \
libqpdf/QPDFObjectHandle.cc \

View File

@ -336,3 +336,23 @@ QPDFObjectHandle erase array bounds 0
qpdf-c called qpdf_check_pdf 0
QPDF xref loop 0
QPDFObjectHandle too deep 0
QPDFFormFieldObjectHelper non-trivial inheritance 0
QPDFFormFieldObjectHelper non-trivial qualified name 0
QPDFFormFieldObjectHelper TU present 0
QPDFFormFieldObjectHelper TM present 0
QPDFFormFieldObjectHelper TU absent 0
QPDFFormFieldObjectHelper TM absent 0
QPDFFormFieldObjectHelper Q present 0
QPDFAnnotationObjectHelper AS present 0
QPDFAnnotationObjectHelper AS absent 0
QPDFAnnotationObjectHelper AP stream 0
QPDFAnnotationObjectHelper AP dictionary 0
QPDFAnnotationObjectHelper AN sub stream 0
QPDFAnnotationObjectHelper AN null 0
QPDFAcroFormDocumentHelper fields not array 0
QPDFAcroFormDocumentHelper orphaned widget 0
QPDFAcroFormDocumentHelper direct field 0
QPDFAcroFormDocumentHelper non-dictionary field 0
QPDFAcroFormDocumentHelper loop 0
QPDFAcroFormDocumentHelper field found 1
QPDFAcroFormDocumentHelper annotation found 1

View File

@ -92,6 +92,38 @@ $td->runtest("PDF doc encoding to Unicode",
{$td->FILE => "pdf-doc-to-utf8.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
show_ntests();
# ----------
$td->notify("--- Form Tests ---");
my @form_tests = (
'minimal',
'form-empty-from-odt',
'form-mod1',
# Atril (MATE Document Viewer) 1.20.1 dumps appearance streams
# when modifying form fields, leaving /NeedAppearances true.
'form-filled-with-atril',
'form-bad-fields-array',
'form-errors',
);
$n_tests += scalar(@form_tests);
# Many of the form*.pdf files were created by converting the
# LibreOffice document storage/form.odt to PDF and then manually
# modifying the resulting PDF in various ways. That file would be good
# starting point for generation of more complex forms should that be
# required in the future. The file storage/form.pdf is a direct export
# from LibreOffice with no modifications.
foreach my $f (@form_tests)
{
$td->runtest("form test: $f",
{$td->COMMAND => "test_driver 43 $f.pdf"},
{$td->FILE => "form-$f.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
}
show_ntests();
# ----------
$td->notify("--- Stream Replacement Tests ---");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,253 @@
iterating over form fields
WARNING: form-bad-fields-array.pdf, object 1 0 at offset 50: /Fields key of /AcroForm dictionary is not an array; ignoring
WARNING: form-bad-fields-array.pdf, object 4 0 at offset 615: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 16 0 at offset 3419: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 17 0 at offset 3775: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 18 0 at offset 4131: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 6 0 at offset 1032: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 7 0 at offset 1413: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 8 0 at offset 1796: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 32 0 at offset 5893: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 33 0 at offset 6251: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 34 0 at offset 6607: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-bad-fields-array.pdf, object 10 0 at offset 2311: this widget annotation is not reachable from /AcroForm in the document catalog
Field: 4 0 R
Parent: none
Fully qualified name: Text Box 1
Partial name: Text Box 1
Alternative name: Text Box 1
Mapping name: Text Box 1
Field type: /Tx
Value: <feff>
Value as string:
Default value: <feff>
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 4 0 R
Field: 6 0 R
Parent: none
Fully qualified name: Check Box 1
Partial name: Check Box 1
Alternative name: Check Box 1
Mapping name: Check Box 1
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 6 0 R
Field: 7 0 R
Parent: none
Fully qualified name: Check Box 2
Partial name: Check Box 2
Alternative name: Check Box 2
Mapping name: Check Box 2
Field type: /Btn
Value: /Yes
Value as string:
Default value: /Yes
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 7 0 R
Field: 8 0 R
Parent: none
Fully qualified name: Check Box 3
Partial name: Check Box 3
Alternative name: Check Box 3
Mapping name: Check Box 3
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 8 0 R
Field: 10 0 R
Parent: none
Fully qualified name: Text Box 2
Partial name: Text Box 2
Alternative name: Text Box 2
Mapping name: Text Box 2
Field type: /Tx
Value: <feff00730061006c00610064002003c002ac>
Value as string: salad πʬ
Default value: <feff00730061006c00610064002003c002ac>
Default value as string: salad πʬ
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 10 0 R
Field: 16 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 16 0 R
Field: 17 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 17 0 R
Field: 18 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 18 0 R
Field: 32 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 32 0 R
Field: 33 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 33 0 R
Field: 34 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 34 0 R
iterating over annotations per page
Page: 11 0 R
Annotation: 4 0 R
Field: 4 0 R
Subtype: /Widget
Rect: [123.4, 692.1, 260.9, 706.7]
Appearance stream (/N): 14 0 R
Appearance stream (/N, /3): null
Annotation: 16 0 R
Field: 16 0 R
Subtype: /Widget
Rect: [149.3, 648.5, 161.6, 660.4]
Appearance state: /1
Appearance stream (/N): 44 0 R
Appearance stream (/N, /3): null
Annotation: 17 0 R
Field: 17 0 R
Subtype: /Widget
Rect: [152.7, 627.3, 165, 639.2]
Appearance state: /Off
Appearance stream (/N): 50 0 R
Appearance stream (/N, /3): null
Annotation: 18 0 R
Field: 18 0 R
Subtype: /Widget
Rect: [151.3, 601.7, 163.6, 613.6]
Appearance state: /Off
Appearance stream (/N): 54 0 R
Appearance stream (/N, /3): 52 0 R
Annotation: 6 0 R
Field: 6 0 R
Subtype: /Widget
Rect: [121.9, 559.1, 134.2, 571]
Appearance state: /Off
Appearance stream (/N): 19 0 R
Appearance stream (/N, /3): null
Annotation: 7 0 R
Field: 7 0 R
Subtype: /Widget
Rect: [118.6, 527.7, 130.9, 539.6]
Appearance state: /Yes
Appearance stream (/N): 26 0 R
Appearance stream (/N, /3): null
Annotation: 8 0 R
Field: 8 0 R
Subtype: /Widget
Rect: [118.6, 500.5, 130.9, 512.4]
Appearance state: /Off
Appearance stream (/N): 28 0 R
Appearance stream (/N, /3): null
Page: 40 0 R
Page: 35 0 R
Annotation: 32 0 R
Field: 32 0 R
Subtype: /Widget
Rect: [118.6, 555.7, 130.9, 567.6]
Appearance state: /Off
Appearance stream (/N): 58 0 R
Appearance stream (/N, /3): null
Annotation: 33 0 R
Field: 33 0 R
Subtype: /Widget
Rect: [119.3, 514.8, 131.6, 526.7]
Appearance state: /2
Appearance stream (/N): 60 0 R
Appearance stream (/N, /3): null
Annotation: 34 0 R
Field: 34 0 R
Subtype: /Widget
Rect: [121.3, 472.5, 133.6, 484.4]
Appearance state: /Off
Appearance stream (/N): 66 0 R
Appearance stream (/N, /3): 64 0 R
Annotation: 10 0 R
Field: 10 0 R
Subtype: /Widget
Rect: [113.6, 378.5, 351.3, 396.3]
Appearance stream (/N): 36 0 R
Appearance stream (/N, /3): null
test 43 done

View File

@ -0,0 +1,241 @@
iterating over form fields
Field: 4 0 R
Parent: none
Fully qualified name: Text Box 1
Partial name: Text Box 1
Alternative name: Text Box 1
Mapping name: Text Box 1
Field type: /Tx
Value: <feff>
Value as string:
Default value: <feff>
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 4 0 R
Field: 6 0 R
Parent: none
Fully qualified name: Check Box 1
Partial name: Check Box 1
Alternative name: Check Box 1
Mapping name: Check Box 1
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 6 0 R
Field: 7 0 R
Parent: none
Fully qualified name: Check Box 2
Partial name: Check Box 2
Alternative name: Check Box 2
Mapping name: Check Box 2
Field type: /Btn
Value: /Yes
Value as string:
Default value: /Yes
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 7 0 R
Field: 8 0 R
Parent: none
Fully qualified name: Check Box 3
Partial name: Check Box 3
Alternative name: Check Box 3
Mapping name: Check Box 3
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 8 0 R
Field: 10 0 R
Parent: none
Fully qualified name: Text Box 2
Partial name: Text Box 2
Alternative name: Text Box 2
Mapping name: Text Box 2
Field type: /Tx
Value: <feff00730061006c00610064002003c002ac>
Value as string: salad πʬ
Default value: <feff00730061006c00610064002003c002ac>
Default value as string: salad πʬ
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 10 0 R
Field: 16 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 16 0 R
Field: 17 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 17 0 R
Field: 18 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 18 0 R
Field: 32 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 32 0 R
Field: 33 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 33 0 R
Field: 34 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 34 0 R
iterating over annotations per page
Page: 11 0 R
Annotation: 4 0 R
Field: 4 0 R
Subtype: /Widget
Rect: [123.4, 692.1, 260.9, 706.7]
Appearance stream (/N): 14 0 R
Appearance stream (/N, /3): null
Annotation: 16 0 R
Field: 16 0 R
Subtype: /Widget
Rect: [149.3, 648.5, 161.6, 660.4]
Appearance state: /1
Appearance stream (/N): 44 0 R
Appearance stream (/N, /3): null
Annotation: 17 0 R
Field: 17 0 R
Subtype: /Widget
Rect: [152.7, 627.3, 165, 639.2]
Appearance state: /Off
Appearance stream (/N): 50 0 R
Appearance stream (/N, /3): null
Annotation: 18 0 R
Field: 18 0 R
Subtype: /Widget
Rect: [151.3, 601.7, 163.6, 613.6]
Appearance state: /Off
Appearance stream (/N): 54 0 R
Appearance stream (/N, /3): 52 0 R
Annotation: 6 0 R
Field: 6 0 R
Subtype: /Widget
Rect: [121.9, 559.1, 134.2, 571]
Appearance state: /Off
Appearance stream (/N): 19 0 R
Appearance stream (/N, /3): null
Annotation: 7 0 R
Field: 7 0 R
Subtype: /Widget
Rect: [118.6, 527.7, 130.9, 539.6]
Appearance state: /Yes
Appearance stream (/N): 26 0 R
Appearance stream (/N, /3): null
Annotation: 8 0 R
Field: 8 0 R
Subtype: /Widget
Rect: [118.6, 500.5, 130.9, 512.4]
Appearance state: /Off
Appearance stream (/N): 28 0 R
Appearance stream (/N, /3): null
Page: 40 0 R
Page: 35 0 R
Annotation: 32 0 R
Field: 32 0 R
Subtype: /Widget
Rect: [118.6, 555.7, 130.9, 567.6]
Appearance state: /Off
Appearance stream (/N): 58 0 R
Appearance stream (/N, /3): null
Annotation: 33 0 R
Field: 33 0 R
Subtype: /Widget
Rect: [119.3, 514.8, 131.6, 526.7]
Appearance state: /2
Appearance stream (/N): 60 0 R
Appearance stream (/N, /3): null
Annotation: 34 0 R
Field: 34 0 R
Subtype: /Widget
Rect: [121.3, 472.5, 133.6, 484.4]
Appearance state: /Off
Appearance stream (/N): 66 0 R
Appearance stream (/N, /3): 64 0 R
Annotation: 10 0 R
Field: 10 0 R
Subtype: /Widget
Rect: [113.6, 378.5, 351.3, 396.3]
Appearance stream (/N): 36 0 R
Appearance stream (/N, /3): null
test 43 done

View File

@ -0,0 +1,246 @@
iterating over form fields
WARNING: form-errors.pdf, object 4 0 at offset 625: loop detected while traversing /AcroForm
WARNING: form-errors.pdf, object 5 0 at offset 993: encountered a direct object as a field or annotation while traversing /AcroForm; ignoring field or annotation
WARNING: form-errors.pdf, object 15 0 at offset 3452: encountered a non-dictionary as a field or annotation while traversing /AcroForm; ignoring field or annotation
WARNING: form-errors.pdf, object 16 0 at offset 3475: this widget annotation is not reachable from /AcroForm in the document catalog
WARNING: form-errors.pdf, object 32 0 at offset 5974: this widget annotation is not reachable from /AcroForm in the document catalog
Field: 4 0 R
Parent: none
Fully qualified name: Text Box 1
Partial name: Text Box 1
Alternative name: Text Box 1
Mapping name: Text Box 1
Field type: /Tx
Value: <feff>
Value as string:
Default value: <feff>
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 4 0 R
Field: 6 0 R
Parent: none
Fully qualified name: Check Box 1
Partial name: Check Box 1
Alternative name: Check Box 1
Mapping name: Check Box 1
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 6 0 R
Field: 7 0 R
Parent: none
Fully qualified name: Check Box 2
Partial name: Check Box 2
Alternative name: Check Box 2
Mapping name: Check Box 2
Field type: /Btn
Value: /Yes
Value as string:
Default value: /Yes
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 7 0 R
Field: 8 0 R
Parent: none
Fully qualified name: Check Box 3
Partial name: Check Box 3
Alternative name: Check Box 3
Mapping name: Check Box 3
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 8 0 R
Field: 10 0 R
Parent: none
Fully qualified name: Text Box 2
Partial name: Text Box 2
Alternative name: Text Box 2
Mapping name: Text Box 2
Field type: /Tx
Value: <feff00730061006c00610064002003c002ac>
Value as string: salad πʬ
Default value: <feff00730061006c00610064002003c002ac>
Default value as string: salad πʬ
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 10 0 R
Field: 16 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 16 0 R
Field: 17 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 17 0 R
Field: 18 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 18 0 R
Field: 32 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 32 0 R
Field: 33 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 33 0 R
Field: 34 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 34 0 R
iterating over annotations per page
Page: 11 0 R
Annotation: 4 0 R
Field: 4 0 R
Subtype: /Widget
Rect: [123.4, 692.1, 260.9, 706.7]
Appearance stream (/N): 14 0 R
Appearance stream (/N, /3): null
Annotation: 16 0 R
Field: 16 0 R
Subtype: /Widget
Rect: [149.3, 648.5, 161.6, 660.4]
Appearance state: /1
Appearance stream (/N): 44 0 R
Appearance stream (/N, /3): null
Annotation: 17 0 R
Field: 17 0 R
Subtype: /Widget
Rect: [152.7, 627.3, 165, 639.2]
Appearance state: /Off
Appearance stream (/N): 50 0 R
Appearance stream (/N, /3): null
Annotation: 18 0 R
Field: 18 0 R
Subtype: /Widget
Rect: [151.3, 601.7, 163.6, 613.6]
Appearance state: /Off
Appearance stream (/N): 54 0 R
Appearance stream (/N, /3): 52 0 R
Annotation: 6 0 R
Field: 6 0 R
Subtype: /Widget
Rect: [121.9, 559.1, 134.2, 571]
Appearance state: /Off
Appearance stream (/N): 19 0 R
Appearance stream (/N, /3): null
Annotation: 7 0 R
Field: 7 0 R
Subtype: /Widget
Rect: [118.6, 527.7, 130.9, 539.6]
Appearance state: /Yes
Appearance stream (/N): 26 0 R
Appearance stream (/N, /3): null
Annotation: 8 0 R
Field: 8 0 R
Subtype: /Widget
Rect: [118.6, 500.5, 130.9, 512.4]
Appearance state: /Off
Appearance stream (/N): 28 0 R
Appearance stream (/N, /3): null
Page: 40 0 R
Page: 35 0 R
Annotation: 32 0 R
Field: 32 0 R
Subtype: /Widget
Rect: [118.6, 555.7, 130.9, 567.6]
Appearance state: /Off
Appearance stream (/N): 58 0 R
Appearance stream (/N, /3): null
Annotation: 33 0 R
Field: 33 0 R
Subtype: /Widget
Rect: [119.3, 514.8, 131.6, 526.7]
Appearance state: /2
Appearance stream (/N): 60 0 R
Appearance stream (/N, /3): null
Annotation: 34 0 R
Field: 34 0 R
Subtype: /Widget
Rect: [121.3, 472.5, 133.6, 484.4]
Appearance state: /Off
Appearance stream (/N): 66 0 R
Appearance stream (/N, /3): 64 0 R
Annotation: 10 0 R
Field: 10 0 R
Subtype: /Widget
Rect: [113.6, 378.5, 351.3, 396.3]
Appearance stream (/N): 36 0 R
Appearance stream (/N, /3): null
test 43 done

View File

@ -0,0 +1,241 @@
iterating over form fields
Field: 4 0 R
Parent: none
Fully qualified name: Text Box 1
Partial name: Text Box 1
Alternative name: Text Box 1
Mapping name: Text Box 1
Field type: /Tx
Value: <feff004800610067006f006f00670061006d00610067006f006f0067006c0065>
Value as string: Hagoogamagoogle
Default value: <feff>
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 4 0 R
Field: 6 0 R
Parent: none
Fully qualified name: Check Box 1
Partial name: Check Box 1
Alternative name: Check Box 1
Mapping name: Check Box 1
Field type: /Btn
Value: /Yes
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 6 0 R
Field: 7 0 R
Parent: none
Fully qualified name: Check Box 2
Partial name: Check Box 2
Alternative name: Check Box 2
Mapping name: Check Box 2
Field type: /Btn
Value: /Yes
Value as string:
Default value: /Yes
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 7 0 R
Field: 8 0 R
Parent: none
Fully qualified name: Check Box 3
Partial name: Check Box 3
Alternative name: Check Box 3
Mapping name: Check Box 3
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 8 0 R
Field: 10 0 R
Parent: none
Fully qualified name: Text Box 2
Partial name: Text Box 2
Alternative name: Text Box 2
Mapping name: Text Box 2
Field type: /Tx
Value: <feff00730061006c00610064002003c002ac00200021>
Value as string: salad πʬ !
Default value: <feff00730061006c00610064002003c002ac>
Default value as string: salad πʬ
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 10 0 R
Field: 16 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /3
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 16 0 R
Field: 17 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /3
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 17 0 R
Field: 18 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /3
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 18 0 R
Field: 32 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 32 0 R
Field: 33 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 33 0 R
Field: 34 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 34 0 R
iterating over annotations per page
Page: 11 0 R
Annotation: 4 0 R
Field: 4 0 R
Subtype: /Widget
Rect: [123.4, 692.1, 260.9, 706.7]
Appearance stream (/N): null
Appearance stream (/N, /3): null
Annotation: 16 0 R
Field: 16 0 R
Subtype: /Widget
Rect: [149.3, 648.5, 161.6, 660.4]
Appearance state: /Off
Appearance stream (/N): 46 0 R
Appearance stream (/N, /3): null
Annotation: 17 0 R
Field: 17 0 R
Subtype: /Widget
Rect: [152.7, 627.3, 165, 639.2]
Appearance state: /Off
Appearance stream (/N): 50 0 R
Appearance stream (/N, /3): null
Annotation: 18 0 R
Field: 18 0 R
Subtype: /Widget
Rect: [151.3, 601.7, 163.6, 613.6]
Appearance state: /3
Appearance stream (/N): 52 0 R
Appearance stream (/N, /3): 52 0 R
Annotation: 6 0 R
Field: 6 0 R
Subtype: /Widget
Rect: [121.9, 559.1, 134.2, 571]
Appearance state: /Yes
Appearance stream (/N): 21 0 R
Appearance stream (/N, /3): null
Annotation: 7 0 R
Field: 7 0 R
Subtype: /Widget
Rect: [118.6, 527.7, 130.9, 539.6]
Appearance state: /Yes
Appearance stream (/N): 26 0 R
Appearance stream (/N, /3): null
Annotation: 8 0 R
Field: 8 0 R
Subtype: /Widget
Rect: [118.6, 500.5, 130.9, 512.4]
Appearance state: /Off
Appearance stream (/N): 28 0 R
Appearance stream (/N, /3): null
Page: 40 0 R
Page: 35 0 R
Annotation: 32 0 R
Field: 32 0 R
Subtype: /Widget
Rect: [118.6, 555.7, 130.9, 567.6]
Appearance state: /Off
Appearance stream (/N): 58 0 R
Appearance stream (/N, /3): null
Annotation: 33 0 R
Field: 33 0 R
Subtype: /Widget
Rect: [119.3, 514.8, 131.6, 526.7]
Appearance state: /2
Appearance stream (/N): 60 0 R
Appearance stream (/N, /3): null
Annotation: 34 0 R
Field: 34 0 R
Subtype: /Widget
Rect: [121.3, 472.5, 133.6, 484.4]
Appearance state: /Off
Appearance stream (/N): 66 0 R
Appearance stream (/N, /3): 64 0 R
Annotation: 10 0 R
Field: 10 0 R
Subtype: /Widget
Rect: [113.6, 378.5, 351.3, 396.3]
Appearance stream (/N): null
Appearance stream (/N, /3): null
test 43 done

View File

@ -0,0 +1,241 @@
iterating over form fields
Field: 4 0 R
Parent: none
Fully qualified name: Text Box 1
Partial name: Text Box 1
Alternative name: Text Box 1
Mapping name: Text Box 1
Field type: /Tx
Value: <feff>
Value as string:
Default value: <feff>
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 0
Annotation: 83 0 R
Field: 6 0 R
Parent: none
Fully qualified name: Check Box 1
Partial name: Check Box 1
Alternative name: Check Box 1
Mapping name: Check Box 1
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 6 0 R
Field: 7 0 R
Parent: none
Fully qualified name: Check Box 2
Partial name: Check Box 2
Alternative name: Check Box 2
Mapping name: Check Box 2
Field type: /Btn
Value: /Yes
Value as string:
Default value: /Yes
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 7 0 R
Field: 8 0 R
Parent: none
Fully qualified name: Check Box 3
Partial name: Check Box 3
Alternative name: Check Box 3
Mapping name: Check Box 3
Field type: /Btn
Value: /Off
Value as string:
Default value: /Off
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 8 0 R
Field: 10 0 R
Parent: none
Fully qualified name: Text Box 2
Partial name: Text Box 2
Alternative name: Text Box 2
Mapping name: Text Box 2
Field type: /Tx
Value: <feff00730061006c00610064002003c002ac>
Value as string: salad πʬ
Default value: <feff00730061006c00610064002003c002ac>
Default value as string: salad πʬ
Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
Quadding: 1
Annotation: 10 0 R
Field: 16 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1.choice1
Partial name: choice1
Alternative name: chice 1
Mapping name: choice 1 TM
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 16 0 R
Field: 17 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 17 0 R
Field: 18 0 R
Parent: 5 0 R
Parent: none
Fully qualified name: r1
Partial name:
Alternative name: r1
Mapping name: r1
Field type: /Btn
Value: /1
Value as string:
Default value: /1
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 18 0 R
Field: 32 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 32 0 R
Field: 33 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 33 0 R
Field: 34 0 R
Parent: 9 0 R
Parent: none
Fully qualified name: r2
Partial name:
Alternative name: r2
Mapping name: r2
Field type: /Btn
Value: /2
Value as string:
Default value: /2
Default value as string:
Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
Quadding: 0
Annotation: 34 0 R
iterating over annotations per page
Page: 11 0 R
Annotation: 83 0 R
Field: 4 0 R
Subtype: /Widget
Rect: [123.4, 692.1, 260.9, 706.7]
Appearance stream (/N): 14 0 R
Appearance stream (/N, /3): null
Annotation: 16 0 R
Field: 16 0 R
Subtype: /Widget
Rect: [149.3, 648.5, 161.6, 660.4]
Appearance state: /1
Appearance stream (/N): 44 0 R
Appearance stream (/N, /3): null
Annotation: 17 0 R
Field: 17 0 R
Subtype: /Widget
Rect: [152.7, 627.3, 165, 639.2]
Appearance state: /Off
Appearance stream (/N): 50 0 R
Appearance stream (/N, /3): null
Annotation: 18 0 R
Field: 18 0 R
Subtype: /Widget
Rect: [151.3, 601.7, 163.6, 613.6]
Appearance state: /Off
Appearance stream (/N): 54 0 R
Appearance stream (/N, /3): 52 0 R
Annotation: 6 0 R
Field: 6 0 R
Subtype: /Widget
Rect: [121.9, 559.1, 134.2, 571]
Appearance state: /Off
Appearance stream (/N): 19 0 R
Appearance stream (/N, /3): null
Annotation: 7 0 R
Field: 7 0 R
Subtype: /Widget
Rect: [118.6, 527.7, 130.9, 539.6]
Appearance state: /Yes
Appearance stream (/N): 26 0 R
Appearance stream (/N, /3): null
Annotation: 8 0 R
Field: 8 0 R
Subtype: /Widget
Rect: [118.6, 500.5, 130.9, 512.4]
Appearance state: /Off
Appearance stream (/N): 28 0 R
Appearance stream (/N, /3): null
Page: 40 0 R
Page: 35 0 R
Annotation: 32 0 R
Field: 32 0 R
Subtype: /Widget
Rect: [118.6, 555.7, 130.9, 567.6]
Appearance state: /Off
Appearance stream (/N): 58 0 R
Appearance stream (/N, /3): null
Annotation: 33 0 R
Field: 33 0 R
Subtype: /Widget
Rect: [119.3, 514.8, 131.6, 526.7]
Appearance state: /2
Appearance stream (/N): 60 0 R
Appearance stream (/N, /3): null
Annotation: 34 0 R
Field: 34 0 R
Subtype: /Widget
Rect: [121.3, 472.5, 133.6, 484.4]
Appearance state: /Off
Appearance stream (/N): 66 0 R
Appearance stream (/N, /3): 64 0 R
Annotation: 10 0 R
Field: 10 0 R
Subtype: /Widget
Rect: [113.6, 378.5, 351.3, 396.3]
Appearance stream (/N): 36 0 R
Appearance stream (/N, /3): null
test 43 done

View File

@ -0,0 +1 @@
no forms

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
This directory contains files that I want to keep around because I
hand-created them or otherwise went to some trouble and used them in
some fashion in the test suite. Any file in this directory should be
referenced in a comment in the test code.

BIN
qpdf/qtest/storage/form.odt Normal file

Binary file not shown.

BIN
qpdf/qtest/storage/form.pdf Normal file

Binary file not shown.

View File

@ -5,6 +5,7 @@
#include <qpdf/QPDFPageDocumentHelper.hh>
#include <qpdf/QPDFPageObjectHelper.hh>
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
#include <qpdf/Pl_StdioFile.hh>
@ -153,6 +154,13 @@ static QPDFObjectHandle createPageContents(QPDF& pdf, std::string const& text)
return QPDFObjectHandle::newStream(&pdf, contents);
}
static void print_rect(std::ostream& out,
QPDFObjectHandle::Rectangle const& r)
{
out << "[" << r.llx << ", " << r.lly << ", "
<< r.urx << ", " << r.ury << "]";
}
void runtest(int n, char const* filename1, char const* arg2)
{
// Most tests here are crafted to work on specific files. Look at
@ -1474,6 +1482,108 @@ void runtest(int n, char const* filename1, char const* arg2)
(r1.urx > 5.59) && (r1.urx < 5.61) &&
(r1.ury > 7.79) && (r1.ury < 7.81));
}
else if (n == 43)
{
// Forms
QPDFAcroFormDocumentHelper afdh(pdf);
if (! afdh.hasAcroForm())
{
std::cout << "no forms\n";
return;
}
std::cout << "iterating over form fields\n";
std::vector<QPDFFormFieldObjectHelper> form_fields =
afdh.getFormFields();
for (std::vector<QPDFFormFieldObjectHelper>::iterator iter =
form_fields.begin();
iter != form_fields.end(); ++iter)
{
QPDFFormFieldObjectHelper ffh(*iter);
std::cout << "Field: " << ffh.getObjectHandle().unparse()
<< std::endl;
QPDFFormFieldObjectHelper node = ffh;
while (! node.isNull())
{
QPDFFormFieldObjectHelper parent(node.getParent());
std::cout << " Parent: "
<< (parent.isNull()
? "none"
: parent.getObjectHandle().unparse())
<< std::endl;
node = parent;
}
std::cout << " Fully qualified name: "
<< ffh.getFullyQualifiedName() << std::endl;
std::cout << " Partial name: "
<< ffh.getPartialName() << std::endl;
std::cout << " Alternative name: "
<< ffh.getAlternativeName() << std::endl;
std::cout << " Mapping name: "
<< ffh.getMappingName() << std::endl;
std::cout << " Field type: "
<< ffh.getFieldType() << std::endl;
std::cout << " Value: "
<< ffh.getValue().unparse() << std::endl;
std::cout << " Value as string: "
<< ffh.getValueAsString() << std::endl;
std::cout << " Default value: "
<< ffh.getDefaultValue().unparse() << std::endl;
std::cout << " Default value as string: "
<< ffh.getDefaultValueAsString() << std::endl;
std::cout << " Default appearance: "
<< ffh.getDefaultAppearance() << std::endl;
std::cout << " Quadding: "
<< ffh.getQuadding() << std::endl;
std::vector<QPDFAnnotationObjectHelper> annotations =
afdh.getAnnotationsForField(ffh);
for (std::vector<QPDFAnnotationObjectHelper>::iterator i2 =
annotations.begin();
i2 != annotations.end(); ++i2)
{
std::cout << " Annotation: "
<< (*i2).getObjectHandle().unparse() << std::endl;
}
}
std::cout << "iterating over annotations per page\n";
std::vector<QPDFPageObjectHelper> pages =
QPDFPageDocumentHelper(pdf).getAllPages();
for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin();
iter != pages.end(); ++iter)
{
std::cout << "Page: " << (*iter).getObjectHandle().unparse()
<< std::endl;
std::vector<QPDFAnnotationObjectHelper> annotations =
afdh.getWidgetAnnotationsForPage(*iter);
for (std::vector<QPDFAnnotationObjectHelper>::iterator i2 =
annotations.begin();
i2 != annotations.end(); ++i2)
{
QPDFAnnotationObjectHelper ah(*i2);
std::cout << " Annotation: " << ah.getObjectHandle().unparse()
<< std::endl;
std::cout << " Field: "
<< (afdh.getFieldForAnnotation(ah).
getObjectHandle().unparse())
<< std::endl;
std::cout << " Subtype: " << ah.getSubtype() << std::endl;
std::cout << " Rect: ";
print_rect(std::cout, ah.getRect());
std::cout << std::endl;
std::string state = ah.getAppearanceState();
if (! state.empty())
{
std::cout << " Appearance state: " << state
<< std::endl;
}
std::cout << " Appearance stream (/N): "
<< ah.getAppearanceStream("/N").unparse()
<< std::endl;
std::cout << " Appearance stream (/N, /3): "
<< ah.getAppearanceStream("/N", "/3").unparse()
<< std::endl;
}
}
}
else
{
throw std::runtime_error(std::string("invalid test ") +