Coverage Report

Created: 2023-09-25 06:35

/src/xpdf-4.04/xpdf/AcroForm.cc
Line
Count
Source (jump to first uncovered line)
1
//========================================================================
2
//
3
// AcroForm.cc
4
//
5
// Copyright 2012 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#ifdef USE_GCC_PRAGMAS
12
#pragma implementation
13
#endif
14
15
#include <stdlib.h>
16
#include <math.h>
17
#include "gmem.h"
18
#include "gmempp.h"
19
#include "GString.h"
20
#include "GList.h"
21
#include "GlobalParams.h"
22
#include "Error.h"
23
#include "Object.h"
24
#include "PDFDoc.h"
25
#include "TextString.h"
26
#include "Gfx.h"
27
#include "GfxFont.h"
28
#include "OptionalContent.h"
29
#include "Annot.h"
30
#include "Lexer.h"
31
#include "XFAScanner.h"
32
#include "UTF8.h"
33
#include "PDF417Barcode.h"
34
#include "AcroForm.h"
35
36
//------------------------------------------------------------------------
37
38
#define acroFormFlagReadOnly           (1 <<  0)  // all
39
#define acroFormFlagRequired           (1 <<  1)  // all
40
#define acroFormFlagNoExport           (1 <<  2)  // all
41
372
#define acroFormFlagMultiline          (1 << 12)  // text
42
#define acroFormFlagPassword           (1 << 13)  // text
43
#define acroFormFlagNoToggleToOff      (1 << 14)  // button
44
134
#define acroFormFlagRadio              (1 << 15)  // button
45
117
#define acroFormFlagPushbutton         (1 << 16)  // button
46
10
#define acroFormFlagCombo              (1 << 17)  // choice
47
#define acroFormFlagEdit               (1 << 18)  // choice
48
#define acroFormFlagSort               (1 << 19)  // choice
49
946
#define acroFormFlagFileSelect         (1 << 20)  // text
50
#define acroFormFlagMultiSelect        (1 << 21)  // choice
51
#define acroFormFlagDoNotSpellCheck    (1 << 22)  // text, choice
52
#define acroFormFlagDoNotScroll        (1 << 23)  // text
53
0
#define acroFormFlagComb               (1 << 24)  // text
54
#define acroFormFlagRadiosInUnison     (1 << 25)  // button
55
#define acroFormFlagRichText           (1 << 25)  // text
56
#define acroFormFlagCommitOnSelChange  (1 << 26)  // choice
57
58
0
#define acroFormQuadLeft   0
59
0
#define acroFormQuadCenter 1
60
0
#define acroFormQuadRight  2
61
62
0
#define acroFormVAlignTop               0
63
0
#define acroFormVAlignMiddle            1
64
0
#define acroFormVAlignMiddleNoDescender 2
65
0
#define acroFormVAlignBottom            3
66
67
0
#define annotFlagHidden    0x0002
68
0
#define annotFlagPrint     0x0004
69
0
#define annotFlagNoView    0x0020
70
71
// distance of Bezier control point from center for circle approximation
72
// = (4 * (sqrt(2) - 1) / 3) * r
73
0
#define bezierCircle 0.55228475
74
75
// limit recursive field-parent lookups to avoid infinite loops
76
6.17k
#define maxFieldObjectDepth 50
77
78
//------------------------------------------------------------------------
79
80
// 5 bars + 5 spaces -- each can be wide (1) or narrow (0)
81
// (there are always exactly 3 wide elements;
82
// the last space is always narrow)
83
static Guchar code3Of9Data[128][10] = {
84
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00
85
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
86
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
87
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
88
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
89
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
90
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
91
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
92
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
93
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
94
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
95
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
96
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
97
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
98
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
99
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
100
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x10
101
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
102
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
103
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
104
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
105
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
106
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
107
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
108
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
109
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
110
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
111
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
112
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
113
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
114
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
115
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
116
  { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20
117
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
118
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
119
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
120
  { 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24
121
  { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25
122
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
123
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
124
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
125
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
126
  { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a
127
  { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b
128
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
129
  { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d
130
  { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e
131
  { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f
132
  { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30
133
  { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1'
134
  { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2'
135
  { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3'
136
  { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4'
137
  { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5'
138
  { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6'
139
  { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7'
140
  { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8'
141
  { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9'
142
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
143
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
144
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
145
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
146
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
147
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
148
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x40
149
  { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41
150
  { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B'
151
  { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C'
152
  { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D'
153
  { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E'
154
  { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F'
155
  { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G'
156
  { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H'
157
  { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I'
158
  { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J'
159
  { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K'
160
  { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L'
161
  { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M'
162
  { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N'
163
  { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O'
164
  { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50
165
  { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q'
166
  { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R'
167
  { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S'
168
  { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T'
169
  { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U'
170
  { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V'
171
  { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W'
172
  { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X'
173
  { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y'
174
  { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z'
175
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
176
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
177
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
178
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
179
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
180
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x60
181
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
182
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
183
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
184
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
185
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
186
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
187
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
188
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
189
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
190
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
191
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
192
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
193
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
194
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
195
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
196
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x70
197
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
198
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
199
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
200
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
201
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
202
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
203
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
204
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
205
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
206
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
207
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
208
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
209
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
210
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
211
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
212
};
213
214
// 3 bars + 3 spaces -- each can be 1, 2, 3, or 4 units wide
215
static Guchar code128Data[107][6] = {
216
  { 2, 1, 2, 2, 2, 2 },
217
  { 2, 2, 2, 1, 2, 2 },
218
  { 2, 2, 2, 2, 2, 1 },
219
  { 1, 2, 1, 2, 2, 3 },
220
  { 1, 2, 1, 3, 2, 2 },
221
  { 1, 3, 1, 2, 2, 2 },
222
  { 1, 2, 2, 2, 1, 3 },
223
  { 1, 2, 2, 3, 1, 2 },
224
  { 1, 3, 2, 2, 1, 2 },
225
  { 2, 2, 1, 2, 1, 3 },
226
  { 2, 2, 1, 3, 1, 2 },
227
  { 2, 3, 1, 2, 1, 2 },
228
  { 1, 1, 2, 2, 3, 2 },
229
  { 1, 2, 2, 1, 3, 2 },
230
  { 1, 2, 2, 2, 3, 1 },
231
  { 1, 1, 3, 2, 2, 2 },
232
  { 1, 2, 3, 1, 2, 2 },
233
  { 1, 2, 3, 2, 2, 1 },
234
  { 2, 2, 3, 2, 1, 1 },
235
  { 2, 2, 1, 1, 3, 2 },
236
  { 2, 2, 1, 2, 3, 1 },
237
  { 2, 1, 3, 2, 1, 2 },
238
  { 2, 2, 3, 1, 1, 2 },
239
  { 3, 1, 2, 1, 3, 1 },
240
  { 3, 1, 1, 2, 2, 2 },
241
  { 3, 2, 1, 1, 2, 2 },
242
  { 3, 2, 1, 2, 2, 1 },
243
  { 3, 1, 2, 2, 1, 2 },
244
  { 3, 2, 2, 1, 1, 2 },
245
  { 3, 2, 2, 2, 1, 1 },
246
  { 2, 1, 2, 1, 2, 3 },
247
  { 2, 1, 2, 3, 2, 1 },
248
  { 2, 3, 2, 1, 2, 1 },
249
  { 1, 1, 1, 3, 2, 3 },
250
  { 1, 3, 1, 1, 2, 3 },
251
  { 1, 3, 1, 3, 2, 1 },
252
  { 1, 1, 2, 3, 1, 3 },
253
  { 1, 3, 2, 1, 1, 3 },
254
  { 1, 3, 2, 3, 1, 1 },
255
  { 2, 1, 1, 3, 1, 3 },
256
  { 2, 3, 1, 1, 1, 3 },
257
  { 2, 3, 1, 3, 1, 1 },
258
  { 1, 1, 2, 1, 3, 3 },
259
  { 1, 1, 2, 3, 3, 1 },
260
  { 1, 3, 2, 1, 3, 1 },
261
  { 1, 1, 3, 1, 2, 3 },
262
  { 1, 1, 3, 3, 2, 1 },
263
  { 1, 3, 3, 1, 2, 1 },
264
  { 3, 1, 3, 1, 2, 1 },
265
  { 2, 1, 1, 3, 3, 1 },
266
  { 2, 3, 1, 1, 3, 1 },
267
  { 2, 1, 3, 1, 1, 3 },
268
  { 2, 1, 3, 3, 1, 1 },
269
  { 2, 1, 3, 1, 3, 1 },
270
  { 3, 1, 1, 1, 2, 3 },
271
  { 3, 1, 1, 3, 2, 1 },
272
  { 3, 3, 1, 1, 2, 1 },
273
  { 3, 1, 2, 1, 1, 3 },
274
  { 3, 1, 2, 3, 1, 1 },
275
  { 3, 3, 2, 1, 1, 1 },
276
  { 3, 1, 4, 1, 1, 1 },
277
  { 2, 2, 1, 4, 1, 1 },
278
  { 4, 3, 1, 1, 1, 1 },
279
  { 1, 1, 1, 2, 2, 4 },
280
  { 1, 1, 1, 4, 2, 2 },
281
  { 1, 2, 1, 1, 2, 4 },
282
  { 1, 2, 1, 4, 2, 1 },
283
  { 1, 4, 1, 1, 2, 2 },
284
  { 1, 4, 1, 2, 2, 1 },
285
  { 1, 1, 2, 2, 1, 4 },
286
  { 1, 1, 2, 4, 1, 2 },
287
  { 1, 2, 2, 1, 1, 4 },
288
  { 1, 2, 2, 4, 1, 1 },
289
  { 1, 4, 2, 1, 1, 2 },
290
  { 1, 4, 2, 2, 1, 1 },
291
  { 2, 4, 1, 2, 1, 1 },
292
  { 2, 2, 1, 1, 1, 4 },
293
  { 4, 1, 3, 1, 1, 1 },
294
  { 2, 4, 1, 1, 1, 2 },
295
  { 1, 3, 4, 1, 1, 1 },
296
  { 1, 1, 1, 2, 4, 2 },
297
  { 1, 2, 1, 1, 4, 2 },
298
  { 1, 2, 1, 2, 4, 1 },
299
  { 1, 1, 4, 2, 1, 2 },
300
  { 1, 2, 4, 1, 1, 2 },
301
  { 1, 2, 4, 2, 1, 1 },
302
  { 4, 1, 1, 2, 1, 2 },
303
  { 4, 2, 1, 1, 1, 2 },
304
  { 4, 2, 1, 2, 1, 1 },
305
  { 2, 1, 2, 1, 4, 1 },
306
  { 2, 1, 4, 1, 2, 1 },
307
  { 4, 1, 2, 1, 2, 1 },
308
  { 1, 1, 1, 1, 4, 3 },
309
  { 1, 1, 1, 3, 4, 1 },
310
  { 1, 3, 1, 1, 4, 1 },
311
  { 1, 1, 4, 1, 1, 3 },
312
  { 1, 1, 4, 3, 1, 1 },
313
  { 4, 1, 1, 1, 1, 3 },
314
  { 4, 1, 1, 3, 1, 1 },
315
  { 1, 1, 3, 1, 4, 1 },
316
  { 1, 1, 4, 1, 3, 1 },
317
  { 3, 1, 1, 1, 4, 1 },
318
  { 4, 1, 1, 1, 3, 1 },
319
  { 2, 1, 1, 4, 1, 2 }, // start code A
320
  { 2, 1, 1, 2, 1, 4 }, // start code B
321
  { 2, 1, 1, 2, 3, 2 }, // start code C
322
  { 2, 3, 3, 1, 1, 1 }  // stop code (without final bar)
323
};
324
325
//------------------------------------------------------------------------
326
327
// map an annotation ref to a page number
328
class AcroFormAnnotPage {
329
public:
330
  AcroFormAnnotPage(int annotNumA, int annotGenA, int pageNumA)
331
11.3k
    { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
332
  int annotNum;
333
  int annotGen;
334
  int pageNum;
335
};
336
337
//------------------------------------------------------------------------
338
// AcroForm
339
//------------------------------------------------------------------------
340
341
699
AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
342
699
  Object acroFormObj2;
343
699
  AcroForm *acroForm;
344
699
  AcroFormField *field;
345
699
  Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
346
699
  int pageNum, i, j;
347
348
  // this is the normal case: acroFormObj is a dictionary, as expected
349
699
  if (acroFormObjA->isDict()) {
350
424
    acroForm = new AcroForm(docA, acroFormObjA);
351
352
424
    if (globalParams->getEnableXFA()) {
353
424
      if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
354
322
  acroForm->xfaScanner = XFAScanner::load(&xfaObj);
355
322
  if (!catalog->getNeedsRendering()) {
356
322
    acroForm->isStaticXFA = gTrue;
357
322
  }
358
322
      }
359
424
      xfaObj.free();
360
424
    }
361
362
424
    if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
363
5
      acroForm->needAppearances = obj1.getBool();
364
5
    }
365
424
    obj1.free();
366
367
424
    acroForm->buildAnnotPageList(catalog);
368
369
424
    if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
370
8
      if (!obj1.isNull()) {
371
0
  error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
372
0
      }
373
8
      obj1.free();
374
8
      delete acroForm;
375
8
      return NULL;
376
8
    }
377
9.33k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
378
8.91k
      obj1.arrayGetNF(i, &obj2);
379
8.91k
      acroForm->scanField(&obj2);
380
8.91k
      obj2.free();
381
8.91k
    }
382
416
    obj1.free();
383
384
    // scan the annotations, looking for Widget-type annots that are
385
    // not attached to the AcroForm object
386
1.25k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
387
843
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
388
31.0k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
389
30.9k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
390
25.5k
      for (j = 0; j < acroForm->fields->getLength(); ++j) {
391
21.6k
        field = (AcroFormField *)acroForm->fields->get(j);
392
21.6k
        if (field->fieldRef.isRef()) {
393
20.6k
    if (field->fieldRef.getRefNum() == annotRef.getRefNum() &&
394
20.6k
        field->fieldRef.getRefGen() == annotRef.getRefGen()) {
395
222
      break;
396
222
    }
397
20.6k
        }
398
21.6k
      }
399
4.16k
      if (j == acroForm->fields->getLength()) {
400
3.94k
        annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
401
3.94k
        if (annotObj.isDict()) {
402
822
    if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
403
293
      acroForm->scanField(&annotRef);
404
293
    }
405
822
    obj1.free();
406
822
        }
407
3.94k
        annotObj.free();
408
3.94k
      }
409
4.16k
    }
410
30.9k
    annotRef.free();
411
30.9k
  }
412
166
      }
413
843
      annotsObj.free();
414
843
    }
415
416
  // if acroFormObjA is a null object, but there are Widget-type
417
  // annots, we still create an AcroForm
418
416
  } else {
419
    // create an empty dict for acroFormObj
420
275
    acroFormObj2.initDict(docA->getXRef());
421
275
    acroForm = new AcroForm(docA, &acroFormObj2);
422
275
    acroFormObj2.free();
423
424
275
    acroForm->buildAnnotPageList(catalog);
425
426
    // scan the annotations, looking for any Widget-type annots
427
1.82k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
428
1.55k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
429
64.2k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
430
64.0k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
431
7.12k
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
432
7.12k
      if (annotObj.isDict()) {
433
1.81k
        if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
434
1.01k
    acroForm->scanField(&annotRef);
435
1.01k
        }
436
1.81k
        obj1.free();
437
1.81k
      }
438
7.12k
      annotObj.free();
439
7.12k
    }
440
64.0k
    annotRef.free();
441
64.0k
  }
442
158
      }
443
1.55k
      annotsObj.free();
444
1.55k
    }
445
446
275
    if (acroForm->fields->getLength() == 0) {
447
258
      delete acroForm;
448
258
      acroForm = NULL;
449
258
    }
450
275
  }
451
452
691
  return acroForm;
453
699
}
454
455
699
AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) {
456
699
  doc = docA;
457
699
  acroFormObjA->copy(&acroFormObj);
458
699
  needAppearances = gFalse;
459
699
  annotPages = new GList();
460
699
  fields = new GList();
461
699
  xfaScanner = NULL;
462
699
  isStaticXFA = gFalse;
463
699
}
464
465
699
AcroForm::~AcroForm() {
466
699
  acroFormObj.free();
467
699
  deleteGList(annotPages, AcroFormAnnotPage);
468
699
  deleteGList(fields, AcroFormField);
469
699
  delete xfaScanner;
470
699
}
471
472
0
const char *AcroForm::getType() {
473
0
  return isStaticXFA ? "static XFA" : "AcroForm";
474
0
}
475
476
699
void AcroForm::buildAnnotPageList(Catalog *catalog) {
477
699
  Object annotsObj, annotObj;
478
699
  int pageNum, i;
479
480
3.51k
  for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
481
2.81k
    if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
482
95.4k
      for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
483
95.0k
  if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
484
11.3k
    annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
485
11.3k
               annotObj.getRefGen(),
486
11.3k
               pageNum));
487
11.3k
  }
488
95.0k
  annotObj.free();
489
95.0k
      }
490
325
    }
491
2.81k
    annotsObj.free();
492
2.81k
  }
493
  //~ sort the list
494
699
}
495
496
0
int AcroForm::lookupAnnotPage(Object *annotRef) {
497
0
  AcroFormAnnotPage *annotPage;
498
0
  int num, gen, i;
499
500
0
  if (!annotRef->isRef()) {
501
0
    return 0;
502
0
  }
503
0
  num = annotRef->getRefNum();
504
0
  gen = annotRef->getRefGen();
505
  //~ use bin search
506
0
  for (i = 0; i < annotPages->getLength(); ++i) {
507
0
    annotPage = (AcroFormAnnotPage *)annotPages->get(i);
508
0
    if (annotPage->annotNum == num && annotPage->annotGen == gen) {
509
0
      return annotPage->pageNum;
510
0
    }
511
0
  }
512
0
  return 0;
513
0
}
514
515
24.2k
void AcroForm::scanField(Object *fieldRef) {
516
24.2k
  AcroFormField *field;
517
24.2k
  Object fieldObj, kidsObj, kidRef, kidObj, subtypeObj;
518
24.2k
  GBool isTerminal;
519
24.2k
  int i;
520
521
24.2k
  fieldRef->fetch(doc->getXRef(), &fieldObj);
522
24.2k
  if (!fieldObj.isDict()) {
523
22.0k
    error(errSyntaxError, -1, "AcroForm field object is wrong type");
524
22.0k
    fieldObj.free();
525
22.0k
    return;
526
22.0k
  }
527
528
  // if this field has a Kids array, and all of the kids have a Parent
529
  // reference (i.e., they're all form fields, not widget
530
  // annotations), then this is a non-terminal field, and we need to
531
  // scan the kids
532
2.16k
  isTerminal = gTrue;
533
2.16k
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
534
287
    isTerminal = gFalse;
535
24.7k
    for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
536
24.4k
      kidsObj.arrayGet(i, &kidObj);
537
24.4k
      if (kidObj.isDict()) {
538
446
  if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
539
139
    isTerminal = gTrue;
540
139
  }
541
446
  subtypeObj.free();
542
446
      }
543
24.4k
      kidObj.free();
544
24.4k
    }
545
287
    if (!isTerminal) {
546
14.1k
      for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
547
13.9k
  kidsObj.arrayGetNF(i, &kidRef);
548
13.9k
  scanField(&kidRef);
549
13.9k
  kidRef.free();
550
13.9k
      }
551
148
    }
552
287
  }
553
2.16k
  kidsObj.free();
554
555
2.16k
  if (isTerminal) {
556
2.01k
    if ((field = AcroFormField::load(this, fieldRef))) {
557
1.07k
      fields->append(field);
558
1.07k
    }
559
2.01k
  }
560
561
2.16k
  fieldObj.free();
562
2.16k
}
563
564
0
void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) {
565
0
  int i;
566
567
0
  for (i = 0; i < fields->getLength(); ++i) {
568
0
    ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing);
569
0
  }
570
0
}
571
572
0
int AcroForm::getNumFields() {
573
0
  return fields->getLength();
574
0
}
575
576
0
AcroFormField *AcroForm::getField(int idx) {
577
0
  return (AcroFormField *)fields->get(idx);
578
0
}
579
580
0
AcroFormField *AcroForm::findField(int pg, double x, double y) {
581
0
  AcroFormField *field;
582
0
  double llx, lly, urx, ury;
583
0
  int i;
584
585
0
  for (i = 0; i < fields->getLength(); ++i) {
586
0
    field = (AcroFormField *)fields->get(i);
587
0
    if (field->getPageNum() == pg) {
588
0
      field->getBBox(&llx, &lly, &urx, &ury);
589
0
      if (llx <= x && x <= urx && lly <= y && y <= ury) {
590
0
  return field;
591
0
      }
592
0
    }
593
0
  }
594
0
  return NULL;
595
0
}
596
597
0
int AcroForm::findFieldIdx(int pg, double x, double y) {
598
0
  AcroFormField *field;
599
0
  double llx, lly, urx, ury;
600
0
  int i;
601
602
0
  for (i = 0; i < fields->getLength(); ++i) {
603
0
    field = (AcroFormField *)fields->get(i);
604
0
    if (field->getPageNum() == pg) {
605
0
      field->getBBox(&llx, &lly, &urx, &ury);
606
0
      if (llx <= x && x <= urx && lly <= y && y <= ury) {
607
0
  return i;
608
0
      }
609
0
    }
610
0
  }
611
0
  return -1;
612
0
}
613
614
//------------------------------------------------------------------------
615
// AcroFormField
616
//------------------------------------------------------------------------
617
618
2.01k
AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
619
2.01k
  GString *typeStr;
620
2.01k
  TextString *nameA;
621
2.01k
  GString *xfaName;
622
2.01k
  Guint flagsA;
623
2.01k
  GBool haveFlags, typeFromParentA;
624
2.01k
  Object fieldObjA, parentObj, parentObj2, obj1, obj2;
625
2.01k
  AcroFormFieldType typeA;
626
2.01k
  XFAField *xfaFieldA;
627
2.01k
  AcroFormField *field;
628
2.01k
  int depth, i0, i1;
629
630
2.01k
  fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
631
632
  //----- get field info
633
634
2.01k
  if (fieldObjA.dictLookup("T", &obj1)->isString()) {
635
1.27k
    nameA = new TextString(obj1.getString());
636
1.27k
  } else {
637
739
    nameA = new TextString();
638
739
  }
639
2.01k
  obj1.free();
640
641
2.01k
  if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
642
1.31k
    typeStr = new GString(obj1.getName());
643
1.31k
    typeFromParentA = gFalse;
644
1.31k
  } else {
645
702
    typeStr = NULL;
646
702
    typeFromParentA = gTrue;
647
702
  }
648
2.01k
  obj1.free();
649
650
2.01k
  if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
651
1.08k
    flagsA = (Guint)obj1.getInt();
652
1.08k
    haveFlags = gTrue;
653
1.08k
  } else {
654
934
    flagsA = 0;
655
934
    haveFlags = gFalse;
656
934
  }
657
2.01k
  obj1.free();
658
659
  //----- get info from parent non-terminal fields
660
661
2.01k
  fieldObjA.dictLookup("Parent", &parentObj);
662
2.01k
  depth = 0;
663
8.08k
  while (parentObj.isDict() && depth < maxFieldObjectDepth) {
664
665
6.06k
    if (parentObj.dictLookup("T", &obj1)->isString()) {
666
5.90k
      if (nameA->getLength()) {
667
5.76k
  nameA->insert(0, (Unicode)'.');
668
5.76k
      }
669
5.90k
      nameA->insert(0, obj1.getString());
670
5.90k
    }
671
6.06k
    obj1.free();
672
673
6.06k
    if (!typeStr) {
674
894
      if (parentObj.dictLookup("FT", &obj1)->isName()) {
675
211
  typeStr = new GString(obj1.getName());
676
211
      }
677
894
      obj1.free();
678
894
    }
679
680
6.06k
    if (!haveFlags) {
681
2.29k
      if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
682
201
  flagsA = (Guint)obj1.getInt();
683
201
  haveFlags = gTrue;
684
201
      }
685
2.29k
      obj1.free();
686
2.29k
    }
687
688
6.06k
    parentObj.dictLookup("Parent", &parentObj2);
689
6.06k
    parentObj.free();
690
6.06k
    parentObj = parentObj2;
691
692
6.06k
    ++depth;
693
6.06k
  }
694
2.01k
  parentObj.free();
695
696
2.01k
  if (!typeStr) {
697
491
    error(errSyntaxError, -1, "Missing type in AcroForm field");
698
491
    goto err1;
699
491
  }
700
701
  //----- get static XFA info
702
703
1.52k
  xfaFieldA = NULL;
704
1.52k
  if (acroFormA->xfaScanner) {
705
    // convert field name to UTF-8, and remove segments that start
706
    // with '#' -- to match the XFA field name
707
33
    xfaName = nameA->toUTF8();
708
33
    i0 = 0;
709
63
    while (i0 < xfaName->getLength()) {
710
30
      i1 = i0;
711
572
      while (i1 < xfaName->getLength()) {
712
542
  if (xfaName->getChar(i1) == '.') {
713
0
    ++i1;
714
0
    break;
715
0
  }
716
542
  ++i1;
717
542
      }
718
30
      if (xfaName->getChar(i0) == '#') {
719
0
  xfaName->del(i0, i1 - i0);
720
30
      } else {
721
30
  i0 = i1;
722
30
      }
723
30
    }
724
33
    xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
725
33
    delete xfaName;
726
33
  }
727
728
  //----- check for a radio button
729
730
  // this is a kludge: if we see a Btn-type field with kids, and the
731
  // Ff entry is missing, assume the kids are radio buttons
732
1.52k
  if (typeFromParentA && !typeStr->cmp("Btn") && !haveFlags) {
733
17
    flagsA = acroFormFlagRadio;
734
17
  }
735
736
  //----- determine field type
737
738
1.52k
  if (!typeStr->cmp("Btn")) {
739
117
    if (flagsA & acroFormFlagPushbutton) {
740
0
      typeA = acroFormFieldPushbutton;
741
117
    } else if (flagsA & acroFormFlagRadio) {
742
29
      typeA = acroFormFieldRadioButton;
743
88
    } else {
744
88
      typeA = acroFormFieldCheckbox;
745
88
    }
746
1.40k
  } else if (!typeStr->cmp("Tx")) {
747
946
    if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
748
0
      typeA = acroFormFieldBarcode;
749
946
    } else if (flagsA & acroFormFlagFileSelect) {
750
574
      typeA = acroFormFieldFileSelect;
751
574
    } else if (flagsA & acroFormFlagMultiline) {
752
2
      typeA = acroFormFieldMultilineText;
753
370
    } else {
754
370
      typeA = acroFormFieldText;
755
370
    }
756
946
  } else if (!typeStr->cmp("Ch")) {
757
10
    if (flagsA & acroFormFlagCombo) {
758
6
      typeA = acroFormFieldComboBox;
759
6
    } else {
760
4
      typeA = acroFormFieldListBox;
761
4
    }
762
453
  } else if (!typeStr->cmp("Sig")) {
763
0
    typeA = acroFormFieldSignature;
764
453
  } else {
765
453
    error(errSyntaxError, -1, "Invalid type in AcroForm field");
766
453
    goto err1;
767
453
  }
768
1.07k
  delete typeStr;
769
770
1.07k
  field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
771
1.07k
          typeA, nameA, flagsA, typeFromParentA, xfaFieldA);
772
1.07k
  fieldObjA.free();
773
1.07k
  return field;
774
775
944
 err1:
776
944
  delete typeStr;
777
944
  delete nameA;
778
944
  fieldObjA.free();
779
944
  return NULL;
780
1.52k
}
781
782
AcroFormField::AcroFormField(AcroForm *acroFormA,
783
           Object *fieldRefA, Object *fieldObjA,
784
           AcroFormFieldType typeA, TextString *nameA,
785
           Guint flagsA, GBool typeFromParentA,
786
1.07k
           XFAField *xfaFieldA) {
787
1.07k
  acroForm = acroFormA;
788
1.07k
  fieldRefA->copy(&fieldRef);
789
1.07k
  fieldObjA->copy(&fieldObj);
790
1.07k
  type = typeA;
791
1.07k
  name = nameA;
792
1.07k
  flags = flagsA;
793
1.07k
  typeFromParent = typeFromParentA;
794
1.07k
  xfaField = xfaFieldA;
795
1.07k
}
796
797
1.07k
AcroFormField::~AcroFormField() {
798
1.07k
  fieldRef.free();
799
1.07k
  fieldObj.free();
800
1.07k
  delete name;
801
1.07k
}
802
803
0
int AcroFormField::getPageNum() {
804
0
  Object kidsObj, annotRef;
805
0
  int pageNum;
806
807
0
  pageNum = 0;
808
0
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
809
0
    if (kidsObj.arrayGetLength() > 0) {
810
0
      kidsObj.arrayGetNF(0, &annotRef);
811
0
      pageNum = acroForm->lookupAnnotPage(&annotRef);
812
0
      annotRef.free();
813
0
    }
814
0
  } else {
815
0
    pageNum = acroForm->lookupAnnotPage(&fieldRef);
816
0
  }
817
0
  kidsObj.free();
818
0
  return pageNum;
819
0
}
820
821
0
const char *AcroFormField::getType() {
822
0
  switch (type) {
823
0
  case acroFormFieldPushbutton:    return "PushButton";
824
0
  case acroFormFieldRadioButton:   return "RadioButton";
825
0
  case acroFormFieldCheckbox:      return "Checkbox";
826
0
  case acroFormFieldFileSelect:    return "FileSelect";
827
0
  case acroFormFieldMultilineText: return "MultilineText";
828
0
  case acroFormFieldText:          return "Text";
829
0
  case acroFormFieldBarcode:       return "Barcode";
830
0
  case acroFormFieldComboBox:      return "ComboBox";
831
0
  case acroFormFieldListBox:       return "ListBox";
832
0
  case acroFormFieldSignature:     return "Signature";
833
0
  default:                         return NULL;
834
0
  }
835
0
}
836
837
0
Unicode *AcroFormField::getName(int *length) {
838
0
  Unicode *u, *ret;
839
0
  int n;
840
841
0
  u = name->getUnicode();
842
0
  n = name->getLength();
843
0
  ret = (Unicode *)gmallocn(n, sizeof(Unicode));
844
0
  memcpy(ret, u, n * sizeof(Unicode));
845
0
  *length = n;
846
0
  return ret;
847
0
}
848
849
0
Unicode *AcroFormField::getValue(int *length) {
850
0
  Object obj1, obj2;
851
0
  Unicode *u;
852
0
  char *s;
853
0
  TextString *ts;
854
0
  GString *gs;
855
0
  int n, i;
856
857
0
  u = NULL;
858
0
  *length = 0;
859
860
  // if this field has a counterpart in the XFA form, take the value
861
  // from the XFA field (NB: an XFA field with no value overrides the
862
  // AcroForm value)
863
0
  if (xfaField) {
864
0
    if (xfaField->getValue()) {
865
0
      u = utf8ToUnicode(xfaField->getValue(), length);
866
0
    }
867
868
  // no XFA form - take the AcroForm value
869
0
  } else {
870
0
    fieldLookup("V", &obj1);
871
0
    if (obj1.isName()) {
872
0
      s = obj1.getName();
873
0
      n = (int)strlen(s);
874
0
      u = (Unicode *)gmallocn(n, sizeof(Unicode));
875
0
      for (i = 0; i < n; ++i) {
876
0
        u[i] = s[i] & 0xff;
877
0
      }
878
0
      *length = n;
879
0
    } else if (obj1.isString()) {
880
0
      ts = new TextString(obj1.getString());
881
0
      n = ts->getLength();
882
0
      u = (Unicode *)gmallocn(n, sizeof(Unicode));
883
0
      memcpy(u, ts->getUnicode(), n * sizeof(Unicode));
884
0
      *length = n;
885
0
      delete ts;
886
0
    } else if (obj1.isDict()) {
887
0
      obj1.dictLookup("Contents", &obj2);
888
0
      if (obj2.isString()) {
889
0
        gs = obj2.getString();
890
0
        n = gs->getLength();
891
0
        u = (Unicode *)gmallocn(n, sizeof(Unicode));
892
0
        for (i = 0; i < n; ++i) {
893
0
    u[i] = gs->getChar(i) & 0xff;
894
0
        }
895
0
        *length = n;
896
0
      }
897
0
      obj2.free();
898
0
    }
899
0
    obj1.free();
900
0
  }
901
902
0
  return u;
903
0
}
904
905
void AcroFormField::getBBox(double *llx, double *lly,
906
0
          double *urx, double *ury) {
907
0
  Object annotObj, rectObj, numObj;
908
0
  double t;
909
910
0
  *llx = *lly = *urx = *ury = 0;
911
912
0
  if (getAnnotObj(&annotObj)->isDict()) {
913
0
    annotObj.dictLookup("Rect", &rectObj);
914
0
    if (rectObj.isArray() && rectObj.arrayGetLength() == 4) {
915
0
      rectObj.arrayGet(0, &numObj);
916
0
      if (numObj.isNum()) {
917
0
  *llx = numObj.getNum();
918
0
      }
919
0
      numObj.free();
920
0
      rectObj.arrayGet(1, &numObj);
921
0
      if (numObj.isNum()) {
922
0
  *lly = numObj.getNum();
923
0
      }
924
0
      numObj.free();
925
0
      rectObj.arrayGet(2, &numObj);
926
0
      if (numObj.isNum()) {
927
0
  *urx = numObj.getNum();
928
0
      }
929
0
      numObj.free();
930
0
      rectObj.arrayGet(3, &numObj);
931
0
      if (numObj.isNum()) {
932
0
  *ury = numObj.getNum();
933
0
      }
934
0
      numObj.free();
935
0
    }
936
0
    rectObj.free();
937
0
  }
938
0
  annotObj.free();
939
940
0
  if (*llx > *urx) {
941
0
    t = *llx;  *llx = *urx;  *urx = t;
942
0
  }
943
0
  if (*lly > *ury) {
944
0
    t = *lly;  *lly = *ury;  *ury = t;
945
0
  }
946
0
}
947
948
0
void AcroFormField::getFont(Ref *fontID, double *fontSize) {
949
0
  Object daObj;
950
0
  GList *daToks;
951
0
  char *fontTag;
952
0
  double tfSize, m2, m3;
953
0
  int tfPos, tmPos, i;
954
955
0
  fontID->num = fontID->gen = -1;
956
0
  *fontSize = 0;
957
958
0
  if (fieldLookup("DA", &daObj)->isString()) {
959
960
    // parse the default appearance string
961
0
    daToks = tokenize(daObj.getString());
962
0
    tfPos = tmPos = -1;
963
0
    for (i = 2; i < daToks->getLength(); ++i) {
964
0
      if (!((GString *)daToks->get(i))->cmp("Tf")) {
965
0
  tfPos = i - 2;
966
0
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
967
0
  tmPos = i - 6;
968
0
      }
969
0
    }
970
971
    // handle the Tf operator
972
0
    if (tfPos >= 0) {
973
0
      fontTag = ((GString *)daToks->get(tfPos))->getCString();
974
0
      if (*fontTag == '/') {
975
0
  ++fontTag;
976
0
      }
977
0
      *fontID = findFontName(fontTag);
978
0
      tfSize = atof(((GString *)daToks->get(tfPos + 1))->getCString());
979
0
    } else {
980
0
      tfSize = 1;
981
0
    }
982
983
    // handle the Tm operator
984
0
    if (tmPos >= 0) {
985
      // transformed font size = sqrt(m[2]^2 + m[3]^2) * size
986
0
      m2 = atof(((GString *)daToks->get(tfPos + 2))->getCString());
987
0
      m3 = atof(((GString *)daToks->get(tfPos + 3))->getCString());
988
0
      tfSize *= sqrt(m2*m2 + m3*m3);
989
0
    }
990
0
    *fontSize = tfSize;
991
992
0
    deleteGList(daToks, GString);
993
0
  }
994
995
0
  daObj.free();
996
0
}
997
998
0
Ref AcroFormField::findFontName(char *fontTag) {
999
0
  Object drObj, fontDictObj, fontObj, baseFontObj;
1000
0
  Ref fontID;
1001
0
  GBool found;
1002
1003
0
  fontID.num = fontID.gen = -1;
1004
0
  found = gFalse;
1005
1006
0
  if (fieldObj.dictLookup("DR", &drObj)->isDict()) {
1007
0
    if (drObj.dictLookup("Font", &fontDictObj)->isDict()) {
1008
0
      if (fontDictObj.dictLookupNF(fontTag, &fontObj)->isRef()) {
1009
0
  fontID = fontObj.getRef();
1010
0
  found = gTrue;
1011
0
      }
1012
0
      fontObj.free();
1013
0
    }
1014
0
    fontDictObj.free();
1015
0
  }
1016
0
  drObj.free();
1017
0
  if (found) {
1018
0
    return fontID;
1019
0
  }
1020
1021
0
  if (acroForm->acroFormObj.dictLookup("DR", &drObj)->isDict()) {
1022
0
    if (drObj.dictLookup("Font", &fontDictObj)->isDict()) {
1023
0
      if (fontDictObj.dictLookupNF(fontTag, &fontObj)->isRef()) {
1024
0
  fontID = fontObj.getRef();
1025
0
      }
1026
0
      fontObj.free();
1027
0
    }
1028
0
    fontDictObj.free();
1029
0
  }
1030
0
  drObj.free();
1031
0
  return fontID;
1032
0
}
1033
1034
0
void AcroFormField::getColor(double *red, double *green, double *blue) {
1035
0
  Object daObj;
1036
0
  GList *daToks;
1037
0
  int i;
1038
1039
0
  *red = *green = *blue = 0;
1040
1041
0
  if (fieldLookup("DA", &daObj)->isString()) {
1042
1043
    // parse the default appearance string
1044
0
    daToks = tokenize(daObj.getString());
1045
0
    for (i = 1; i < daToks->getLength(); ++i) {
1046
1047
      // handle the g operator
1048
0
      if (!((GString *)daToks->get(i))->cmp("g")) {
1049
0
  *red = *green = *blue =
1050
0
      atof(((GString *)daToks->get(i - 1))->getCString());
1051
0
  break;
1052
1053
      // handle the rg operator
1054
0
      } else if (i >= 3 && !((GString *)daToks->get(i))->cmp("rg")) {
1055
0
  *red = atof(((GString *)daToks->get(i - 3))->getCString());
1056
0
  *green = atof(((GString *)daToks->get(i - 2))->getCString());
1057
0
  *blue = atof(((GString *)daToks->get(i - 1))->getCString());
1058
0
  break;
1059
0
      }
1060
0
    }
1061
1062
0
    deleteGList(daToks, GString);
1063
0
  }
1064
1065
0
  daObj.free();
1066
0
}
1067
1068
0
int AcroFormField::getMaxLen() {
1069
0
  Object obj;
1070
0
  int len;
1071
1072
0
  if (fieldLookup("MaxLen", &obj)->isInt()) {
1073
0
    len = obj.getInt();
1074
0
  } else {
1075
0
    len = -1;
1076
0
  }
1077
0
  obj.free();
1078
0
  return len;
1079
0
}
1080
1081
0
void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) {
1082
0
  Object kidsObj, annotRef, annotObj;
1083
0
  int i;
1084
1085
  // find the annotation object(s)
1086
0
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
1087
0
    for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
1088
0
      kidsObj.arrayGetNF(i, &annotRef);
1089
0
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
1090
0
      drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj);
1091
0
      annotObj.free();
1092
0
      annotRef.free();
1093
0
    }
1094
0
  } else {
1095
0
    drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj);
1096
0
  }
1097
0
  kidsObj.free();
1098
0
}
1099
1100
void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
1101
0
            Object *annotRef, Object *annotObj) {
1102
0
  Object obj1, obj2;
1103
0
  double xMin, yMin, xMax, yMax, t;
1104
0
  int annotFlags;
1105
0
  GBool oc, render;
1106
1107
0
  if (!annotObj->isDict()) {
1108
0
    return;
1109
0
  }
1110
1111
  //----- get the page number
1112
1113
  // the "P" (page) field in annotations is optional, so we can't
1114
  // depend on it here
1115
0
  if (acroForm->lookupAnnotPage(annotRef) != pageNum) {
1116
0
    return;
1117
0
  }
1118
1119
  //----- check annotation flags
1120
1121
0
  if (annotObj->dictLookup("F", &obj1)->isInt()) {
1122
0
    annotFlags = obj1.getInt();
1123
0
  } else {
1124
0
    annotFlags = 0;
1125
0
  }
1126
0
  obj1.free();
1127
0
  if ((annotFlags & annotFlagHidden) ||
1128
0
      (printing && !(annotFlags & annotFlagPrint)) ||
1129
0
      (!printing && (annotFlags & annotFlagNoView))) {
1130
0
    return;
1131
0
  }
1132
1133
  //----- check the optional content entry
1134
1135
0
  annotObj->dictLookupNF("OC", &obj1);
1136
0
  if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) {
1137
0
    obj1.free();
1138
0
    return;
1139
0
  }
1140
0
  obj1.free();
1141
1142
  //----- get the bounding box
1143
1144
0
  if (annotObj->dictLookup("Rect", &obj1)->isArray() &&
1145
0
      obj1.arrayGetLength() == 4) {
1146
0
    xMin = yMin = xMax = yMax = 0;
1147
0
    if (obj1.arrayGet(0, &obj2)->isNum()) {
1148
0
      xMin = obj2.getNum();
1149
0
    }
1150
0
    obj2.free();
1151
0
    if (obj1.arrayGet(1, &obj2)->isNum()) {
1152
0
      yMin = obj2.getNum();
1153
0
    }
1154
0
    obj2.free();
1155
0
    if (obj1.arrayGet(2, &obj2)->isNum()) {
1156
0
      xMax = obj2.getNum();
1157
0
    }
1158
0
    obj2.free();
1159
0
    if (obj1.arrayGet(3, &obj2)->isNum()) {
1160
0
      yMax = obj2.getNum();
1161
0
    }
1162
0
    obj2.free();
1163
0
    if (xMin > xMax) {
1164
0
      t = xMin; xMin = xMax; xMax = t;
1165
0
    }
1166
0
    if (yMin > yMax) {
1167
0
      t = yMin; yMin = yMax; yMax = t;
1168
0
    }
1169
0
  } else {
1170
0
    error(errSyntaxError, -1, "Bad bounding box for annotation");
1171
0
    obj1.free();
1172
0
    return;
1173
0
  }
1174
0
  obj1.free();
1175
1176
  //----- draw it
1177
1178
0
  render = gFalse;
1179
0
  if (acroForm->needAppearances) {
1180
0
    render = gTrue;
1181
0
  } else if (xfaField && xfaField->getValue()) {
1182
0
    render = gTrue;
1183
0
  } else {
1184
0
    if (!annotObj->dictLookup("AP", &obj1)->isDict()) {
1185
0
      render = gTrue;
1186
0
    }
1187
0
    obj1.free();
1188
0
  }
1189
0
  if (render) {
1190
0
    drawNewAppearance(gfx, annotObj->getDict(),
1191
0
          xMin, yMin, xMax, yMax);
1192
0
  } else {
1193
0
    drawExistingAppearance(gfx, annotObj->getDict(),
1194
0
         xMin, yMin, xMax, yMax);
1195
0
  }
1196
0
}
1197
1198
// Draw the existing appearance stream for a single annotation
1199
// attached to this field.
1200
void AcroFormField::drawExistingAppearance(Gfx *gfx, Dict *annot,
1201
             double xMin, double yMin,
1202
0
             double xMax, double yMax) {
1203
0
  Object apObj, asObj, appearance, obj1;
1204
1205
  //----- get the appearance stream
1206
1207
0
  if (annot->lookup("AP", &apObj)->isDict()) {
1208
0
    apObj.dictLookup("N", &obj1);
1209
0
    if (obj1.isDict()) {
1210
0
      if (annot->lookup("AS", &asObj)->isName()) {
1211
0
  obj1.dictLookupNF(asObj.getName(), &appearance);
1212
0
      } else if (obj1.dictGetLength() == 1) {
1213
0
  obj1.dictGetValNF(0, &appearance);
1214
0
      } else {
1215
0
  obj1.dictLookupNF("Off", &appearance);
1216
0
      }
1217
0
      asObj.free();
1218
0
    } else {
1219
0
      apObj.dictLookupNF("N", &appearance);
1220
0
    }
1221
0
    obj1.free();
1222
0
  }
1223
0
  apObj.free();
1224
1225
  //----- draw it
1226
1227
0
  if (!appearance.isNone()) {
1228
0
    gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
1229
0
    appearance.free();
1230
0
  }
1231
0
}
1232
1233
// Regenerate the appearance for this field, and draw it.
1234
void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
1235
              double xMin, double yMin,
1236
0
              double xMax, double yMax) {
1237
0
  GString *appearBuf;
1238
0
  Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj;
1239
0
  Object resources, fontResources, defaultFont, gfxStateDict;
1240
0
  Object obj1, obj2, obj3, obj4;
1241
0
  Dict *mkDict;
1242
0
  MemStream *appearStream;
1243
0
  GfxFontDict *fontDict;
1244
0
  GBool hasCaption;
1245
0
  double dx, dy, r;
1246
0
  GString *val, *caption, *da;
1247
0
  GString **text;
1248
0
  GBool done;
1249
0
  GBool *selection;
1250
0
  AnnotBorderType borderType;
1251
0
  double borderWidth;
1252
0
  double *borderDash;
1253
0
  GString *appearanceState;
1254
0
  int borderDashLength, rot, quadding, vAlign, comb, nOptions, topIdx, i;
1255
1256
0
  appearBuf = new GString();
1257
#if 0 //~debug
1258
  appearBuf->appendf("1 1 0 rg 0 0 {0:.4f} {1:.4f} re f\n",
1259
         xMax - xMin, yMax - yMin);
1260
#endif
1261
#if 0 //~debug
1262
  appearBuf->appendf("1 1 0 RG 0 0 {0:.4f} {1:.4f} re s\n",
1263
         xMax - xMin, yMax - yMin);
1264
#endif
1265
1266
  // get the appearance characteristics (MK) dictionary
1267
0
  if (annot->lookup("MK", &mkObj)->isDict()) {
1268
0
    mkDict = mkObj.getDict();
1269
0
  } else {
1270
0
    mkDict = NULL;
1271
0
  }
1272
1273
  // draw the background
1274
0
  if (mkDict) {
1275
0
    if (mkDict->lookup("BG", &obj1)->isArray() &&
1276
0
  obj1.arrayGetLength() > 0) {
1277
0
      setColor(obj1.getArray(), gTrue, 0, appearBuf);
1278
0
      appearBuf->appendf("0 0 {0:.4f} {1:.4f} re f\n",
1279
0
       xMax - xMin, yMax - yMin);
1280
0
    }
1281
0
    obj1.free();
1282
0
  }
1283
1284
  // get the field type
1285
0
  fieldLookup("FT", &ftObj);
1286
1287
  // draw the border
1288
0
  borderType = annotBorderSolid;
1289
0
  borderWidth = 1;
1290
0
  borderDash = NULL;
1291
0
  borderDashLength = 0;
1292
0
  if (annot->lookup("BS", &obj1)->isDict()) {
1293
0
    if (obj1.dictLookup("S", &obj2)->isName()) {
1294
0
      if (obj2.isName("S")) {
1295
0
  borderType = annotBorderSolid;
1296
0
      } else if (obj2.isName("D")) {
1297
0
  borderType = annotBorderDashed;
1298
0
      } else if (obj2.isName("B")) {
1299
0
  borderType = annotBorderBeveled;
1300
0
      } else if (obj2.isName("I")) {
1301
0
  borderType = annotBorderInset;
1302
0
      } else if (obj2.isName("U")) {
1303
0
  borderType = annotBorderUnderlined;
1304
0
      }
1305
0
    }
1306
0
    obj2.free();
1307
0
    if (obj1.dictLookup("W", &obj2)->isNum()) {
1308
0
      borderWidth = obj2.getNum();
1309
0
    }
1310
0
    obj2.free();
1311
0
    if (obj1.dictLookup("D", &obj2)->isArray()) {
1312
0
      borderDashLength = obj2.arrayGetLength();
1313
0
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
1314
0
      for (i = 0; i < borderDashLength; ++i) {
1315
0
  if (obj2.arrayGet(i, &obj3)->isNum()) {
1316
0
    borderDash[i] = obj3.getNum();
1317
0
  } else {
1318
0
    borderDash[i] = 1;
1319
0
  }
1320
0
  obj3.free();
1321
0
      }
1322
0
    }
1323
0
    obj2.free();
1324
0
  } else {
1325
0
    obj1.free();
1326
0
    if (annot->lookup("Border", &obj1)->isArray()) {
1327
0
      if (obj1.arrayGetLength() >= 3) {
1328
0
  if (obj1.arrayGet(2, &obj2)->isNum()) {
1329
0
    borderWidth = obj2.getNum();
1330
0
  }
1331
0
  obj2.free();
1332
0
  if (obj1.arrayGetLength() >= 4) {
1333
0
    if (obj1.arrayGet(3, &obj2)->isArray()) {
1334
0
      borderType = annotBorderDashed;
1335
0
      borderDashLength = obj2.arrayGetLength();
1336
0
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
1337
0
      for (i = 0; i < borderDashLength; ++i) {
1338
0
        if (obj2.arrayGet(i, &obj3)->isNum()) {
1339
0
    borderDash[i] = obj3.getNum();
1340
0
        } else {
1341
0
    borderDash[i] = 1;
1342
0
        }
1343
0
        obj3.free();
1344
0
      }
1345
0
    } else {
1346
      // Adobe draws no border at all if the last element is of
1347
      // the wrong type.
1348
0
      borderWidth = 0;
1349
0
    }
1350
0
    obj2.free();
1351
0
  }
1352
0
      }
1353
0
    }
1354
0
  }
1355
0
  obj1.free();
1356
0
  if (mkDict) {
1357
0
    if (borderWidth > 0) {
1358
0
      mkDict->lookup("BC", &obj1);
1359
0
      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
1360
0
  obj1.free();
1361
0
  mkDict->lookup("BG", &obj1);
1362
0
      }
1363
0
      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
1364
0
  dx = xMax - xMin;
1365
0
  dy = yMax - yMin;
1366
1367
  // radio buttons with no caption have a round border
1368
0
  hasCaption = mkDict->lookup("CA", &obj2)->isString();
1369
0
  obj2.free();
1370
0
  if (ftObj.isName("Btn") && (flags & acroFormFlagRadio) && !hasCaption) {
1371
0
    r = 0.5 * (dx < dy ? dx : dy);
1372
0
    switch (borderType) {
1373
0
    case annotBorderDashed:
1374
0
      appearBuf->append("[");
1375
0
      for (i = 0; i < borderDashLength; ++i) {
1376
0
        appearBuf->appendf(" {0:.4f}", borderDash[i]);
1377
0
      }
1378
0
      appearBuf->append("] 0 d\n");
1379
      // fall through to the solid case
1380
0
    case annotBorderSolid:
1381
0
    case annotBorderUnderlined:
1382
0
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1383
0
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1384
0
      drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * borderWidth, "s",
1385
0
           appearBuf);
1386
0
      break;
1387
0
    case annotBorderBeveled:
1388
0
    case annotBorderInset:
1389
0
      appearBuf->appendf("{0:.4f} w\n", 0.5 * borderWidth);
1390
0
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1391
0
      drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * borderWidth, "s",
1392
0
           appearBuf);
1393
0
      setColor(obj1.getArray(), gFalse,
1394
0
         borderType == annotBorderBeveled ? 1 : -1,
1395
0
         appearBuf);
1396
0
      drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth,
1397
0
            appearBuf);
1398
0
      setColor(obj1.getArray(), gFalse,
1399
0
         borderType == annotBorderBeveled ? -1 : 1,
1400
0
         appearBuf);
1401
0
      drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth,
1402
0
          appearBuf);
1403
0
      break;
1404
0
    }
1405
1406
0
  } else {
1407
0
    switch (borderType) {
1408
0
    case annotBorderDashed:
1409
0
      appearBuf->append("[");
1410
0
      for (i = 0; i < borderDashLength; ++i) {
1411
0
        appearBuf->appendf(" {0:.4f}", borderDash[i]);
1412
0
      }
1413
0
      appearBuf->append("] 0 d\n");
1414
      // fall through to the solid case
1415
0
    case annotBorderSolid:
1416
0
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1417
0
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1418
0
      appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n",
1419
0
             0.5 * borderWidth,
1420
0
             dx - borderWidth, dy - borderWidth);
1421
0
      break;
1422
0
    case annotBorderBeveled:
1423
0
    case annotBorderInset:
1424
0
      setColor(obj1.getArray(), gTrue,
1425
0
         borderType == annotBorderBeveled ? 1 : -1,
1426
0
         appearBuf);
1427
0
      appearBuf->append("0 0 m\n");
1428
0
      appearBuf->appendf("0 {0:.4f} l\n", dy);
1429
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
1430
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1431
0
             dx - borderWidth, dy - borderWidth);
