Coverage Report

Created: 2025-09-04 08:08

/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.81k
#define acroFormFlagMultiline          (1 << 12)  // text
38
#define acroFormFlagPassword           (1 << 13)  // text
39
#define acroFormFlagNoToggleToOff      (1 << 14)  // button
40
983
#define acroFormFlagRadio              (1 << 15)  // button
41
1.00k
#define acroFormFlagPushbutton         (1 << 16)  // button
42
0
#define acroFormFlagCombo              (1 << 17)  // choice
43
#define acroFormFlagEdit               (1 << 18)  // choice
44
#define acroFormFlagSort               (1 << 19)  // choice
45
4.85k
#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
20.9k
#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
8.05k
    { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
328
  int annotNum;
329
  int annotGen;
330
  int pageNum;
331
};
332
333
//------------------------------------------------------------------------
334
// AcroForm
335
//------------------------------------------------------------------------
336
337
846
AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
338
846
  Object acroFormObj2;
339
846
  AcroForm *acroForm;
340
846
  AcroFormField *field;
341
846
  Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
342
846
  char *touchedObjs;
343
846
  int pageNum, i, j;
344
345
846
  touchedObjs = (char *)gmalloc(docA->getXRef()->getNumObjects());
346
846
  memset(touchedObjs, 0, docA->getXRef()->getNumObjects());
347
348
  // this is the normal case: acroFormObj is a dictionary, as expected
349
846
  if (acroFormObjA->isDict()) {
350
508
    acroForm = new AcroForm(docA, acroFormObjA);
351
352
508
    if (globalParams->getEnableXFA()) {
353
508
      if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
354
337
  acroForm->xfaScanner = XFAScanner::load(&xfaObj);
355
337
  if (!catalog->getNeedsRendering()) {
356
329
    acroForm->isStaticXFA = gTrue;
357
329
  }
358
337
      }
359
508
      xfaObj.free();
360
508
    }
361
362
508
    if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
363
1
      acroForm->needAppearances = obj1.getBool();
364
1
    }
365
508
    obj1.free();
366
367
508
    acroForm->buildAnnotPageList(catalog);
368
369
508
    if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
370
7
      if (!obj1.isNull()) {
371
1
  error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
372
1
      }
373
7
      obj1.free();
374
7
      delete acroForm;
375
7
      gfree(touchedObjs);
376
7
      return NULL;
377
7
    }
378
34.1k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
379
33.6k
      obj1.arrayGetNF(i, &obj2);
380
33.6k
      acroForm->scanField(&obj2, touchedObjs);
381
33.6k
      obj2.free();
382
33.6k
    }
383
501
    obj1.free();
384
385
    // scan the annotations, looking for Widget-type annots that are
386
    // not attached to the AcroForm object
387
6.22k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
388
5.72k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
389
31.9k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
390
31.7k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
391
147k
      for (j = 0; j < acroForm->fields->getLength(); ++j) {
392
140k
        field = (AcroFormField *)acroForm->fields->get(j);
393
140k
        if (field->fieldRef.isRef()) {
394
103k
    if (field->fieldRef.getRefNum() == annotRef.getRefNum() &&
395
103k
        field->fieldRef.getRefGen() == annotRef.getRefGen()) {
396
497
      break;
397
497
    }
398
103k
        }
399
140k
      }
400
7.41k
      if (j == acroForm->fields->getLength()) {
401
6.91k
        annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
402
6.91k
        if (annotObj.isDict()) {
403
1.52k
    if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
404
893
      acroForm->scanField(&annotRef, touchedObjs);
405
893
    }
406
1.52k
    obj1.free();
407
1.52k
        }
408
6.91k
        annotObj.free();
409
6.91k
      }
410
7.41k
    }
411
31.7k
    annotRef.free();
412
31.7k
  }
413
175
      }
414
5.72k
      annotsObj.free();
415
5.72k
    }
416
417
  // if acroFormObjA is a null object, but there are Widget-type
418
  // annots, we still create an AcroForm
