aboutsummaryrefslogtreecommitdiffstats
path: root/include/qpdf/QPDFAcroFormDocumentHelper.hh
blob: 8f4f2039805ac4b9bf6b77a065a8eeafcfa681de (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright (c) 2005-2021 Jay Berkenbilt
//
// This file is part of qpdf.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Versions of qpdf prior to version 7 were released under the terms
// of version 2.0 of the Artistic License. At your option, you may
// continue to consider qpdf to be licensed under those terms. Please
// see the manual for additional information.

#ifndef QPDFACROFORMDOCUMENTHELPER_HH
#define QPDFACROFORMDOCUMENTHELPER_HH

// This document helper is intended to help with operations on
// interactive forms. Here are the key things to know:

// * The PDF specification talks about interactive forms and also
//   about form XObjects. While form XObjects appear in parts of
//   interactive forms, this class is concerned about interactive
//   forms, not form XObjects.
//
// * Interactive forms are discussed in the PDF Specification (ISO PDF
//   32000-1:2008) section 12.7. Also relevant is the section about
//   Widget annotations. Annotations are discussed in section 12.5
//   with annotation dictionaries discussed in 12.5.1. Widget
//   annotations are discussed specifically in section 12.5.6.19.
//
// * What you need to know about the structure of interactive forms in
//   PDF files:
//
//   - The document catalog contains the key "/AcroForm" which
//     contains a list of fields. Fields are represented as a tree
//     structure much like pages. Nodes in the fields tree may contain
//     other fields. Fields may inherit values of many of their
//     attributes from ancestors in the tree.
//
//   - Fields may also have children that are widget annotations. As a
//     special case, and a cause of considerable confusion, if a field
//     has a single annotation as a child, the annotation dictionary
//     may be merged with the field dictionary. In that case, the
//     field and the annotation are in the same object. Note that,
//     while field dictionary attributes are inherited, annotation
//     dictionary attributes are not.
//
//   - A page dictionary contains a key called "/Annots" which
//     contains a simple list of annotations. For any given annotation
//     of subtype "/Widget", you should encounter that annotation in
//     the "/Annots" dictionary of a page, and you should also be able
//     to reach it by traversing through the "/AcroForm" dictionary
//     from the document catalog. In the simplest case (and also a
//     very common case), a form field's widget annotation will be
//     merged with the field object, and the object will appear
//     directly both under "/Annots" in the page dictionary and under
//     "/Fields" in the "/AcroForm" dictionary. In a more complex
//     case, you may have to trace through various "/Kids" elements in
//     the "/AcroForm" field entry until you find the annotation
//     dictionary.


#include <qpdf/QPDFDocumentHelper.hh>

#include <qpdf/DLL.h>

#include <qpdf/QPDFAnnotationObjectHelper.hh>
#include <qpdf/QPDFFormFieldObjectHelper.hh>
#include <qpdf/QPDFPageObjectHelper.hh>

#include <map>
#include <set>
#include <vector>

class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
{
  public:
    QPDF_DLL
    QPDFAcroFormDocumentHelper(QPDF&);
    QPDF_DLL
    virtual ~QPDFAcroFormDocumentHelper()
    {
    }

    // This class lazily creates an internal cache of the mapping
    // among form fields, annotations, and pages. Methods within this
    // class preserve the validity of this cache. However, if you
    // modify pages' annotation dictionaries, the document's /AcroForm
    // dictionary, or any form fields manually in a way that alters
    // the association between forms, fields, annotations, and pages,
    // it may cause this cache to become invalid. This method marks
    // the cache invalid and forces it to be regenerated the next time
    // it is needed.
    QPDF_DLL
    void invalidateCache();

    QPDF_DLL
    bool
    hasAcroForm();

    // Return a vector of all terminal fields in a document. Terminal
    // fields are fields that have no children that are also fields.
    // Terminal fields may still have children that are annotations.
    // Intermediate nodes in the fields tree are not included in this
    // list, but you can still reach them through the getParent method
    // of the field object helper.
    QPDF_DLL
    std::vector<QPDFFormFieldObjectHelper>
    getFormFields();

    // Return the annotations associated with a terminal field. Note
    // that in the case of a field having a single annotation, the
    // underlying object will typically be the same as the underlying
    // object for the field.
    QPDF_DLL
    std::vector<QPDFAnnotationObjectHelper>
    getAnnotationsForField(QPDFFormFieldObjectHelper);

    // Return annotations of subtype /Widget for a page.
    QPDF_DLL
    std::vector<QPDFAnnotationObjectHelper>
    getWidgetAnnotationsForPage(QPDFPageObjectHelper);

    // Return the terminal field that is associated with this
    // annotation. If the annotation dictionary is merged with the
    // field dictionary, the underlying object will be the same, but
    // this is not always the case. Note that if you call this method
    // with an annotation that is not a widget annotation, there will
    // not be an associated field, and this method will raise an
    // exception.
    QPDF_DLL
    QPDFFormFieldObjectHelper
    getFieldForAnnotation(QPDFAnnotationObjectHelper);

    // Return the current value of /NeedAppearances. If
    // /NeedAppearances is missing, return false as that is how PDF
    // viewers are supposed to interpret it.
    QPDF_DLL
    bool getNeedAppearances();

    // Indicate whether appearance streams must be regenerated. If you
    // modify a field value, you should call setNeedAppearances(true)
    // unless you also generate an appearance stream for the
    // corresponding annotation at the same time. If you generate
    // appearance streams for all fields, you can call
    // setNeedAppearances(false). If you use
    // QPDFFormFieldObjectHelper::setV, it will automatically call
    // this method unless you tell it not to.
    QPDF_DLL
    void setNeedAppearances(bool);

    // If /NeedAppearances is false, do nothing. Otherwise generate
    // appearance streams for all widget annotations that need them.
    // See comments in QPDFFormFieldObjectHelper.hh for
    // generateAppearance for limitations. For checkbox and radio
    // button fields, this code ensures that appearance state is
    // consistent with the field's value and uses any pre-existing
    // appearance streams.
    QPDF_DLL
    void generateAppearancesIfNeeded();

  private:
    void analyze();
    void traverseField(QPDFObjectHandle field,
                       QPDFObjectHandle parent,
                       int depth, std::set<QPDFObjGen>& visited);

    class Members
    {
        friend class QPDFAcroFormDocumentHelper;

      public:
        QPDF_DLL
        ~Members();

      private:
        Members();
        Members(Members const&);

        bool cache_valid;
        std::map<QPDFObjGen,
                 std::vector<QPDFAnnotationObjectHelper>
                 > field_to_annotations;
        std::map<QPDFObjGen, QPDFFormFieldObjectHelper> annotation_to_field;
    };

    PointerHolder<Members> m;
};

#endif // QPDFACROFORMDOCUMENTHELPER_HH