1432
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1433
0
             borderWidth, dy - borderWidth);
1434
0
      appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
1435
0
      appearBuf->append("f\n");
1436
0
      setColor(obj1.getArray(), gTrue,
1437
0
         borderType == annotBorderBeveled ? -1 : 1,
1438
0
         appearBuf);
1439
0
      appearBuf->append("0 0 m\n");
1440
0
      appearBuf->appendf("{0:.4f} 0 l\n", dx);
1441
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
1442
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1443
0
             dx - borderWidth, dy - borderWidth);
1444
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1445
0
             dx - borderWidth, borderWidth);
1446
0
      appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
1447
0
      appearBuf->append("f\n");
1448
0
      break;
1449
0
    case annotBorderUnderlined:
1450
0
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1451
0
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1452
0
      appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx);
1453
0
      break;
1454
0
    }
1455
1456
    // clip to the inside of the border
1457
0
    appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n",
1458
0
           borderWidth,
1459
0
           dx - 2 * borderWidth, dy - 2 * borderWidth);
1460
0
  }
1461
0
      }
1462
0
      obj1.free();
1463
0
    }
1464
0
  }
1465
0
  gfree(borderDash);
1466
1467
  // get the resource dictionary
1468
0
  buildDefaultResourceDict(&drObj);
1469
1470
  // build the font dictionary
