Commit 0b05111db80469d3f556209bfd856af1fda9b142

Authored by Jay Berkenbilt
1 parent 0dadf17a

Implement helper class for interactive forms

ChangeLog
1 2018-06-20 Jay Berkenbilt <ejb@ql.org> 1 2018-06-20 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * Added new classes QPDFAcroFormDocumentHelper,
  4 + QPDFFormFieldObjectHelper, and QPDFAnnotationObjectHelper to
  5 + assist with working with interactive forms in PDF files. At
  6 + present, API methods for reading forms, form fields, and widget
  7 + annotations have been added. It is likely that some additional
  8 + methods for modifying forms will be added in the future. Note that
  9 + qpdf remains a library whose function is primarily focused around
  10 + document structure and metadata rather than content. As such, it
  11 + is not expected that qpdf will have higher level APIs for
  12 + generating form contents, but qpdf will hopefully gain the
  13 + capability to deal with the bookkeeping aspects of wiring up all
  14 + the objects, which could make it a useful library for other
  15 + software that works with PDF interactive forms. PDF forms are
  16 + complex, and the terminology around them is confusing. Please see
  17 + comments at the top of QPDFAcroFormDocumentHelper.hh for
  18 + additional discussion.
  19 +
3 * Added new classes QPDFPageDocumentHelper and QPDFPageObjctHelper 20 * Added new classes QPDFPageDocumentHelper and QPDFPageObjctHelper
4 for page-level API functions. These classes introduce a new API 21 for page-level API functions. These classes introduce a new API
5 pattern of document helpers and object helpers in qpdf. The helper 22 pattern of document helpers and object helpers in qpdf. The helper
include/qpdf/QPDFAcroFormDocumentHelper.hh 0 → 100644
  1 +// Copyright (c) 2005-2018 Jay Berkenbilt
  2 +//
  3 +// This file is part of qpdf.
  4 +//
  5 +// Licensed under the Apache License, Version 2.0 (the "License");
  6 +// you may not use this file except in compliance with the License.
  7 +// You may obtain a copy of the License at
  8 +//
  9 +// http://www.apache.org/licenses/LICENSE-2.0
  10 +//
  11 +// Unless required by applicable law or agreed to in writing, software
  12 +// distributed under the License is distributed on an "AS IS" BASIS,
  13 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +// See the License for the specific language governing permissions and
  15 +// limitations under the License.
  16 +//
  17 +// Versions of qpdf prior to version 7 were released under the terms
  18 +// of version 2.0 of the Artistic License. At your option, you may
  19 +// continue to consider qpdf to be licensed under those terms. Please
  20 +// see the manual for additional information.
  21 +
  22 +#ifndef __QPDFACROFORMDOCUMENTHELPER_HH__
  23 +#define __QPDFACROFORMDOCUMENTHELPER_HH__
  24 +
  25 +// This document helper is intended to help with operations on
  26 +// interactive forms. Here are the key things to know:
  27 +
  28 +// * The PDF specification talks about interactive forms and also
  29 +// about form XObjects. While form XObjects appear in parts of
  30 +// interactive forms, this class is concerned about interactive
  31 +// forms, not form XObjects.
  32 +//
  33 +// * Interactive forms are discussed in the PDF Specification (ISO PDF
  34 +// 32000-1:2008) section 12.7. Also relevant is the section about
  35 +// Widget annotations. Annotations are discussed in section 12.5
  36 +// with annotation dictionaries discussed in 12.5.1. Widget
  37 +// annotations are discussed specifically in section 12.5.6.19.
  38 +//
  39 +// * What you need to know about the structure of interactive forms in
  40 +// PDF files:
  41 +//
  42 +// - The document catalog contains the key "/AcroForm" which
  43 +// contains a list of fields. Fields are represented as a tree
  44 +// structure much like pages. Nodes in the fields tree may contain
  45 +// other fields. Fields may inherit values of many of their
  46 +// attributes from ancestors in the tree.
  47 +//
  48 +// - Fields may also have children that are widget annotations. As a
  49 +// special case, and a cause of considerable confusion, if a field
  50 +// has a single annotation as a child, the annotation dictionary
  51 +// may be merged with the field dictionary. In that case, the
  52 +// field and the annotation are in the same object. Note that,
  53 +// while field dictionary attributes are inherited, annotation
  54 +// dictionary attributes are not.
  55 +//
  56 +// - A page dictionary contains a key called "/Annots" which
  57 +// contains a simple list of annotations. For any given annotation
  58 +// of subtype "/Widget", you should encounter that annotation in
  59 +// the "/Annots" dictionary of a page, and you should also be able
  60 +// to reach it by traversing through the "/AcroForm" dictionary
  61 +// from the document catalog. In the simplest case (and also a
  62 +// very common case), a form field's widget annotation will be
  63 +// merged with the field object, and the object will appear
  64 +// directly both under "/Annots" in the page dictionary and under
  65 +// "/Fields" in the "/AcroForm" dictionary. In a more complex
  66 +// case, you may have to trace through various "/Kids" elements in
  67 +// the "/AcroForm" field entry until you find the annotation
  68 +// dictionary.
  69 +
  70 +
  71 +#include <qpdf/QPDFDocumentHelper.hh>
  72 +
  73 +#include <qpdf/DLL.h>
  74 +
  75 +#include <qpdf/QPDFAnnotationObjectHelper.hh>
  76 +#include <qpdf/QPDFFormFieldObjectHelper.hh>
  77 +#include <qpdf/QPDFPageObjectHelper.hh>
  78 +
  79 +#include <map>
  80 +#include <set>
  81 +#include <vector>
  82 +
  83 +class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
  84 +{
  85 + public:
  86 + QPDFAcroFormDocumentHelper(QPDF&);
  87 +
  88 + // This class lazily creates an internal cache of the mapping
  89 + // among form fields, annotations, and pages. Methods within this
  90 + // class preserve the validity of this cache. However, if you
  91 + // modify pages' annotation dictionaries, the document's /AcroForm
  92 + // dictionary, or any form fields manually in a way that alters
  93 + // the association between forms, fields, annotations, and pages,
  94 + // it may cause this cache to become invalid. This method marks
  95 + // the cache invalid and forces it to be regenerated the next time
  96 + // it is needed.
  97 + QPDF_DLL
  98 + void invalidateCache();
  99 +
  100 + QPDF_DLL
  101 + bool
  102 + hasAcroForm();
  103 +
  104 + // Return a vector of all terminal fields in a document. Terminal
  105 + // fields are fields that have no children that are also fields.
  106 + // Terminal fields may still have children that are annotations.
  107 + // Intermediate nodes in the fields tree are not included in this
  108 + // list, but you can still reach them through the getParent method
  109 + // of the field object helper.
  110 + QPDF_DLL
  111 + std::vector<QPDFFormFieldObjectHelper>
  112 + getFormFields();
  113 +
  114 + // Return the annotations associated with a terminal field. Note
  115 + // that in the case of a field having a single annotation, the
  116 + // underlying object will typically be the same as the underlying
  117 + // object for the field.
  118 + QPDF_DLL
  119 + std::vector<QPDFAnnotationObjectHelper>
  120 + getAnnotationsForField(QPDFFormFieldObjectHelper);
  121 +
  122 + // Return annotations of subtype /Widget for a page.
  123 + QPDF_DLL
  124 + std::vector<QPDFAnnotationObjectHelper>
  125 + getWidgetAnnotationsForPage(QPDFPageObjectHelper);
  126 +
  127 + // Return the terminal field that is associated with this
  128 + // annotation. If the annotation dictionary is merged with the
  129 + // field dictionary, the underlying object will be the same, but
  130 + // this is not always the case. Note that if you call this method
  131 + // with an annotation that is not a widget annotation, there will
  132 + // not be an associated field, and this method will raise an
  133 + // exception.
  134 + QPDF_DLL
  135 + QPDFFormFieldObjectHelper
  136 + getFieldForAnnotation(QPDFAnnotationObjectHelper);
  137 +
  138 + private:
  139 + void analyze();
  140 + void traverseField(QPDFObjectHandle field,
  141 + QPDFObjectHandle parent,
  142 + int depth, std::set<QPDFObjGen>& visited);
  143 +
  144 + class Members
  145 + {
  146 + friend class QPDFAcroFormDocumentHelper;
  147 +
  148 + public:
  149 + ~Members();
  150 +
  151 + private:
  152 + Members();
  153 + Members(Members const&);
  154 +
  155 + bool cache_valid;
  156 + std::map<QPDFObjGen,
  157 + std::vector<QPDFAnnotationObjectHelper>
  158 + > field_to_annotations;
  159 + std::map<QPDFObjGen, QPDFFormFieldObjectHelper> annotation_to_field;
  160 + };
  161 +
  162 + PointerHolder<Members> m;
  163 +};
  164 +
  165 +#endif // __QPDFACROFORMDOCUMENTHELPER_HH__
include/qpdf/QPDFAnnotationObjectHelper.hh 0 → 100644
  1 +// Copyright (c) 2005-2018 Jay Berkenbilt
  2 +//
  3 +// This file is part of qpdf.
  4 +//
  5 +// Licensed under the Apache License, Version 2.0 (the "License");
  6 +// you may not use this file except in compliance with the License.
  7 +// You may obtain a copy of the License at
  8 +//
  9 +// http://www.apache.org/licenses/LICENSE-2.0
  10 +//
  11 +// Unless required by applicable law or agreed to in writing, software
  12 +// distributed under the License is distributed on an "AS IS" BASIS,
  13 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +// See the License for the specific language governing permissions and
  15 +// limitations under the License.
  16 +//
  17 +// Versions of qpdf prior to version 7 were released under the terms
  18 +// of version 2.0 of the Artistic License. At your option, you may
  19 +// continue to consider qpdf to be licensed under those terms. Please
  20 +// see the manual for additional information.
  21 +
  22 +#ifndef __QPDFANNOTATIONOBJECTHELPER_HH__
  23 +#define __QPDFANNOTATIONOBJECTHELPER_HH__
  24 +
  25 +#include <qpdf/QPDFObjectHelper.hh>
  26 +
  27 +#include <qpdf/DLL.h>
  28 +
  29 +class QPDFAnnotationObjectHelper: public QPDFObjectHelper
  30 +{
  31 + public:
  32 + QPDFAnnotationObjectHelper(QPDFObjectHandle);
  33 +
  34 + // This class provides helper methods for certain types of
  35 + // annotations. At its introduction, it only supports Widget
  36 + // annotations, but other types of annotations may be supported in
  37 + // the future. For additional information about interactive forms,
  38 + // please see the comments at the top of
  39 + // QPDFAcroFormDocumentHelper.hh.
  40 +
  41 + // Return the subtype of the annotation as a string (e.g.
  42 + // "/Widget"). Returns the empty string if the subtype (which is
  43 + // required by the spec) is missing.
  44 + QPDF_DLL
  45 + std::string getSubtype();
  46 +
  47 + QPDF_DLL
  48 + QPDFObjectHandle::Rectangle getRect();
  49 +
  50 + QPDF_DLL
  51 + QPDFObjectHandle getAppearanceDictionary();
  52 +
  53 + // Return the appearance state as given in "/AS", or the empty
  54 + // string if none is given.
  55 + QPDF_DLL
  56 + std::string getAppearanceState();
  57 +
  58 + // Return a specific stream. "which" may be one of "/N", "/R", or
  59 + // "/D" to indicate the normal, rollover, or down appearance
  60 + // stream. (Any value may be passed to "which"; if an appearance
  61 + // stream of that name exists, it will be returned.) If the value
  62 + // associated with "which" in the appearance dictionary is a
  63 + // subdictionary, an appearance state may be specified to select
  64 + // which appearance stream is desired. If not specified, the
  65 + // appearance state in "/AS" will used.
  66 + QPDF_DLL
  67 + QPDFObjectHandle getAppearanceStream(std::string const& which,
  68 + std::string const& state = "");
  69 +
  70 + private:
  71 + class Members
  72 + {
  73 + friend class QPDFPageObjectHelper;
  74 +
  75 + public:
  76 + ~Members();
  77 +
  78 + private:
  79 + Members();
  80 + Members(Members const&);
  81 + };
  82 +
  83 + PointerHolder<Members> m;
  84 +};
  85 +
  86 +#endif // __QPDFANNOTATIONOBJECTHELPER_HH__