419
501
  } else {
420
    // create an empty dict for acroFormObj
421
338
    acroFormObj2.initDict(docA->getXRef());
422
338
    acroForm = new AcroForm(docA, &acroFormObj2);
423
338
    acroFormObj2.free();
424
425
338
    acroForm->buildAnnotPageList(catalog);
426
427
    // scan the annotations, looking for any Widget-type annots
428
2.80k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
429
2.46k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
430
2.82k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
431
2.74k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
432
636
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
433
636
      if (annotObj.isDict()) {
434
207
        if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
435
140
    acroForm->scanField(&annotRef, touchedObjs);
436
140
        }
437
207
        obj1.free();
438
207
      }
439
636
      annotObj.free();
440
636
    }
441
2.74k
    annotRef.free();
442
2.74k
  }
443
75
      }
444
2.46k
      annotsObj.free();
445
2.46k
    }
446
447
338
    if (acroForm->fields->getLength() == 0) {
448
326
      delete acroForm;
449
326
      acroForm = NULL;
450
326
    }
451
338
  }
452
453
839
  gfree(touchedObjs);
454
455
839
  return acroForm;
456
846
}
457
458
846
AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) {
459
846
  doc = docA;
460
846
  acroFormObjA->copy(&acroFormObj);
461
846
  needAppearances = gFalse;
462
846
  annotPages = new GList();
463
846
  fields = new GList();
464
846
  xfaScanner = NULL;
465
846
  isStaticXFA = gFalse;
466
846
}
467
468
846
AcroForm::~AcroForm() {
469
846
  acroFormObj.free();
470
846
  deleteGList(annotPages, AcroFormAnnotPage);
471
846
  deleteGList(fields, AcroFormField);
472
846
  delete xfaScanner;
473
846
}
474
475
0
const char *AcroForm::getType() {
476
0
  return isStaticXFA ? "static XFA" : "AcroForm";
477
0
}
478
479
846
void AcroForm::buildAnnotPageList(Catalog *catalog) {
480
846
  Object annotsObj, annotObj;
481
846
  int pageNum, i;
482
483
9.04k
  for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
484
8.20k
    if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
485
34.7k
      for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
486
34.5k
  if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
487
8.05k
    annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
488
8.05k
               annotObj.getRefGen(),
489
8.05k
               pageNum));
490
8.05k
  }
491
34.5k
  annotObj.free();
492
34.5k
      }
493
250
    }
494
8.20k
    annotsObj.free();
495
8.20k
  }
496
  //~ sort the list
497
846
}
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
42.0k
void AcroForm::scanField(Object *fieldRef, char *touchedObjs) {
519
42.0k
  AcroFormField *field;
520
42.0k
  Object fieldObj, kidsObj, kidRef, kidObj, subtypeObj;
521
42.0k
  GBool isTerminal;
522
42.0k
  int i;
523
524
  // check for an object loop
525
42.0k
  if (fieldRef->isRef()) {
526
9.99k
    if (fieldRef->getRefNum() < 0 ||
527
9.99k
  fieldRef->getRefNum() >= doc->getXRef()->getNumObjects() ||
528
9.99k
  touchedObjs[fieldRef->getRefNum()]) {
529
1.50k
      return;
530
1.50k
    }
531
8.49k
    touchedObjs[fieldRef->getRefNum()] = 1;
532
8.49k
  }
533
534
40.5k
  fieldRef->fetch(doc->getXRef(), &fieldObj);
535
40.5k
  if (!fieldObj.isDict()) {
536
32.7k
    error(errSyntaxError, -1, "AcroForm field object is wrong type");
537
32.7k
    fieldObj.free();
538
32.7k
    return;
539
32.7k
  }
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
7.82k
  isTerminal = gTrue;
546
7.82k
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
547
923
    isTerminal = gFalse;
548
15.3k
    for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
549
14.3k
      kidsObj.arrayGet(i, &kidObj);
550
14.3k
      if (kidObj.isDict()) {
551
3.39k
  if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
552
228
    isTerminal = gTrue;
553
228
  }
554
3.39k
  subtypeObj.free();
555
3.39k
      }