1471
0
  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
1472
0
    fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict());
1473
0
  } else {
1474
0
    fontDict = NULL;
1475
0
  }
1476
0
  obj1.free();
1477
1478
  // get the default appearance string
1479
0
  if (fieldLookup("DA", &obj1)->isString()) {
1480
0
    da = obj1.getString()->copy();
1481
0
  } else {
1482
0
    da = NULL;
1483
0
  }
1484
0
  obj1.free();
1485
1486
  // get the rotation value
1487
0
  rot = 0;
1488
0
  if (mkDict) {
1489
0
    if (mkDict->lookup("R", &obj1)->isInt()) {
1490
0
      rot = obj1.getInt();
1491
0
    }
1492
0
    obj1.free();
1493
0
  }
1494
1495
  // get the appearance state
1496
0
  annot->lookup("AP", &apObj);
1497
0
  annot->lookup("AS", &asObj);
1498
0
  appearanceState = NULL;
1499
0
  if (asObj.isName()) {
1500
0
    appearanceState = new GString(asObj.getName());
1501
0
  } else if (apObj.isDict()) {
1502
0
    apObj.dictLookup("N", &obj1);
1503
0
    if (obj1.isDict() && obj1.dictGetLength() == 1) {
1504
0
      appearanceState = new GString(obj1.dictGetKey(0));
1505
0
    }
1506
0
    obj1.free();
1507
0
  }
1508
0
  if (!appearanceState) {
1509
0
    appearanceState = new GString("Off");
1510
0
  }
1511
0
  asObj.free();