include/qpdf/QPDFFormFieldObjectHelper.hh 0 → 100644
  1 +// Copyright (c) 2005-2018 Jay Berkenbilt
  2 +//
  3 +// This file is part of qpdf.
  4 +//
  5 +// Licensed under the Apache License, Version 2.0 (the "License");
  6 +// you may not use this file except in compliance with the License.
  7 +// You may obtain a copy of the License at
  8 +//
  9 +// http://www.apache.org/licenses/LICENSE-2.0
  10 +//
  11 +// Unless required by applicable law or agreed to in writing, software
  12 +// distributed under the License is distributed on an "AS IS" BASIS,
  13 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +// See the License for the specific language governing permissions and
  15 +// limitations under the License.
  16 +//
  17 +// Versions of qpdf prior to version 7 were released under the terms
  18 +// of version 2.0 of the Artistic License. At your option, you may
  19 +// continue to consider qpdf to be licensed under those terms. Please
  20 +// see the manual for additional information.
  21 +
  22 +#ifndef __QPDFFORMFIELDOBJECTHELPER_HH__
  23 +#define __QPDFFORMFIELDOBJECTHELPER_HH__
  24 +
  25 +// This object helper helps with form fields for interactive forms.
  26 +// Please see comments in QPDFAcroFormDocumentHelper.hh for additional
  27 +// details.
  28 +
  29 +#include <qpdf/QPDFObjectHelper.hh>
  30 +
  31 +#include <qpdf/DLL.h>
  32 +
  33 +class QPDFFormFieldObjectHelper: public QPDFObjectHelper
  34 +{
  35 + public:
  36 + QPDFFormFieldObjectHelper();
  37 + QPDFFormFieldObjectHelper(QPDFObjectHandle);
  38 +
  39 + QPDF_DLL
  40 + bool isNull();
  41 +
  42 + // Return the field's parent. A form field object helper whose
  43 + // underlying object is null is returned if there is no parent.
  44 + // This condition may be tested by calling isNull().
  45 + QPDF_DLL
  46 + QPDFFormFieldObjectHelper getParent();
  47 +
  48 + // Get a field value, possibly inheriting the value from an
  49 + // ancestor node.
  50 + QPDF_DLL
  51 + QPDFObjectHandle getInheritableFieldValue(std::string const& name);
  52 +
  53 + // Get an inherited field value as a string. If it is not a
  54 + // string, silently return the empty string.
  55 + QPDF_DLL
  56 + std::string getInheritableFieldValueAsString(std::string const& name);
  57 +
  58 + // Get an inherited field value of type name as a string
  59 + // representing the name. If it is not a name, silently return
  60 + // the empty string.
  61 + QPDF_DLL
  62 + std::string getInheritableFieldValueAsName(std::string const& name);
  63 +
  64 + // Returns the value of /FT if present, otherwise returns the
  65 + // empty string.
  66 + QPDF_DLL
  67 + std::string getFieldType();
  68 +
  69 + QPDF_DLL
  70 + std::string getFullyQualifiedName();
  71 +
  72 + QPDF_DLL
  73 + std::string getPartialName();
  74 +
  75 + // Return the alternative field name (/TU), which is the field
  76 + // name intended to be presented to users. If not present, fall
  77 + // back to the fully qualified name.
  78 + QPDF_DLL
  79 + std::string getAlternativeName();
  80 +
  81 + // Return the mapping field name (/TM). If not present, fall back
  82 + // to the alternative name, then to the partial name.
  83 + QPDF_DLL
  84 + std::string getMappingName();
  85 +
  86 + QPDF_DLL
  87 + QPDFObjectHandle getValue();
  88 +
  89 + // Return the field's value as a string. If this is called with a
  90 + // field whose value is not a string, the empty string will be
  91 + // silently returned.
  92 + QPDF_DLL
  93 + std::string getValueAsString();
  94 +
  95 + QPDF_DLL
  96 + QPDFObjectHandle getDefaultValue();
  97 +
  98 + // Return the field's default value as a string. If this is called
  99 + // with a field whose value is not a string, the empty string will
  100 + // be silently returned.
  101 + QPDF_DLL
  102 + std::string getDefaultValueAsString();
  103 +
  104 + // Return the default appearance string, taking inheritance from
  105 + // the field tree into account. Returns the empty string if the
  106 + // default appearance string is not available (because it's
  107 + // erroneously absent or because this is not a variable text
  108 + // field).
  109 + QPDF_DLL
  110 + std::string getDefaultAppearance();
  111 +
  112 + // Return the quadding value, taking inheritance from the field
  113 + // tree into account. Returns 0 if quadding is not specified.
  114 + QPDF_DLL
  115 + int getQuadding();
  116 +
  117 + private:
  118 + class Members
  119 + {
  120 + friend class QPDFFormFieldObjectHelper;
  121 +
  122 + public:
  123 + ~Members();
  124 +
  125 + private:
  126 + Members();
  127 + Members(Members const&);
  128 + };
  129 +
  130 + PointerHolder<Members> m;
  131 +};
  132 +
  133 +#endif // __QPDFFORMFIELDOBJECTHELPER_HH__
include/qpdf/QPDFPageObjectHelper.hh
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 #define __QPDFPAGEOBJECTHELPER_HH__ 23 #define __QPDFPAGEOBJECTHELPER_HH__
24 24
25 #include <qpdf/QPDFObjectHelper.hh> 25 #include <qpdf/QPDFObjectHelper.hh>
  26 +#include <qpdf/QPDFAnnotationObjectHelper.hh>
26 27
27 #include <qpdf/DLL.h> 28 #include <qpdf/DLL.h>
28 29
@@ -43,6 +44,13 @@ class QPDFPageObjectHelper: public QPDFObjectHelper @@ -43,6 +44,13 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
43 QPDF_DLL 44 QPDF_DLL
44 std::map<std::string, QPDFObjectHandle> getPageImages(); 45 std::map<std::string, QPDFObjectHandle> getPageImages();
45 46
  47 + // Return the annotations in the page's "/Annots" list, if any. If
  48 + // only_subtype is non-empty, only include annotations of the
  49 + // given subtype.
  50 + QPDF_DLL
  51 + std::vector<QPDFAnnotationObjectHelper> getAnnotations(
  52 + std::string const& only_subtype = "");
  53 +
46 // Returns a vector of stream objects representing the content 54 // Returns a vector of stream objects representing the content
47 // streams for the given page. This routine allows the caller to 55 // streams for the given page. This routine allows the caller to
48 // not care whether there are one or more than one content streams 56 // not care whether there are one or more than one content streams
libqpdf/QPDFAcroFormDocumentHelper.cc 0 → 100644
  1 +#include <qpdf/QPDFAcroFormDocumentHelper.hh>
  2 +
  3 +#include <qpdf/QTC.hh>
  4 +#include <qpdf/QPDFPageDocumentHelper.hh>
  5 +
  6 +QPDFAcroFormDocumentHelper::Members::~Members()
  7 +{
  8 +}
  9 +
  10 +QPDFAcroFormDocumentHelper::Members::Members() :
  11 + cache_valid(false)
  12 +{
  13 +}
  14 +
  15 +QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) :
  16 + QPDFDocumentHelper(qpdf),
  17 + m(new Members())
  18 +{
  19 +}
  20 +
  21 +void
  22 +QPDFAcroFormDocumentHelper::invalidateCache()
  23 +{
  24 + this->m->cache_valid = false;
  25 + this->m->field_to_annotations.clear();
  26 + this->m->annotation_to_field.clear();
  27 +}
  28 +
  29 +bool
  30 +QPDFAcroFormDocumentHelper::hasAcroForm()
  31 +{
  32 + return this->qpdf.getRoot().hasKey("/AcroForm");
  33 +}
  34 +
  35 +std::vector<QPDFFormFieldObjectHelper>
  36 +QPDFAcroFormDocumentHelper::getFormFields()
  37 +{
  38 + analyze();
  39 + std::vector<QPDFFormFieldObjectHelper> result;
  40 + for (std::map<QPDFObjGen,
  41 + std::vector<QPDFAnnotationObjectHelper> >::iterator iter =
  42 + this->m->field_to_annotations.begin();
  43 + iter != this->m->field_to_annotations.end(); ++iter)
  44 + {
  45 + result.push_back(this->qpdf.getObjectByObjGen((*iter).first));
  46 + }
  47 + return result;
  48 +}
  49 +
  50 +std::vector<QPDFAnnotationObjectHelper>
  51 +QPDFAcroFormDocumentHelper::getAnnotationsForField(QPDFFormFieldObjectHelper h)
  52 +{
  53 + analyze();
  54 + std::vector<QPDFAnnotationObjectHelper> result;
  55 + QPDFObjGen og(h.getObjectHandle().getObjGen());
  56 + if (this->m->field_to_annotations.count(og))
  57 + {
  58 + result = this->m->field_to_annotations[og];
  59 + }
  60 + return result;
  61 +}
  62 +
  63 +std::vector<QPDFAnnotationObjectHelper>
  64 +QPDFAcroFormDocumentHelper::getWidgetAnnotationsForPage(QPDFPageObjectHelper h)
  65 +{
  66 + return h.getAnnotations("/Widget");
  67 +}
  68 +
  69 +QPDFFormFieldObjectHelper
  70 +QPDFAcroFormDocumentHelper::getFieldForAnnotation(QPDFAnnotationObjectHelper h)
  71 +{
  72 + QPDFObjectHandle oh = h.getObjectHandle();
  73 + if (! (oh.isDictionary() &&
  74 + oh.getKey("/Subtype").isName() &&
  75 + (oh.getKey("/Subtype").getName() == "/Widget")))
  76 + {
  77 + throw std::logic_error(
  78 + "QPDFAnnotationObjectHelper::getFieldForAnnotation called for"
  79 + " non-/Widget annotation");
  80 + }
  81 + analyze();
  82 + QPDFFormFieldObjectHelper result(QPDFObjectHandle::newNull());
  83 + QPDFObjGen og(oh.getObjGen());
  84 + if (this->m->annotation_to_field.count(og))
  85 + {
  86 + result = this->m->annotation_to_field[og];
  87 + }
  88 + return result;
  89 +}
  90 +
  91 +void
  92 +QPDFAcroFormDocumentHelper::analyze()
  93 +{
  94 + if (this->m->cache_valid)
  95 + {
  96 + return;
  97 + }
  98 + this->m->cache_valid = true;
  99 + QPDFObjectHandle acroform = this->qpdf.getRoot().getKey("/AcroForm");
  100 + if (! (acroform.isDictionary() && acroform.hasKey("/Fields")))
  101 + {
  102 + return;
  103 + }
  104 + QPDFObjectHandle fields = acroform.getKey("/Fields");
  105 + if (! fields.isArray())
  106 + {
  107 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array");
  108 + acroform.warnIfPossible(
  109 + "/Fields key of /AcroForm dictionary is not an array; ignoring");
  110 + fields = QPDFObjectHandle::newArray();
  111 + }
  112 +
  113 + // Traverse /AcroForm to find annotations and map them
  114 + // bidirectionally to fields.
  115 +
  116 + std::set<QPDFObjGen> visited;
  117 + size_t nfields = fields.getArrayNItems();
  118 + QPDFObjectHandle null(QPDFObjectHandle::newNull());
  119 + for (size_t i = 0; i < nfields; ++i)
  120 + {
  121 + traverseField(fields.getArrayItem(i), null, 0, visited);
  122 + }
  123 +
  124 + // All Widget annotations should have been encountered by
  125 + // traversing /AcroForm, but in case any weren't, find them by
  126 + // walking through pages, and treat any widget annotation that is
  127 + // not associated with a field as its own field. This just ensures
  128 + // that requesting the field for any annotation we find through a
  129 + // page's /Annots list will have some associated field. Note that
  130 + // a file that contains this kind of error will probably not
  131 + // actually work with most viewers.
  132 +
  133 + QPDFPageDocumentHelper dh(this->qpdf);
  134 + std::vector<QPDFPageObjectHelper> pages = dh.getAllPages();
  135 + for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin();
  136 + iter != pages.end(); ++iter)
  137 + {
  138 + QPDFPageObjectHelper ph(*iter);
  139 + std::vector<QPDFAnnotationObjectHelper> annots =
  140 + getWidgetAnnotationsForPage(ph);
  141 + for (std::vector<QPDFAnnotationObjectHelper>::iterator i2 =
  142 + annots.begin();
  143 + i2 != annots.end(); ++i2)
  144 + {
  145 + QPDFObjectHandle annot((*i2).getObjectHandle());
  146 + QPDFObjGen og(annot.getObjGen());
  147 + if (this->m->annotation_to_field.count(og) == 0)
  148 + {
  149 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper orphaned widget");
  150 + // This is not supposed to happen, but it's easy
  151 + // enough for us to handle this case. Treat the
  152 + // annotation as its own field. This could allow qpdf
  153 + // to sensibly handle a case such as a PDF creator
  154 + // adding a self-contained annotation (merged with the
  155 + // field dictionary) to the page's /Annots array and
  156 + // forgetting to also put it in /AcroForm.
  157 + annot.warnIfPossible(
  158 + "this widget annotation is not"
  159 + " reachable from /AcroForm in the document catalog");
  160 + this->m->annotation_to_field[og] =
  161 + QPDFFormFieldObjectHelper(annot);
  162 + this->m->field_to_annotations[og].push_back(
  163 + QPDFAnnotationObjectHelper(annot));
  164 + }
  165 + }
  166 + }
  167 +}
  168 +
  169 +void
  170 +QPDFAcroFormDocumentHelper::traverseField(
  171 + QPDFObjectHandle field, QPDFObjectHandle parent, int depth,
  172 + std::set<QPDFObjGen>& visited)
  173 +{
  174 + if (depth > 100)
  175 + {
  176 + // Arbitrarily cut off recursion at a fixed depth to avoid
  177 + // specially crafted files that could cause stack overflow.
  178 + return;
  179 + }
  180 + if (! field.isIndirect())
  181 + {
  182 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper direct field");
  183 + field.warnIfPossible(
  184 + "encountered a direct object as a field or annotation while"
  185 + " traversing /AcroForm; ignoring field or annotation");
  186 + return;
  187 + }
  188 + if (! field.isDictionary())
  189 + {
  190 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper non-dictionary field");
  191 + field.warnIfPossible(
  192 + "encountered a non-dictionary as a field or annotation while"
  193 + " traversing /AcroForm; ignoring field or annotation");
  194 + return;
  195 + }
  196 + QPDFObjGen og(field.getObjGen());
  197 + if (visited.count(og) != 0)
  198 + {
  199 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper loop");
  200 + field.warnIfPossible("loop detected while traversing /AcroForm");
  201 + return;
  202 + }
  203 + visited.insert(og);
  204 +
  205 + // A dictionary encountered while traversing the /AcroForm field
  206 + // may be a form field, an annotation, or the merger of the two. A
  207 + // field that has no fields below it is a terminal. If a terminal
  208 + // field looks like an annotation, it is an annotation because
  209 + // annotation dictionary fields can be merged with terminal field
  210 + // dictionaries. Otherwise, the annotation fields might be there
  211 + // to be inherited by annotations below it.
  212 +
  213 + bool is_annotation = false;
  214 + bool is_field = (0 == depth);
  215 + QPDFObjectHandle kids = field.getKey("/Kids");
  216 + if (kids.isArray())
  217 + {
  218 + is_field = true;
  219 + size_t nkids = kids.getArrayNItems();
  220 + for (size_t k = 0; k < nkids; ++k)
  221 + {
  222 + traverseField(kids.getArrayItem(k), field, 1 + depth, visited);
  223 + }
  224 + }
  225 + else
  226 + {
  227 + if (field.hasKey("/Parent"))
  228 + {
  229 + is_field = true;
  230 + }
  231 + if (field.hasKey("/Subtype") ||
  232 + field.hasKey("/Rect") ||
  233 + field.hasKey("/AP"))
  234 + {
  235 + is_annotation = true;
  236 + }
  237 + }
  238 +
  239 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper field found",
  240 + (depth == 0) ? 0 : 1);
  241 + QTC::TC("qpdf", "QPDFAcroFormDocumentHelper annotation found",
  242 + (is_field ? 0 : 1));
  243 +
  244 + if (is_annotation)
  245 + {
  246 + QPDFObjectHandle our_field = (is_field ? field : parent);
  247 + this->m->field_to_annotations[our_field.getObjGen()].push_back(
  248 + QPDFAnnotationObjectHelper(field));
  249 + this->m->annotation_to_field[og] =
  250 + QPDFFormFieldObjectHelper(our_field);
  251 + }
  252 +}
