Coverage Report

Created: 2025-08-28 06:46

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