1512
0
  apObj.free();
1513
1514
0
  int valueLength;
1515
0
  Unicode *value = getValue(&valueLength);
1516
1517
  // draw the field contents
1518
0
  if (ftObj.isName("Btn")) {
1519
0
    caption = NULL;
1520
0
    if (mkDict) {
1521
0
      if (mkDict->lookup("CA", &obj1)->isString()) {
1522
0
  caption = obj1.getString()->copy();
1523
0
      }
1524
0
      obj1.free();
1525
0
    }
1526
    // radio button
1527
0
    if (flags & acroFormFlagRadio) {
1528
      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
1529
0
      if (value && unicodeStringEqual(value, valueLength,
1530
0
              appearanceState->getCString())) {
1531
0
  if (caption) {
1532
0
    drawText(caption, da, fontDict, gFalse, 0,
1533
0
       acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
1534
0
       gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
1535
0
       borderWidth, gFalse, appearBuf);
1536
0
  } else {
1537
0
    if (mkDict) {
1538
0
      if (mkDict->lookup("BC", &obj2)->isArray() &&
1539
0
    obj2.arrayGetLength() > 0) {
1540
0
        dx = xMax - xMin;
1541
0
        dy = yMax - yMin;
1542
0
        setColor(obj2.getArray(), gTrue, 0, appearBuf);
1543
0
        drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), "f",
1544
0
       appearBuf);
1545
0
      }
1546
0
      obj2.free();
1547
0
    }
1548
0
  }
1549
0
      }
1550
    // pushbutton
1551
0
    } else if (flags & acroFormFlagPushbutton) {
1552
0
      if (caption) {
1553
0
  drawText(caption, da, fontDict, gFalse, 0,
1554
0
     acroFormQuadCenter, acroFormVAlignMiddle,
1555
0
     gFalse, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
1556
0
     borderWidth, gFalse, appearBuf);
1557
0
      }
1558
    // checkbox
1559
0
    } else {
1560
0
      if (value && !(unicodeStringEqual(value, valueLength, "Off") ||
1561
0
         unicodeStringEqual(value, valueLength, "No") ||
1562
0
         unicodeStringEqual(value, valueLength, "0") ||
1563
0
         valueLength == 0)) {
1564
0
  if (!caption) {
1565
0
    caption = new GString("3"); // ZapfDingbats checkmark
1566
0
  }
1567
0
  drawText(caption, da, fontDict, gFalse, 0,
1568
0
     acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
1569
0
     gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
1570
0
     borderWidth, gFalse, appearBuf);
1571
0
      }
1572
0
    }
1573
0
    if (caption) {
1574
0
      delete caption;
1575
0
    }
1576
0
  } else if (ftObj.isName("Tx")) {
1577
0
    XFAFieldBarcodeInfo *barcodeInfo = xfaField ? xfaField->getBarcodeInfo()
1578
0
                                                : (XFAFieldBarcodeInfo *)NULL;
1579
0
    if (value) {
1580
      //~ value strings can be Unicode
1581
0
      GString *valueLatin1 = unicodeToLatin1(value, valueLength);
1582
0
      if (barcodeInfo) {
1583
0
  drawBarcode(valueLatin1, da, fontDict, rot, xMin, yMin, xMax, yMax,
1584
0
        barcodeInfo, appearBuf);
1585
0
      } else {
1586
0
  if (fieldLookup("Q", &obj2)->isInt()) {
1587
0
    quadding = obj2.getInt();
1588
0
  } else {
1589
0
    quadding = acroFormQuadLeft;
1590
0
  }
1591
0
  obj2.free();
1592
0
  vAlign = (flags & acroFormFlagMultiline) ? acroFormVAlignTop
1593
0
                                           : acroFormVAlignMiddle;
1594
0
  XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
1595
0
                                            : (XFAFieldLayoutInfo *)NULL;
1596
0
  if (layoutInfo) {
1597
0
    switch (layoutInfo->hAlign) {
1598
0
    case xfaFieldLayoutHAlignLeft:
1599
0
    default:
1600
0
      quadding = acroFormQuadLeft;
1601
0
      break;
1602
0
    case xfaFieldLayoutHAlignCenter:
1603
0
      quadding = acroFormQuadCenter;
1604
0
      break;
1605
0
    case xfaFieldLayoutHAlignRight:
1606
0
      quadding = acroFormQuadRight;
1607
0
      break;
1608
0
    }
1609
0
    switch (layoutInfo->vAlign) {
1610
0
    case xfaFieldLayoutVAlignTop:
1611
0
    default:
1612
0
      vAlign = acroFormVAlignTop;
1613
0
      break;
1614
0
    case xfaFieldLayoutVAlignMiddle:
1615
0
      vAlign = acroFormVAlignMiddle;
1616
0
      break;
1617
0
    case xfaFieldLayoutVAlignBottom:
1618
0
      vAlign = acroFormVAlignBottom;
1619
0
      break;
1620
0
    }
1621
0
  }
1622
0
  comb = 0;
1623
0
  if (flags & acroFormFlagComb) {
1624
0
    if (fieldLookup("MaxLen", &obj2)->isInt()) {
1625
0
      comb = obj2.getInt();
1626
0
    }
1627
0
    obj2.free();
1628
0
  }
1629
0
  XFAFieldPictureInfo *pictureInfo =
1630
0
      xfaField ? xfaField->getPictureInfo()
1631
0
               : (XFAFieldPictureInfo *)NULL;
1632
0
  GString *value2 = valueLatin1;
1633
0
  if (pictureInfo) {
1634
0
    switch (pictureInfo->subtype) {
1635
0
    case xfaFieldPictureDateTime:
1636
0
      value2 = pictureFormatDateTime(valueLatin1, pictureInfo->format);
1637
0
      break;
1638
0
    case xfaFieldPictureNumeric:
1639
0
      value2 = pictureFormatNumber(valueLatin1, pictureInfo->format);
1640
0
      break;
1641
0
    case xfaFieldPictureText:
1642
0
      value2 = pictureFormatText(valueLatin1, pictureInfo->format);
1643
0
      break;
1644
0
    }
1645
0
  }
1646
0
  drawText(value2, da, fontDict,
1647
0
     flags & acroFormFlagMultiline, comb, quadding, vAlign,
1648
0
     gTrue, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
1649
0
     borderWidth, gFalse, appearBuf);
1650
0
  if (value2 != valueLatin1) {
1651
0
    delete value2;
1652
0
  }
1653
0
      }
1654
0
      delete valueLatin1;
1655
0
    }
1656
0
  } else if (ftObj.isName("Ch")) {
1657
    //~ value/option strings can be Unicode
1658
0
    if (fieldLookup("Q", &obj1)->isInt()) {
1659
0
      quadding = obj1.getInt();
1660
0
    } else {
1661
0
      quadding = acroFormQuadLeft;
1662
0
    }
1663
0
    obj1.free();
1664
0
    vAlign = acroFormVAlignMiddle;
1665
0
    XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
1666
0
                                              : (XFAFieldLayoutInfo *)NULL;
1667
0
    if (layoutInfo) {
1668
0
      switch (layoutInfo->hAlign) {
1669
0
      case xfaFieldLayoutHAlignLeft:
1670
0
      default:
1671
0
  quadding = acroFormQuadLeft;
1672
0
  break;
1673
0
      case xfaFieldLayoutHAlignCenter:
1674
0
  quadding = acroFormQuadCenter;
1675
0
  break;
1676
0
      case xfaFieldLayoutHAlignRight:
1677
0
  quadding = acroFormQuadRight;
1678
0
  break;
1679
0
      }
1680
0
      switch (layoutInfo->vAlign) {
1681
0
      case xfaFieldLayoutVAlignTop:
1682
0
      default:
1683
0
  vAlign = acroFormVAlignTop;
1684
0
  break;
1685
0
      case xfaFieldLayoutVAlignMiddle:
1686
0
  vAlign = acroFormVAlignMiddle;
1687
0
  break;
1688
0
      case xfaFieldLayoutVAlignBottom:
1689
0
  vAlign = acroFormVAlignBottom;
1690
0
  break;
1691
0
      }
1692
0
    }
1693
    // combo box
1694
0
    if (flags & acroFormFlagCombo) {
1695
0
      if (value) {
1696
0
  val = unicodeToLatin1(value, valueLength);
1697
0
  if (fieldObj.dictLookup("Opt", &obj2)->isArray()) {
1698
0
    for (i = 0, done = false; i < obj2.arrayGetLength() && !done; ++i) {
1699
0
      obj2.arrayGet(i, &obj3);
1700
0
      if (obj3.isArray() && obj3.arrayGetLength() == 2) {
1701
0
        if (obj3.arrayGet(0, &obj4)->isString() &&
1702
0
      obj4.getString()->cmp(val) == 0) {
1703
0
    obj4.free();
1704
0
    if (obj3.arrayGet(1, &obj4)->isString()) {
1705
0
      delete val;
1706
0
      val = obj4.getString()->copy();
1707
0
    }
1708
0
    done = gTrue;
1709
0
        }
1710
0
        obj4.free();
1711
0
      }
1712
0
      obj3.free();
1713
0
    }
1714
0
  }
1715
0
  obj2.free();
1716
0
  drawText(val, da, fontDict,
1717
0
     gFalse, 0, quadding, vAlign, gTrue, gFalse, rot,
1718
0
     0, 0, xMax - xMin, yMax - yMin, borderWidth,
1719
0
     gFalse, appearBuf);
1720
0
  delete val;
1721
  //~ Acrobat draws a popup icon on the right side
1722
0
      }
1723
    // list box
1724
0
    } else {
1725
0
      if (fieldObj.dictLookup("Opt", &obj1)->isArray()) {
1726
0
  nOptions = obj1.arrayGetLength();
1727
  // get the option text
1728
0
  text = (GString **)gmallocn(nOptions, sizeof(GString *));
1729
0
  for (i = 0; i < nOptions; ++i) {
1730
0
    text[i] = NULL;
1731
0
    obj1.arrayGet(i, &obj2);
1732
0
    if (obj2.isString()) {
1733
0
      text[i] = obj2.getString()->copy();
1734
0
    } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
1735
0
      if (obj2.arrayGet(1, &obj3)->isString()) {
1736
0
        text[i] = obj3.getString()->copy();
1737
0
      }
1738
0
      obj3.free();
1739
0
    }
1740
0
    obj2.free();
1741
0
    if (!text[i]) {
1742
0
      text[i] = new GString();
1743
0
    }
1744
0
  }
1745
  // get the selected option(s)
1746
0
  selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
1747
  //~ need to use the I field in addition to the V field
1748
0
  for (i = 0; i < nOptions; ++i) {
1749
0
    selection[i] = unicodeStringEqual(value, valueLength, text[i]);
1750
0
  }
1751
  // get the top index
1752
0
  if (fieldObj.dictLookup("TI", &obj2)->isInt()) {
1753
0
    topIdx = obj2.getInt();
1754
0
    if (topIdx < 0 || topIdx >= nOptions) {
1755
0
      topIdx = 0;
1756
0
    }
1757
0
  } else {
1758
0
    topIdx = 0;
1759
0
  }
1760
0
  obj2.free();
1761
  // draw the text
1762
0
  drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding,
1763
0
        xMin, yMin, xMax, yMax, borderWidth, appearBuf);
1764
0
  for (i = 0; i < nOptions; ++i) {
1765
0
    delete text[i];
1766
0
  }
1767
0
  gfree(text);
1768
0
  gfree(selection);
1769
0
      }
1770
0
      obj1.free();
1771
0
    }
1772
0
  } else if (ftObj.isName("Sig")) {
1773
    //~ check to see if background is already drawn
1774
0
    gfxStateDict.initDict(acroForm->doc->getXRef());
1775
0
    obj1.initReal(0.5);
1776
0
    gfxStateDict.dictAdd(copyString("ca"), &obj1);
1777
0
    appearBuf->append("/GS1 gs\n");
1778
0
    appearBuf->appendf("0.7 0.7 1 rg 0 0 {0:.2f} {1:.2f} re f\n",
1779
0
           xMax - xMin, yMax - yMin);
1780
0
    caption = new GString("SIGN HERE");
1781
0
    if (da) {
1782
0
      delete da;
1783
0
    }
1784
0
    da = new GString("/Helv 10 Tf 1 0 0 rg");
1785
0
    drawText(caption, da, fontDict, gFalse, 0,
1786
0
       acroFormQuadLeft, acroFormVAlignMiddle, gFalse, gFalse, rot,
1787
0
       0, 0, xMax - xMin, yMax - yMin, borderWidth, gFalse, appearBuf);
1788
0
    delete caption;
1789
0
  } else {
1790
0
    error(errSyntaxError, -1, "Unknown field type");
1791
0
  }
1792
1793
0
  gfree(value);
1794
1795
0
  delete appearanceState;
1796
0
  if (da) {
1797
0
    delete da;
1798
0
  }
1799
1800
  // build the appearance stream dictionary
1801
0
  appearDict.initDict(acroForm->doc->getXRef());
1802
0
  appearDict.dictAdd(copyString("Length"),
1803
0
         obj1.initInt(appearBuf->getLength()));
1804
0
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
1805
0
  obj1.initArray(acroForm->doc->getXRef());
1806
0
  obj1.arrayAdd(obj2.initReal(0));
1807
0
  obj1.arrayAdd(obj2.initReal(0));
1808
0
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
1809
0
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
1810
0
  appearDict.dictAdd(copyString("BBox"), &obj1);
1811
1812
  // set the resource dictionary; add a default font
1813
0
  if (drObj.isDict()) {
1814
0
    drObj.copy(&resources);
1815
0
  } else {
1816
0
    resources.initDict(acroForm->doc->getXRef());
1817
0
  }
1818
0
  drObj.free();
1819
0
  fontResources.initDict(acroForm->doc->getXRef());
1820
0
  if (resources.dictLookup("Font", &obj1)->isDict()) {
1821
0
    for (i = 0; i < obj1.dictGetLength(); ++i) {
1822
0
      obj1.dictGetValNF(i, &obj2);
1823
0
      fontResources.dictAdd(copyString(obj1.dictGetKey(i)), &obj2);
1824
0
    }
1825
0
  }
1826
0
  obj1.free();
1827
0
  defaultFont.initDict(acroForm->doc->getXRef());
1828
0
  defaultFont.dictAdd(copyString("Type"), obj1.initName("Font"));
1829
0
  defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1"));
1830
0
  defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica"));
1831
0
  defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding"));
1832
0
  fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
1833
0
  resources.dictAdd(copyString("Font"), &fontResources);
1834
0
  if (gfxStateDict.isDict()) {
1835
0
    obj1.initDict(acroForm->doc->getXRef());
1836
0
    obj1.dictAdd(copyString("GS1"), &gfxStateDict);
1837
0
    resources.dictAdd(copyString("ExtGState"), &obj1);
1838
0
  }
1839
0
  appearDict.dictAdd(copyString("Resources"), &resources);
1840
1841
  // build the appearance stream
1842
0
  appearStream = new MemStream(appearBuf->getCString(), 0,
1843
0
             appearBuf->getLength(), &appearDict);
1844
0
  appearance.initStream(appearStream);
1845
1846
  // draw it
1847
0
  gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
1848
1849
0
  appearance.free();
1850
0
  delete appearBuf;
1851
0
  appearBuf = NULL;
1852
0
  if (fontDict) {
1853
0
    delete fontDict;
1854
0
  }
1855
0
  ftObj.free();
1856
0
  mkObj.free();
1857
0
}
1858
1859
// Set the current fill or stroke color, based on <a> (which should
1860
// have 1, 3, or 4 elements).  If <adjust> is +1, color is brightened;
1861
// if <adjust> is -1, color is darkened; otherwise color is not
1862
// modified.
1863
void AcroFormField::setColor(Array *a, GBool fill, int adjust,
1864
0
           GString *appearBuf) {
1865
0
  Object obj1;
1866
0
  double color[4];
1867
0
  int nComps, i;
1868
1869
0
  nComps = a->getLength();
1870
0
  if (nComps > 4) {
1871
0
    nComps = 4;
1872
0
  }
1873
0
  for (i = 0; i < nComps && i < 4; ++i) {
1874
0
    if (a->get(i, &obj1)->isNum()) {
1875
0
      color[i] = obj1.getNum();
1876
0
    } else {
1877
0
      color[i] = 0;
1878
0
    }
1879
0
    obj1.free();
1880
0
  }
1881
0
  if (nComps == 4) {
1882
0
    adjust = -adjust;
1883
0
  }
1884
0
  if (adjust > 0) {
1885
0
    for (i = 0; i < nComps; ++i) {
1886
0
      color[i] = 0.5 * color[i] + 0.5;
1887
0
    }
1888
0
  } else if (adjust < 0) {
1889
0
    for (i = 0; i < nComps; ++i) {
1890
0
      color[i] = 0.5 * color[i];
1891
0
    }
1892
0
  }
1893
0
  if (nComps == 4) {
1894
0
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1895
0
           color[0], color[1], color[2], color[3],
1896
0
           fill ? 'k' : 'K');
1897
0
  } else if (nComps == 3) {
1898
0
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1899
0
           color[0], color[1], color[2],
1900
0
           fill ? "rg" : "RG");
1901
0
  } else {
1902
0
    appearBuf->appendf("{0:.2f} {1:c}\n",
1903
0
           color[0],
1904
0
           fill ? 'g' : 'G');
1905
0
  }
1906
0
}
1907
1908
// Draw the variable text or caption for a field.
1909
void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
1910
           GBool multiline, int comb,
1911
           int quadding, int vAlign,
1912
           GBool txField, GBool forceZapfDingbats, int rot,
1913
           double x, double y, double width, double height,
1914
           double border, GBool whiteBackground,