libqpdf/QPDFAnnotationObjectHelper.cc 0 → 100644
  1 +#include <qpdf/QPDFAnnotationObjectHelper.hh>
  2 +#include <qpdf/QTC.hh>
  3 +
  4 +QPDFAnnotationObjectHelper::Members::~Members()
  5 +{
  6 +}
  7 +
  8 +QPDFAnnotationObjectHelper::Members::Members()
  9 +{
  10 +}
  11 +
  12 +QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) :
  13 + QPDFObjectHelper(oh)
  14 +{
  15 +}
  16 +
  17 +std::string
  18 +QPDFAnnotationObjectHelper::getSubtype()
  19 +{
  20 + return this->oh.getKey("/Subtype").getName();
  21 +}
  22 +
  23 +QPDFObjectHandle::Rectangle
  24 +QPDFAnnotationObjectHelper::getRect()
  25 +{
  26 + return this->oh.getKey("/Rect").getArrayAsRectangle();
  27 +}
  28 +
  29 +QPDFObjectHandle
  30 +QPDFAnnotationObjectHelper::getAppearanceDictionary()
  31 +{
  32 + return this->oh.getKey("/AP");
  33 +}
  34 +
  35 +std::string
  36 +QPDFAnnotationObjectHelper::getAppearanceState()
  37 +{
  38 + if (this->oh.getKey("/AS").isName())
  39 + {
  40 + QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS present");
  41 + return this->oh.getKey("/AS").getName();
  42 + }
  43 + QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS absent");
  44 + return "";
  45 +}
  46 +
  47 +QPDFObjectHandle
  48 +QPDFAnnotationObjectHelper::getAppearanceStream(
  49 + std::string const& which,
  50 + std::string const& state)
  51 +{
  52 + QPDFObjectHandle ap = getAppearanceDictionary();
  53 + std::string desired_state = state.empty() ? getAppearanceState() : state;
  54 + if (ap.isDictionary())
  55 + {
  56 + QPDFObjectHandle ap_sub = ap.getKey(which);
  57 + if (ap_sub.isStream() && desired_state.empty())
  58 + {
  59 + QTC::TC("qpdf", "QPDFAnnotationObjectHelper AP stream");
  60 + return ap_sub;
  61 + }
  62 + if (ap_sub.isDictionary() && (! desired_state.empty()))
  63 + {
  64 + QTC::TC("qpdf", "QPDFAnnotationObjectHelper AP dictionary");
  65 + QPDFObjectHandle ap_sub_val = ap_sub.getKey(desired_state);
  66 + if (ap_sub_val.isStream())
  67 + {
  68 + QTC::TC("qpdf", "QPDFAnnotationObjectHelper AN sub stream");
  69 + return ap_sub_val;
  70 + }
  71 + }
  72 + }
  73 + QTC::TC("qpdf", "QPDFAnnotationObjectHelper AN null");
  74 + return QPDFObjectHandle::newNull();
  75 +}
libqpdf/QPDFFormFieldObjectHelper.cc 0 → 100644
  1 +#include <qpdf/QPDFFormFieldObjectHelper.hh>
  2 +#include <qpdf/QTC.hh>
  3 +
  4 +QPDFFormFieldObjectHelper::Members::~Members()
  5 +{
  6 +}
  7 +
  8 +QPDFFormFieldObjectHelper::Members::Members()
  9 +{
  10 +}
  11 +
  12 +QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) :
  13 + QPDFObjectHelper(oh),
  14 + m(new Members())
  15 +{
  16 +}
  17 +
  18 +QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() :
  19 + QPDFObjectHelper(QPDFObjectHandle::newNull()),
  20 + m(new Members())
  21 +{
  22 +}
  23 +
  24 +bool
  25 +QPDFFormFieldObjectHelper::isNull()
  26 +{
  27 + return this->oh.isNull();
  28 +}
  29 +
  30 +QPDFFormFieldObjectHelper
  31 +QPDFFormFieldObjectHelper::getParent()
  32 +{
  33 + return this->oh.getKey("/Parent"); // may be null
  34 +}
  35 +
  36 +QPDFObjectHandle
  37 +QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
  38 +{
  39 + QPDFObjectHandle node = this->oh;
  40 + QPDFObjectHandle result(node.getKey(name));
  41 + std::set<QPDFObjGen> seen;
  42 + while (result.isNull() && node.hasKey("/Parent"))
  43 + {
  44 + seen.insert(node.getObjGen());
  45 + node = node.getKey("/Parent");
  46 + if (seen.count(node.getObjGen()))
  47 + {
  48 + break;
  49 + }
  50 + result = node.getKey(name);
  51 + if (! result.isNull())
  52 + {
  53 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper non-trivial inheritance");
  54 + }
  55 + }
  56 + return result;
  57 +}
  58 +
  59 +std::string
  60 +QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(
  61 + std::string const& name)
  62 +{
  63 + QPDFObjectHandle fv = getInheritableFieldValue(name);
  64 + std::string result;
  65 + if (fv.isString())
  66 + {
  67 + result = fv.getUTF8Value();
  68 + }
  69 + return result;
  70 +}
  71 +
  72 +std::string
  73 +QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(
  74 + std::string const& name)
  75 +{
  76 + QPDFObjectHandle fv = getInheritableFieldValue(name);
  77 + std::string result;
  78 + if (fv.isName())
  79 + {
  80 + result = fv.getName();
  81 + }
  82 + return result;
  83 +}
  84 +
  85 +std::string
  86 +QPDFFormFieldObjectHelper::getFieldType()
  87 +{
  88 + return getInheritableFieldValueAsName("/FT");
  89 +}
  90 +
  91 +std::string
  92 +QPDFFormFieldObjectHelper::getFullyQualifiedName()
  93 +{
  94 + std::string result;
  95 + QPDFObjectHandle node = this->oh;
  96 + std::set<QPDFObjGen> seen;
  97 + while ((! node.isNull()) && (seen.count(node.getObjGen()) == 0))
  98 + {
  99 + if (node.getKey("/T").isString())
  100 + {
  101 + if (! result.empty())
  102 + {
  103 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper non-trivial qualified name");
  104 + result = "." + result;
  105 + }
  106 + result = node.getKey("/T").getUTF8Value() + result;
  107 + }
  108 + seen.insert(node.getObjGen());
  109 + node = node.getKey("/Parent");
  110 + }
  111 + return result;
  112 +}
  113 +
  114 +std::string
  115 +QPDFFormFieldObjectHelper::getPartialName()
  116 +{
  117 + std::string result;
  118 + if (this->oh.getKey("/T").isString())
  119 + {
  120 + result = this->oh.getKey("/T").getUTF8Value();
  121 + }
  122 + return result;
  123 +}
  124 +
  125 +std::string
  126 +QPDFFormFieldObjectHelper::getAlternativeName()
  127 +{
  128 + if (this->oh.getKey("/TU").isString())
  129 + {
  130 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present");
  131 + return this->oh.getKey("/TU").getUTF8Value();
  132 + }
  133 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent");
  134 + return getFullyQualifiedName();
  135 +}
  136 +
  137 +std::string
  138 +QPDFFormFieldObjectHelper::getMappingName()
  139 +{
  140 + if (this->oh.getKey("/TM").isString())
  141 + {
  142 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present");
  143 + return this->oh.getKey("/TM").getUTF8Value();
  144 + }
  145 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent");
  146 + return getAlternativeName();
  147 +}
  148 +
  149 +QPDFObjectHandle
  150 +QPDFFormFieldObjectHelper::getValue()
  151 +{
  152 + return getInheritableFieldValue("/V");
  153 +}
  154 +
  155 +std::string
  156 +QPDFFormFieldObjectHelper::getValueAsString()
  157 +{
  158 + return getInheritableFieldValueAsString("/V");
  159 +}
  160 +
  161 +QPDFObjectHandle
  162 +QPDFFormFieldObjectHelper::getDefaultValue()
  163 +{
  164 + return getInheritableFieldValue("/DV");
  165 +}
  166 +
  167 +std::string
  168 +QPDFFormFieldObjectHelper::getDefaultValueAsString()
  169 +{
  170 + return getInheritableFieldValueAsString("/DV");
  171 +}
  172 +
  173 +std::string
  174 +QPDFFormFieldObjectHelper::getDefaultAppearance()
  175 +{
  176 + return getInheritableFieldValueAsString("/DA");
  177 +}
  178 +
  179 +int
  180 +QPDFFormFieldObjectHelper::getQuadding()
  181 +{
  182 + int result = 0;
  183 + QPDFObjectHandle fv = getInheritableFieldValue("/Q");
  184 + if (fv.isInteger())
  185 + {
  186 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper Q present");
  187 + result = static_cast<int>(fv.getIntValue());
  188 + }
  189 + return result;
  190 +}
