From 0b05111db80469d3f556209bfd856af1fda9b142 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Tue, 19 Jun 2018 09:26:41 -0400 Subject: Implement helper class for interactive forms --- include/qpdf/QPDFAcroFormDocumentHelper.hh | 165 +++++++++++++++++++++++++++++ include/qpdf/QPDFAnnotationObjectHelper.hh | 86 +++++++++++++++ include/qpdf/QPDFFormFieldObjectHelper.hh | 133 +++++++++++++++++++++++ include/qpdf/QPDFPageObjectHelper.hh | 8 ++ 4 files changed, 392 insertions(+) create mode 100644 include/qpdf/QPDFAcroFormDocumentHelper.hh create mode 100644 include/qpdf/QPDFAnnotationObjectHelper.hh create mode 100644 include/qpdf/QPDFFormFieldObjectHelper.hh (limited to 'include') diff --git a/include/qpdf/QPDFAcroFormDocumentHelper.hh b/include/qpdf/QPDFAcroFormDocumentHelper.hh new file mode 100644 index 00000000..d786fff4 --- /dev/null +++ b/include/qpdf/QPDFAcroFormDocumentHelper.hh @@ -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 + +#include + +#include +#include +#include + +#include +#include +#include + +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 + 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 + getAnnotationsForField(QPDFFormFieldObjectHelper); + + // Return annotations of subtype /Widget for a page. + QPDF_DLL + std::vector + 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& visited); + + class Members + { + friend class QPDFAcroFormDocumentHelper; + + public: + ~Members(); + + private: + Members(); + Members(Members const&); + + bool cache_valid; + std::map + > field_to_annotations; + std::map annotation_to_field; + }; + + PointerHolder m; +}; + +#endif // __QPDFACROFORMDOCUMENTHELPER_HH__ diff --git a/include/qpdf/QPDFAnnotationObjectHelper.hh b/include/qpdf/QPDFAnnotationObjectHelper.hh new file mode 100644 index 00000000..d64388da --- /dev/null +++ b/include/qpdf/QPDFAnnotationObjectHelper.hh @@ -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 + +#include + +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 m; +}; + +#endif // __QPDFANNOTATIONOBJECTHELPER_HH__ diff --git a/include/qpdf/QPDFFormFieldObjectHelper.hh b/include/qpdf/QPDFFormFieldObjectHelper.hh new file mode 100644 index 00000000..b45955a3 --- /dev/null +++ b/include/qpdf/QPDFFormFieldObjectHelper.hh @@ -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 + +#include + +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 m; +}; + +#endif // __QPDFFORMFIELDOBJECTHELPER_HH__ diff --git a/include/qpdf/QPDFPageObjectHelper.hh b/include/qpdf/QPDFPageObjectHelper.hh index 2d307f34..9226a748 100644 --- a/include/qpdf/QPDFPageObjectHelper.hh +++ b/include/qpdf/QPDFPageObjectHelper.hh @@ -23,6 +23,7 @@ #define __QPDFPAGEOBJECTHELPER_HH__ #include +#include #include @@ -43,6 +44,13 @@ class QPDFPageObjectHelper: public QPDFObjectHelper QPDF_DLL std::map 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 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 -- cgit v1.2.3-54-g00ecf