1915
0
           GString *appearBuf) {
1916
0
  GString *text2;
1917
0
  GList *daToks;
1918
0
  GString *tok;
1919
0
  GfxFont *font;
1920
0
  double dx, dy;
1921
0
  double fontSize, fontSize2, topBorder, xx, xPrev, yy, w, wMax;
1922
0
  double offset, offset2, charWidth, ascent, descent;
1923
0
  int tfPos, tmPos, nLines, i, j, k, c;
1924
1925
  //~ if there is no MK entry, this should use the existing content stream,
1926
  //~ and only replace the marked content portion of it
1927
  //~ (this is only relevant for Tx fields)
1928
1929
  // check for a Unicode string
1930
  //~ this currently drops all non-Latin1 characters
1931
0
  if (text->getLength() >= 2 &&
1932
0
      text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
1933
0
    text2 = new GString();
1934
0
    for (i = 2; i+1 < text->getLength(); i += 2) {
1935
0
      c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
1936
0
      if (c <= 0xff) {
1937
0
  text2->append((char)c);
1938
0
      } else {
1939
0
  text2->append('?');
1940
0
      }
1941
0
    }
1942
0
  } else {
1943
0
    text2 = text;
1944
0
  }
1945
0
  if (text2->getLength() == 0) {
1946
0
    if (text2 != text) {
1947
0
      delete text2;
1948
0
    }
1949
0
    return;
1950
0
  }
1951
1952
  // parse the default appearance string
1953
0
  tfPos = tmPos = -1;
1954
0
  if (da) {
1955
0
    daToks = tokenize(da);
1956
0
    for (i = 2; i < daToks->getLength(); ++i) {
1957
0
      if (!((GString *)daToks->get(i))->cmp("Tf")) {
1958
0
  tfPos = i - 2;
1959
0
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
1960
0
  tmPos = i - 6;
1961
0
      }
1962
0
    }
1963
0
  } else {
1964
0
    daToks = NULL;
1965
0
  }
1966
1967
  // force ZapfDingbats
1968
  //~ this should create the font if needed (?)
1969
0
  if (forceZapfDingbats) {
1970
0
    if (tfPos >= 0) {
1971
0
      tok = (GString *)daToks->get(tfPos);
1972
0
      if (tok->cmp("/ZaDb")) {
1973
0
  tok->clear();
1974
0
  tok->append("/ZaDb");
1975
0
      }
1976
0
    }
1977
0
  }
1978
1979
  // get the font and font size
1980
0
  font = NULL;
1981
0
  fontSize = 0;
1982
0
  if (tfPos >= 0) {
1983
0
    tok = (GString *)daToks->get(tfPos);
1984
0
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
1985
0
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
1986
0
  error(errSyntaxError, -1, "Unknown font in field's DA string");
1987
0
  tok->clear();
1988
0
  tok->append("/xpdf_default_font");
1989
0
      }
1990
0
    } else {
1991
0
      error(errSyntaxError, -1,
1992
0
      "Invalid font name in 'Tf' operator in field's DA string");
1993
0
    }
1994
0
    tok = (GString *)daToks->get(tfPos + 1);
1995
0
    fontSize = atof(tok->getCString());
1996
0
  } else {
1997
0
    error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
1998
0
    fontSize = 0;
1999
0
    if (!daToks) {
2000
0
      daToks = new GList();
2001
0
    }
2002
0
    tfPos = daToks->getLength();
2003
0
    daToks->append(new GString("/xpdf_default_font"));
2004
0
    daToks->append(new GString("10"));
2005
0
    daToks->append(new GString("Tf"));
2006
0
  }
2007
2008
  // setup
2009
0
  if (txField) {
2010
0
    appearBuf->append("/Tx BMC\n");
2011
0
  }
2012
0
  appearBuf->append("q\n");
2013
0
  if (rot == 90) {
2014
0
    appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", width);
2015
0
    dx = height;
2016
0
    dy = width;
2017
0
  } else if (rot == 180) {
2018
0
    appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", width, height);
2019
0
    dx = width;
2020
0
    dy = height;
2021
0
  } else if (rot == 270) {
2022
0
    appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", height);
2023
0
    dx = height;
2024
0
    dy = width;
2025
0
  } else { // assume rot == 0
2026
0
    dx = width;
2027
0
    dy = height;
2028
0
  }
2029
2030
  // multi-line text
2031
0
  if (multiline) {
2032
    // note: the comb flag is ignored in multiline mode
2033
2034
0
    wMax = dx - 2 * border - 4;
2035
2036
0
#if 1 //~tmp
2037
    // this is a kludge that appears to match Adobe's behavior
2038
0
    if (height > 15) {
2039
0
      topBorder = 5;
2040
0
    } else {
2041
0
      topBorder = 2;
2042
0
    }
2043
#else
2044
    topBorder = 5;
2045
#endif
2046
2047
    // compute font autosize
2048
0
    if (fontSize == 0) {
2049
0
      for (fontSize = 10; fontSize > 1; --fontSize) {
2050
0
  yy = dy - topBorder;
2051
0
  i = 0;
2052
0
  while (i < text2->getLength()) {
2053
0
    getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2054
0
    i = k;
2055
0
    yy -= fontSize;
2056
0
  }
2057
  // approximate the descender for the last line
2058
0
  if (yy >= 0.25 * fontSize && w <= wMax) {
2059
0
    break;
2060
0
  }
2061
0
      }
2062
0
      if (tfPos >= 0) {
2063
0
  tok = (GString *)daToks->get(tfPos + 1);
2064
0
  tok->clear();
2065
0
  tok->appendf("{0:.2f}", fontSize);
2066
0
      }
2067
0
    }
2068
2069
    // starting y coordinate
2070
0
    nLines = 0;
2071
0
    i = 0;
2072
0
    while (i < text2->getLength()) {
2073
0
      getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2074
0
      i = k;
2075
0
      ++nLines;
2076
0
    }
2077
0
    if (font) {
2078
0
      ascent = font->getDeclaredAscent() * fontSize;
2079
0
      descent = font->getDescent() * fontSize;
2080
0
    } else {
2081
0
      ascent = 0.75 * fontSize;
2082
0
      descent = -0.25 * fontSize;
2083
0
    }
2084
0
    switch (vAlign) {
2085
0
    case acroFormVAlignTop:
2086
0
    default:
2087
0
      yy = dy - ascent - topBorder;
2088
0
      break;
2089
0
    case acroFormVAlignMiddle:
2090
0
      yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize - descent;
2091
0
      break;
2092
0
    case acroFormVAlignMiddleNoDescender:
2093
0
      yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize;
2094
0
      break;
2095
0
    case acroFormVAlignBottom:
2096
0
      yy = (nLines - 1) * fontSize - descent;
2097
0
      break;
2098
0
    }
2099
    // if the field is shorter than a line of text, Acrobat positions
2100
    // the text relative to the bottom edge
2101
0
    if (dy < fontSize + topBorder) {
2102
0
      yy = 2 - descent;
2103
0
    }
2104
    // each line of text starts with a Td operator that moves down a
2105
    // line -- so move up a line here
2106
0
    yy += fontSize;
2107
2108
0
    appearBuf->append("BT\n");
2109
2110
    // set the font matrix
2111
0
    if (tmPos >= 0) {
2112
0
      tok = (GString *)daToks->get(tmPos + 4);
2113
0
      tok->clear();
2114
0
      tok->appendf("{0:.4f}", x);
2115
0
      tok = (GString *)daToks->get(tmPos + 5);
2116
0
      tok->clear();
2117
0
      tok->appendf("{0:.4f}", y + yy);
2118
0
    }
2119
2120
    // write the DA string
2121
0
    if (daToks) {
2122
0
      for (i = 0; i < daToks->getLength(); ++i) {
2123
0
  appearBuf->append((GString *)daToks->get(i))->append(' ');
2124
0
      }
2125
0
    }
2126
2127
    // write the font matrix (if not part of the DA string)
2128
0
    if (tmPos < 0) {
2129
0
      appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y + yy);
2130
0
    }
2131
2132
    // write a series of lines of text
2133
0
    i = 0;
2134
0
    xPrev = 0;
2135
0
    while (i < text2->getLength()) {
2136
2137
0
      getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2138
2139
      // compute text start position
2140
0
      switch (quadding) {
2141
0
      case acroFormQuadLeft:
2142
0
      default:
2143
0
  xx = border + 2;
2144
0
  break;
2145
0
      case acroFormQuadCenter:
2146
0
  xx = (dx - w) / 2;
2147
0
  break;
2148
0
      case acroFormQuadRight:
2149
0
  xx = dx - border - 2 - w;
2150
0
  break;
2151
0
      }
2152
2153
      // draw the line
2154
0
      appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx - xPrev, -fontSize);
2155
0
      appearBuf->append('(');
2156
0
      for (; i < j; ++i) {
2157
0
  c = text2->getChar(i) & 0xff;
2158
0
  if (c == '(' || c == ')' || c == '\\') {
2159
0
    appearBuf->append('\\');
2160
0
    appearBuf->append((char)c);
2161
0
  } else if (c < 0x20 || c >= 0x80) {
2162
0
    appearBuf->appendf("\\{0:03o}", c);
2163
0
  } else {
2164
0
    appearBuf->append((char)c);
2165
0
  }
2166
0
      }
2167
0
      appearBuf->append(") Tj\n");
2168
2169
      // next line
2170
0
      i = k;
2171
0
      xPrev = xx;
2172
0
    }
2173
2174
0
    appearBuf->append("ET\n");
2175
2176
  // single-line text
2177
0
  } else {
2178
    //~ replace newlines with spaces? - what does Acrobat do?
2179
2180
    // comb formatting
2181
0
    if (comb > 0) {
2182
2183
      // compute comb spacing
2184
0
      w = dx / comb;
2185
2186
      // compute font autosize
2187
0
      if (fontSize == 0) {
2188
0
  fontSize = dy - 2 * border;
2189
0
  if (w < fontSize) {
2190
0
    fontSize = w;
2191
0
  }
2192
0
  fontSize = floor(fontSize);
2193
0
  if (fontSize > 10) {
2194
0
    fontSize = 10;
2195
0
  }
2196
0
  if (tfPos >= 0) {
2197
0
    tok = (GString *)daToks->get(tfPos + 1);
2198
0
    tok->clear();
2199
0
    tok->appendf("{0:.4f}", fontSize);
2200
0
  }
2201
0
      }
2202
2203
      // compute text start position
2204
0
      switch (quadding) {
2205
0
      case acroFormQuadLeft:
2206
0
      default:
2207
0
  xx = 0;
2208
0
  break;
2209
0
      case acroFormQuadCenter:
2210
0
  xx = ((comb - text2->getLength()) / 2) * w;
2211
0
  break;
2212
0
      case acroFormQuadRight:
2213
0
  xx = (comb - text2->getLength()) * w;
2214
0
  break;
2215
0
      }
2216
0
      if (font) {
2217
0
  ascent = font->getDeclaredAscent() * fontSize;
2218
0
  descent = font->getDescent() * fontSize;
2219
0
      } else {
2220
0
  ascent = 0.75 * fontSize;
2221
0
  descent = -0.25 * fontSize;
2222
0
      }
2223
0
      switch (vAlign) {
2224
0
      case acroFormVAlignTop:
2225
0
      default:
2226
0
  yy = dy - ascent;
2227
0
  break;
2228
0
      case acroFormVAlignMiddle:
2229
0
  yy = 0.5 * (dy - ascent - descent);
2230
0
  break;
2231
0
      case acroFormVAlignMiddleNoDescender:
2232
0
  yy = 0.5 * (dy - ascent);
2233
0
  break;
2234
0
      case acroFormVAlignBottom:
2235
0
  yy = -descent;
2236
0
  break;
2237
0
      }
2238
2239
0
      appearBuf->append("BT\n");
2240
2241
      // set the font matrix
2242
0
      if (tmPos >= 0) {
2243
0
  tok = (GString *)daToks->get(tmPos + 4);
2244
0
  tok->clear();
2245
0
  tok->appendf("{0:.4f}", x + xx);
2246
0
  tok = (GString *)daToks->get(tmPos + 5);
2247
0
  tok->clear();
2248
0
  tok->appendf("{0:.4f}", y + yy);
2249
0
      }
2250
2251
      // write the DA string
2252
0
      if (daToks) {
2253
0
  for (i = 0; i < daToks->getLength(); ++i) {
2254
0
    appearBuf->append((GString *)daToks->get(i))->append(' ');
2255
0
  }
2256
0
      }
2257
2258
      // write the font matrix (if not part of the DA string)
2259
0
      if (tmPos < 0) {
2260
0
  appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
2261
0
      }
2262
2263
      // write the text string
2264
0
      offset = 0;
2265
0
      for (i = 0; i < text2->getLength(); ++i) {
2266
0
  c = text2->getChar(i) & 0xff;
2267
0
  if (c >= 0x20 && c < 0x80) {
2268
0
    if (font && !font->isCIDFont()) {
2269
0
      charWidth = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
2270
0
    } else {
2271
      // otherwise, make a crude estimate
2272
0
      charWidth = 0.5 * fontSize;
2273
0
    }
2274
0
    offset2 = 0.5 * (w - charWidth);
2275
0
    appearBuf->appendf("{0:.4f} 0 Td\n", offset + offset2);
2276
0
    if (c == '(' || c == ')' || c == '\\') {
2277
0
      appearBuf->appendf("(\\{0:c}) Tj\n", c);
2278
0
    } else {
2279
0
      appearBuf->appendf("({0:c}) Tj\n", c);
2280
0
    }
2281
0
    offset = w - offset2;
2282
0
  } else {
2283
0
    offset += w;
2284
0
  }
2285
0
      }
2286
2287
0
      appearBuf->append("ET\n");
2288
2289
    // regular (non-comb) formatting
2290
0
    } else {
2291
2292
      // compute string width
2293
0
      if (font && !font->isCIDFont()) {
2294
0
  w = 0;
2295
0
  for (i = 0; i < text2->getLength(); ++i) {
2296
0
    w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
2297
0
  }
2298
0
      } else {
2299
  // otherwise, make a crude estimate
2300
0
  w = text2->getLength() * 0.5;
2301
0
      }
2302
2303
      // compute font autosize
2304
0
      if (fontSize == 0) {
2305
0
  fontSize = dy - 2 * border;
2306
0
  fontSize2 = (dx - 4 - 2 * border) / w;
2307
0
  if (fontSize2 < fontSize) {
2308
0
    fontSize = fontSize2;
2309
0
  }
2310
0
  fontSize = floor(fontSize);
2311
0
  if (fontSize > 10) {
2312
0
    fontSize = 10;
2313
0
  }
2314
0
  if (tfPos >= 0) {
2315
0
    tok = (GString *)daToks->get(tfPos + 1);
2316
0
    tok->clear();
2317
0
    tok->appendf("{0:.4f}", fontSize);
2318
0
  }
2319
0
      }
2320
2321
      // compute text start position
2322
0
      w *= fontSize;
2323
0
      switch (quadding) {
2324
0
      case acroFormQuadLeft:
2325
0
      default:
2326
0
  xx = border + 2;
2327
0
  break;
2328
0
      case acroFormQuadCenter:
2329
0
  xx = (dx - w) / 2;
2330
0
  break;
2331
0
      case acroFormQuadRight:
2332
0
  xx = dx - border - 2 - w;
2333
0
  break;
2334
0
      }
2335
0
      if (font) {
2336
0
  ascent = font->getDeclaredAscent() * fontSize;
2337
0
  descent = font->getDescent() * fontSize;
2338
0
      } else {
2339
0
  ascent = 0.75 * fontSize;
2340
0
  descent = -0.25 * fontSize;
2341
0
      }
2342
0
      switch (vAlign) {
2343
0
      case acroFormVAlignTop:
2344
0
      default:
2345
0
  yy = dy - ascent;
2346
0
  break;
2347
0
      case acroFormVAlignMiddle:
2348
0
  yy = 0.5 * (dy - ascent - descent);
2349
0
  break;
2350
0
      case acroFormVAlignMiddleNoDescender:
2351
0
  yy = 0.5 * (dy - ascent);
2352
0
  break;
2353
0
      case acroFormVAlignBottom:
2354
0
  yy = -descent;
2355
0
  break;
2356
0
      }
2357
2358
0
      if (whiteBackground) {
2359
0
  appearBuf->appendf("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n",
2360
0
         xx - 0.25 * fontSize, yy - 0.35 * fontSize,
2361
0
         w + 0.5 * fontSize, 1.2 * fontSize);
2362
0
      }
2363
2364
0
      appearBuf->append("BT\n");
2365
2366
      // set the font matrix
2367
0
      if (tmPos >= 0) {
2368
0
  tok = (GString *)daToks->get(tmPos + 4);
2369
0
  tok->clear();
2370
0
  tok->appendf("{0:.4f}", x + xx);
2371
0
  tok = (GString *)daToks->get(tmPos + 5);
2372
0
  tok->clear();
2373
0
  tok->appendf("{0:.4f}", y + yy);
2374
0
      }
2375
2376
      // write the DA string
2377
0
      if (daToks) {
2378
0
  for (i = 0; i < daToks->getLength(); ++i) {
2379
0
    appearBuf->append((GString *)daToks->get(i))->append(' ');
2380
0
  }
2381
0
      }
2382
2383
      // write the font matrix (if not part of the DA string)
2384
0
      if (tmPos < 0) {
2385
0
  appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
2386
0
      }
2387
2388
      // write the text string
2389
0
      appearBuf->append('(');
2390
0
      for (i = 0; i < text2->getLength(); ++i) {
2391
0
  c = text2->getChar(i) & 0xff;
2392
0
  if (c == '(' || c == ')' || c == '\\') {
2393
0
    appearBuf->append('\\');
2394
0
    appearBuf->append((char)c);
2395
0
  } else if (c < 0x20 || c >= 0x80) {
2396
0
    appearBuf->appendf("\\{0:03o}", c);
2397
0
  } else {
2398
0
    appearBuf->append((char)c);
2399
0
  }
2400
0
      }
2401
0
      appearBuf->append(") Tj\n");
2402
0
    }
2403
2404
0
    appearBuf->append("ET\n");
2405
0
  }
2406
2407
  // cleanup
2408
0
  appearBuf->append("Q\n");
2409
0
  if (txField) {
2410
0
    appearBuf->append("EMC\n");
2411
0
  }
2412
2413
0
  if (daToks) {
2414
0
    deleteGList(daToks, GString);
2415
0
  }
2416
0
  if (text2 != text) {
2417
0
    delete text2;
2418
0
  }
2419
0
}
2420
2421
// Draw the variable text or caption for a field.
2422
void AcroFormField::drawListBox(GString **text, GBool *selection,
2423
        int nOptions, int topIdx,
2424
        GString *da, GfxFontDict *fontDict,
2425
        GBool quadding, double xMin, double yMin,
2426
        double xMax, double yMax, double border,
2427
0
        GString *appearBuf) {
2428
0
  GList *daToks;
2429
0
  GString *tok;
2430
0
  GfxFont *font;
2431
0
  double fontSize, fontSize2, x, y, w, wMax;
2432
0
  int tfPos, tmPos, i, j, c;
2433
2434
  //~ if there is no MK entry, this should use the existing content stream,
2435
  //~ and only replace the marked content portion of it
2436
  //~ (this is only relevant for Tx fields)
2437
2438
  // parse the default appearance string
2439
0
  tfPos = tmPos = -1;
2440
0
  if (da) {
2441
0
    daToks = tokenize(da);
2442
0
    for (i = 2; i < daToks->getLength(); ++i) {
2443
0
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
2444
0
  tfPos = i - 2;
2445
0
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
2446
0
  tmPos = i - 6;
2447
0
      }
2448
0
    }
2449
0
  } else {
2450
0
    daToks = NULL;
2451
0
  }
2452
2453
  // get the font and font size
2454
0
  font = NULL;