556
14.3k
      kidObj.free();
557
14.3k
    }
558
923
    if (!isTerminal) {
559
8.07k
      for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
560
7.37k
  kidsObj.arrayGetNF(i, &kidRef);
561
7.37k
  scanField(&kidRef, touchedObjs);
562
7.37k
  kidRef.free();
563
7.37k
      }
564
695
    }
565
923
  }
566
7.82k
  kidsObj.free();
567
568
7.82k
  if (isTerminal) {
569
7.12k
    if ((field = AcroFormField::load(this, fieldRef))) {
570
5.85k
      fields->append(field);
571
5.85k
    }
572
7.12k
  }
573
574
7.82k
  fieldObj.free();
575
7.82k
}
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
7.12k
AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
632
7.12k
  GString *typeStr;
633
7.12k
  TextString *nameA;
634
7.12k
  GString *xfaName;
635
7.12k
  Guint flagsA;
636
7.12k
  GBool haveFlags, typeFromParentA;
637
7.12k
  Object fieldObjA, parentObj, parentObj2, obj1, obj2;
638
7.12k
  AcroFormFieldType typeA;
639
7.12k
  XFAField *xfaFieldA;
640
7.12k
  AcroFormField *field;
641
7.12k
  int depth, i0, i1;
642
643
7.12k
  fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
644
645
  //----- get field info
646
647
7.12k
  if (fieldObjA.dictLookup("T", &obj1)->isString()) {
648
5.78k
    nameA = new TextString(obj1.getString());
649
5.78k
  } else {
650
1.33k
    nameA = new TextString();
651
1.33k
  }
652
7.12k
  obj1.free();
653
654
7.12k
  if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
655
5.71k
    typeStr = new GString(obj1.getName());
656
5.71k
    typeFromParentA = gFalse;
657
5.71k
  } else {
658
1.40k
    typeStr = NULL;
659
1.40k
    typeFromParentA = gTrue;
660
1.40k
  }
661
7.12k
  obj1.free();
662
663
7.12k
  if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
664
4.45k
    flagsA = (Guint)obj1.getInt();
665
4.45k
    haveFlags = gTrue;
666
4.45k
  } else {
667
2.67k
    flagsA = 0;
668
2.67k
    haveFlags = gFalse;
669
2.67k
  }
670
7.12k
  obj1.free();
671
672
  //----- get info from parent non-terminal fields
673
674
7.12k
  fieldObjA.dictLookup("Parent", &parentObj);
675
7.12k
  depth = 0;
676
27.8k
  while (parentObj.isDict() && depth < maxFieldObjectDepth) {
677
678
20.7k
    if (parentObj.dictLookup("T", &obj1)->isString()) {
679
20.2k
      if (nameA->getLength()) {
680
19.7k
  nameA->insert(0, (Unicode)'.');
681
19.7k
      }
682
20.2k
      nameA->insert(0, obj1.getString());
683
20.2k
    }
684
20.7k
    obj1.free();
685
686
20.7k
    if (!typeStr) {
687
773
      if (parentObj.dictLookup("FT", &obj1)->isName()) {
688
312
  typeStr = new GString(obj1.getName());
689
312
      }
690
773
      obj1.free();
691
773
    }
692
693
20.7k
    if (!haveFlags) {
694
6.32k
      if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
695
319
  flagsA = (Guint)obj1.getInt();
696
319
  haveFlags = gTrue;
697
319
      }
698
6.32k
      obj1.free();
699
6.32k
    }
700
701
20.7k
    parentObj.dictLookup("Parent", &parentObj2);
702
20.7k
    parentObj.free();
703
20.7k
    parentObj = parentObj2;
704
705
20.7k
    ++depth;
706
20.7k
  }
707
7.12k
  parentObj.free();
708
709
7.12k
  if (!typeStr) {
710
1.09k
    error(errSyntaxError, -1, "Missing type in AcroForm field");
711
1.09k
    goto err1;
712
1.09k
  }
713
714
  //----- get static XFA info
715
716
6.02k
  xfaFieldA = NULL;
