Coverage Report

Created: 2026-04-04 06:06

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