2455
0
  fontSize = 0;
2456
0
  if (tfPos >= 0) {
2457
0
    tok = (GString *)daToks->get(tfPos);
2458
0
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2459
0
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2460
0
  error(errSyntaxError, -1, "Unknown font in field's DA string");
2461
0
  tok->clear();
2462
0
  tok->append("/xpdf_default_font");
2463
0
      }
2464
0
    } else {
2465
0
      error(errSyntaxError, -1,
2466
0
      "Invalid font name in 'Tf' operator in field's DA string");
2467
0
    }
2468
0
    tok = (GString *)daToks->get(tfPos + 1);
2469
0
    fontSize = atof(tok->getCString());
2470
0
  } else {
2471
0
    error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
2472
0
  }
2473
2474
  // compute font autosize
2475
0
  if (fontSize == 0) {
2476
0
    wMax = 0;
2477
0
    for (i = 0; i < nOptions; ++i) {
2478
0
      if (font && !font->isCIDFont()) {
2479
0
  w = 0;
2480
0
  for (j = 0; j < text[i]->getLength(); ++j) {
2481
0
    w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
2482
0
  }
2483
0
      } else {
2484
  // otherwise, make a crude estimate
2485
0
  w = text[i]->getLength() * 0.5;
2486
0
      }
2487
0
      if (w > wMax) {
2488
0
  wMax = w;
2489
0
      }
2490
0
    }
2491
0
    fontSize = yMax - yMin - 2 * border;
2492
0
    fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
2493
0
    if (fontSize2 < fontSize) {
2494
0
      fontSize = fontSize2;
2495
0
    }
2496
0
    fontSize = floor(fontSize);
2497
0
    if (fontSize > 10) {
2498
0
      fontSize = 10;
2499
0
    }
2500
0
    if (tfPos >= 0) {
2501
0
      tok = (GString *)daToks->get(tfPos + 1);
2502
0
      tok->clear();
2503
0
      tok->appendf("{0:.4f}", fontSize);
2504
0
    }
2505
0
  }
2506
2507
  // draw the text
2508
0
  y = yMax - yMin - 1.1 * fontSize;
2509
0
  for (i = topIdx; i < nOptions; ++i) {
2510
2511
    // setup
2512
0
    appearBuf->append("q\n");
2513
2514
    // draw the background if selected
2515
0
    if (selection[i]) {
2516
0
      appearBuf->append("0 g f\n");
2517
0
      appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
2518
0
        border,
2519
0
        y - 0.2 * fontSize,
2520
0
        xMax - xMin - 2 * border,
2521
0
        1.1 * fontSize);
2522
0
    }
2523
2524
    // setup
2525
0
    appearBuf->append("BT\n");
2526
2527
    // compute string width
2528
0
    if (font && !font->isCIDFont()) {
2529
0
      w = 0;
2530
0
      for (j = 0; j < text[i]->getLength(); ++j) {
2531
0
  w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
2532
0
      }
2533
0
    } else {
2534
      // otherwise, make a crude estimate
2535
0
      w = text[i]->getLength() * 0.5;
2536
0
    }
2537
2538
    // compute text start position
2539
0
    w *= fontSize;
2540
0
    switch (quadding) {
2541
0
    case acroFormQuadLeft:
2542
0
    default:
2543
0
      x = border + 2;
2544
0
      break;
2545
0
    case acroFormQuadCenter:
2546
0
      x = (xMax - xMin - w) / 2;
2547
0
      break;
2548
0
    case acroFormQuadRight:
2549
0
      x = xMax - xMin - border - 2 - w;
2550
0
      break;
2551
0
    }
2552
2553
    // set the font matrix
2554
0
    if (tmPos >= 0) {
2555
0
      tok = (GString *)daToks->get(tmPos + 4);
2556
0
      tok->clear();
2557
0
      tok->appendf("{0:.4f}", x);
2558
0
      tok = (GString *)daToks->get(tmPos + 5);
2559
0
      tok->clear();
2560
0
      tok->appendf("{0:.4f}", y);
2561
0
    }
2562
2563
    // write the DA string
2564
0
    if (daToks) {
2565
0
      for (j = 0; j < daToks->getLength(); ++j) {
2566
0
  appearBuf->append((GString *)daToks->get(j))->append(' ');
2567
0
      }
2568
0
    }
2569
2570
    // write the font matrix (if not part of the DA string)
2571
0
    if (tmPos < 0) {
2572
0
      appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y);
2573
0
    }
2574
2575
    // change the text color if selected
2576
0
    if (selection[i]) {
2577
0
      appearBuf->append("1 g\n");
2578
0
    }
2579
2580
    // write the text string
2581
0
    appearBuf->append('(');
2582
0
    for (j = 0; j < text[i]->getLength(); ++j) {
2583
0
      c = text[i]->getChar(j) & 0xff;
2584
0
      if (c == '(' || c == ')' || c == '\\') {
2585
0
  appearBuf->append('\\');
2586
0
  appearBuf->append((char)c);
2587
0
      } else if (c < 0x20 || c >= 0x80) {
2588
0
  appearBuf->appendf("\\{0:03o}", c);
2589
0
      } else {
2590
0
  appearBuf->append((char)c);
2591
0
      }
2592
0
    }
2593
0
    appearBuf->append(") Tj\n");
2594
2595
    // cleanup
2596
0
    appearBuf->append("ET\n");
2597
0
    appearBuf->append("Q\n");
2598
2599
    // next line
2600
0
    y -= 1.1 * fontSize;
2601
0
  }
2602
2603
0
  if (daToks) {
2604
0
    deleteGList(daToks, GString);
2605
0
  }
2606
0
}
2607
2608
// Figure out how much text will fit on the next line.  Returns:
2609
// *end = one past the last character to be included
2610
// *width = width of the characters start .. end-1
2611
// *next = index of first character on the following line
2612
void AcroFormField::getNextLine(GString *text, int start,
2613
        GfxFont *font, double fontSize, double wMax,
2614
0
        int *end, double *width, int *next) {
2615
0
  double w, dw;
2616
0
  int j, k, c;
2617
2618
  // figure out how much text will fit on the line
2619
  //~ what does Adobe do with tabs?
2620
0
  w = 0;
2621
0
  for (j = start; j < text->getLength() && w <= wMax; ++j) {
2622
0
    c = text->getChar(j) & 0xff;
2623
0
    if (c == 0x0a || c == 0x0d) {
2624
0
      break;
2625
0
    }
2626
0
    if (font && !font->isCIDFont()) {
2627
0
      dw = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
2628
0
    } else {
2629
      // otherwise, make a crude estimate
2630
0
      dw = 0.5 * fontSize;
2631
0
    }
2632
0
    w += dw;
2633
0
  }
2634
0
  if (w > wMax) {
2635
0
    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
2636
0
    for (; k > start && text->getChar(k-1) == ' '; --k) ;
2637
0
    if (k > start) {
2638
0
      j = k;
2639
0
    }
2640
0
    if (j == start) {
2641
      // handle the pathological case where the first character is
2642
      // too wide to fit on the line all by itself
2643
0
      j = start + 1;
2644
0
    }
2645
0
  }
2646
0
  *end = j;
2647
2648
  // compute the width
2649
0
  w = 0;
2650
0
  for (k = start; k < j; ++k) {
2651
0
    if (font && !font->isCIDFont()) {
2652
0
      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
2653
0
    } else {
2654
      // otherwise, make a crude estimate
2655
0
      dw = 0.5 * fontSize;
2656
0
    }
2657
0
    w += dw;
2658
0
  }
2659
0
  *width = w;
2660
2661
  // next line
2662
0
  while (j < text->getLength() && text->getChar(j) == ' ') {
2663
0
    ++j;
2664
0
  }
2665
0
  if (j < text->getLength() && text->getChar(j) == 0x0d) {
2666
0
    ++j;
2667
0
  }
2668
0
  if (j < text->getLength() && text->getChar(j) == 0x0a) {
2669
0
    ++j;
2670
0
  }
2671
0
  *next = j;
2672
0
}
2673
2674
// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
2675
// <cmd> is used to draw the circle ("f", "s", or "b").
2676
void AcroFormField::drawCircle(double cx, double cy, double r,
2677
0
             const char *cmd, GString *appearBuf) {
2678
0
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2679
0
         cx + r, cy);
2680
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2681
0
         cx + r, cy + bezierCircle * r,
2682
0
         cx + bezierCircle * r, cy + r,
2683
0
         cx, cy + r);
2684
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2685
0
         cx - bezierCircle * r, cy + r,
2686
0
         cx - r, cy + bezierCircle * r,
2687
0
         cx - r, cy);
2688
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2689
0
         cx - r, cy - bezierCircle * r,
2690
0
         cx - bezierCircle * r, cy - r,
2691
0
         cx, cy - r);
2692
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2693
0
         cx + bezierCircle * r, cy - r,
2694
0
         cx + r, cy - bezierCircle * r,
2695
0
         cx + r, cy);
2696
0
  appearBuf->appendf("{0:s}\n", cmd);
2697
0
}
2698
2699
// Draw the top-left half of an (approximate) circle of radius <r>
2700
// centered at (<cx>, <cy>).
2701
void AcroFormField::drawCircleTopLeft(double cx, double cy, double r,
2702
0
              GString *appearBuf) {
2703
0
  double r2;
2704
2705
0
  r2 = r / sqrt(2.0);
2706
0
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2707
0
         cx + r2, cy + r2);
2708
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2709
0
         cx + (1 - bezierCircle) * r2,
2710
0
         cy + (1 + bezierCircle) * r2,
2711
0
         cx - (1 - bezierCircle) * r2,
2712
0
         cy + (1 + bezierCircle) * r2,
2713
0
         cx - r2,
2714
0
         cy + r2);
2715
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2716
0
         cx - (1 + bezierCircle) * r2,
2717
0
         cy + (1 - bezierCircle) * r2,
2718
0
         cx - (1 + bezierCircle) * r2,
2719
0
         cy - (1 - bezierCircle) * r2,
2720
0
         cx - r2,
2721
0
         cy - r2);
2722
0
  appearBuf->append("S\n");
2723
0
}
2724
2725
// Draw the bottom-right half of an (approximate) circle of radius <r>
2726
// centered at (<cx>, <cy>).
2727
void AcroFormField::drawCircleBottomRight(double cx, double cy, double r,
2728
0
            GString *appearBuf) {
2729
0
  double r2;
2730
2731
0
  r2 = r / sqrt(2.0);
2732
0
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2733
0
         cx - r2, cy - r2);
2734
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2735
0
         cx - (1 - bezierCircle) * r2,
2736
0
         cy - (1 + bezierCircle) * r2,
2737
0
         cx + (1 - bezierCircle) * r2,
2738
0
         cy - (1 + bezierCircle) * r2,
2739
0
         cx + r2,
2740
0
         cy - r2);
2741
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2742
0
         cx + (1 + bezierCircle) * r2,
2743
0
         cy - (1 - bezierCircle) * r2,
2744
0
         cx + (1 + bezierCircle) * r2,
2745
0
         cy + (1 - bezierCircle) * r2,
2746
0
         cx + r2,
2747
0
         cy + r2);
2748
0
  appearBuf->append("S\n");
2749
0
}
2750
2751
void AcroFormField::drawBarcode(GString *value, GString *da,
2752
        GfxFontDict *fontDict, int rot,
2753
        double xMin, double yMin,
2754
        double xMax, double yMax,
2755
        XFAFieldBarcodeInfo *barcodeInfo,
2756
0
        GString *appearBuf) {
2757
  //--- handle rotation
2758
0
  double w, h;
2759
0
  appearBuf->append("q\n");
2760
0
  switch (rot) {
2761
0
  case 0:
2762
0
  default:
2763
0
    w = xMax - xMin;
2764
0
    h = yMax - yMin;
2765
0
    break;
2766
0
  case 90:
2767
0
    appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin);
2768
0
    w = yMax - yMin;
2769
0
    h = xMax - xMin;
2770
0
    break;
2771
0
  case 180:
2772
0
    appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
2773
0
    w = yMax - yMin;
2774
0
    h = xMax - xMin;
2775
0
    break;
2776
0
  case 270:
2777
0
    appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
2778
0
    w = yMax - yMin;
2779
0
    h = xMax - xMin;
2780
0
    break;
2781
0
  }
2782
2783
  //--- get the font size
2784
0
  double fontSize = 0.2 * h;
2785
0
  if (da) {
2786
0
    GList *daToks = tokenize(da);
2787
0
    for (int i = 2; i < daToks->getLength(); ++i) {
2788
0
      if (!((GString *)daToks->get(i))->cmp("Tf")) {
2789
0
  fontSize = atof(((GString *)daToks->get(i - 1))->getCString());
2790
0
  break;
2791
0
      }
2792
0
    }
2793
0
    deleteGList(daToks, GString);
2794
0
  }
2795
2796
  //--- compute the embedded text type position
2797
0
  GBool doText = gTrue;
2798
0
  double yText = 0;
2799
0
  int vAlign = acroFormVAlignTop;
2800
0
  double yBarcode = 0;
2801
0
  double hBarcode = 0;
2802
0
  GBool whiteBackground = gFalse;
2803
  //~ this uses an estimate of the font baseline position
2804
0
  if (barcodeInfo->textLocation &&
2805
0
      !barcodeInfo->textLocation->cmp("above")) {
2806
0
    yText = h;
2807
0
    vAlign = acroFormVAlignTop;
2808
0
    yBarcode = 0;
2809
0
    hBarcode = h - fontSize;
2810
0
  } else if (barcodeInfo->textLocation &&
2811
0
       !barcodeInfo->textLocation->cmp("belowEmbedded")) {
2812
0
    yText = 0;
2813
0
    vAlign = acroFormVAlignBottom;
2814
0
    yBarcode = 0;
2815
0
    hBarcode = h;
2816
0
    whiteBackground = gTrue;
2817
0
  } else if (barcodeInfo->textLocation &&
2818
0
       !barcodeInfo->textLocation->cmp("aboveEmbedded")) {
2819
0
    yText = h;
2820
0
    vAlign = acroFormVAlignTop;
2821
0
    yBarcode = 0;
2822
0
    hBarcode = h;
2823
0
    whiteBackground = gTrue;
2824
0
  } else if (barcodeInfo->textLocation &&
2825
0
       !barcodeInfo->textLocation->cmp("none")) {
2826
0
    doText = gFalse;
2827
0
  } else { // default is "below"
2828
0
    yText = 0;
2829
0
    vAlign = acroFormVAlignBottom;
2830
0
    yBarcode = fontSize;
2831
0
    hBarcode = h - fontSize;
2832
0
  }
2833
0
  double wText = w;
2834
2835
  //--- remove extraneous start/stop chars
2836
0
  GString *value2 = value->copy();
2837
0
  if (!barcodeInfo->barcodeType->cmp("code3Of9")) {
2838
0
    if (value2->getLength() >= 1 && value2->getChar(0) == '*') {
2839
0
      value2->del(0);
2840
0
    }
2841
0
    if (value2->getLength() >= 1 &&
2842
0
  value2->getChar(value2->getLength() - 1) == '*') {
2843
0
      value2->del(value2->getLength() - 1);
2844
0
    }
2845
0
  }
2846
2847
  //--- draw the bar code
2848
0
  if (!barcodeInfo->barcodeType->cmp("code3Of9")) {
2849
0
    if (!barcodeInfo->dataLength) {
2850
0
      error(errSyntaxError, -1,
2851
0
      "Missing 'dataLength' attribute in barcode field");
2852
0
      goto err;
2853
0
    }
2854
0
    appearBuf->append("0 g\n");
2855
0
    double wNarrow = w / ((7 + 3 * barcodeInfo->wideNarrowRatio)
2856
0
        * (barcodeInfo->dataLength + 2));
2857
0
    double xx = 0;
2858
0
    for (int i = -1; i <= value2->getLength(); ++i) {
2859
0
      int c;
2860
0
      if (i < 0 || i >= value2->getLength()) {
2861
0
  c = '*';
2862
0
      } else {
2863
0
  c = value2->getChar(i) & 0x7f;
2864
0
      }
2865
0
      for (int j = 0; j < 10; j += 2) {
2866
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
2867
0
         xx, yBarcode,
2868
0
         (code3Of9Data[c][j] ? barcodeInfo->wideNarrowRatio
2869
0
                             : 1) * wNarrow,
2870
0
         hBarcode);
2871
0
  xx += ((code3Of9Data[c][j] ? barcodeInfo->wideNarrowRatio : 1) +
2872
0
         (code3Of9Data[c][j+1] ? barcodeInfo->wideNarrowRatio : 1))
2873
0
        * wNarrow;
2874
0
      }
2875
0
    }
2876
    // center the text on the drawn barcode (not the max length barcode)
2877
0
    wText = (value2->getLength() + 2) * (7 + 3 * barcodeInfo->wideNarrowRatio)
2878
0
            * wNarrow;
2879
0
  } else if (!barcodeInfo->barcodeType->cmp("code128B")) {
2880
0
    if (!barcodeInfo->dataLength) {
2881
0
      error(errSyntaxError, -1,
2882
0
      "Missing 'dataLength' attribute in barcode field");
2883
0
      goto err;
2884
0
    }
2885
0
    appearBuf->append("0 g\n");
2886
0
    double wNarrow = w / (11 * (barcodeInfo->dataLength + 3) + 2);
2887
0
    double xx = 0;
2888
0
    int checksum = 0;
2889
0
    for (int i = -1; i <= value2->getLength() + 1; ++i) {
2890
0
      int c;
2891
0
      if (i == -1) {
2892
  // start code B
2893
0
  c = 104;
2894
0
  checksum += c;
2895
0
      } else if (i == value2->getLength()) {
2896
  // checksum
2897
0
  c = checksum % 103;
2898
0
      } else if (i == value2->getLength() + 1) {
2899
  // stop code
2900
0
  c = 106;
2901
0
      } else {
2902
0
  c = value2->getChar(i) & 0xff;
2903
0
  if (c >= 32 && c <= 127) {
2904
0
    c -= 32;
2905
0
  } else {
2906
0
    c = 0;
2907
0
  }    
2908
0
  checksum += (i + 1) * c;
2909
0
      }
2910
0
      for (int j = 0; j < 6; j += 2) {
2911
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
2912
0
         xx, yBarcode,
2913
0
         code128Data[c][j] * wNarrow, hBarcode);
2914
0
  xx += (code128Data[c][j] + code128Data[c][j+1]) * wNarrow;
2915
0
      }
2916
0
    }
2917
    // final bar of the stop code
2918
0
    appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
2919
0
           xx, yBarcode, 2 * wNarrow, hBarcode);
2920
    // center the text on the drawn barcode (not the max length barcode)
2921
0
    wText = (11 * (value2->getLength() + 3) + 2) * wNarrow;