717
6.02k
  if (acroFormA->xfaScanner) {
718
    // convert field name to UTF-8, and remove segments that start
719
    // with '#' -- to match the XFA field name
720
111
    xfaName = nameA->toUTF8();
721
111
    i0 = 0;
722
276
    while (i0 < xfaName->getLength()) {
723
165
      i1 = i0;
724
5.12k
      while (i1 < xfaName->getLength()) {
725
5.04k
  if (xfaName->getChar(i1) == '.') {
726
85
    ++i1;
727
85
    break;
728
85
  }
729
4.96k
  ++i1;
730
4.96k
      }
731
165
      if (xfaName->getChar(i0) == '#') {
732
13
  xfaName->del(i0, i1 - i0);
733
152
      } else {
734
152
  i0 = i1;
735
152
      }
736
165
    }
737
111
    xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
738
111
    delete xfaName;
739
111
  }
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.02k
  if (typeFromParentA && !typeStr->cmp("Btn") && !haveFlags) {
746
93
    flagsA = acroFormFlagRadio;
747
93
  }
748
749
  //----- determine field type
750
751
6.02k
  if (!typeStr->cmp("Btn")) {
752
1.00k
    if (flagsA & acroFormFlagPushbutton) {
753
115
      typeA = acroFormFieldPushbutton;
754
890
    } else if (flagsA & acroFormFlagRadio) {
755
118
      typeA = acroFormFieldRadioButton;
756
772
    } else {
757
772
      typeA = acroFormFieldCheckbox;
758
772
    }
759
5.02k
  } else if (!typeStr->cmp("Tx")) {
760
4.85k
    if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
761
0
      typeA = acroFormFieldBarcode;
762
4.85k
    } else if (flagsA & acroFormFlagFileSelect) {
763
37
      typeA = acroFormFieldFileSelect;
764
4.81k
    } else if (flagsA & acroFormFlagMultiline) {
765
73
      typeA = acroFormFieldMultilineText;
766
4.74k
    } else {
767
4.74k
      typeA = acroFormFieldText;
768
4.74k
    }
769
4.85k
  } else if (!typeStr->cmp("Ch")) {
770
0
    if (flagsA & acroFormFlagCombo) {
771
0
      typeA = acroFormFieldComboBox;
772
0
    } else {
773
0
      typeA = acroFormFieldListBox;
774
0
    }
775
169
  } else if (!typeStr->cmp("Sig")) {
776
0
    typeA = acroFormFieldSignature;
777
169
  } else {
778
169
    error(errSyntaxError, -1, "Invalid type in AcroForm field");
779
169
    goto err1;
780
169
  }
781
5.85k
  delete typeStr;
782
783
5.85k
  field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
784
5.85k
          typeA, nameA, flagsA, typeFromParentA, xfaFieldA);
785
5.85k
  fieldObjA.free();
786
5.85k
  return field;
787
788
1.26k
 err1:
789
1.26k
  delete typeStr;
790
1.26k
  delete nameA;
791
1.26k
  fieldObjA.free();
792
1.26k
  return NULL;
793
6.02k
}
794
795
AcroFormField::AcroFormField(AcroForm *acroFormA,
796
           Object *fieldRefA, Object *fieldObjA,
797
           AcroFormFieldType typeA, TextString *nameA,
798
           Guint flagsA, GBool typeFromParentA,
799
5.85k
           XFAField *xfaFieldA) {
800
5.85k
  acroForm = acroFormA;
801
5.85k
  fieldRefA->copy(&fieldRef);
802
5.85k
  fieldObjA->copy(&fieldObj);
803
5.85k
  type = typeA;
804
5.85k
  name = nameA;
805
5.85k
  flags = flagsA;
806
5.85k
  typeFromParent = typeFromParentA;
807
5.85k
  xfaField = xfaFieldA;
808
5.85k
}
809
810
5.85k
AcroFormField::~AcroFormField() {
811
5.85k
  fieldRef.free();
812
5.85k
  fieldObj.free();
813
5.85k
  delete name;
814
5.85k
}
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
}