libqpdf/QPDFPageObjectHelper.cc
@@ -19,6 +19,29 @@ QPDFPageObjectHelper::getPageImages() @@ -19,6 +19,29 @@ QPDFPageObjectHelper::getPageImages()
19 return this->oh.getPageImages(); 19 return this->oh.getPageImages();
20 } 20 }
21 21
  22 +std::vector<QPDFAnnotationObjectHelper>
  23 +QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype)
  24 +{
  25 + std::vector<QPDFAnnotationObjectHelper> result;
  26 + QPDFObjectHandle annots = this->oh.getKey("/Annots");
  27 + if (annots.isArray())
  28 + {
  29 + size_t nannots = annots.getArrayNItems();
  30 + for (size_t i = 0; i < nannots; ++i)
  31 + {
  32 + QPDFObjectHandle annot = annots.getArrayItem(i);
  33 + if (only_subtype.empty() ||
  34 + (annot.isDictionary() &&
  35 + annot.getKey("/Subtype").isName() &&
  36 + (only_subtype == annot.getKey("/Subtype").getName())))
  37 + {
  38 + result.push_back(QPDFAnnotationObjectHelper(annot));
  39 + }
  40 + }
  41 + }
  42 + return result;
  43 +}
  44 +
22 std::vector<QPDFObjectHandle> 45 std::vector<QPDFObjectHandle>
23 QPDFPageObjectHelper::getPageContents() 46 QPDFPageObjectHelper::getPageContents()
24 { 47 {
libqpdf/build.mk
@@ -35,7 +35,10 @@ SRCS_libqpdf = \ @@ -35,7 +35,10 @@ SRCS_libqpdf = \
35 libqpdf/Pl_StdioFile.cc \ 35 libqpdf/Pl_StdioFile.cc \
36 libqpdf/Pl_TIFFPredictor.cc \ 36 libqpdf/Pl_TIFFPredictor.cc \
37 libqpdf/QPDF.cc \ 37 libqpdf/QPDF.cc \
  38 + libqpdf/QPDFAcroFormDocumentHelper.cc \
  39 + libqpdf/QPDFAnnotationObjectHelper.cc \
38 libqpdf/QPDFExc.cc \ 40 libqpdf/QPDFExc.cc \
  41 + libqpdf/QPDFFormFieldObjectHelper.cc \
39 libqpdf/QPDFObjGen.cc \ 42 libqpdf/QPDFObjGen.cc \
40 libqpdf/QPDFObject.cc \ 43 libqpdf/QPDFObject.cc \
41 libqpdf/QPDFObjectHandle.cc \ 44 libqpdf/QPDFObjectHandle.cc \
qpdf/qpdf.testcov
@@ -336,3 +336,23 @@ QPDFObjectHandle erase array bounds 0 @@ -336,3 +336,23 @@ QPDFObjectHandle erase array bounds 0
336 qpdf-c called qpdf_check_pdf 0 336 qpdf-c called qpdf_check_pdf 0
337 QPDF xref loop 0 337 QPDF xref loop 0
338 QPDFObjectHandle too deep 0 338 QPDFObjectHandle too deep 0
  339 +QPDFFormFieldObjectHelper non-trivial inheritance 0
  340 +QPDFFormFieldObjectHelper non-trivial qualified name 0
  341 +QPDFFormFieldObjectHelper TU present 0
  342 +QPDFFormFieldObjectHelper TM present 0
  343 +QPDFFormFieldObjectHelper TU absent 0
  344 +QPDFFormFieldObjectHelper TM absent 0
  345 +QPDFFormFieldObjectHelper Q present 0
  346 +QPDFAnnotationObjectHelper AS present 0
  347 +QPDFAnnotationObjectHelper AS absent 0
  348 +QPDFAnnotationObjectHelper AP stream 0
  349 +QPDFAnnotationObjectHelper AP dictionary 0
  350 +QPDFAnnotationObjectHelper AN sub stream 0
  351 +QPDFAnnotationObjectHelper AN null 0
  352 +QPDFAcroFormDocumentHelper fields not array 0
  353 +QPDFAcroFormDocumentHelper orphaned widget 0
  354 +QPDFAcroFormDocumentHelper direct field 0
  355 +QPDFAcroFormDocumentHelper non-dictionary field 0
  356 +QPDFAcroFormDocumentHelper loop 0
  357 +QPDFAcroFormDocumentHelper field found 1
  358 +QPDFAcroFormDocumentHelper annotation found 1
qpdf/qtest/qpdf.test
@@ -94,6 +94,38 @@ $td-&gt;runtest(&quot;PDF doc encoding to Unicode&quot;, @@ -94,6 +94,38 @@ $td-&gt;runtest(&quot;PDF doc encoding to Unicode&quot;,
94 94
95 show_ntests(); 95 show_ntests();
96 # ---------- 96 # ----------
  97 +$td->notify("--- Form Tests ---");
  98 +
  99 +my @form_tests = (
  100 + 'minimal',
  101 + 'form-empty-from-odt',
  102 + 'form-mod1',
  103 + # Atril (MATE Document Viewer) 1.20.1 dumps appearance streams
  104 + # when modifying form fields, leaving /NeedAppearances true.
  105 + 'form-filled-with-atril',
  106 + 'form-bad-fields-array',
  107 + 'form-errors',
  108 + );
  109 +
  110 +$n_tests += scalar(@form_tests);
  111 +
  112 +# Many of the form*.pdf files were created by converting the
  113 +# LibreOffice document storage/form.odt to PDF and then manually
  114 +# modifying the resulting PDF in various ways. That file would be good
  115 +# starting point for generation of more complex forms should that be
  116 +# required in the future. The file storage/form.pdf is a direct export
  117 +# from LibreOffice with no modifications.
  118 +
  119 +foreach my $f (@form_tests)
  120 +{
  121 + $td->runtest("form test: $f",
  122 + {$td->COMMAND => "test_driver 43 $f.pdf"},
  123 + {$td->FILE => "form-$f.out", $td->EXIT_STATUS => 0},
  124 + $td->NORMALIZE_NEWLINES);
  125 +}
  126 +
  127 +show_ntests();
  128 +# ----------
97 $td->notify("--- Stream Replacement Tests ---"); 129 $td->notify("--- Stream Replacement Tests ---");
98 $n_tests += 8; 130 $n_tests += 8;
99 131
qpdf/qtest/qpdf/form-bad-fields-array.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/form-empty-from-odt.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/form-errors.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/form-filled-with-atril.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/form-form-bad-fields-array.out 0 → 100644
  1 +iterating over form fields
  2 +WARNING: form-bad-fields-array.pdf, object 1 0 at offset 50: /Fields key of /AcroForm dictionary is not an array; ignoring
  3 +WARNING: form-bad-fields-array.pdf, object 4 0 at offset 615: this widget annotation is not reachable from /AcroForm in the document catalog
  4 +WARNING: form-bad-fields-array.pdf, object 16 0 at offset 3419: this widget annotation is not reachable from /AcroForm in the document catalog
  5 +WARNING: form-bad-fields-array.pdf, object 17 0 at offset 3775: this widget annotation is not reachable from /AcroForm in the document catalog
  6 +WARNING: form-bad-fields-array.pdf, object 18 0 at offset 4131: this widget annotation is not reachable from /AcroForm in the document catalog
  7 +WARNING: form-bad-fields-array.pdf, object 6 0 at offset 1032: this widget annotation is not reachable from /AcroForm in the document catalog
  8 +WARNING: form-bad-fields-array.pdf, object 7 0 at offset 1413: this widget annotation is not reachable from /AcroForm in the document catalog
  9 +WARNING: form-bad-fields-array.pdf, object 8 0 at offset 1796: this widget annotation is not reachable from /AcroForm in the document catalog
  10 +WARNING: form-bad-fields-array.pdf, object 32 0 at offset 5893: this widget annotation is not reachable from /AcroForm in the document catalog
  11 +WARNING: form-bad-fields-array.pdf, object 33 0 at offset 6251: this widget annotation is not reachable from /AcroForm in the document catalog
  12 +WARNING: form-bad-fields-array.pdf, object 34 0 at offset 6607: this widget annotation is not reachable from /AcroForm in the document catalog
  13 +WARNING: form-bad-fields-array.pdf, object 10 0 at offset 2311: this widget annotation is not reachable from /AcroForm in the document catalog
  14 +Field: 4 0 R
  15 + Parent: none
  16 + Fully qualified name: Text Box 1
  17 + Partial name: Text Box 1
  18 + Alternative name: Text Box 1
  19 + Mapping name: Text Box 1
  20 + Field type: /Tx
  21 + Value: <feff>
  22 + Value as string:
  23 + Default value: <feff>
  24 + Default value as string:
  25 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  26 + Quadding: 0
  27 + Annotation: 4 0 R
  28 +Field: 6 0 R
  29 + Parent: none
  30 + Fully qualified name: Check Box 1
  31 + Partial name: Check Box 1
  32 + Alternative name: Check Box 1
  33 + Mapping name: Check Box 1
  34 + Field type: /Btn
  35 + Value: /Off
  36 + Value as string:
  37 + Default value: /Off
  38 + Default value as string:
  39 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  40 + Quadding: 0
  41 + Annotation: 6 0 R
  42 +Field: 7 0 R
  43 + Parent: none
  44 + Fully qualified name: Check Box 2
  45 + Partial name: Check Box 2
  46 + Alternative name: Check Box 2
  47 + Mapping name: Check Box 2
  48 + Field type: /Btn
  49 + Value: /Yes
  50 + Value as string:
  51 + Default value: /Yes
  52 + Default value as string:
  53 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  54 + Quadding: 0
  55 + Annotation: 7 0 R
  56 +Field: 8 0 R
  57 + Parent: none
  58 + Fully qualified name: Check Box 3
  59 + Partial name: Check Box 3
  60 + Alternative name: Check Box 3
  61 + Mapping name: Check Box 3
  62 + Field type: /Btn
  63 + Value: /Off
  64 + Value as string:
  65 + Default value: /Off
  66 + Default value as string:
  67 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  68 + Quadding: 0
  69 + Annotation: 8 0 R
  70 +Field: 10 0 R
  71 + Parent: none
  72 + Fully qualified name: Text Box 2
  73 + Partial name: Text Box 2
  74 + Alternative name: Text Box 2
  75 + Mapping name: Text Box 2
  76 + Field type: /Tx
  77 + Value: <feff00730061006c00610064002003c002ac>
  78 + Value as string: salad πʬ
  79 + Default value: <feff00730061006c00610064002003c002ac>
  80 + Default value as string: salad πʬ
  81 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  82 + Quadding: 0
  83 + Annotation: 10 0 R
  84 +Field: 16 0 R
  85 + Parent: 5 0 R
  86 + Parent: none
  87 + Fully qualified name: r1
  88 + Partial name:
  89 + Alternative name: r1
  90 + Mapping name: r1
  91 + Field type: /Btn
  92 + Value: /1
  93 + Value as string:
  94 + Default value: /1
  95 + Default value as string:
  96 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  97 + Quadding: 0
  98 + Annotation: 16 0 R
  99 +Field: 17 0 R
  100 + Parent: 5 0 R
  101 + Parent: none
  102 + Fully qualified name: r1
  103 + Partial name:
  104 + Alternative name: r1
  105 + Mapping name: r1
  106 + Field type: /Btn
  107 + Value: /1
  108 + Value as string:
  109 + Default value: /1
  110 + Default value as string:
  111 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  112 + Quadding: 0
  113 + Annotation: 17 0 R
  114 +Field: 18 0 R
  115 + Parent: 5 0 R
  116 + Parent: none
  117 + Fully qualified name: r1
  118 + Partial name:
  119 + Alternative name: r1
  120 + Mapping name: r1
  121 + Field type: /Btn
  122 + Value: /1
  123 + Value as string:
  124 + Default value: /1
  125 + Default value as string:
  126 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  127 + Quadding: 0
  128 + Annotation: 18 0 R
  129 +Field: 32 0 R
  130 + Parent: 9 0 R
  131 + Parent: none
  132 + Fully qualified name: r2
  133 + Partial name:
  134 + Alternative name: r2
  135 + Mapping name: r2
  136 + Field type: /Btn
  137 + Value: /2
  138 + Value as string:
  139 + Default value: /2
  140 + Default value as string:
  141 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  142 + Quadding: 0
  143 + Annotation: 32 0 R
  144 +Field: 33 0 R
  145 + Parent: 9 0 R
  146 + Parent: none
  147 + Fully qualified name: r2
  148 + Partial name:
  149 + Alternative name: r2
  150 + Mapping name: r2
  151 + Field type: /Btn
  152 + Value: /2
  153 + Value as string:
  154 + Default value: /2
  155 + Default value as string:
  156 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  157 + Quadding: 0
  158 + Annotation: 33 0 R
  159 +Field: 34 0 R
  160 + Parent: 9 0 R
  161 + Parent: none
  162 + Fully qualified name: r2
  163 + Partial name:
  164 + Alternative name: r2
  165 + Mapping name: r2
  166 + Field type: /Btn
  167 + Value: /2
  168 + Value as string:
  169 + Default value: /2
  170 + Default value as string:
  171 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  172 + Quadding: 0
  173 + Annotation: 34 0 R
  174 +iterating over annotations per page
  175 +Page: 11 0 R
  176 + Annotation: 4 0 R
  177 + Field: 4 0 R
  178 + Subtype: /Widget
  179 + Rect: [123.4, 692.1, 260.9, 706.7]
  180 + Appearance stream (/N): 14 0 R
  181 + Appearance stream (/N, /3): null
  182 + Annotation: 16 0 R
  183 + Field: 16 0 R
  184 + Subtype: /Widget
  185 + Rect: [149.3, 648.5, 161.6, 660.4]
  186 + Appearance state: /1
  187 + Appearance stream (/N): 44 0 R
  188 + Appearance stream (/N, /3): null
  189 + Annotation: 17 0 R
  190 + Field: 17 0 R
  191 + Subtype: /Widget
  192 + Rect: [152.7, 627.3, 165, 639.2]
  193 + Appearance state: /Off
  194 + Appearance stream (/N): 50 0 R
  195 + Appearance stream (/N, /3): null
  196 + Annotation: 18 0 R
  197 + Field: 18 0 R
  198 + Subtype: /Widget
  199 + Rect: [151.3, 601.7, 163.6, 613.6]
  200 + Appearance state: /Off
  201 + Appearance stream (/N): 54 0 R
  202 + Appearance stream (/N, /3): 52 0 R
  203 + Annotation: 6 0 R
  204 + Field: 6 0 R
  205 + Subtype: /Widget
  206 + Rect: [121.9, 559.1, 134.2, 571]
  207 + Appearance state: /Off
  208 + Appearance stream (/N): 19 0 R
  209 + Appearance stream (/N, /3): null
  210 + Annotation: 7 0 R
  211 + Field: 7 0 R
  212 + Subtype: /Widget
  213 + Rect: [118.6, 527.7, 130.9, 539.6]
  214 + Appearance state: /Yes
  215 + Appearance stream (/N): 26 0 R
  216 + Appearance stream (/N, /3): null
  217 + Annotation: 8 0 R
  218 + Field: 8 0 R
  219 + Subtype: /Widget
  220 + Rect: [118.6, 500.5, 130.9, 512.4]
  221 + Appearance state: /Off
  222 + Appearance stream (/N): 28 0 R
  223 + Appearance stream (/N, /3): null
  224 +Page: 40 0 R
  225 +Page: 35 0 R
  226 + Annotation: 32 0 R
  227 + Field: 32 0 R
  228 + Subtype: /Widget
  229 + Rect: [118.6, 555.7, 130.9, 567.6]
  230 + Appearance state: /Off
  231 + Appearance stream (/N): 58 0 R
  232 + Appearance stream (/N, /3): null
  233 + Annotation: 33 0 R
  234 + Field: 33 0 R
  235 + Subtype: /Widget
  236 + Rect: [119.3, 514.8, 131.6, 526.7]
  237 + Appearance state: /2
  238 + Appearance stream (/N): 60 0 R
  239 + Appearance stream (/N, /3): null
  240 + Annotation: 34 0 R
  241 + Field: 34 0 R
  242 + Subtype: /Widget
  243 + Rect: [121.3, 472.5, 133.6, 484.4]
  244 + Appearance state: /Off
  245 + Appearance stream (/N): 66 0 R
  246 + Appearance stream (/N, /3): 64 0 R
  247 + Annotation: 10 0 R
  248 + Field: 10 0 R
  249 + Subtype: /Widget
  250 + Rect: [113.6, 378.5, 351.3, 396.3]
  251 + Appearance stream (/N): 36 0 R
  252 + Appearance stream (/N, /3): null
  253 +test 43 done
qpdf/qtest/qpdf/form-form-empty-from-odt.out 0 → 100644
  1 +iterating over form fields
  2 +Field: 4 0 R
  3 + Parent: none
  4 + Fully qualified name: Text Box 1
  5 + Partial name: Text Box 1
  6 + Alternative name: Text Box 1
  7 + Mapping name: Text Box 1
  8 + Field type: /Tx
  9 + Value: <feff>
  10 + Value as string:
  11 + Default value: <feff>
  12 + Default value as string:
  13 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  14 + Quadding: 0
  15 + Annotation: 4 0 R
  16 +Field: 6 0 R
  17 + Parent: none
  18 + Fully qualified name: Check Box 1
  19 + Partial name: Check Box 1
  20 + Alternative name: Check Box 1
  21 + Mapping name: Check Box 1
  22 + Field type: /Btn
  23 + Value: /Off
  24 + Value as string:
  25 + Default value: /Off
  26 + Default value as string:
  27 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  28 + Quadding: 0
  29 + Annotation: 6 0 R
  30 +Field: 7 0 R
  31 + Parent: none
  32 + Fully qualified name: Check Box 2
  33 + Partial name: Check Box 2
  34 + Alternative name: Check Box 2
  35 + Mapping name: Check Box 2
  36 + Field type: /Btn
  37 + Value: /Yes
  38 + Value as string:
  39 + Default value: /Yes
  40 + Default value as string:
  41 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  42 + Quadding: 0
  43 + Annotation: 7 0 R
  44 +Field: 8 0 R
  45 + Parent: none
  46 + Fully qualified name: Check Box 3
  47 + Partial name: Check Box 3
  48 + Alternative name: Check Box 3
  49 + Mapping name: Check Box 3
  50 + Field type: /Btn
  51 + Value: /Off
  52 + Value as string:
  53 + Default value: /Off
  54 + Default value as string:
  55 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  56 + Quadding: 0
  57 + Annotation: 8 0 R
  58 +Field: 10 0 R
  59 + Parent: none
  60 + Fully qualified name: Text Box 2
  61 + Partial name: Text Box 2
  62 + Alternative name: Text Box 2
  63 + Mapping name: Text Box 2
  64 + Field type: /Tx
  65 + Value: <feff00730061006c00610064002003c002ac>
  66 + Value as string: salad πʬ
  67 + Default value: <feff00730061006c00610064002003c002ac>
  68 + Default value as string: salad πʬ
  69 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  70 + Quadding: 0
  71 + Annotation: 10 0 R
  72 +Field: 16 0 R
  73 + Parent: 5 0 R
  74 + Parent: none
  75 + Fully qualified name: r1
  76 + Partial name:
  77 + Alternative name: r1
  78 + Mapping name: r1
  79 + Field type: /Btn
  80 + Value: /1
  81 + Value as string:
  82 + Default value: /1
  83 + Default value as string:
  84 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  85 + Quadding: 0
  86 + Annotation: 16 0 R
  87 +Field: 17 0 R
  88 + Parent: 5 0 R
  89 + Parent: none
  90 + Fully qualified name: r1
  91 + Partial name:
  92 + Alternative name: r1
  93 + Mapping name: r1
  94 + Field type: /Btn
  95 + Value: /1
  96 + Value as string:
  97 + Default value: /1
  98 + Default value as string:
  99 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  100 + Quadding: 0
  101 + Annotation: 17 0 R
  102 +Field: 18 0 R
  103 + Parent: 5 0 R
  104 + Parent: none
  105 + Fully qualified name: r1
  106 + Partial name:
  107 + Alternative name: r1
  108 + Mapping name: r1
  109 + Field type: /Btn
  110 + Value: /1
  111 + Value as string:
  112 + Default value: /1
  113 + Default value as string:
  114 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  115 + Quadding: 0
  116 + Annotation: 18 0 R
  117 +Field: 32 0 R
  118 + Parent: 9 0 R
  119 + Parent: none
  120 + Fully qualified name: r2
  121 + Partial name:
  122 + Alternative name: r2
  123 + Mapping name: r2
  124 + Field type: /Btn
  125 + Value: /2
  126 + Value as string:
  127 + Default value: /2
  128 + Default value as string:
  129 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  130 + Quadding: 0
  131 + Annotation: 32 0 R
  132 +Field: 33 0 R
  133 + Parent: 9 0 R
  134 + Parent: none
  135 + Fully qualified name: r2
  136 + Partial name:
  137 + Alternative name: r2
  138 + Mapping name: r2
  139 + Field type: /Btn
  140 + Value: /2
  141 + Value as string:
  142 + Default value: /2
  143 + Default value as string:
  144 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  145 + Quadding: 0
  146 + Annotation: 33 0 R
  147 +Field: 34 0 R
  148 + Parent: 9 0 R
  149 + Parent: none
  150 + Fully qualified name: r2
  151 + Partial name:
  152 + Alternative name: r2
  153 + Mapping name: r2
  154 + Field type: /Btn
  155 + Value: /2
  156 + Value as string:
  157 + Default value: /2
  158 + Default value as string:
  159 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  160 + Quadding: 0
  161 + Annotation: 34 0 R
  162 +iterating over annotations per page
  163 +Page: 11 0 R
  164 + Annotation: 4 0 R
  165 + Field: 4 0 R
  166 + Subtype: /Widget
  167 + Rect: [123.4, 692.1, 260.9, 706.7]
  168 + Appearance stream (/N): 14 0 R
  169 + Appearance stream (/N, /3): null
  170 + Annotation: 16 0 R
  171 + Field: 16 0 R
  172 + Subtype: /Widget
  173 + Rect: [149.3, 648.5, 161.6, 660.4]
  174 + Appearance state: /1
  175 + Appearance stream (/N): 44 0 R
  176 + Appearance stream (/N, /3): null
  177 + Annotation: 17 0 R
  178 + Field: 17 0 R
  179 + Subtype: /Widget
  180 + Rect: [152.7, 627.3, 165, 639.2]
  181 + Appearance state: /Off
  182 + Appearance stream (/N): 50 0 R
  183 + Appearance stream (/N, /3): null
  184 + Annotation: 18 0 R
  185 + Field: 18 0 R
  186 + Subtype: /Widget
  187 + Rect: [151.3, 601.7, 163.6, 613.6]
  188 + Appearance state: /Off
  189 + Appearance stream (/N): 54 0 R
  190 + Appearance stream (/N, /3): 52 0 R
  191 + Annotation: 6 0 R
  192 + Field: 6 0 R
  193 + Subtype: /Widget
  194 + Rect: [121.9, 559.1, 134.2, 571]
  195 + Appearance state: /Off
  196 + Appearance stream (/N): 19 0 R
  197 + Appearance stream (/N, /3): null
  198 + Annotation: 7 0 R
  199 + Field: 7 0 R
  200 + Subtype: /Widget
  201 + Rect: [118.6, 527.7, 130.9, 539.6]
  202 + Appearance state: /Yes
  203 + Appearance stream (/N): 26 0 R
  204 + Appearance stream (/N, /3): null
  205 + Annotation: 8 0 R
  206 + Field: 8 0 R
  207 + Subtype: /Widget
  208 + Rect: [118.6, 500.5, 130.9, 512.4]
  209 + Appearance state: /Off
  210 + Appearance stream (/N): 28 0 R
  211 + Appearance stream (/N, /3): null
  212 +Page: 40 0 R
  213 +Page: 35 0 R
  214 + Annotation: 32 0 R
  215 + Field: 32 0 R
  216 + Subtype: /Widget
  217 + Rect: [118.6, 555.7, 130.9, 567.6]
  218 + Appearance state: /Off
  219 + Appearance stream (/N): 58 0 R
  220 + Appearance stream (/N, /3): null
  221 + Annotation: 33 0 R
  222 + Field: 33 0 R
  223 + Subtype: /Widget
  224 + Rect: [119.3, 514.8, 131.6, 526.7]
  225 + Appearance state: /2
  226 + Appearance stream (/N): 60 0 R
  227 + Appearance stream (/N, /3): null
  228 + Annotation: 34 0 R
  229 + Field: 34 0 R
  230 + Subtype: /Widget
  231 + Rect: [121.3, 472.5, 133.6, 484.4]
  232 + Appearance state: /Off
  233 + Appearance stream (/N): 66 0 R
  234 + Appearance stream (/N, /3): 64 0 R
  235 + Annotation: 10 0 R
  236 + Field: 10 0 R
  237 + Subtype: /Widget
  238 + Rect: [113.6, 378.5, 351.3, 396.3]
  239 + Appearance stream (/N): 36 0 R
  240 + Appearance stream (/N, /3): null
  241 +test 43 done
qpdf/qtest/qpdf/form-form-errors.out 0 → 100644
  1 +iterating over form fields
  2 +WARNING: form-errors.pdf, object 4 0 at offset 625: loop detected while traversing /AcroForm
  3 +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
  4 +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
  5 +WARNING: form-errors.pdf, object 16 0 at offset 3475: this widget annotation is not reachable from /AcroForm in the document catalog
  6 +WARNING: form-errors.pdf, object 32 0 at offset 5974: this widget annotation is not reachable from /AcroForm in the document catalog
  7 +Field: 4 0 R
  8 + Parent: none
  9 + Fully qualified name: Text Box 1
  10 + Partial name: Text Box 1
  11 + Alternative name: Text Box 1
  12 + Mapping name: Text Box 1
  13 + Field type: /Tx
  14 + Value: <feff>
  15 + Value as string:
  16 + Default value: <feff>
  17 + Default value as string:
  18 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  19 + Quadding: 0
  20 + Annotation: 4 0 R
  21 +Field: 6 0 R
  22 + Parent: none
  23 + Fully qualified name: Check Box 1
  24 + Partial name: Check Box 1
  25 + Alternative name: Check Box 1
  26 + Mapping name: Check Box 1
  27 + Field type: /Btn
  28 + Value: /Off
  29 + Value as string:
  30 + Default value: /Off
  31 + Default value as string:
  32 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  33 + Quadding: 0
  34 + Annotation: 6 0 R
  35 +Field: 7 0 R
  36 + Parent: none
  37 + Fully qualified name: Check Box 2
  38 + Partial name: Check Box 2
  39 + Alternative name: Check Box 2
  40 + Mapping name: Check Box 2
  41 + Field type: /Btn
  42 + Value: /Yes
  43 + Value as string:
  44 + Default value: /Yes
  45 + Default value as string:
  46 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  47 + Quadding: 0
  48 + Annotation: 7 0 R
  49 +Field: 8 0 R
  50 + Parent: none
  51 + Fully qualified name: Check Box 3
  52 + Partial name: Check Box 3
  53 + Alternative name: Check Box 3
  54 + Mapping name: Check Box 3
  55 + Field type: /Btn
  56 + Value: /Off
  57 + Value as string:
  58 + Default value: /Off
  59 + Default value as string:
  60 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  61 + Quadding: 0
  62 + Annotation: 8 0 R
  63 +Field: 10 0 R
  64 + Parent: none
  65 + Fully qualified name: Text Box 2
  66 + Partial name: Text Box 2
  67 + Alternative name: Text Box 2
  68 + Mapping name: Text Box 2
  69 + Field type: /Tx
  70 + Value: <feff00730061006c00610064002003c002ac>
  71 + Value as string: salad πʬ
  72 + Default value: <feff00730061006c00610064002003c002ac>
  73 + Default value as string: salad πʬ
  74 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  75 + Quadding: 0
  76 + Annotation: 10 0 R
  77 +Field: 16 0 R
  78 + Parent: 5 0 R
  79 + Parent: none
  80 + Fully qualified name: r1
  81 + Partial name:
  82 + Alternative name: r1
  83 + Mapping name: r1
  84 + Field type: /Btn
  85 + Value: /1
  86 + Value as string:
  87 + Default value: /1
  88 + Default value as string:
  89 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  90 + Quadding: 0
  91 + Annotation: 16 0 R
  92 +Field: 17 0 R
  93 + Parent: 5 0 R
  94 + Parent: none
  95 + Fully qualified name: r1
  96 + Partial name:
  97 + Alternative name: r1
  98 + Mapping name: r1
  99 + Field type: /Btn
  100 + Value: /1
  101 + Value as string:
  102 + Default value: /1
  103 + Default value as string:
  104 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  105 + Quadding: 0
  106 + Annotation: 17 0 R
  107 +Field: 18 0 R
  108 + Parent: 5 0 R
  109 + Parent: none
  110 + Fully qualified name: r1
  111 + Partial name:
  112 + Alternative name: r1
  113 + Mapping name: r1
  114 + Field type: /Btn
  115 + Value: /1
  116 + Value as string:
  117 + Default value: /1
  118 + Default value as string:
  119 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  120 + Quadding: 0
  121 + Annotation: 18 0 R
  122 +Field: 32 0 R
  123 + Parent: 9 0 R
  124 + Parent: none
  125 + Fully qualified name: r2
  126 + Partial name:
  127 + Alternative name: r2
  128 + Mapping name: r2
  129 + Field type: /Btn
  130 + Value: /2
  131 + Value as string:
  132 + Default value: /2
  133 + Default value as string:
  134 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  135 + Quadding: 0
  136 + Annotation: 32 0 R
  137 +Field: 33 0 R
  138 + Parent: 9 0 R
  139 + Parent: none
  140 + Fully qualified name: r2
  141 + Partial name:
  142 + Alternative name: r2
  143 + Mapping name: r2
  144 + Field type: /Btn
  145 + Value: /2
  146 + Value as string:
  147 + Default value: /2
  148 + Default value as string:
  149 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  150 + Quadding: 0
  151 + Annotation: 33 0 R
  152 +Field: 34 0 R
  153 + Parent: 9 0 R
  154 + Parent: none
  155 + Fully qualified name: r2
  156 + Partial name:
  157 + Alternative name: r2
  158 + Mapping name: r2
  159 + Field type: /Btn
  160 + Value: /2
  161 + Value as string:
  162 + Default value: /2
  163 + Default value as string:
  164 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  165 + Quadding: 0
  166 + Annotation: 34 0 R
  167 +iterating over annotations per page
  168 +Page: 11 0 R
  169 + Annotation: 4 0 R
  170 + Field: 4 0 R
  171 + Subtype: /Widget
  172 + Rect: [123.4, 692.1, 260.9, 706.7]
  173 + Appearance stream (/N): 14 0 R
  174 + Appearance stream (/N, /3): null
  175 + Annotation: 16 0 R
  176 + Field: 16 0 R
  177 + Subtype: /Widget
  178 + Rect: [149.3, 648.5, 161.6, 660.4]
  179 + Appearance state: /1
  180 + Appearance stream (/N): 44 0 R
  181 + Appearance stream (/N, /3): null
  182 + Annotation: 17 0 R
  183 + Field: 17 0 R
  184 + Subtype: /Widget
  185 + Rect: [152.7, 627.3, 165, 639.2]
  186 + Appearance state: /Off
  187 + Appearance stream (/N): 50 0 R
  188 + Appearance stream (/N, /3): null
  189 + Annotation: 18 0 R
  190 + Field: 18 0 R
  191 + Subtype: /Widget
  192 + Rect: [151.3, 601.7, 163.6, 613.6]
  193 + Appearance state: /Off
  194 + Appearance stream (/N): 54 0 R
  195 + Appearance stream (/N, /3): 52 0 R
  196 + Annotation: 6 0 R
  197 + Field: 6 0 R
  198 + Subtype: /Widget
  199 + Rect: [121.9, 559.1, 134.2, 571]
  200 + Appearance state: /Off
  201 + Appearance stream (/N): 19 0 R
  202 + Appearance stream (/N, /3): null
  203 + Annotation: 7 0 R
  204 + Field: 7 0 R
  205 + Subtype: /Widget
  206 + Rect: [118.6, 527.7, 130.9, 539.6]
  207 + Appearance state: /Yes
  208 + Appearance stream (/N): 26 0 R
  209 + Appearance stream (/N, /3): null
  210 + Annotation: 8 0 R
  211 + Field: 8 0 R
  212 + Subtype: /Widget
  213 + Rect: [118.6, 500.5, 130.9, 512.4]
  214 + Appearance state: /Off
  215 + Appearance stream (/N): 28 0 R
  216 + Appearance stream (/N, /3): null
  217 +Page: 40 0 R
  218 +Page: 35 0 R
  219 + Annotation: 32 0 R
  220 + Field: 32 0 R
  221 + Subtype: /Widget
  222 + Rect: [118.6, 555.7, 130.9, 567.6]
  223 + Appearance state: /Off
  224 + Appearance stream (/N): 58 0 R
  225 + Appearance stream (/N, /3): null
  226 + Annotation: 33 0 R
  227 + Field: 33 0 R
  228 + Subtype: /Widget
  229 + Rect: [119.3, 514.8, 131.6, 526.7]
  230 + Appearance state: /2
  231 + Appearance stream (/N): 60 0 R
  232 + Appearance stream (/N, /3): null
  233 + Annotation: 34 0 R
  234 + Field: 34 0 R
  235 + Subtype: /Widget
  236 + Rect: [121.3, 472.5, 133.6, 484.4]
  237 + Appearance state: /Off
  238 + Appearance stream (/N): 66 0 R
  239 + Appearance stream (/N, /3): 64 0 R
  240 + Annotation: 10 0 R
  241 + Field: 10 0 R
  242 + Subtype: /Widget
  243 + Rect: [113.6, 378.5, 351.3, 396.3]
  244 + Appearance stream (/N): 36 0 R
  245 + Appearance stream (/N, /3): null
  246 +test 43 done
qpdf/qtest/qpdf/form-form-filled-with-atril.out 0 → 100644
  1 +iterating over form fields
  2 +Field: 4 0 R
  3 + Parent: none
  4 + Fully qualified name: Text Box 1
  5 + Partial name: Text Box 1
  6 + Alternative name: Text Box 1
  7 + Mapping name: Text Box 1
  8 + Field type: /Tx
  9 + Value: <feff004800610067006f006f00670061006d00610067006f006f0067006c0065>
  10 + Value as string: Hagoogamagoogle
  11 + Default value: <feff>
  12 + Default value as string:
  13 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  14 + Quadding: 0
  15 + Annotation: 4 0 R
  16 +Field: 6 0 R
  17 + Parent: none
  18 + Fully qualified name: Check Box 1
  19 + Partial name: Check Box 1
  20 + Alternative name: Check Box 1
  21 + Mapping name: Check Box 1
  22 + Field type: /Btn
  23 + Value: /Yes
  24 + Value as string:
  25 + Default value: /Off
  26 + Default value as string:
  27 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  28 + Quadding: 0
  29 + Annotation: 6 0 R
  30 +Field: 7 0 R
  31 + Parent: none
  32 + Fully qualified name: Check Box 2
  33 + Partial name: Check Box 2
  34 + Alternative name: Check Box 2
  35 + Mapping name: Check Box 2
  36 + Field type: /Btn
  37 + Value: /Yes
  38 + Value as string:
  39 + Default value: /Yes
  40 + Default value as string:
  41 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  42 + Quadding: 0
  43 + Annotation: 7 0 R
  44 +Field: 8 0 R
  45 + Parent: none
  46 + Fully qualified name: Check Box 3
  47 + Partial name: Check Box 3
  48 + Alternative name: Check Box 3
  49 + Mapping name: Check Box 3
  50 + Field type: /Btn
  51 + Value: /Off
  52 + Value as string:
  53 + Default value: /Off
  54 + Default value as string:
  55 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  56 + Quadding: 0
  57 + Annotation: 8 0 R
  58 +Field: 10 0 R
  59 + Parent: none
  60 + Fully qualified name: Text Box 2
  61 + Partial name: Text Box 2
  62 + Alternative name: Text Box 2
  63 + Mapping name: Text Box 2
  64 + Field type: /Tx
  65 + Value: <feff00730061006c00610064002003c002ac00200021>
  66 + Value as string: salad πʬ !
  67 + Default value: <feff00730061006c00610064002003c002ac>
  68 + Default value as string: salad πʬ
  69 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  70 + Quadding: 0
  71 + Annotation: 10 0 R
  72 +Field: 16 0 R
  73 + Parent: 5 0 R
  74 + Parent: none
  75 + Fully qualified name: r1
  76 + Partial name:
  77 + Alternative name: r1
  78 + Mapping name: r1
  79 + Field type: /Btn
  80 + Value: /3
  81 + Value as string:
  82 + Default value: /1
  83 + Default value as string:
  84 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  85 + Quadding: 0
  86 + Annotation: 16 0 R
  87 +Field: 17 0 R
  88 + Parent: 5 0 R
  89 + Parent: none
  90 + Fully qualified name: r1
  91 + Partial name:
  92 + Alternative name: r1
  93 + Mapping name: r1
  94 + Field type: /Btn
  95 + Value: /3
  96 + Value as string:
  97 + Default value: /1
  98 + Default value as string:
  99 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  100 + Quadding: 0
  101 + Annotation: 17 0 R
  102 +Field: 18 0 R
  103 + Parent: 5 0 R
  104 + Parent: none
  105 + Fully qualified name: r1
  106 + Partial name:
  107 + Alternative name: r1
  108 + Mapping name: r1
  109 + Field type: /Btn
  110 + Value: /3
  111 + Value as string:
  112 + Default value: /1
  113 + Default value as string:
  114 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  115 + Quadding: 0
  116 + Annotation: 18 0 R
  117 +Field: 32 0 R
  118 + Parent: 9 0 R
  119 + Parent: none
  120 + Fully qualified name: r2
  121 + Partial name:
  122 + Alternative name: r2
  123 + Mapping name: r2
  124 + Field type: /Btn
  125 + Value: /2
  126 + Value as string:
  127 + Default value: /2
  128 + Default value as string:
  129 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  130 + Quadding: 0
  131 + Annotation: 32 0 R
  132 +Field: 33 0 R
  133 + Parent: 9 0 R
  134 + Parent: none
  135 + Fully qualified name: r2
  136 + Partial name:
  137 + Alternative name: r2
  138 + Mapping name: r2
  139 + Field type: /Btn
  140 + Value: /2
  141 + Value as string:
  142 + Default value: /2
  143 + Default value as string:
  144 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  145 + Quadding: 0
  146 + Annotation: 33 0 R
  147 +Field: 34 0 R
  148 + Parent: 9 0 R
  149 + Parent: none
  150 + Fully qualified name: r2
  151 + Partial name:
  152 + Alternative name: r2
  153 + Mapping name: r2
  154 + Field type: /Btn
  155 + Value: /2
  156 + Value as string:
  157 + Default value: /2
  158 + Default value as string:
  159 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  160 + Quadding: 0
  161 + Annotation: 34 0 R
  162 +iterating over annotations per page
  163 +Page: 11 0 R
  164 + Annotation: 4 0 R
  165 + Field: 4 0 R
  166 + Subtype: /Widget
  167 + Rect: [123.4, 692.1, 260.9, 706.7]
  168 + Appearance stream (/N): null
  169 + Appearance stream (/N, /3): null
  170 + Annotation: 16 0 R
  171 + Field: 16 0 R
  172 + Subtype: /Widget
  173 + Rect: [149.3, 648.5, 161.6, 660.4]
  174 + Appearance state: /Off
  175 + Appearance stream (/N): 46 0 R
  176 + Appearance stream (/N, /3): null
  177 + Annotation: 17 0 R
  178 + Field: 17 0 R
  179 + Subtype: /Widget
  180 + Rect: [152.7, 627.3, 165, 639.2]
  181 + Appearance state: /Off
  182 + Appearance stream (/N): 50 0 R
  183 + Appearance stream (/N, /3): null
  184 + Annotation: 18 0 R
  185 + Field: 18 0 R
  186 + Subtype: /Widget
  187 + Rect: [151.3, 601.7, 163.6, 613.6]
  188 + Appearance state: /3
  189 + Appearance stream (/N): 52 0 R
  190 + Appearance stream (/N, /3): 52 0 R
  191 + Annotation: 6 0 R
  192 + Field: 6 0 R
  193 + Subtype: /Widget
  194 + Rect: [121.9, 559.1, 134.2, 571]
  195 + Appearance state: /Yes
  196 + Appearance stream (/N): 21 0 R
  197 + Appearance stream (/N, /3): null
  198 + Annotation: 7 0 R
  199 + Field: 7 0 R
  200 + Subtype: /Widget
  201 + Rect: [118.6, 527.7, 130.9, 539.6]
  202 + Appearance state: /Yes
  203 + Appearance stream (/N): 26 0 R
  204 + Appearance stream (/N, /3): null
  205 + Annotation: 8 0 R
  206 + Field: 8 0 R
  207 + Subtype: /Widget
  208 + Rect: [118.6, 500.5, 130.9, 512.4]
  209 + Appearance state: /Off
  210 + Appearance stream (/N): 28 0 R
  211 + Appearance stream (/N, /3): null
  212 +Page: 40 0 R
  213 +Page: 35 0 R
  214 + Annotation: 32 0 R
  215 + Field: 32 0 R
  216 + Subtype: /Widget
  217 + Rect: [118.6, 555.7, 130.9, 567.6]
  218 + Appearance state: /Off
  219 + Appearance stream (/N): 58 0 R
  220 + Appearance stream (/N, /3): null
  221 + Annotation: 33 0 R
  222 + Field: 33 0 R
  223 + Subtype: /Widget
  224 + Rect: [119.3, 514.8, 131.6, 526.7]
  225 + Appearance state: /2
  226 + Appearance stream (/N): 60 0 R
  227 + Appearance stream (/N, /3): null
  228 + Annotation: 34 0 R
  229 + Field: 34 0 R
  230 + Subtype: /Widget
  231 + Rect: [121.3, 472.5, 133.6, 484.4]
  232 + Appearance state: /Off
  233 + Appearance stream (/N): 66 0 R
  234 + Appearance stream (/N, /3): 64 0 R
  235 + Annotation: 10 0 R
  236 + Field: 10 0 R
  237 + Subtype: /Widget
  238 + Rect: [113.6, 378.5, 351.3, 396.3]
  239 + Appearance stream (/N): null
  240 + Appearance stream (/N, /3): null
  241 +test 43 done
qpdf/qtest/qpdf/form-form-mod1.out 0 → 100644
  1 +iterating over form fields
  2 +Field: 4 0 R
  3 + Parent: none
  4 + Fully qualified name: Text Box 1
  5 + Partial name: Text Box 1
  6 + Alternative name: Text Box 1
  7 + Mapping name: Text Box 1
  8 + Field type: /Tx
  9 + Value: <feff>
  10 + Value as string:
  11 + Default value: <feff>
  12 + Default value as string:
  13 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  14 + Quadding: 0
  15 + Annotation: 83 0 R
  16 +Field: 6 0 R
  17 + Parent: none
  18 + Fully qualified name: Check Box 1
  19 + Partial name: Check Box 1
  20 + Alternative name: Check Box 1
  21 + Mapping name: Check Box 1
  22 + Field type: /Btn
  23 + Value: /Off
  24 + Value as string:
  25 + Default value: /Off
  26 + Default value as string:
  27 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  28 + Quadding: 0
  29 + Annotation: 6 0 R
  30 +Field: 7 0 R
  31 + Parent: none
  32 + Fully qualified name: Check Box 2
  33 + Partial name: Check Box 2
  34 + Alternative name: Check Box 2
  35 + Mapping name: Check Box 2
  36 + Field type: /Btn
  37 + Value: /Yes
  38 + Value as string:
  39 + Default value: /Yes
  40 + Default value as string:
  41 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  42 + Quadding: 0
  43 + Annotation: 7 0 R
  44 +Field: 8 0 R
  45 + Parent: none
  46 + Fully qualified name: Check Box 3
  47 + Partial name: Check Box 3
  48 + Alternative name: Check Box 3
  49 + Mapping name: Check Box 3
  50 + Field type: /Btn
  51 + Value: /Off
  52 + Value as string:
  53 + Default value: /Off
  54 + Default value as string:
  55 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  56 + Quadding: 0
  57 + Annotation: 8 0 R
  58 +Field: 10 0 R
  59 + Parent: none
  60 + Fully qualified name: Text Box 2
  61 + Partial name: Text Box 2
  62 + Alternative name: Text Box 2
  63 + Mapping name: Text Box 2
  64 + Field type: /Tx
  65 + Value: <feff00730061006c00610064002003c002ac>
  66 + Value as string: salad πʬ
  67 + Default value: <feff00730061006c00610064002003c002ac>
  68 + Default value as string: salad πʬ
  69 + Default appearance: 0.18039 0.20392 0.21176 rg /F2 12 Tf
  70 + Quadding: 1
  71 + Annotation: 10 0 R
  72 +Field: 16 0 R
  73 + Parent: 5 0 R
  74 + Parent: none
  75 + Fully qualified name: r1.choice1
  76 + Partial name: choice1
  77 + Alternative name: chice 1
  78 + Mapping name: choice 1 TM
  79 + Field type: /Btn
  80 + Value: /1
  81 + Value as string:
  82 + Default value: /1
  83 + Default value as string:
  84 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  85 + Quadding: 0
  86 + Annotation: 16 0 R
  87 +Field: 17 0 R
  88 + Parent: 5 0 R
  89 + Parent: none
  90 + Fully qualified name: r1
  91 + Partial name:
  92 + Alternative name: r1
  93 + Mapping name: r1
  94 + Field type: /Btn
  95 + Value: /1
  96 + Value as string:
  97 + Default value: /1
  98 + Default value as string:
  99 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  100 + Quadding: 0
  101 + Annotation: 17 0 R
  102 +Field: 18 0 R
  103 + Parent: 5 0 R
  104 + Parent: none
  105 + Fully qualified name: r1
  106 + Partial name:
  107 + Alternative name: r1
  108 + Mapping name: r1
  109 + Field type: /Btn
  110 + Value: /1
  111 + Value as string:
  112 + Default value: /1
  113 + Default value as string:
  114 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  115 + Quadding: 0
  116 + Annotation: 18 0 R
  117 +Field: 32 0 R
  118 + Parent: 9 0 R
  119 + Parent: none
  120 + Fully qualified name: r2
  121 + Partial name:
  122 + Alternative name: r2
  123 + Mapping name: r2
  124 + Field type: /Btn
  125 + Value: /2
  126 + Value as string:
  127 + Default value: /2
  128 + Default value as string:
  129 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  130 + Quadding: 0
  131 + Annotation: 32 0 R
  132 +Field: 33 0 R
  133 + Parent: 9 0 R
  134 + Parent: none
  135 + Fully qualified name: r2
  136 + Partial name:
  137 + Alternative name: r2
  138 + Mapping name: r2
  139 + Field type: /Btn
  140 + Value: /2
  141 + Value as string:
  142 + Default value: /2
  143 + Default value as string:
  144 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  145 + Quadding: 0
  146 + Annotation: 33 0 R
  147 +Field: 34 0 R
  148 + Parent: 9 0 R
  149 + Parent: none
  150 + Fully qualified name: r2
  151 + Partial name:
  152 + Alternative name: r2
  153 + Mapping name: r2
  154 + Field type: /Btn
  155 + Value: /2
  156 + Value as string:
  157 + Default value: /2
  158 + Default value as string:
  159 + Default appearance: 0.18039 0.20392 0.21176 rg /ZaDi 0 Tf
  160 + Quadding: 0
  161 + Annotation: 34 0 R
  162 +iterating over annotations per page
  163 +Page: 11 0 R
  164 + Annotation: 83 0 R
  165 + Field: 4 0 R
  166 + Subtype: /Widget
  167 + Rect: [123.4, 692.1, 260.9, 706.7]
  168 + Appearance stream (/N): 14 0 R
  169 + Appearance stream (/N, /3): null
  170 + Annotation: 16 0 R
  171 + Field: 16 0 R
  172 + Subtype: /Widget
  173 + Rect: [149.3, 648.5, 161.6, 660.4]
  174 + Appearance state: /1
  175 + Appearance stream (/N): 44 0 R
  176 + Appearance stream (/N, /3): null
  177 + Annotation: 17 0 R
  178 + Field: 17 0 R
  179 + Subtype: /Widget
  180 + Rect: [152.7, 627.3, 165, 639.2]
  181 + Appearance state: /Off
  182 + Appearance stream (/N): 50 0 R
  183 + Appearance stream (/N, /3): null
  184 + Annotation: 18 0 R
  185 + Field: 18 0 R
  186 + Subtype: /Widget
  187 + Rect: [151.3, 601.7, 163.6, 613.6]
  188 + Appearance state: /Off
  189 + Appearance stream (/N): 54 0 R
  190 + Appearance stream (/N, /3): 52 0 R
  191 + Annotation: 6 0 R
  192 + Field: 6 0 R
  193 + Subtype: /Widget
  194 + Rect: [121.9, 559.1, 134.2, 571]
  195 + Appearance state: /Off
  196 + Appearance stream (/N): 19 0 R
  197 + Appearance stream (/N, /3): null
  198 + Annotation: 7 0 R
  199 + Field: 7 0 R
  200 + Subtype: /Widget
  201 + Rect: [118.6, 527.7, 130.9, 539.6]
  202 + Appearance state: /Yes
  203 + Appearance stream (/N): 26 0 R
  204 + Appearance stream (/N, /3): null
  205 + Annotation: 8 0 R
  206 + Field: 8 0 R
  207 + Subtype: /Widget
  208 + Rect: [118.6, 500.5, 130.9, 512.4]
  209 + Appearance state: /Off
  210 + Appearance stream (/N): 28 0 R
  211 + Appearance stream (/N, /3): null
  212 +Page: 40 0 R
  213 +Page: 35 0 R
  214 + Annotation: 32 0 R
  215 + Field: 32 0 R
  216 + Subtype: /Widget
  217 + Rect: [118.6, 555.7, 130.9, 567.6]
  218 + Appearance state: /Off
  219 + Appearance stream (/N): 58 0 R
  220 + Appearance stream (/N, /3): null
  221 + Annotation: 33 0 R
  222 + Field: 33 0 R
  223 + Subtype: /Widget
  224 + Rect: [119.3, 514.8, 131.6, 526.7]
  225 + Appearance state: /2
  226 + Appearance stream (/N): 60 0 R
  227 + Appearance stream (/N, /3): null
  228 + Annotation: 34 0 R
  229 + Field: 34 0 R
  230 + Subtype: /Widget
  231 + Rect: [121.3, 472.5, 133.6, 484.4]
  232 + Appearance state: /Off
  233 + Appearance stream (/N): 66 0 R
  234 + Appearance stream (/N, /3): 64 0 R
  235 + Annotation: 10 0 R
  236 + Field: 10 0 R
  237 + Subtype: /Widget
  238 + Rect: [113.6, 378.5, 351.3, 396.3]
  239 + Appearance stream (/N): 36 0 R
  240 + Appearance stream (/N, /3): null
  241 +test 43 done
qpdf/qtest/qpdf/form-minimal.out 0 → 100644
  1 +no forms
qpdf/qtest/qpdf/form-mod1.pdf 0 → 100644
No preview for this file type
qpdf/qtest/storage/README 0 → 100644
  1 +This directory contains files that I want to keep around because I
  2 +hand-created them or otherwise went to some trouble and used them in
  3 +some fashion in the test suite. Any file in this directory should be
  4 +referenced in a comment in the test code.
qpdf/qtest/storage/form.odt 0 → 100644
No preview for this file type
qpdf/qtest/storage/form.pdf 0 → 100644
No preview for this file type
qpdf/test_driver.cc
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 5
6 #include <qpdf/QPDFPageDocumentHelper.hh> 6 #include <qpdf/QPDFPageDocumentHelper.hh>
7 #include <qpdf/QPDFPageObjectHelper.hh> 7 #include <qpdf/QPDFPageObjectHelper.hh>
  8 +#include <qpdf/QPDFAcroFormDocumentHelper.hh>
8 #include <qpdf/QUtil.hh> 9 #include <qpdf/QUtil.hh>
9 #include <qpdf/QTC.hh> 10 #include <qpdf/QTC.hh>
10 #include <qpdf/Pl_StdioFile.hh> 11 #include <qpdf/Pl_StdioFile.hh>
@@ -153,6 +154,13 @@ static QPDFObjectHandle createPageContents(QPDF&amp; pdf, std::string const&amp; text) @@ -153,6 +154,13 @@ static QPDFObjectHandle createPageContents(QPDF&amp; pdf, std::string const&amp; text)
153 return QPDFObjectHandle::newStream(&pdf, contents); 154 return QPDFObjectHandle::newStream(&pdf, contents);
154 } 155 }
155 156
  157 +static void print_rect(std::ostream& out,
  158 + QPDFObjectHandle::Rectangle const& r)
  159 +{
  160 + out << "[" << r.llx << ", " << r.lly << ", "
  161 + << r.urx << ", " << r.ury << "]";
  162 +}
  163 +
156 void runtest(int n, char const* filename1, char const* arg2) 164 void runtest(int n, char const* filename1, char const* arg2)
157 { 165 {
158 // Most tests here are crafted to work on specific files. Look at 166 // 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) @@ -1474,6 +1482,108 @@ void runtest(int n, char const* filename1, char const* arg2)
1474 (r1.urx > 5.59) && (r1.urx < 5.61) && 1482 (r1.urx > 5.59) && (r1.urx < 5.61) &&
1475 (r1.ury > 7.79) && (r1.ury < 7.81)); 1483 (r1.ury > 7.79) && (r1.ury < 7.81));
1476 } 1484 }
  1485 + else if (n == 43)
  1486 + {
  1487 + // Forms
  1488 + QPDFAcroFormDocumentHelper afdh(pdf);
  1489 + if (! afdh.hasAcroForm())
  1490 + {
  1491 + std::cout << "no forms\n";
  1492 + return;
  1493 + }
  1494 + std::cout << "iterating over form fields\n";
  1495 + std::vector<QPDFFormFieldObjectHelper> form_fields =
  1496 + afdh.getFormFields();
  1497 + for (std::vector<QPDFFormFieldObjectHelper>::iterator iter =
  1498 + form_fields.begin();
  1499 + iter != form_fields.end(); ++iter)
  1500 + {
  1501 + QPDFFormFieldObjectHelper ffh(*iter);
  1502 + std::cout << "Field: " << ffh.getObjectHandle().unparse()
  1503 + << std::endl;
  1504 + QPDFFormFieldObjectHelper node = ffh;
  1505 + while (! node.isNull())
  1506 + {
  1507 + QPDFFormFieldObjectHelper parent(node.getParent());
  1508 + std::cout << " Parent: "
  1509 + << (parent.isNull()
  1510 + ? "none"
  1511 + : parent.getObjectHandle().unparse())
  1512 + << std::endl;
  1513 + node = parent;
  1514 + }
  1515 + std::cout << " Fully qualified name: "
  1516 + << ffh.getFullyQualifiedName() << std::endl;
  1517 + std::cout << " Partial name: "
  1518 + << ffh.getPartialName() << std::endl;
  1519 + std::cout << " Alternative name: "
  1520 + << ffh.getAlternativeName() << std::endl;
  1521 + std::cout << " Mapping name: "
  1522 + << ffh.getMappingName() << std::endl;
  1523 + std::cout << " Field type: "
  1524 + << ffh.getFieldType() << std::endl;
  1525 + std::cout << " Value: "
  1526 + << ffh.getValue().unparse() << std::endl;
  1527 + std::cout << " Value as string: "
  1528 + << ffh.getValueAsString() << std::endl;
  1529 + std::cout << " Default value: "
  1530 + << ffh.getDefaultValue().unparse() << std::endl;
  1531 + std::cout << " Default value as string: "
  1532 + << ffh.getDefaultValueAsString() << std::endl;
  1533 + std::cout << " Default appearance: "
  1534 + << ffh.getDefaultAppearance() << std::endl;
  1535 + std::cout << " Quadding: "
  1536 + << ffh.getQuadding() << std::endl;
  1537 + std::vector<QPDFAnnotationObjectHelper> annotations =
  1538 + afdh.getAnnotationsForField(ffh);
  1539 + for (std::vector<QPDFAnnotationObjectHelper>::iterator i2 =
  1540 + annotations.begin();
  1541 + i2 != annotations.end(); ++i2)
  1542 + {
  1543 + std::cout << " Annotation: "
  1544 + << (*i2).getObjectHandle().unparse() << std::endl;
  1545 + }
  1546 + }
  1547 + std::cout << "iterating over annotations per page\n";
  1548 + std::vector<QPDFPageObjectHelper> pages =
  1549 + QPDFPageDocumentHelper(pdf).getAllPages();
  1550 + for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin();
  1551 + iter != pages.end(); ++iter)
  1552 + {
  1553 + std::cout << "Page: " << (*iter).getObjectHandle().unparse()
  1554 + << std::endl;
  1555 + std::vector<QPDFAnnotationObjectHelper> annotations =
  1556 + afdh.getWidgetAnnotationsForPage(*iter);
  1557 + for (std::vector<QPDFAnnotationObjectHelper>::iterator i2 =
  1558 + annotations.begin();
  1559 + i2 != annotations.end(); ++i2)
  1560 + {
  1561 + QPDFAnnotationObjectHelper ah(*i2);
  1562 + std::cout << " Annotation: " << ah.getObjectHandle().unparse()
  1563 + << std::endl;
  1564 + std::cout << " Field: "
  1565 + << (afdh.getFieldForAnnotation(ah).
  1566 + getObjectHandle().unparse())
  1567 + << std::endl;
  1568 + std::cout << " Subtype: " << ah.getSubtype() << std::endl;
  1569 + std::cout << " Rect: ";
  1570 + print_rect(std::cout, ah.getRect());
  1571 + std::cout << std::endl;
  1572 + std::string state = ah.getAppearanceState();
  1573 + if (! state.empty())
  1574 + {
  1575 + std::cout << " Appearance state: " << state
  1576 + << std::endl;
  1577 + }
  1578 + std::cout << " Appearance stream (/N): "
  1579 + << ah.getAppearanceStream("/N").unparse()
  1580 + << std::endl;
  1581 + std::cout << " Appearance stream (/N, /3): "
  1582 + << ah.getAppearanceStream("/N", "/3").unparse()
  1583 + << std::endl;
  1584 + }
  1585 + }
  1586 + }
1477 else 1587 else
1478 { 1588 {
1479 throw std::runtime_error(std::string("invalid test ") + 1589 throw std::runtime_error(std::string("invalid test ") +