2922
0
  } else if (!barcodeInfo->barcodeType->cmp("pdf417")) {
2923
0
    drawPDF417Barcode(w, h, barcodeInfo->moduleWidth,
2924
0
          barcodeInfo->moduleHeight,
2925
0
          barcodeInfo->errorCorrectionLevel,
2926
0
          value2, appearBuf);
2927
0
    doText = gFalse;
2928
0
  } else {
2929
0
    error(errSyntaxError, -1,
2930
0
    "Unimplemented barcode type '{0:t}' in barcode field",
2931
0
    barcodeInfo->barcodeType);
2932
0
  }
2933
  //~ add other barcode types here
2934
2935
  //--- draw the embedded text
2936
0
  if (doText) {
2937
0
    drawText(value2, da, fontDict, gFalse, 0, acroFormQuadCenter,
2938
0
       vAlign, gFalse, gFalse, 0, 0, yText, wText, yText + fontSize,
2939
0
       0, whiteBackground, appearBuf);
2940
0
  }
2941
2942
0
  appearBuf->append("Q\n");
2943
2944
0
 err:
2945
0
  delete value2;
2946
0
}
2947
2948
0
GList *AcroFormField::tokenize(GString *s) {
2949
0
  GList *toks;
2950
0
  int i, j;
2951
2952
0
  toks = new GList();
2953
0
  i = 0;
2954
0
  while (i < s->getLength()) {
2955
0
    while (i < s->getLength() && Lexer::isSpace(s->getChar(i))) {
2956
0
      ++i;
2957
0
    }
2958
0
    if (i < s->getLength()) {
2959
0
      for (j = i + 1;
2960
0
     j < s->getLength() && !Lexer::isSpace(s->getChar(j));
2961
0
     ++j) ;
2962
0
      toks->append(new GString(s, i, j - i));
2963
0
      i = j;
2964
0
    }
2965
0
  }
2966
0
  return toks;
2967
0
}
2968
2969
0
Object *AcroFormField::getResources(Object *res) {
2970
0
  Object kidsObj, annotObj, obj1;
2971
0
  int i;
2972
2973
0
  if (acroForm->needAppearances) {
2974
0
    fieldLookup("DR", res);
2975
0
  } else {
2976
0
    res->initArray(acroForm->doc->getXRef());
2977
    // find the annotation object(s)
2978
0
    if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
2979
0
      for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
2980
0
  kidsObj.arrayGet(i, &annotObj);
2981
0
  if (annotObj.isDict()) {
2982
0
    if (getAnnotResources(annotObj.getDict(), &obj1)->isDict()) {
2983
0
      res->arrayAdd(&obj1);
2984
0
    } else {
2985
0
      obj1.free();
2986
0
    }
2987
0
  }
2988
0
  annotObj.free();
2989
0
      }
2990
0
    } else {
2991
0
      if (getAnnotResources(fieldObj.getDict(), &obj1)->isDict()) {
2992
0
  res->arrayAdd(&obj1);
2993
0
      } else {
2994
0
  obj1.free();
2995
0
      }
2996
0
    }
2997
0
    kidsObj.free();
2998
0
  }
2999
3000
0
  return res;
3001
0
}
3002
3003
0
Object *AcroFormField::getFieldRef(Object *ref) {
3004
0
  return fieldRef.copy(ref);
3005
0
}
3006
3007
0
Object *AcroFormField::getValueObj(Object *val) {
3008
0
  return fieldLookup("V", val);
3009
0
}
3010
3011
0
Object *AcroFormField::getParentRef(Object *parent) {
3012
0
  return fieldObj.dictLookupNF("Parent", parent);
3013
0
}
3014
3015
// Get the first annotation object associated with this field.
3016
0
Object *AcroFormField::getAnnotObj(Object *annotObj) {
3017
0
  Object kidsObj;
3018
3019
0
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
3020
0
    if (kidsObj.arrayGetLength() > 0) {
3021
0
      kidsObj.arrayGet(0, annotObj);
3022
0
    } else {
3023
0
      annotObj->initNull();
3024
0
    }
3025
0
  } else {
3026
0
    fieldObj.copy(annotObj);
3027
0
  }
3028
0
  kidsObj.free();
3029
0
  return annotObj;
3030
0
}
3031
3032
0
Object *AcroFormField::getAnnotResources(Dict *annot, Object *res) {
3033
0
  Object apObj, asObj, appearance, obj1;
3034
3035
  // get the appearance stream
3036
0
  if (annot->lookup("AP", &apObj)->isDict()) {
3037
0
    apObj.dictLookup("N", &obj1);
3038
0
    if (obj1.isDict()) {
3039
0
      if (annot->lookup("AS", &asObj)->isName()) {
3040
0
  obj1.dictLookup(asObj.getName(), &appearance);
3041
0
      } else if (obj1.dictGetLength() == 1) {
3042
0
  obj1.dictGetVal(0, &appearance);
3043
0
      } else {
3044
0
  obj1.dictLookup("Off", &appearance);
3045
0
      }
3046
0
      asObj.free();
3047
0
    } else {
3048
0
      obj1.copy(&appearance);
3049
0
    }
3050
0
    obj1.free();
3051
0
  }
3052
0
  apObj.free();
3053
3054
0
  if (appearance.isStream()) {
3055
0
    appearance.streamGetDict()->lookup("Resources", res);
3056
0
  } else {
3057
0
    res->initNull();
3058
0
  }
3059
0
  appearance.free();
3060
3061
0
  return res;
3062
0
}
3063
3064
// Merge the field and AcroForm DR objects.
3065
0
void AcroFormField::buildDefaultResourceDict(Object *dr) {
3066
0
  Object formDR, fieldDR, resDict, newResDict, resObj;
3067
0
  char *resType, *resName;
3068
0
  int i, j;
3069
3070
  // NB: we need to deep-copy the dictionaries here, because multiple
3071
  // threads can be sharing these objects.
3072
3073
0
  dr->initDict(acroForm->doc->getXRef());
3074
3075
0
  acroForm->acroFormObj.dictLookup("DR", &formDR);
3076
0
  if (formDR.isDict()) {
3077
0
    for (i = 0; i < formDR.dictGetLength(); ++i) {
3078
0
      resType = formDR.dictGetKey(i);
3079
0
      formDR.dictGetVal(i, &resDict);
3080
0
      if (resDict.isDict()) {
3081
0
  newResDict.initDict(acroForm->doc->getXRef());
3082
0
  dr->dictAdd(copyString(resType), &newResDict);
3083
0
  for (j = 0; j < resDict.dictGetLength(); ++j) {
3084
0
    resName = resDict.dictGetKey(j);
3085
0
    resDict.dictGetValNF(j, &resObj);
3086
0
    newResDict.dictAdd(copyString(resName), &resObj);
3087
0
  }
3088
0
      }
3089
0
      resDict.free();
3090
0
    }
3091
0
  }
3092
0
  formDR.free();
3093
3094
0
  fieldObj.dictLookup("DR", &fieldDR);
3095
0
  if (fieldDR.isDict()) {
3096
0
    for (i = 0; i < fieldDR.dictGetLength(); ++i) {
3097
0
      resType = fieldDR.dictGetKey(i);
3098
0
      fieldDR.dictGetVal(i, &resDict);
3099
0
      if (resDict.isDict()) {
3100
0
  dr->dictLookup(resType, &newResDict);
3101
0
  if (!newResDict.isDict()) {
3102
0
    newResDict.free();
3103
0
    newResDict.initDict(acroForm->doc->getXRef());
3104
0
  }
3105
0
  dr->dictAdd(copyString(resType), &newResDict);
3106
0
  for (j = 0; j < resDict.dictGetLength(); ++j) {
3107
0
    resName = resDict.dictGetKey(j);
3108
0
    resDict.dictGetValNF(j, &resObj);
3109
0
    newResDict.dictAdd(copyString(resName), &resObj);
3110
0
  }
3111
0
      }
3112
0
      resDict.free();
3113
0
    }
3114
0
  }
3115
0
  fieldDR.free();
3116
0
}
3117
3118
// Look up an inheritable field dictionary entry.
3119
0
Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
3120
0
  return fieldLookup(fieldObj.getDict(), key, obj);
3121
0
}
3122
3123
0
Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
3124
0
  Object parent, parent2;
3125
0
  int depth;
3126
3127
0
  if (!dict->lookup(key, obj)->isNull()) {
3128
0
    return obj;
3129
0
  }
3130
0
  obj->free();
3131
3132
0
  dict->lookup("Parent", &parent)->isDict();
3133
0
  depth = 0;
3134
0
  while (parent.isDict() && depth < maxFieldObjectDepth) {
3135
0
    if (!parent.dictLookup(key, obj)->isNull()) {
3136
0
      parent.free();
3137
0
      return obj;
3138
0
    }
3139
0
    obj->free();
3140
0
    parent.dictLookup("Parent", &parent2);
3141
0
    parent.free();
3142
0
    parent = parent2;
3143
0
    ++depth;
3144
0
  }
3145
0
  parent.free();
3146
3147
  // some fields don't specify a parent, so we check the AcroForm
3148
  // dictionary just in case
3149
0
  acroForm->acroFormObj.dictLookup(key, obj);
3150
0
  return obj;
3151
0
}
3152
3153
0
Unicode *AcroFormField::utf8ToUnicode(GString *s, int *unicodeLength) {
3154
0
  int n = 0;
3155
0
  int i = 0;
3156
0
  Unicode u;
3157
0
  while (getUTF8(s, &i, &u)) {
3158
0
    ++n;
3159
0
  }
3160
0
  Unicode *uVec = (Unicode *)gmallocn(n, sizeof(Unicode));
3161
0
  n = 0;
3162
0
  i = 0;
3163
0
  while (getUTF8(s, &i, &uVec[n])) {
3164
0
    ++n;
3165
0
  }
3166
0
  *unicodeLength = n;
3167
0
  return uVec;
3168
0
}
3169
3170
0
GString *AcroFormField::unicodeToLatin1(Unicode *u, int unicodeLength) {
3171
0
  GString *s = new GString();
3172
0
  for (int i = 0; i < unicodeLength; ++i) {
3173
0
    if (u[i] <= 0xff) {
3174
0
      s->append((char)u[i]);
3175
0
    }
3176
0
  }
3177
0
  return s;
3178
0
}
3179
3180
GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
3181
0
          GString *s) {
3182
0
  if (s->getLength() != unicodeLength) {
3183
0
    return gFalse;
3184
0
  }
3185
0
  for (int i = 0; i < unicodeLength; ++i) {
3186
0
    if ((s->getChar(i) & 0xff) != u[i]) {
3187
0
      return gFalse;
3188
0
    }
3189
0
  }
3190
0
  return gTrue;
3191
0
}
3192
3193
GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
3194
0
          const char *s) {
3195
0
  for (int i = 0; i < unicodeLength; ++i) {
3196
0
    if (!s[i] || (s[i] & 0xff) != u[i]) {
3197
0
      return gFalse;
3198
0
    }
3199
0
  }
3200
0
  return gTrue;
3201
0
}
3202
3203
//------------------------------------------------------------------------
3204
// 'picture' formatting
3205
//------------------------------------------------------------------------
3206
3207
class PictureNode {
3208
public:
3209
0
  virtual ~PictureNode() {}
3210
0
  virtual GBool isLiteral() { return gFalse; }
3211
0
  virtual GBool isSign() { return gFalse; }
3212
0
  virtual GBool isDigit() { return gFalse; }
3213
0
  virtual GBool isDecPt() { return gFalse; }
3214
0
  virtual GBool isSeparator() { return gFalse; }
3215
0
  virtual GBool isYear() { return gFalse; }
3216
0
  virtual GBool isMonth() { return gFalse; }
3217
0
  virtual GBool isDay() { return gFalse; }
3218
0
  virtual GBool isHour() { return gFalse; }
3219
0
  virtual GBool isMinute() { return gFalse; }
3220
0
  virtual GBool isSecond() { return gFalse; }
3221
0
  virtual GBool isChar() { return gFalse; }
3222
};
3223
3224
class PictureLiteral: public PictureNode {
3225
public:
3226
0
  PictureLiteral(GString *sA) { s = sA; }
3227
0
  virtual ~PictureLiteral() { delete s; }
3228
0
  virtual GBool isLiteral() { return gTrue; }
3229
  GString *s;
3230
};
3231
3232
class PictureSign: public PictureNode {
3233
public:
3234
0
  PictureSign(char cA) { c = cA; }
3235
0
  virtual GBool isSign() { return gTrue; }
3236
  char c;
3237
};
3238
3239
class PictureDigit: public PictureNode {
3240
public:
3241
0
  PictureDigit(char cA) { c = cA; pos = 0; }
3242
0
  virtual GBool isDigit() { return gTrue; }
3243
  char c;
3244
  int pos;
3245
};
3246
3247
class PictureDecPt: public PictureNode {
3248
public:
3249
0
  PictureDecPt() { }
3250
0
  virtual GBool isDecPt() { return gTrue; }
3251
};
3252
3253
class PictureSeparator: public PictureNode {
3254
public:
3255
0
  PictureSeparator() { }
3256
0
  virtual GBool isSeparator() { return gTrue; }
3257
};
3258
3259
class PictureYear: public PictureNode {
3260
public:
3261
0
  PictureYear(int nDigitsA) { nDigits = nDigitsA; }
3262
0
  virtual GBool isYear() { return gTrue; }
3263
  int nDigits;
3264
};
3265
3266
class PictureMonth: public PictureNode {
3267
public:
3268
0
  PictureMonth(int nDigitsA) { nDigits = nDigitsA; }
3269
0
  virtual GBool isMonth() { return gTrue; }
3270
  int nDigits;
3271
};
3272
3273
class PictureDay: public PictureNode {
3274
public:
3275
0
  PictureDay(int nDigitsA) { nDigits = nDigitsA; }
3276
0
  virtual GBool isDay() { return gTrue; }
3277
  int nDigits;
3278
};
3279
3280
class PictureHour: public PictureNode {
3281
public:
3282
  PictureHour(GBool is24HourA, int nDigitsA)
3283
0
    { is24Hour = is24HourA; nDigits = nDigitsA; }
3284
0
  virtual GBool isHour() { return gTrue; }
3285
  GBool is24Hour;
3286
  int nDigits;
3287
};
3288
3289
class PictureMinute: public PictureNode {
3290
public:
3291
0
  PictureMinute(int nDigitsA) { nDigits = nDigitsA; }
3292
0
  virtual GBool isMinute() { return gTrue; }
3293
  int nDigits;
3294
};
3295
3296
class PictureSecond: public PictureNode {
3297
public:
3298
0
  PictureSecond(int nDigitsA) { nDigits = nDigitsA; }
3299
0
  virtual GBool isSecond() { return gTrue; }
3300
  int nDigits;
3301
};
3302
3303
class PictureChar: public PictureNode {
3304
public:
3305
0
  PictureChar() {}
3306
0
  virtual GBool isChar() { return gTrue; }
3307
};
3308
3309
GString *AcroFormField::pictureFormatDateTime(GString *value,
3310
0
                GString *picture) {
3311
0
  GList *pic;
3312
0
  PictureNode *node;
3313
0
  GString *ret, *s;
3314
0
  char c;
3315
0
  int year, month, day, hour, min, sec;
3316
0
  int len, picStart, picEnd, u, n, i, j;
3317
3318
0
  len = value->getLength();
3319
0
  if (len == 0) {
3320
0
    return value->copy();
3321
0
  }
3322
3323
  //--- parse the value
3324
3325
  // expected format is yyyy(-mm(-dd)?)?Thh(:mm(:ss)?)?
3326
  // where:
3327
  // - the '-'s and ':'s are optional
3328
  // - the 'T' is literal
3329
  // - we're ignoring optional time zone info at the end
3330
  // (if the value is not in this canonical format, we just punt and
3331
  // return the value string)
3332
  //~ another option would be to parse the value following the
3333
  //~   <ui><picture> element
3334
0
  year = month = day = hour = min = sec = 0;
3335
0
  i = 0;
3336
0
  if (!(i + 4 <= len && isValidInt(value, i, 4))) {
3337
0
    return value->copy();
3338
0
  }
3339
0
  year = convertInt(value, i, 4);
3340
0
  i += 4;
3341
0
  if (i < len && value->getChar(i) == '-') {
3342
0
    ++i;
3343
0
  }
3344
0
  if (i + 2 <= len && isValidInt(value, i, 2)) {
3345
0
    month = convertInt(value, i, 2);
3346
0
    i += 2;
3347
0
    if (i < len && value->getChar(i) == '-') {
3348
0
      ++i;
3349
0
    }
3350
0
    if (i + 2 <= len && isValidInt(value, i, 2)) {
3351
0
      day = convertInt(value, i, 2);
3352
0
      i += 2;
3353
0
    }
3354
0
  }
3355
0
  if (i < len) {
3356
0
    if (value->getChar(i) != 'T') {
3357
0
      return value->copy();
3358
0
    }
3359
0
    ++i;
3360
0
    if (!(i + 2 <= len && isValidInt(value, i, 2))) {
3361
0
      return value->copy();
3362
0
    }
3363
0
    hour = convertInt(value, i, 2);
3364
0
    i += 2;
3365
0
    if (i < len && value->getChar(i) == ':') {
3366
0
      ++i;
3367
0
    }
3368
0
    if (i + 2 <= len && isValidInt(value, i, 2)) {
3369
0
      min = convertInt(value, i, 2);
3370
0
      i += 2;
3371
0
      if (i < len && value->getChar(i) == ':') {
3372
0
  ++i;
3373
0
      }
3374
0
      if (i + 2 <= len && isValidInt(value, i, 2)) {
3375
0
  sec = convertInt(value, i, 2);
3376
0
  i += 2;
3377
0
      }
3378
0
    }
3379
0
  }
3380
0
  if (i < len) {
3381
0
    return value->copy();
3382
0
  }
3383
3384
  //--- skip the category and locale in the picture
3385
3386
0
  picStart = 0;
3387
0
  picEnd = picture->getLength();
3388
0
  for (i = 0; i < picture->getLength(); ++i) {
3389
0
    c = picture->getChar(i);
3390
0
    if (c == '{') {
3391
0
      picStart = i + 1;
3392
0
      for (picEnd = picStart;
3393
0
     picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
3394
0
     ++picEnd) ;
3395
0
      break;
3396
0
    } else if (!((c >= 'a' && c <= 'z') ||
3397
0
     (c >= 'A' && c <= 'Z') ||
3398
0
     c == '(' ||
3399
0
     c == ')')) {
3400
0
      break;
3401
0
    }
3402
0
  }
3403
3404
  //--- parse the picture
3405
3406
0
  pic = new GList();
3407
0
  i = picStart;
3408
0
  while (i < picEnd) {
3409
0
    c = picture->getChar(i);
3410
0
    ++i;
3411
0
    if (c == '\'') {
3412
0
      s = new GString();
3413
0
      while (i < picEnd) {
3414
0
  c = picture->getChar(i);
3415
0
  if (c == '\'') {
3416
0
    ++i;
3417
0
    if (i < picEnd && picture->getChar(i) == '\'') {
3418
0
      s->append('\'');
3419
0
      ++i;
3420
0
    } else {
3421
0
      break;
3422
0
    }
3423
0
  } else if (c == '\\') {
3424
0
    ++i;
3425
0
    if (i == picEnd) {
3426
0
      break;
3427
0
    }
3428
0
    c = picture->getChar(i);
3429
0
    ++i;
3430
0
    if (c == 'u' && i+4 <= picEnd) {
3431
0
      u = 0;
3432
0
      for (j = 0; j < 4; ++j, ++i) {
3433
0
        c = picture->getChar(i);
3434
0
        u <<= 4;
3435
0
        if (c >= '0' && c <= '9') {
3436
0
    u += c - '0';
3437
0
        } else if (c >= 'a' && c <= 'f') {
3438
0
    u += c - 'a' + 10;
3439
0
        } else if (c >= 'A' && c <= 'F') {
3440
0
    u += c - 'A' + 10;
3441
0
        }
3442
0
      }
3443
      //~ this should convert to UTF-8 (?)
3444
0
      if (u <= 0xff) {
3445
0
        s->append((char)u);
3446
0
      }
3447
0
    } else {
3448
0
      s->append(c);
3449
0
    }
3450
0
  } else {
3451
0
    s->append(c);
3452
0
  }
3453
0
      }
3454
0
      pic->append(new PictureLiteral(s));
3455
0
    } else if (c == ',' || c == '-' || c == ':' ||
3456
0
         c == '/' || c == '.' || c == ' ') {
3457
0
      s = new GString();
3458
0
      s->append(c);
3459
0
      pic->append(new PictureLiteral(s));
3460
0
    } else if (c == 'Y') {
3461
0
      for (n = 1; n < 4 && i < picEnd && picture->getChar(i) == 'Y'; ++n, ++i) ;
3462
0
      pic->append(new PictureYear(n));
3463
0
    } else if (c == 'M') {
3464
0
      for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
3465
0
      pic->append(new PictureMonth(n));
3466
0
    } else if (c == 'D') {
3467
0
      for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'D'; ++n, ++i) ;
3468
0
      pic->append(new PictureDay(n));
3469
0
    } else if (c == 'h') {
3470
0
      for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'h'; ++n, ++i) ;
3471
0
      pic->append(new PictureHour(gFalse, n));
3472
0
    } else if (c == 'H') {
3473
0
      for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'H'; ++n, ++i) ;
3474
0
      pic->append(new PictureHour(gTrue, n));
3475
0
    } else if (c == 'M') {
3476
0
      for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
3477
0
      pic->append(new PictureMinute(n));
3478
0
    } else if (c == 'S') {
3479
0
      for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'S'; ++n, ++i) ;
3480
0
      pic->append(new PictureSecond(n));
3481
0
    }
3482
0
  }
3483
3484
  //--- generate formatted text
3485
3486
0
  ret = new GString();
3487
0
  for (i = 0; i < pic->getLength(); ++i) {
3488
0
    node = (PictureNode *)pic->get(i);
3489
0
    if (node->isLiteral()) {
3490
0
      ret->append(((PictureLiteral *)node)->s);
3491
0
    } else if (node->isYear()) {
3492
0
      if (((PictureYear *)node)->nDigits == 2) {
3493
0
  if (year >= 1930 && year < 2030) {
3494
0
    ret->appendf("{0:02d}", year % 100);
3495
0
  } else {
3496
0
    ret->append("??");
3497
0
  }
3498
0
      } else {
3499
0
  ret->appendf("{0:04d}", year);
3500
0
      }
3501
0
    } else if (node->isMonth()) {
3502
0
      if (((PictureMonth *)node)->nDigits == 1) {
3503
0
  ret->appendf("{0:d}", month);
3504
0
      } else {
3505
0
  ret->appendf("{0:02d}", month);
3506
0
      }
3507
0
    } else if (node->isDay()) {
3508
0
      if (((PictureDay *)node)->nDigits == 1) {
3509
0
  ret->appendf("{0:d}", day);
3510
0
      } else {
3511
0
  ret->appendf("{0:02d}", day);
3512
0
      }
3513
0
    } else if (node->isHour()) {
3514
0
      if (((PictureHour *)node)->is24Hour) {
3515
0
  n = hour;
3516
0
      } else {
3517
0
  n = hour % 12;
3518
0
  if (n == 0) {
3519
0
    n = 12;
3520
0
  }
3521
0
      }
3522
0
      if (((PictureHour *)node)->nDigits == 1) {
3523
0
  ret->appendf("{0:d}", n);
3524
0
      } else {
3525
0
  ret->appendf("{0:02d}", n);
3526
0
      }
3527
0
    } else if (node->isMinute()) {
3528
0
      if (((PictureMinute *)node)->nDigits == 1) {
3529
0
  ret->appendf("{0:d}", min);
3530
0
      } else {
3531
0
  ret->appendf("{0:02d}", min);
3532
0
      }
3533
0
    } else if (node->isSecond()) {
3534
0
      if (((PictureSecond *)node)->nDigits == 1) {
3535
0
  ret->appendf("{0:d}", sec);
3536
0
      } else {
3537
0
  ret->appendf("{0:02d}", sec);
3538
0
      }
3539
0
    }
3540
0
  }
3541
0
  deleteGList(pic, PictureNode);
3542
3543
0
  return ret;
3544
0
}
3545
3546
0
GString *AcroFormField::pictureFormatNumber(GString *value, GString *picture) {
3547
0
  GList *pic;
3548
0
  PictureNode *node;
3549
0
  GString *ret, *s;
3550
0
  GBool neg, haveDigits;
3551
0
  char c;
3552
0
  int start, decPt, trailingZero, len;
3553
0
  int picStart, picEnd, u, pos, i, j;
3554
3555
0
  len = value->getLength();
3556
0
  if (len == 0) {
3557
0
    return value->copy();
3558
0
  }
3559
3560
  //--- parse the value
3561
3562
  // -nnnn.nnnn0000
3563
  //  ^   ^    ^   ^
3564
  //  |   |    |   +-- len
3565
  //  |   |    +------ trailingZero
3566
  //  |   +----------- decPt
3567
  //  +--------------- start
3568
0
  start = 0;
3569
0
  neg = gFalse;
3570
0
  if (value->getChar(start) == '-') {
3571
0
    neg = gTrue;
3572
0
    ++start;
3573
0
  } else if (value->getChar(start) == '+') {
3574
0
    ++start;
3575
0
  }
3576
0
  for (decPt = start; decPt < len && value->getChar(decPt) != '.'; ++decPt) ;
3577
0
  for (trailingZero = len;
3578
0
       trailingZero > decPt && value->getChar(trailingZero - 1) == '0';
3579
0
       --trailingZero) ;
3580
3581
  //--- skip the category and locale in the picture
3582
3583
0
  picStart = 0;
3584
0
  picEnd = picture->getLength();
3585
0
  for (i = 0; i < picture->getLength(); ++i) {
3586
0
    c = picture->getChar(i);
3587
0
    if (c == '{') {
3588
0
      picStart = i + 1;
3589
0
      for (picEnd = picStart;
3590
0
     picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
3591
0
     ++picEnd) ;
3592
0
      break;
3593
0
    } else if (!((c >= 'a' && c <= 'z') ||
3594
0
     (c >= 'A' && c <= 'Z') ||
3595
0
     c == '(' ||
3596
0
     c == ')')) {
3597
0
      break;
3598
0
    }
3599
0
  }
3600
3601
  //--- parse the picture
3602
3603
0
  pic = new GList();
3604
0
  i = picStart;
3605
0
  while (i < picEnd) {
3606
0
    c = picture->getChar(i);
3607
0
    ++i;
3608
0
    if (c == '\'') {
3609
0
      s = new GString();
3610
0
      while (i < picEnd) {
3611
0
  c = picture->getChar(i);
3612
0
  if (c == '\'') {
3613
0
    ++i;
3614
0
    if (i < picEnd && picture->getChar(i) == '\'') {
3615
0
      s->append('\'');
3616
0
      ++i;
3617
0
    } else {
3618
0
      break;
3619
0
    }
3620
0
  } else if (c == '\\') {
3621
0
    ++i;
3622
0
    if (i == picEnd) {
3623
0
      break;
3624
0
    }
3625
0
    c = picture->getChar(i);
3626
0
    ++i;
3627
0
    if (c == 'u' && i+4 <= picEnd) {
3628
0
      u = 0;
3629
0
      for (j = 0; j < 4; ++j, ++i) {
3630
0
        c = picture->getChar(i);
3631
0
        u <<= 4;
3632
0
        if (c >= '0' && c <= '9') {
3633
0
    u += c - '0';
3634
0
        } else if (c >= 'a' && c <= 'F') {
3635
0
    u += c - 'a' + 10;
3636
0
        } else if (c >= 'A' && c <= 'F') {
3637
0
    u += c - 'A' + 10;
3638
0
        }
3639
0
      }
3640
      //~ this should convert to UTF-8 (?)
3641
0
      if (u <= 0xff) {
3642
0
        s->append((char)u);
3643
0
      }
3644
0
    } else {
3645
0
      s->append(c);
3646
0
    }
3647
0
  } else {
3648
0
    s->append(c);
3649
0
    ++i;
3650
0
  }
3651
0
      }
3652
0
      pic->append(new PictureLiteral(s));
3653
0
    } else if (c == '-' || c == ':' || c == '/' || c == ' ') {
3654
0
      s = new GString();
3655
0
      s->append(c);
3656
0
      pic->append(new PictureLiteral(s));
3657
0
    } else if (c == 's' || c == 'S') {
3658
0
      pic->append(new PictureSign(c));
3659
0
    } else if (c == 'Z' || c == 'z' || c == '8' || c == '9') {
3660
0
      pic->append(new PictureDigit(c));
3661
0
    } else if (c == '.') {
3662
0
      pic->append(new PictureDecPt());
3663
0
    } else if (c == ',') {
3664
0
      pic->append(new PictureSeparator());
3665
0
    }
3666
0
  }
3667
0
  for (i = 0; i < pic->getLength(); ++i) {
3668
0
    node = (PictureNode *)pic->get(i);
3669
0
    if (node->isDecPt()) {
3670
0
      break;
3671
0
    }
3672
0
  }
3673
0
  pos = 0;
3674
0
  for (j = i - 1; j >= 0; --j) {
3675
0
    node = (PictureNode *)pic->get(j);
3676
0
    if (node->isDigit()) {
3677
0
      ((PictureDigit *)node)->pos = pos;
3678
0
      ++pos;
3679
0
    }
3680
0
  }
3681
0
  pos = -1;
3682
0
  for (j = i + 1; j < pic->getLength(); ++j) {
3683
0
    node = (PictureNode *)pic->get(j);
3684
0
    if (node->isDigit()) {
3685
0
      ((PictureDigit *)node)->pos = pos;
3686
0
      --pos;
3687
0
    }
3688
0
  }
3689
3690
  //--- generate formatted text
3691
3692
0
  ret = new GString();
3693
0
  haveDigits = gFalse;
3694
0
  for (i = 0; i < pic->getLength(); ++i) {
3695
0
    node = (PictureNode *)pic->get(i);
3696
0
    if (node->isLiteral()) {
3697
0
      ret->append(((PictureLiteral *)node)->s);
3698
0
    } else if (node->isSign()) {
3699
0
      if (((PictureSign *)node)->c == 'S') {
3700
0
  ret->append(neg ? '-' : ' ');
3701
0
      } else {
3702
0
  if (neg) {
3703
0
    ret->append('-');
3704
0
  }
3705
0
      }
3706
0
    } else if (node->isDigit()) {
3707
0
      pos = ((PictureDigit *)node)->pos;
3708
0
      c = ((PictureDigit *)node)->c;
3709
0
      if (pos >= 0 && pos < decPt - start) {
3710
0
  ret->append(value->getChar(decPt - 1 - pos));
3711
0
  haveDigits = gTrue;
3712
0
      } else if (pos < 0 && -pos <= trailingZero - decPt - 1) {
3713
0
  ret->append(value->getChar(decPt - pos));
3714
0
  haveDigits = gTrue;
3715
0
      } else if (c == '8' &&
3716
0
     pos < 0 &&
3717
0
     -pos <= len - decPt - 1) {
3718
0
  ret->append('0');
3719
0
  haveDigits = gTrue;
3720
0
      } else if (c == '9') {
3721
0
  ret->append('0');
3722
0
  haveDigits = gTrue;
3723
0
      } else if (c == 'Z' && pos >= 0) {
3724
0
  ret->append(' ');
3725
0
      }
3726
0
    } else if (node->isDecPt()) {
3727
0
      if (!(i+1 < pic->getLength() &&
3728
0
      ((PictureNode *)pic->get(i+1))->isDigit() &&
3729
0
      ((PictureDigit *)pic->get(i+1))->c == 'z') ||
3730
0
    trailingZero > decPt + 1) {
3731
0
  ret->append('.');
3732
0
      }
3733
0
    } else if (node->isSeparator()) {
3734
0
      if (haveDigits) {
3735
0
  ret->append(',');
3736
0
      }
3737
0
    }
3738
0
  }
3739
0
  deleteGList(pic, PictureNode);
3740
3741
0
  return ret;
3742
0
}
3743
3744
0
GString *AcroFormField::pictureFormatText(GString *value, GString *picture) {
3745
0
  GList *pic;
3746
0
  PictureNode *node;
3747
0
  GString *ret, *s;
3748
0
  char c;
3749
0
  int len, picStart, picEnd, u, i, j;
3750
3751
0
  len = value->getLength();
3752
0
  if (len == 0) {
3753
0
    return value->copy();
3754
0
  }
3755
3756
  //--- skip the category and locale in the picture
3757
3758
0
  picStart = 0;
3759
0
  picEnd = picture->getLength();
3760
0
  for (i = 0; i < picture->getLength(); ++i) {
3761
0
    c = picture->getChar(i);
3762
0
    if (c == '{') {
3763
0
      picStart = i + 1;
3764
0
      for (picEnd = picStart;
3765
0
     picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
3766
0
     ++picEnd) ;
3767
0
      break;
3768
0
    } else if (!((c >= 'a' && c <= 'z') ||
3769
0
     (c >= 'A' && c <= 'Z') ||
3770
0
     c == '(' ||
3771
0
     c == ')')) {
3772
0
      break;
3773
0
    }
3774
0
  }
3775
3776
  //--- parse the picture
3777
3778
0
  pic = new GList();
3779
0
  i = picStart;
3780
0
  while (i < picEnd) {
3781
0
    c = picture->getChar(i);
3782
0
    ++i;
3783
0
    if (c == '\'') {
3784
0
      s = new GString();
3785
0
      while (i < picEnd) {
3786
0
  c = picture->getChar(i);
3787
0
  if (c == '\'') {
3788
0
    ++i;
3789
0
    if (i < picEnd && picture->getChar(i) == '\'') {
3790
0
      s->append('\'');
3791
0
      ++i;
3792
0
    } else {
3793
0
      break;
3794
0
    }
3795
0
  } else if (c == '\\') {
3796
0
    ++i;
3797
0
    if (i == picEnd) {
3798
0
      break;
3799
0
    }
3800
0
    c = picture->getChar(i);
3801
0
    ++i;
3802
0
    if (c == 'u' && i+4 <= picEnd) {
3803
0
      u = 0;
3804
0
      for (j = 0; j < 4; ++j, ++i) {
3805
0
        c = picture->getChar(i);
3806
0
        u <<= 4;
3807
0
        if (c >= '0' && c <= '9') {
3808
0
    u += c - '0';
3809
0
        } else if (c >= 'a' && c <= 'F') {
3810
0
    u += c - 'a' + 10;
3811
0
        } else if (c >= 'A' && c <= 'F') {
3812
0
    u += c - 'A' + 10;
3813
0
        }
3814
0
      }
3815
      //~ this should convert to UTF-8 (?)
3816
0
      if (u <= 0xff) {
3817
0
        s->append((char)u);
3818
0
      }
3819
0
    } else {
3820
0
      s->append(c);
3821
0
    }
3822
0
  } else {
3823
0
    s->append(c);
3824
0
    ++i;
3825
0
  }
3826
0
      }
3827
0
      pic->append(new PictureLiteral(s));
3828
0
    } else if (c == ',' || c == '-' || c == ':' ||
3829
0
         c == '/' || c == '.' || c == ' ') {
3830
0
      s = new GString();
3831
0
      s->append(c);
3832
0
      pic->append(new PictureLiteral(s));
3833
0
    } else if (c == 'A' || c == 'X' || c == 'O' || c == '0' || c == '9') {
3834
0
      pic->append(new PictureChar());
3835
0
    }
3836
0
  }
3837
3838
  //--- generate formatted text
3839
3840
0
  ret = new GString();
3841
0
  j = 0;
3842
0
  for (i = 0; i < pic->getLength(); ++i) {
3843
0
    node = (PictureNode *)pic->get(i);
3844
0
    if (node->isLiteral()) {
3845
0
      ret->append(((PictureLiteral *)node)->s);
3846
0
    } else if (node->isChar()) {
3847
      // if there are more chars in the picture than in the value,
3848
      // Adobe renders the value as-is, without picture formatting
3849
0
      if (j >= value->getLength()) {
3850
0
  delete ret;
3851
0
  ret = value->copy();
3852
0
  break;
3853
0
      }
3854
0
      ret->append(value->getChar(j));
3855
0
      ++j;
3856
0
    }
3857
0
  }
3858
0
  deleteGList(pic, PictureNode);
3859
3860
0
  return ret;
3861
0
}
3862
3863
0
GBool AcroFormField::isValidInt(GString *s, int start, int len) {
3864
0
  int i;
3865
3866
0
  for (i = 0; i < len; ++i) {
3867
0
    if (!(start + i < s->getLength() &&
3868
0
    s->getChar(start + i) >= '0' &&
3869
0
    s->getChar(start + i) <= '9')) {
3870
0
      return gFalse;
3871
0
    }
3872
0
  }
3873
0
  return gTrue;
3874
0
}
3875
3876
0
int AcroFormField::convertInt(GString *s, int start, int len) {
3877
0
  char c;
3878
0
  int x, i;
3879
3880
0
  x = 0;
3881
0
  for (i = 0; i < len && start + i < s->getLength(); ++i) {
3882
0
    c = s->getChar(start + i);
3883
0
    if (c < '0' || c > '9') {
3884
0
      break;
3885
0
    }
3886
0
    x = x * 10 + (c - '0');
3887
0
  }
3888
0
  return x;
3889
0
}