Coverage Report

Created: 2026-08-22 06:39

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
2.88k
#define acroFormFlagMultiline          (1 << 12)  // text
38
#define acroFormFlagPassword           (1 << 13)  // text
39
#define acroFormFlagNoToggleToOff      (1 << 14)  // button
40
1.76k
#define acroFormFlagRadio              (1 << 15)  // button
41
1.61k
#define acroFormFlagPushbutton         (1 << 16)  // button
42
1.10k
#define acroFormFlagCombo              (1 << 17)  // choice
43
#define acroFormFlagEdit               (1 << 18)  // choice
44
#define acroFormFlagSort               (1 << 19)  // choice
45
2.03k
#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
431
#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
7.78k
#define acroFormQuadLeft   0
55
687
#define acroFormQuadCenter 1
56
277
#define acroFormQuadRight  2
57
58
291
#define acroFormVAlignTop               0
59
1.81k
#define acroFormVAlignMiddle            1
60
536
#define acroFormVAlignMiddleNoDescender 2
61
0
#define acroFormVAlignBottom            3
62
63
3.60k
#define annotFlagHidden    0x0002
64
1.46k
#define annotFlagPrint     0x0004
65
2.10k
#define annotFlagNoView    0x0020
66
67
// distance of Bezier control point from center for circle approximation
68
// = (4 * (sqrt(2) - 1) / 3) * r
69
184
#define bezierCircle 0.55228475
70
71
// limit recursive field-parent lookups to avoid infinite loops
72
15.6k
#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
104k
    { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
328
  int annotNum;
329
  int annotGen;
330
  int pageNum;
331
};
332
333
//------------------------------------------------------------------------
334
// AcroForm
335
//------------------------------------------------------------------------
336
337
15.1k
AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
338
15.1k
  Object acroFormObj2;
339
15.1k
  AcroForm *acroForm;
340
15.1k
  AcroFormField *field;
341
15.1k
  Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
342
15.1k
  char *touchedObjs;
343
15.1k
  int pageNum, i, j;
344
345
15.1k
  touchedObjs = (char *)gmalloc(docA->getXRef()->getNumObjects());
346
15.1k
  memset(touchedObjs, 0, docA->getXRef()->getNumObjects());
347
348
  // this is the normal case: acroFormObj is a dictionary, as expected
349
15.1k
  if (acroFormObjA->isDict()) {
350
1.33k
    acroForm = new AcroForm(docA, acroFormObjA);
351
352
1.33k
    if (globalParams->getEnableXFA()) {
353
1.33k
      if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
354
561
  acroForm->xfaScanner = XFAScanner::load(&xfaObj);
355
561
  if (!catalog->getNeedsRendering()) {
356
561
    acroForm->isStaticXFA = gTrue;
357
561
  }
358
561
      }
359
1.33k
      xfaObj.free();
360
1.33k
    }
361
362
1.33k
    if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
363
347
      acroForm->needAppearances = obj1.getBool();
364
347
    }
365
1.33k
    obj1.free();
366
367
1.33k
    acroForm->buildAnnotPageList(catalog);
368
369
1.33k
    if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
370
16
      if (!obj1.isNull()) {
371
1
  error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
372
1
      }
373
16
      obj1.free();
374
16
      delete acroForm;
375
16
      gfree(touchedObjs);
376
16
      return NULL;
377
16
    }
378
9.50k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
379
8.18k
      obj1.arrayGetNF(i, &obj2);
380
8.18k
      acroForm->scanField(&obj2, touchedObjs);
381
8.18k
      obj2.free();
382
8.18k
    }
383
1.32k
    obj1.free();
384
385
    // scan the annotations, looking for Widget-type annots that are
386
    // not attached to the AcroForm object
387
21.5k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
388
20.1k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
389
16.4k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
390
15.6k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
391
64.1k
      for (j = 0; j < acroForm->fields->getLength(); ++j) {
392
58.3k
        field = (AcroFormField *)acroForm->fields->get(j);
393
58.3k
        if (field->fieldRef.isRef()) {
394
58.3k
    if (field->fieldRef.getRefNum() == annotRef.getRefNum() &&
395
1.84k
        field->fieldRef.getRefGen() == annotRef.getRefGen()) {
396
1.82k
      break;
397
1.82k
    }
398
58.3k
        }
399
58.3k
      }
400
7.59k
      if (j == acroForm->fields->getLength()) {
401
5.77k
        annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
402
5.77k
        if (annotObj.isDict()) {
403
3.09k
    if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
404
918
      acroForm->scanField(&annotRef, touchedObjs);
405
918
    }
406
3.09k
    obj1.free();
407
3.09k
        }
408
5.77k
        annotObj.free();
409
5.77k
      }
410
7.59k
    }
411
15.6k
    annotRef.free();
412
15.6k
  }
413
714
      }
414
20.1k
      annotsObj.free();
415
20.1k
    }
416
417
  // if acroFormObjA is a null object, but there are Widget-type
418
  // annots, we still create an AcroForm
419
13.8k
  } else {
420
    // create an empty dict for acroFormObj
421
13.8k
    acroFormObj2.initDict(docA->getXRef());
422
13.8k
    acroForm = new AcroForm(docA, &acroFormObj2);
423
13.8k
    acroFormObj2.free();
424
425
13.8k
    acroForm->buildAnnotPageList(catalog);
426
427
    // scan the annotations, looking for any Widget-type annots
428
165k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
429
151k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
430
495k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
431
486k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
432
96.3k
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
433
96.3k
      if (annotObj.isDict()) {
434
15.1k
        if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
435
8.43k
    acroForm->scanField(&annotRef, touchedObjs);
436
8.43k
        }
437
15.1k
        obj1.free();
438
15.1k
      }
439
96.3k
      annotObj.free();
440
96.3k
    }
441
486k
    annotRef.free();
442
486k
  }
443
9.36k
      }
444
151k
      annotsObj.free();
445
151k
    }
446
447
13.8k
    if (acroForm->fields->getLength() == 0) {
448
13.7k
      delete acroForm;
449
13.7k
      acroForm = NULL;
450
13.7k
    }
451
13.8k
  }
452
453
15.1k
  gfree(touchedObjs);
454
455
15.1k
  return acroForm;
456
15.1k
}
457
458
15.1k
AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) {
459
15.1k
  doc = docA;
460
15.1k
  acroFormObjA->copy(&acroFormObj);
461
15.1k
  needAppearances = gFalse;
462
15.1k
  annotPages = new GList();
463
15.1k
  fields = new GList();
464
15.1k
  xfaScanner = NULL;
465
15.1k
  isStaticXFA = gFalse;
466
15.1k
}
467
468
15.1k
AcroForm::~AcroForm() {
469
15.1k
  acroFormObj.free();
470
15.1k
  deleteGList(annotPages, AcroFormAnnotPage);
471
15.1k
  deleteGList(fields, AcroFormField);
472
15.1k
  delete xfaScanner;
473
15.1k
}
474
475
0
const char *AcroForm::getType() {
476
0
  return isStaticXFA ? "static XFA" : "AcroForm";
477
0
}
478
479
15.1k
void AcroForm::buildAnnotPageList(Catalog *catalog) {
480
15.1k
  Object annotsObj, annotObj;
481
15.1k
  int pageNum, i;
482
483
186k
  for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
484
171k
    if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
485
512k
      for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
486
502k
  if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
487
104k
    annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
488
104k
               annotObj.getRefGen(),
489
104k
               pageNum));
490
104k
  }
491
502k
  annotObj.free();
492
502k
      }
493
10.0k
    }
494
171k
    annotsObj.free();
495
171k
  }
496
  //~ sort the list
497
15.1k
}
498
499
15.5k
int AcroForm::lookupAnnotPage(Object *annotRef) {
500
15.5k
  AcroFormAnnotPage *annotPage;
501
15.5k
  int num, gen, i;
502
503
15.5k
  if (!annotRef->isRef()) {
504
324
    return 0;
505
324
  }
506
15.2k
  num = annotRef->getRefNum();
507
15.2k
  gen = annotRef->getRefGen();
508
  //~ use bin search
509
50.2M
  for (i = 0; i < annotPages->getLength(); ++i) {
510
50.2M
    annotPage = (AcroFormAnnotPage *)annotPages->get(i);
511
50.2M
    if (annotPage->annotNum == num && annotPage->annotGen == gen) {
512
9.58k
      return annotPage->pageNum;
513
9.58k
    }
514
50.2M
  }
515
5.67k
  return 0;
516
15.2k
}
517
518
21.5k
void AcroForm::scanField(Object *fieldRef, char *touchedObjs) {
519
21.5k
  AcroFormField *field;
520
21.5k
  Object fieldObj, kidsRef, kidsObj, kidRef, kidObj, subtypeObj;
521
21.5k
  GBool isTerminal;
522
21.5k
  int i;
523
524
  // check for an object loop
525
21.5k
  if (fieldRef->isRef()) {
526
17.0k
    if (fieldRef->getRefNum() < 0 ||
527
17.0k
  fieldRef->getRefNum() >= doc->getXRef()->getNumObjects() ||
528
16.9k
  touchedObjs[fieldRef->getRefNum()]) {
529
8.06k
      return;
530
8.06k
    }
531
9.01k
    touchedObjs[fieldRef->getRefNum()] = 1;
532
9.01k
  }
533
534
13.5k
  fieldRef->fetch(doc->getXRef(), &fieldObj);
535
13.5k
  if (!fieldObj.isDict()) {
536
8.22k
    error(errSyntaxError, -1, "AcroForm field object is wrong type");
537
8.22k
    fieldObj.free();
538
8.22k
    return;
539
8.22k
  }
540
541
  // look for a Kids entry, and check for an object loop there
542
5.29k
  if (fieldObj.dictLookupNF("Kids", &kidsRef)->isRef()) {
543
14
    if (kidsRef.getRefNum() < 0 ||
544
14
  kidsRef.getRefNum() >= doc->getXRef()->getNumObjects() ||
545
11
  touchedObjs[kidsRef.getRefNum()]) {
546
11
      kidsRef.free();
547
11
      fieldObj.free();
548
11
      return;
549
11
    }
550
3
    touchedObjs[kidsRef.getRefNum()] = 1;
551
3
    kidsRef.fetch(doc->getXRef(), &kidsObj);
552
5.27k
  } else {
553
5.27k
    kidsRef.copy(&kidsObj);
554
5.27k
  }
555
5.28k
  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.28k
  isTerminal = gTrue;
562
5.28k
  if (kidsObj.isArray()) {
563
987
    isTerminal = gFalse;
564
6.36k
    for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
565
5.37k
      kidsObj.arrayGet(i, &kidObj);
566
5.37k
      if (kidObj.isDict()) {
567
2.20k
  if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
568
355
    isTerminal = gTrue;
569
355
  }
570
2.20k
  subtypeObj.free();
571
2.20k
      }
572
5.37k
      kidObj.free();
573
5.37k
    }
574
987
    if (!isTerminal) {
575
4.68k
      for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
576
4.05k
  kidsObj.arrayGetNF(i, &kidRef);
577
4.05k
  scanField(&kidRef, touchedObjs);
578
4.05k
  kidRef.free();
579
4.05k
      }
580
632
    }
581
987
  }
582
5.28k
  kidsObj.free();
583
584
5.28k
  if (isTerminal) {
585
4.64k
    if ((field = AcroFormField::load(this, fieldRef))) {
586
3.78k
      fields->append(field);
587
3.78k
    }
588
4.64k
  }
589
590
5.28k
  fieldObj.free();
591
5.28k
}
592
593
23.9k
void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) {
594
23.9k
  int i;
595
596
37.0k
  for (i = 0; i < fields->getLength(); ++i) {
597
13.0k
    ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing);
598
13.0k
  }
599
23.9k
}
600
601
4.41k
int AcroForm::getNumFields() {
602
4.41k
  return fields->getLength();
603
4.41k
}
604
605
3.62k
AcroFormField *AcroForm::getField(int idx) {
606
3.62k
  return (AcroFormField *)fields->get(idx);
607
3.62k
}
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
4.64k
AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
648
4.64k
  GString *typeStr;
649
4.64k
  TextString *nameA;
650
4.64k
  GString *xfaName;
651
4.64k
  Guint flagsA;
652
4.64k
  GBool haveFlags, typeFromParentA;
653
4.64k
  Object fieldObjA, parentObj, parentObj2, obj1, obj2;
654
4.64k
  AcroFormFieldType typeA;
655
4.64k
  XFAField *xfaFieldA;
656
4.64k
  AcroFormField *field;
657
4.64k
  int depth, i0, i1;
658
659
4.64k
  fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
660
661
  //----- get field info
662
663
4.64k
  if (fieldObjA.dictLookup("T", &obj1)->isString()) {
664
3.42k
    nameA = new TextString(obj1.getString());
665
3.42k
  } else {
666
1.22k
    nameA = new TextString();
667
1.22k
  }
668
4.64k
  obj1.free();
669
670
4.64k
  if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
671
3.68k
    typeStr = new GString(obj1.getName());
672
3.68k
    typeFromParentA = gFalse;
673
3.68k
  } else {
674
963
    typeStr = NULL;
675
963
    typeFromParentA = gTrue;
676
963
  }
677
4.64k
  obj1.free();
678
679
4.64k
  if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
680
2.03k
    flagsA = (Guint)obj1.getInt();
681
2.03k
    haveFlags = gTrue;
682
2.61k
  } else {
683
2.61k
    flagsA = 0;
684
2.61k
    haveFlags = gFalse;
685
2.61k
  }
686
4.64k
  obj1.free();
687
688
  //----- get info from parent non-terminal fields
689
690
4.64k
  fieldObjA.dictLookup("Parent", &parentObj);
691
4.64k
  depth = 0;
692
16.7k
  while (parentObj.isDict() && depth < maxFieldObjectDepth) {
693
694
12.1k
    if (parentObj.dictLookup("T", &obj1)->isString()) {
695
10.4k
      if (nameA->getLength()) {
696
10.0k
  nameA->insert(0, (Unicode)'.');
697
10.0k
      }
698
10.4k
      nameA->insert(0, obj1.getString());
699
10.4k
    }
700
12.1k
    obj1.free();
701
702
12.1k
    if (!typeStr) {
703
2.46k
      if (parentObj.dictLookup("FT", &obj1)->isName()) {
704
225
  typeStr = new GString(obj1.getName());
705
225
      }
706
2.46k
      obj1.free();
707
2.46k
    }
708
709
12.1k
    if (!haveFlags) {
710
6.28k
      if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
711
262
  flagsA = (Guint)obj1.getInt();
712
262
  haveFlags = gTrue;
713
262
      }
714
6.28k
      obj1.free();
715
6.28k
    }
716
717
12.1k
    parentObj.dictLookup("Parent", &parentObj2);
718
12.1k
    parentObj.free();
719
12.1k
    parentObj = parentObj2;
720
721
12.1k
    ++depth;
722
12.1k
  }
723
4.64k
  parentObj.free();
724
725
4.64k
  if (!typeStr) {
726
738
    error(errSyntaxError, -1, "Missing type in AcroForm field");
727
738
    goto err1;
728
738
  }
729
730
  //----- get static XFA info
731
732
3.91k
  xfaFieldA = NULL;
733
3.91k
  if (acroFormA->xfaScanner) {
734
    // convert field name to UTF-8, and remove segments that start
735
    // with '#' -- to match the XFA field name
736
751
    xfaName = nameA->toUTF8();
737
751
    i0 = 0;
738
3.68k
    while (i0 < xfaName->getLength()) {
739
2.93k
      i1 = i0;
740
126k
      while (i1 < xfaName->getLength()) {
741
125k
  if (xfaName->getChar(i1) == '.') {
742
2.19k
    ++i1;
743
2.19k
    break;
744
2.19k
  }
745
123k
  ++i1;
746
123k
      }
747
2.93k
      if (xfaName->getChar(i0) == '#') {
748
729
  xfaName->del(i0, i1 - i0);
749
2.20k
      } else {
750
2.20k
  i0 = i1;
751
2.20k
      }
752
2.93k
    }
753
751
    xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
754
751
    delete xfaName;
755
751
  }
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
3.91k
  if (typeFromParentA && !typeStr->cmp("Btn") && !haveFlags) {
762
32
    flagsA = acroFormFlagRadio;
763
32
  }
764
765
  //----- determine field type
766
767
3.91k
  if (!typeStr->cmp("Btn")) {
768
1.08k
    if (flagsA & acroFormFlagPushbutton) {
769
158
      typeA = acroFormFieldPushbutton;
770
930
    } else if (flagsA & acroFormFlagRadio) {
771
380
      typeA = acroFormFieldRadioButton;
772
550
    } else {
773
550
      typeA = acroFormFieldCheckbox;
774
550
    }
775
2.82k
  } else if (!typeStr->cmp("Tx")) {
776
2.03k
    if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
777
0
      typeA = acroFormFieldBarcode;
778
2.03k
    } else if (flagsA & acroFormFlagFileSelect) {
779
13
      typeA = acroFormFieldFileSelect;
780
2.02k
    } else if (flagsA & acroFormFlagMultiline) {
781
227
      typeA = acroFormFieldMultilineText;
782
1.79k
    } else {
783
1.79k
      typeA = acroFormFieldText;
784
1.79k
    }
785
2.03k
  } else if (!typeStr->cmp("Ch")) {
786
667
    if (flagsA & acroFormFlagCombo) {
787
313
      typeA = acroFormFieldComboBox;
788
354
    } else {
789
354
      typeA = acroFormFieldListBox;
790
354
    }
791
667
  } else if (!typeStr->cmp("Sig")) {
792
0
    typeA = acroFormFieldSignature;
793
123
  } else {
794
123
    error(errSyntaxError, -1, "Invalid type in AcroForm field");
795
123
    goto err1;
796
123
  }
797
3.78k
  delete typeStr;
798
799
3.78k
  field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
800
3.78k
          typeA, nameA, flagsA, typeFromParentA, xfaFieldA);
801
3.78k
  fieldObjA.free();
802
3.78k
  return field;
803
804
861
 err1:
805
861
  delete typeStr;
806
861
  delete nameA;
807
861
  fieldObjA.free();
808
861
  return NULL;
809
3.91k
}
810
811
AcroFormField::AcroFormField(AcroForm *acroFormA,
812
           Object *fieldRefA, Object *fieldObjA,
813
           AcroFormFieldType typeA, TextString *nameA,
814
           Guint flagsA, GBool typeFromParentA,
815
3.78k
           XFAField *xfaFieldA) {
816
3.78k
  acroForm = acroFormA;
817
3.78k
  fieldRefA->copy(&fieldRef);
818
3.78k
  fieldObjA->copy(&fieldObj);
819
3.78k
  type = typeA;
820
3.78k
  name = nameA;
821
3.78k
  flags = flagsA;
822
3.78k
  typeFromParent = typeFromParentA;
823
3.78k
  xfaField = xfaFieldA;
824
3.78k
}
825
826
3.78k
AcroFormField::~AcroFormField() {
827
3.78k
  fieldRef.free();
828
3.78k
  fieldObj.free();
829
3.78k
  delete name;
830
3.78k
}
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.36k
Unicode *AcroFormField::getValue(int *length) {
879
2.36k
  Object obj1, obj2;
880
2.36k
  Unicode *u;
881
2.36k
  char *s;
882
2.36k
  TextString *ts;
883
2.36k
  GString *gs;
884
2.36k
  int n, i;
885
886
2.36k
  u = NULL;
887
2.36k
  *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.36k
  if (globalParams->getPreferXFAFieldValues() && xfaField) {
893
321
    if (xfaField->getValue()) {
894
267
      u = utf8ToUnicode(xfaField->getValue(), length);
895
267
    }
896
897
  // no XFA form - take the AcroForm value
898
2.04k
  } else {
899
2.04k
    fieldLookup("V", &obj1);
900
2.04k
    if (obj1.isName()) {
901
617
      s = obj1.getName();
902
617
      n = (int)strlen(s);
903
617
      u = (Unicode *)gmallocn(n, sizeof(Unicode));
904
1.80k
      for (i = 0; i < n; ++i) {
905
1.18k
        u[i] = s[i] & 0xff;
906
1.18k
      }
907
617
      *length = n;
908
1.42k
    } else if (obj1.isString()) {
909
519
      ts = new TextString(obj1.getString());
910
519
      n = ts->getLength();
911
519
      u = (Unicode *)gmallocn(n, sizeof(Unicode));
912
519
      memcpy(u, ts->getUnicode(), n * sizeof(Unicode));
913
519
      *length = n;
914
519
      delete ts;
915
908
    } else if (obj1.isDict()) {
916
31
      obj1.dictLookup("Contents", &obj2);
917
31
      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
31
      obj2.free();
927
31
    }
928
2.04k
    obj1.free();
929
2.04k
  }
930
931
2.36k
  return u;
932
2.36k
}
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
13.0k
void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) {
1111
13.0k
  Object kidsObj, annotRef, annotObj;
1112
13.0k
  int i;
1113
1114
  // find the annotation object(s)
1115
13.0k
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
1116
13.1k
    for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
1117
11.0k
      kidsObj.arrayGetNF(i, &annotRef);
1118
11.0k
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
1119
11.0k
      drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj);
1120
11.0k
      annotObj.free();
1121
11.0k
      annotRef.free();
1122
11.0k
    }
1123
10.9k
  } else {
1124
10.9k
    drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj);
1125
10.9k
  }
1126
13.0k
  kidsObj.free();
1127
13.0k
}
1128
1129
void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
1130
21.9k
            Object *annotRef, Object *annotObj) {
1131
21.9k
  Object obj1, obj2;
1132
21.9k
  double xMin, yMin, xMax, yMax, t;
1133
21.9k
  int annotFlags;
1134
21.9k
  GBool oc, render;
1135
1136
21.9k
  if (!annotObj->isDict()) {
1137
6.41k
    return;
1138
6.41k
  }
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
15.5k
  if (acroForm->lookupAnnotPage(annotRef) != pageNum) {
1145
11.9k
    return;
1146
11.9k
  }
1147
1148
  //----- check annotation flags
1149
1150
3.60k
  if (annotObj->dictLookup("F", &obj1)->isInt()) {
1151
3.39k
    annotFlags = obj1.getInt();
1152
3.39k
  } else {
1153
213
    annotFlags = 0;
1154
213
  }
1155
3.60k
  obj1.free();
1156
3.60k
  if ((annotFlags & annotFlagHidden) ||
1157
3.57k
      (printing && !(annotFlags & annotFlagPrint)) ||
1158
3.52k
      (!printing && (annotFlags & annotFlagNoView))) {
1159
94
    return;
1160
94
  }
1161
1162
  //----- check the optional content entry
1163
1164
3.51k
  annotObj->dictLookupNF("OC", &obj1);
1165
3.51k
  if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) {
1166
0
    obj1.free();
1167
0
    return;
1168
0
  }
1169
3.51k
  obj1.free();
1170
1171
  //----- get the bounding box
1172
1173
3.51k
  if (annotObj->dictLookup("Rect", &obj1)->isArray() &&
1174
3.29k
      obj1.arrayGetLength() == 4) {
1175
3.10k
    xMin = yMin = xMax = yMax = 0;
1176
3.10k
    if (obj1.arrayGet(0, &obj2)->isNum()) {
1177
2.87k
      xMin = obj2.getNum();
1178
2.87k
    }
1179
3.10k
    obj2.free();
1180
3.10k
    if (obj1.arrayGet(1, &obj2)->isNum()) {
1181
3.03k
      yMin = obj2.getNum();
1182
3.03k
    }
1183
3.10k
    obj2.free();
1184
3.10k
    if (obj1.arrayGet(2, &obj2)->isNum()) {
1185
2.79k
      xMax = obj2.getNum();
1186
2.79k
    }
1187
3.10k
    obj2.free();
1188
3.10k
    if (obj1.arrayGet(3, &obj2)->isNum()) {
1189
2.98k
      yMax = obj2.getNum();
1190
2.98k
    }
1191
3.10k
    obj2.free();
1192
3.10k
    if (xMin > xMax) {
1193
534
      t = xMin; xMin = xMax; xMax = t;
1194
534
    }
1195
3.10k
    if (yMin > yMax) {
1196
259
      t = yMin; yMin = yMax; yMax = t;
1197
259
    }
1198
3.10k
  } else {
1199
411
    error(errSyntaxError, -1, "Bad bounding box for annotation");
1200
411
    obj1.free();
1201
411
    return;
1202
411
  }
1203
3.10k
  obj1.free();
1204
1205
  //----- draw it
1206
1207
3.10k
  render = gFalse;
1208
3.10k
  if (acroForm->needAppearances) {
1209
1.10k
    render = gTrue;
1210
1.99k
  } else if (xfaField && xfaField->getValue()) {
1211
267
    render = gTrue;
1212
1.73k
  } else {
1213
1.73k
    if (!annotObj->dictLookup("AP", &obj1)->isDict()) {
1214
991
      render = gTrue;
1215
991
    }
1216
1.73k
    obj1.free();
1217
1.73k
  }
1218
3.10k
  if (render) {
1219
2.36k
    drawNewAppearance(gfx, annotObj->getDict(),
1220
2.36k
          xMin, yMin, xMax, yMax);
1221
2.36k
  } else {
1222
739
    drawExistingAppearance(gfx, annotObj->getDict(),
1223
739
         xMin, yMin, xMax, yMax);
1224
739
  }
1225
3.10k
}
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
739
             double xMax, double yMax) {
1232
739
  Object apObj, asObj, appearance, obj1;
1233
1234
  //----- get the appearance stream
1235
1236
739
  if (annot->lookup("AP", &apObj)->isDict()) {
1237
739
    apObj.dictLookup("N", &obj1);
1238
739
    if (obj1.isDict()) {
1239
252
      if (annot->lookup("AS", &asObj)->isName()) {
1240
206
  obj1.dictLookupNF(asObj.getName(), &appearance);
1241
206
      } else if (obj1.dictGetLength() == 1) {
1242
8
  obj1.dictGetValNF(0, &appearance);
1243
38
      } else {
1244
38
  obj1.dictLookupNF("Off", &appearance);
1245
38
      }
1246
252
      asObj.free();
1247
487
    } else {
1248
487
      apObj.dictLookupNF("N", &appearance);
1249
487
    }
1250
739
    obj1.free();
1251
739
  }
1252
739
  apObj.free();
1253
1254
  //----- draw it
1255
1256
739
  if (!appearance.isNone()) {
1257
739
    gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
1258
739
    appearance.free();
1259
739
  }
1260
739
}
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.36k
              double xMax, double yMax) {
1266
2.36k
  GString *appearBuf;
1267
2.36k
  Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj;
1268
2.36k
  Object resources, fontResources, defaultFont, gfxStateDict;
1269
2.36k
  Object obj1, obj2, obj3, obj4;
1270
2.36k
  Dict *mkDict;
1271
2.36k
  MemStream *appearStream;
1272
2.36k
  GfxFontDict *fontDict;
1273
2.36k
  GBool hasCaption;
1274
2.36k
  double dx, dy, r;
1275
2.36k
  GString *val, *caption, *da;
1276
2.36k
  GString **text;
1277
2.36k
  GBool done;
1278
2.36k
  GBool *selection;
1279
2.36k
  AnnotBorderType borderType;
1280
2.36k
  double borderWidth;
1281
2.36k
  double *borderDash;
1282
2.36k
  GString *appearanceState;
1283
2.36k
  int borderDashLength, rot, quadding, vAlign, comb, nOptions, topIdx, i;
1284
1285
2.36k
  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.36k
  if (annot->lookup("MK", &mkObj)->isDict()) {
1297
1.58k
    mkDict = mkObj.getDict();
1298
1.58k
  } else {
1299
778
    mkDict = NULL;
1300
778
  }
1301
1302
  // draw the background
1303
2.36k
  if (mkDict) {
1304
1.58k
    if (mkDict->lookup("BG", &obj1)->isArray() &&
1305
67
  obj1.arrayGetLength() > 0) {
1306
67
      setColor(obj1.getArray(), gTrue, 0, appearBuf);
1307
67
      appearBuf->appendf("0 0 {0:.4f} {1:.4f} re f\n",
1308
67
       xMax - xMin, yMax - yMin);
1309
67
    }
1310
1.58k
    obj1.free();
1311
1.58k
  }
1312
1313
  // get the field type
1314
2.36k
  fieldLookup("FT", &ftObj);
1315
1316
  // draw the border
1317
2.36k
  borderType = annotBorderSolid;
1318
2.36k
  borderWidth = 1;
1319
2.36k
  borderDash = NULL;
1320
2.36k
  borderDashLength = 0;
1321
2.36k
  if (annot->lookup("BS", &obj1)->isDict()) {
1322
332
    if (obj1.dictLookup("S", &obj2)->isName()) {
1323
247
      if (obj2.isName("S")) {
1324
161
  borderType = annotBorderSolid;
1325
161
      } else if (obj2.isName("D")) {
1326
10
  borderType = annotBorderDashed;
1327
76
      } else if (obj2.isName("B")) {
1328
25
  borderType = annotBorderBeveled;
1329
51
      } else if (obj2.isName("I")) {
1330
6
  borderType = annotBorderInset;
1331
45
      } else if (obj2.isName("U")) {
1332
26
  borderType = annotBorderUnderlined;
1333
26
      }
1334
247
    }
1335
332
    obj2.free();
1336
332
    if (obj1.dictLookup("W", &obj2)->isNum()) {
1337
224
      borderWidth = obj2.getNum();
1338
224
    }
1339
332
    obj2.free();
1340
332
    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
332
    obj2.free();
1353
2.03k
  } else {
1354
2.03k
    obj1.free();
1355
2.03k
    if (annot->lookup("Border", &obj1)->isArray()) {
1356
37
      if (obj1.arrayGetLength() >= 3) {
1357
34
  if (obj1.arrayGet(2, &obj2)->isNum()) {
1358
13
    borderWidth = obj2.getNum();
1359
13
  }
1360
34
  obj2.free();
1361
34
  if (obj1.arrayGetLength() >= 4) {
1362
24
    if (obj1.arrayGet(3, &obj2)->isArray()) {
1363
19
      borderType = annotBorderDashed;
1364
19
      borderDashLength = obj2.arrayGetLength();
1365
19
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
1366
224
      for (i = 0; i < borderDashLength; ++i) {
1367
205
        if (obj2.arrayGet(i, &obj3)->isNum()) {
1368
62
    borderDash[i] = obj3.getNum();
1369
143
        } else {
1370
143
    borderDash[i] = 1;
1371
143
        }
1372
205
        obj3.free();
1373
205
      }
1374
19
    } else {
1375
      // Adobe draws no border at all if the last element is of
1376
      // the wrong type.
1377
5
      borderWidth = 0;
1378
5
    }
1379
24
    obj2.free();
1380
24
  }
1381
34
      }
1382
37
    }
1383
2.03k
  }
1384
2.36k
  obj1.free();
1385
2.36k
  if (mkDict) {
1386
1.58k
    if (borderWidth > 0) {
1387
1.56k
      mkDict->lookup("BC", &obj1);
1388
1.56k
      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
1389
1.46k
  obj1.free();
1390
1.46k
  mkDict->lookup("BG", &obj1);
1391
1.46k
      }
1392
1.56k
      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
1393
161
  dx = xMax - xMin;
1394
161
  dy = yMax - yMin;
1395
1396
  // radio buttons with no caption have a round border
1397
161
  hasCaption = mkDict->lookup("CA", &obj2)->isString();
1398
161
  obj2.free();
1399
161
  if (ftObj.isName("Btn") && (flags & acroFormFlagRadio) && !hasCaption) {
1400
11
    r = 0.5 * (dx < dy ? dx : dy);
1401
11
    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
5
    case annotBorderSolid:
1410
5
    case annotBorderUnderlined:
1411
5
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1412
5
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1413
5
      drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * borderWidth, "s",
1414
5
           appearBuf);
1415
5
      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
11
    }
1434
1435
150
  } else {
1436
150
    switch (borderType) {
1437
0
    case annotBorderDashed:
1438
0
      appearBuf->append("[");
1439
0
      for (i = 0; i < borderDashLength; ++i) {
1440
0
        appearBuf->appendf(" {0:.4f}", borderDash[i]);
1441
0
      }
1442
0
      appearBuf->append("] 0 d\n");
1443
      // fall through to the solid case
1444
142
    case annotBorderSolid:
1445
142
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1446
142
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1447
142
      appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n",
1448
142
             0.5 * borderWidth,
1449
142
             dx - borderWidth, dy - borderWidth);
1450
142
      break;
1451
8
    case annotBorderBeveled:
1452
8
    case annotBorderInset:
1453
8
      setColor(obj1.getArray(), gTrue,
1454
8
         borderType == annotBorderBeveled ? 1 : -1,
1455
8
         appearBuf);
1456
8
      appearBuf->append("0 0 m\n");
1457
8
      appearBuf->appendf("0 {0:.4f} l\n", dy);
1458
8
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
1459
8
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1460
8
             dx - borderWidth, dy - borderWidth);
1461
8
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1462
8
             borderWidth, dy - borderWidth);
1463
8
      appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
1464
8
      appearBuf->append("f\n");
1465
8
      setColor(obj1.getArray(), gTrue,
1466
8
         borderType == annotBorderBeveled ? -1 : 1,
1467
8
         appearBuf);
1468
8
      appearBuf->append("0 0 m\n");
1469
8
      appearBuf->appendf("{0:.4f} 0 l\n", dx);
1470
8
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
1471
8
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1472
8
             dx - borderWidth, dy - borderWidth);
1473
8
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1474
8
             dx - borderWidth, borderWidth);
1475
8
      appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
1476
8
      appearBuf->append("f\n");
1477
8
      break;
1478
0
    case annotBorderUnderlined:
1479
0
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1480
0
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1481
0
      appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx);
1482
0
      break;
1483
150
    }
1484
1485
    // clip to the inside of the border
1486
150
    appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n",
1487
150
           borderWidth,
1488
150
           dx - 2 * borderWidth, dy - 2 * borderWidth);
1489
150
  }
1490
161
      }
1491
1.56k
      obj1.free();
1492
1.56k
    }
1493
1.58k
  }
1494
2.36k
  gfree(borderDash);
1495
1496
  // get the resource dictionary
1497
2.36k
  buildDefaultResourceDict(&drObj);
1498
1499
  // build the font dictionary
1500
2.36k
  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
1501
1.66k
    fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict());
1502
1.66k
  } else {
1503
704
    fontDict = NULL;
1504
704
  }
1505
2.36k
  obj1.free();
1506
1507
  // get the default appearance string
1508
2.36k
  if (fieldLookup("DA", &obj1)->isString()) {
1509
2.01k
    da = obj1.getString()->copy();
1510
2.01k
  } else {
1511
348
    da = NULL;
1512
348
  }
1513
2.36k
  obj1.free();
1514
1515
  // get the rotation value
1516
2.36k
  rot = 0;
1517
2.36k
  if (mkDict) {
1518
1.58k
    if (mkDict->lookup("R", &obj1)->isInt()) {
1519
24
      rot = obj1.getInt();
1520
24
    }
1521
1.58k
    obj1.free();
1522
1.58k
  }
1523
1524
  // get the appearance state
1525
2.36k
  annot->lookup("AP", &apObj);
1526
2.36k
  annot->lookup("AS", &asObj);
1527
2.36k
  appearanceState = NULL;
1528
2.36k
  if (asObj.isName()) {
1529
499
    appearanceState = new GString(asObj.getName());
1530
1.86k
  } else if (apObj.isDict()) {
1531
527
    apObj.dictLookup("N", &obj1);
1532
527
    if (obj1.isDict() && obj1.dictGetLength() == 1) {
1533
21
      appearanceState = new GString(obj1.dictGetKey(0));
1534
21
    }
1535
527
    obj1.free();
1536
527
  }
1537
2.36k
  if (!appearanceState) {
1538
1.84k
    appearanceState = new GString("Off");
1539
1.84k
  }
1540
2.36k
  asObj.free();
1541
2.36k
  apObj.free();
1542
1543
2.36k
  int valueLength;
1544
2.36k
  Unicode *value = getValue(&valueLength);
1545
1546
  // draw the field contents
1547
2.36k
  if (ftObj.isName("Btn")) {
1548
748
    caption = NULL;
1549
748
    if (mkDict) {
1550
488
      if (mkDict->lookup("CA", &obj1)->isString()) {
1551
250
  caption = obj1.getString()->copy();
1552
250
      }
1553
488
      obj1.free();
1554
488
    }
1555
    // radio button
1556
748
    if (flags & acroFormFlagRadio) {
1557
      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
1558
219
      if (value && unicodeStringEqual(value, valueLength,
1559
166
              appearanceState->getCString())) {
1560
57
  if (caption) {
1561
16
    drawText(caption, da, fontDict, gFalse, 0,
1562
16
       acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
1563
16
       gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
1564
16
       borderWidth, gFalse, appearBuf);
1565
41
  } else {
1566
41
    if (mkDict) {
1567
16
      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
16
      obj2.free();
1576
16
    }
1577
41
  }
1578
57
      }
1579
    // pushbutton
1580
529
    } else if (flags & acroFormFlagPushbutton) {
1581
132
      if (caption) {
1582
21
  drawText(caption, da, fontDict, gFalse, 0,
1583
21
     acroFormQuadCenter, acroFormVAlignMiddle,
1584
21
     gFalse, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
1585
21
     borderWidth, gFalse, appearBuf);
1586
21
      }
1587
    // checkbox
1588
397
    } else {
1589
397
      if (value && !(unicodeStringEqual(value, valueLength, "Off") ||
1590
268
         unicodeStringEqual(value, valueLength, "No") ||
1591
258
         unicodeStringEqual(value, valueLength, "0") ||
1592
252
         valueLength == 0)) {
1593
252
  if (!caption) {
1594
181
    caption = new GString("3"); // ZapfDingbats checkmark
1595
181
  }
1596
252
  drawText(caption, da, fontDict, gFalse, 0,
1597
252
     acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
1598
252
     gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
1599
252
     borderWidth, gFalse, appearBuf);
1600
252
      }
1601
397
    }
1602
748
    if (caption) {
1603
431
      delete caption;
1604
431
    }
1605
1.61k
  } else if (ftObj.isName("Tx")) {
1606
1.17k
    XFAFieldBarcodeInfo *barcodeInfo = xfaField ? xfaField->getBarcodeInfo()
1607
1.17k
                                                : (XFAFieldBarcodeInfo *)NULL;
1608
1.17k
    if (value) {
1609
      //~ value strings can be Unicode
1610
431
      GString *valueLatin1 = unicodeToLatin1(value, valueLength);
1611
431
      if (barcodeInfo) {
1612
0
  drawBarcode(valueLatin1, da, fontDict, rot, xMin, yMin, xMax, yMax,
1613
0
        barcodeInfo, appearBuf);
1614
431
      } else {
1615
431
  if (fieldLookup("Q", &obj2)->isInt()) {
1616
267
    quadding = obj2.getInt();
1617
267
  } else {
1618
164
    quadding = acroFormQuadLeft;
1619
164
  }
1620
431
  obj2.free();
1621
431
  vAlign = (flags & acroFormFlagMultiline) ? acroFormVAlignTop
1622
431
                                           : acroFormVAlignMiddle;
1623
431
  XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
1624
431
                                            : (XFAFieldLayoutInfo *)NULL;
1625
431
  if (layoutInfo) {
1626
250
    switch (layoutInfo->hAlign) {
1627
160
    case xfaFieldLayoutHAlignLeft:
1628
160
    default:
1629
160
      quadding = acroFormQuadLeft;
1630
160
      break;
1631
42
    case xfaFieldLayoutHAlignCenter:
1632
42
      quadding = acroFormQuadCenter;
1633
42
      break;
1634
48
    case xfaFieldLayoutHAlignRight:
1635
48
      quadding = acroFormQuadRight;
1636
48
      break;
1637
250
    }
1638
250
    switch (layoutInfo->vAlign) {
1639
56
    case xfaFieldLayoutVAlignTop:
1640
56
    default:
1641
56
      vAlign = acroFormVAlignTop;
1642
56
      break;
1643
194
    case xfaFieldLayoutVAlignMiddle:
1644
194
      vAlign = acroFormVAlignMiddle;
1645
194
      break;
1646
0
    case xfaFieldLayoutVAlignBottom:
1647
0
      vAlign = acroFormVAlignBottom;
1648
0
      break;
1649
250
    }
1650
250
  }
1651
431
  comb = 0;
1652
431
  if (flags & acroFormFlagComb) {
1653
80
    if (fieldLookup("MaxLen", &obj2)->isInt()) {
1654
73
      comb = obj2.getInt();
1655
73
    }
1656
80
    obj2.free();
1657
80
  }
1658
431
  XFAFieldPictureInfo *pictureInfo =
1659
431
      xfaField ? xfaField->getPictureInfo()
1660
431
               : (XFAFieldPictureInfo *)NULL;
1661
431
  GString *value2 = valueLatin1;
1662
431
  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
431
  drawText(value2, da, fontDict,
1676
431
     flags & acroFormFlagMultiline, comb, quadding, vAlign,
1677
431
     gTrue, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
1678
431
     borderWidth, gFalse, appearBuf);
1679
431
  if (value2 != valueLatin1) {
1680
0
    delete value2;
1681
0
  }
1682
431
      }
1683
431
      delete valueLatin1;
1684
431
    }
1685
1.17k
  } else if (ftObj.isName("Ch")) {
1686
    //~ value/option strings can be Unicode
1687
437
    if (fieldLookup("Q", &obj1)->isInt()) {
1688
12
      quadding = obj1.getInt();
1689
425
    } else {
1690
425
      quadding = acroFormQuadLeft;
1691
425
    }
1692
437
    obj1.free();
1693
437
    vAlign = acroFormVAlignMiddle;
1694
437
    XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
1695
437
                                              : (XFAFieldLayoutInfo *)NULL;
1696
437
    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
437
    if (flags & acroFormFlagCombo) {
1724
180
      if (value) {
1725
88
  val = unicodeToLatin1(value, valueLength);
1726
88
  if (fieldObj.dictLookup("Opt", &obj2)->isArray()) {
1727
1.39k
    for (i = 0, done = false; i < obj2.arrayGetLength() && !done; ++i) {
1728
1.31k
      obj2.arrayGet(i, &obj3);
1729
1.31k
      if (obj3.isArray() && obj3.arrayGetLength() == 2) {
1730
24
        if (obj3.arrayGet(0, &obj4)->isString() &&
1731
15
      obj4.getString()->cmp(val) == 0) {
1732
10
    obj4.free();
1733
10
    if (obj3.arrayGet(1, &obj4)->isString()) {
1734
10
      delete val;
1735
10
      val = obj4.getString()->copy();
1736
10
    }
1737
10
    done = gTrue;
1738
10
        }
1739
24
        obj4.free();
1740
24
      }
1741
1.31k
      obj3.free();
1742
1.31k
    }
1743
76
  }
1744
88
  obj2.free();
1745
88
  drawText(val, da, fontDict,
1746
88
     gFalse, 0, quadding, vAlign, gTrue, gFalse, rot,
1747
88
     0, 0, xMax - xMin, yMax - yMin, borderWidth,
1748
88
     gFalse, appearBuf);
1749
88
  delete val;
1750
  //~ Acrobat draws a popup icon on the right side
1751
88
      }
1752
    // list box
1753
257
    } else {
1754
257
      if (fieldObj.dictLookup("Opt", &obj1)->isArray()) {
1755
212
  nOptions = obj1.arrayGetLength();
1756
  // get the option text
1757
212
  text = (GString **)gmallocn(nOptions, sizeof(GString *));
1758
2.94k
  for (i = 0; i < nOptions; ++i) {
1759
2.73k
    text[i] = NULL;
1760
2.73k
    obj1.arrayGet(i, &obj2);
1761
2.73k
    if (obj2.isString()) {
1762
772
      text[i] = obj2.getString()->copy();
1763
1.96k
    } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
1764
96
      if (obj2.arrayGet(1, &obj3)->isString()) {
1765
79
        text[i] = obj3.getString()->copy();
1766
79
      }
1767
96
      obj3.free();
1768
96
    }
1769
2.73k
    obj2.free();
1770
2.73k
    if (!text[i]) {
1771
1.88k
      text[i] = new GString();
1772
1.88k
    }
1773
2.73k
  }
1774
  // get the selected option(s)
1775
212
  selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
1776
  //~ need to use the I field in addition to the V field
1777
2.94k
  for (i = 0; i < nOptions; ++i) {
1778
2.73k
    selection[i] = unicodeStringEqual(value, valueLength, text[i]);
1779
2.73k
  }
1780
  // get the top index
1781
212
  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
212
  } else {
1787
212
    topIdx = 0;
1788
212
  }
1789
212
  obj2.free();
1790
  // draw the text
1791
212
  drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding,
1792
212
        xMin, yMin, xMax, yMax, borderWidth, appearBuf);
1793
2.94k
  for (i = 0; i < nOptions; ++i) {
1794
2.73k
    delete text[i];
1795
2.73k
  }
1796
212
  gfree(text);
1797
212
  gfree(selection);
1798
212
      }
1799
257
      obj1.free();
1800
257
    }
1801
437
  } 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
10
  } else {
1819
10
    error(errSyntaxError, -1, "Unknown field type");
1820
10
  }
1821
1822
2.36k
  gfree(value);
1823
1824
2.36k
  delete appearanceState;
1825
2.36k
  if (da) {
1826
2.01k
    delete da;
1827
2.01k
  }
1828
1829
  // build the appearance stream dictionary
1830
2.36k
  appearDict.initDict(acroForm->doc->getXRef());
1831
2.36k
  appearDict.dictAdd(copyString("Length"),
1832
2.36k
         obj1.initInt(appearBuf->getLength()));
1833
2.36k
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
1834
2.36k
  obj1.initArray(acroForm->doc->getXRef());
1835
2.36k
  obj1.arrayAdd(obj2.initReal(0));
1836
2.36k
  obj1.arrayAdd(obj2.initReal(0));
1837
2.36k
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
1838
2.36k
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
1839
2.36k
  appearDict.dictAdd(copyString("BBox"), &obj1);
1840
1841
  // set the resource dictionary; add a default font
1842
2.36k
  if (drObj.isDict()) {
1843
2.36k
    drObj.copy(&resources);
1844
2.36k
  } else {
1845
0
    resources.initDict(acroForm->doc->getXRef());
1846
0
  }
1847
2.36k
  drObj.free();
1848
2.36k
  fontResources.initDict(acroForm->doc->getXRef());
1849
2.36k
  if (resources.dictLookup("Font", &obj1)->isDict()) {
1850
7.29k
    for (i = 0; i < obj1.dictGetLength(); ++i) {
1851
5.63k
      obj1.dictGetValNF(i, &obj2);
1852
5.63k
      fontResources.dictAdd(copyString(obj1.dictGetKey(i)), &obj2);
1853
5.63k
    }
1854
1.66k
  }
1855
2.36k
  obj1.free();
1856
2.36k
  defaultFont.initDict(acroForm->doc->getXRef());
1857
2.36k
  defaultFont.dictAdd(copyString("Type"), obj1.initName("Font"));
1858
2.36k
  defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1"));
1859
2.36k
  defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica"));
1860
2.36k
  defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding"));
1861
2.36k
  fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
1862
2.36k
  resources.dictAdd(copyString("Font"), &fontResources);
1863
2.36k
  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.36k
  appearDict.dictAdd(copyString("Resources"), &resources);
1869
1870
  // build the appearance stream
1871
2.36k
  appearStream = new MemStream(appearBuf->getCString(), 0,
1872
2.36k
             appearBuf->getLength(), &appearDict);
1873
2.36k
  appearance.initStream(appearStream);
1874
1875
  // draw it
1876
2.36k
  gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
1877
1878
2.36k
  appearance.free();
1879
2.36k
  delete appearBuf;
1880
2.36k
  appearBuf = NULL;
1881
2.36k
  if (fontDict) {
1882
1.66k
    delete fontDict;
1883
1.66k
  }
1884
2.36k
  ftObj.free();
1885
2.36k
  mkObj.free();
1886
2.36k
}
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
248
           GString *appearBuf) {
1894
248
  Object obj1;
1895
248
  double color[4];
1896
248
  int nComps, i;
1897
1898
248
  nComps = a->getLength();
1899
248
  if (nComps > 4) {
1900
8
    nComps = 4;
1901
8
  }
1902
722
  for (i = 0; i < nComps && i < 4; ++i) {
1903
474
    if (a->get(i, &obj1)->isNum()) {
1904
386
      color[i] = obj1.getNum();
1905
386
    } else {
1906
88
      color[i] = 0;
1907
88
    }
1908
474
    obj1.free();
1909
474
  }
1910
248
  if (nComps == 4) {
1911
25
    adjust = -adjust;
1912
25
  }
1913
248
  if (adjust > 0) {
1914
43
    for (i = 0; i < nComps; ++i) {
1915
29
      color[i] = 0.5 * color[i] + 0.5;
1916
29
    }
1917
234
  } else if (adjust < 0) {
1918
43
    for (i = 0; i < nComps; ++i) {
1919
29
      color[i] = 0.5 * color[i];
1920
29
    }
1921
14
  }
1922
248
  if (nComps == 4) {
1923
25
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1924
25
           color[0], color[1], color[2], color[3],
1925
25
           fill ? 'k' : 'K');
1926
223
  } else if (nComps == 3) {
1927
71
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1928
71
           color[0], color[1], color[2],
1929
71
           fill ? "rg" : "RG");
1930
152
  } else {
1931
152
    appearBuf->appendf("{0:.2f} {1:c}\n",
1932
152
           color[0],
1933
152
           fill ? 'g' : 'G');
1934
152
  }
1935
248
}
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
808
           GString *appearBuf) {
1945
808
  GString *text2;
1946
808
  GList *daToks;
1947
808
  GString *tok;
1948
808
  GfxFont *font;
1949
808
  double dx, dy;
1950
808
  double fontSize, fontSize2, topBorder, xx, xPrev, yy, w, wMax;
1951
808
  double offset, offset2, charWidth, ascent, descent;
1952
808
  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
808
  if (text->getLength() >= 2 &&
1961
538
      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
808
  } else {
1972
808
    text2 = text;
1973
808
  }
1974
808
  if (text2->getLength() == 0) {
1975
6
    if (text2 != text) {
1976
0
      delete text2;
1977
0
    }
1978
6
    return;
1979
6
  }
1980
1981
  // parse the default appearance string
1982
802
  tfPos = tmPos = -1;
1983
802
  if (da) {
1984
661
    daToks = tokenize(da);
1985
22.0k
    for (i = 2; i < daToks->getLength(); ++i) {
1986
21.4k
      if (!((GString *)daToks->get(i))->cmp("Tf")) {
1987
531
  tfPos = i - 2;
1988
20.9k
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
1989
25
  tmPos = i - 6;
1990
25
      }
1991
21.4k
    }
1992
661
  } else {
1993
141
    daToks = NULL;
1994
141
  }
1995
1996
  // force ZapfDingbats
1997
  //~ this should create the font if needed (?)
1998
802
  if (forceZapfDingbats) {
1999
268
    if (tfPos >= 0) {
2000
159
      tok = (GString *)daToks->get(tfPos);
2001
159
      if (tok->cmp("/ZaDb")) {
2002
159
  tok->clear();
2003
159
  tok->append("/ZaDb");
2004
159
      }
2005
159
    }
2006
268
  }
2007
2008
  // get the font and font size
2009
802
  font = NULL;
2010
802
  fontSize = 0;
2011
802
  if (tfPos >= 0) {
2012
530
    tok = (GString *)daToks->get(tfPos);
2013
530
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2014
473
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2015
221
  error(errSyntaxError, -1, "Unknown font in field's DA string");
2016
221
  tok->clear();
2017
221
  tok->append("/xpdf_default_font");
2018
221
      }
2019
473
    } else {
2020
57
      error(errSyntaxError, -1,
2021
57
      "Invalid font name in 'Tf' operator in field's DA string");
2022
57
    }
2023
530
    tok = (GString *)daToks->get(tfPos + 1);
2024
530
    fontSize = atof(tok->getCString());
2025
530
  } else {
2026
272
    error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
2027
272
    fontSize = 0;
2028
272
    if (!daToks) {
2029
141
      daToks = new GList();
2030
141
    }
2031
272
    tfPos = daToks->getLength();
2032
272
    daToks->append(new GString("/xpdf_default_font"));
2033
272
    daToks->append(new GString("10"));
2034
272
    daToks->append(new GString("Tf"));
2035
272
  }
2036
2037
  // setup
2038
802
  if (txField) {
2039
513
    appearBuf->append("/Tx BMC\n");
2040
513
  }
2041
802
  appearBuf->append("q\n");
2042
802
  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
802
  } 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
802
  } 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
802
  } else { // assume rot == 0
2055
802
    dx = width;
2056
802
    dy = height;
2057
802
  }
2058
2059
  // multi-line text
2060
802
  if (multiline) {
2061
    // note: the comb flag is ignored in multiline mode
2062
2063
100
    wMax = dx - 2 * border - 4;
2064
2065
    // this is a kludge that appears to match Adobe's behavior
2066
100
    if (height > 15) {
2067
54
      topBorder = 5;
2068
54
    } else {
2069
46
      topBorder = 2;
2070
46
    }
2071
2072
    // compute font autosize
2073
100
    if (fontSize == 0) {
2074
555
      for (fontSize = 10; fontSize > 1; --fontSize) {
2075
512
  yy = dy - topBorder;
2076
512
  w = 0;
2077
512
  i = 0;
2078
48.5k
  while (i < text2->getLength()) {
2079
48.0k
    getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2080
48.0k
    i = k;
2081
48.0k
    yy -= fontSize;
2082
48.0k
  }
2083
  // approximate the descender for the last line
2084
512
  if (yy >= 0.25 * fontSize && w <= wMax) {
2085
29
    break;
2086
29
  }
2087
512
      }
2088
72
      if (tfPos >= 0) {
2089
72
  tok = (GString *)daToks->get(tfPos + 1);
2090
72
  tok->clear();
2091
72
  tok->appendf("{0:.2f}", fontSize);
2092
72
      }
2093
72
    }
2094
2095
    // starting y coordinate
2096
100
    nLines = 0;
2097
100
    i = 0;
2098
4.29k
    while (i < text2->getLength()) {
2099
4.19k
      getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2100
4.19k
      i = k;
2101
4.19k
      ++nLines;
2102
4.19k
    }
2103
100
    if (font) {
2104
21
      ascent = font->getDeclaredAscent() * fontSize;
2105
21
      descent = font->getDescent() * fontSize;
2106
79
    } else {
2107
79
      ascent = 0.75 * fontSize;
2108
79
      descent = -0.25 * fontSize;
2109
79
    }
2110
100
    switch (vAlign) {
2111
82
    case acroFormVAlignTop:
2112
82
    default:
2113
82
      yy = dy - ascent - topBorder;
2114
82
      break;
2115
18
    case acroFormVAlignMiddle:
2116
18
      yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize - descent;
2117
18
      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
100
    }
2125
    // if the field is shorter than a line of text, Acrobat positions
2126
    // the text relative to the bottom edge
2127
100
    if (dy < fontSize + topBorder) {
2128
7
      yy = 2 - descent;
2129
7
    }
2130
    // each line of text starts with a Td operator that moves down a
2131
    // line -- so move up a line here
2132
100
    yy += fontSize;
2133
2134
100
    appearBuf->append("BT\n");
2135
2136
    // set the font matrix
2137
100
    if (tmPos >= 0) {
2138
3
      tok = (GString *)daToks->get(tmPos + 4);
2139
3
      tok->clear();
2140
3
      tok->appendf("{0:.4f}", x);
2141
3
      tok = (GString *)daToks->get(tmPos + 5);
2142
3
      tok->clear();
2143
3
      tok->appendf("{0:.4f}", y + yy);
2144
3
    }
2145
2146
    // write the DA string
2147
100
    if (daToks) {
2148
2.54k
      for (i = 0; i < daToks->getLength(); ++i) {
2149
2.44k
  appearBuf->append((GString *)daToks->get(i))->append(' ');
2150
2.44k
      }
2151
100
    }
2152
2153
    // write the font matrix (if not part of the DA string)
2154
100
    if (tmPos < 0) {
2155
97
      appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y + yy);
2156
97
    }
2157
2158
    // write a series of lines of text
2159
100
    i = 0;
2160
100
    xPrev = 0;
2161
4.29k
    while (i < text2->getLength()) {
2162
2163
4.19k
      getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2164
2165
      // compute text start position
2166
4.19k
      switch (quadding) {
2167
3.97k
      case acroFormQuadLeft:
2168
3.97k
      default:
2169
3.97k
  xx = border + 2;
2170
3.97k
  break;
2171
31
      case acroFormQuadCenter:
2172
31
  xx = (dx - w) / 2;
2173
31
  break;
2174
183
      case acroFormQuadRight:
2175
183
  xx = dx - border - 2 - w;
2176
183
  break;
2177
4.19k
      }
2178
2179
      // draw the line
2180
4.19k
      appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx - xPrev, -fontSize);
2181
4.19k
      appearBuf->append('(');
2182
49.8k
      for (; i < j; ++i) {
2183
45.6k
  c = text2->getChar(i) & 0xff;
2184
45.6k
  if (c == '(' || c == ')' || c == '\\') {
2185
377
    appearBuf->append('\\');
2186
377
    appearBuf->append((char)c);
2187
45.2k
  } else if (c < 0x20 || c >= 0x80) {
2188
8.09k
    appearBuf->appendf("\\{0:03o}", c);
2189
37.2k
  } else {
2190
37.2k
    appearBuf->append((char)c);
2191
37.2k
  }
2192
45.6k
      }
2193
4.19k
      appearBuf->append(") Tj\n");
2194
2195
      // next line
2196
4.19k
      i = k;
2197
4.19k
      xPrev = xx;
2198
4.19k
    }
2199
2200
100
    appearBuf->append("ET\n");
2201
2202
  // single-line text
2203
702
  } else {
2204
    //~ replace newlines with spaces? - what does Acrobat do?
2205
2206
    // comb formatting
2207
702
    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
71
      if (xfaField) {
2215
32
  XFAFieldLayoutInfo *layoutInfo = xfaField->getLayoutInfo();
2216
32
  if (layoutInfo) {
2217
29
    dx -= layoutInfo->marginLeft + layoutInfo->marginRight;
2218
29
  }
2219
32
      }
2220
2221
      // compute comb spacing
2222
71
      w = dx / comb;
2223
2224
      // compute font autosize
2225
71
      if (fontSize == 0) {
2226
34
  fontSize = dy - 2 * border;
2227
34
  if (w < fontSize) {
2228
31
    fontSize = w;
2229
31
  }
2230
34
  fontSize = floor(fontSize);
2231
34
  if (fontSize > 10) {
2232
5
    fontSize = 10;
2233
5
  }
2234
34
  if (tfPos >= 0) {
2235
34
    tok = (GString *)daToks->get(tfPos + 1);
2236
34
    tok->clear();
2237
34
    tok->appendf("{0:.4f}", fontSize);
2238
34
  }
2239
34
      }
2240
2241
      // compute text start position
2242
71
      switch (quadding) {
2243
68
      case acroFormQuadLeft:
2244
68
      default:
2245
68
  xx = 0;
2246
68
  break;
2247
1
      case acroFormQuadCenter:
2248
1
  xx = ((comb - text2->getLength()) / 2) * w;
2249
1
  break;
2250
2
      case acroFormQuadRight:
2251
2
  xx = (comb - text2->getLength()) * w;
2252
2
  break;
2253
71
      }
2254
71
      if (font) {
2255
27
  ascent = font->getDeclaredAscent() * fontSize;
2256
27
  descent = font->getDescent() * fontSize;
2257
44
      } else {
2258
44
  ascent = 0.75 * fontSize;
2259
44
  descent = -0.25 * fontSize;
2260
44
      }
2261
71
      switch (vAlign) {
2262
4
      case acroFormVAlignTop:
2263
4
      default:
2264
4
  yy = dy - ascent;
2265
4
  break;
2266
67
      case acroFormVAlignMiddle:
2267
67
  yy = 0.5 * (dy - ascent - descent);
2268
67
  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
71
      }
2276
2277
71
      appearBuf->append("BT\n");
2278
2279
      // set the font matrix
2280
71
      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
71
      if (daToks) {
2291
539
  for (i = 0; i < daToks->getLength(); ++i) {
2292
468
    appearBuf->append((GString *)daToks->get(i))->append(' ');
2293
468
  }
2294
71
      }
2295
2296
      // write the font matrix (if not part of the DA string)
2297
71
      if (tmPos < 0) {
2298
71
  appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
2299
71
      }
2300
2301
      // write the text string
2302
71
      offset = 0;
2303
48.0k
      for (i = 0; i < text2->getLength(); ++i) {
2304
47.9k
  c = text2->getChar(i) & 0xff;
2305
47.9k
  if (c >= 0x20 && c < 0x80) {
2306
32.8k
    if (font && !font->isCIDFont()) {
2307
652
      charWidth = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
2308
32.1k
    } else {
2309
      // otherwise, make a crude estimate
2310
32.1k
      charWidth = 0.5 * fontSize;
2311
32.1k
    }
2312
32.8k
    offset2 = 0.5 * (w - charWidth);
2313
32.8k
    appearBuf->appendf("{0:.4f} 0 Td\n", offset + offset2);
2314
32.8k
    if (c == '(' || c == ')' || c == '\\') {
2315
405
      appearBuf->appendf("(\\{0:c}) Tj\n", c);
2316
32.4k
    } else {
2317
32.4k
      appearBuf->appendf("({0:c}) Tj\n", c);
2318
32.4k
    }
2319
32.8k
    offset = w - offset2;
2320
32.8k
  } else {
2321
15.1k
    offset += w;
2322
15.1k
  }
2323
47.9k
      }
2324
2325
71
      appearBuf->append("ET\n");
2326
2327
    // regular (non-comb) formatting
2328
631
    } else {
2329
2330
      // compute string width
2331
631
      if (font && !font->isCIDFont()) {
2332
204
  w = 0;
2333
25.9k
  for (i = 0; i < text2->getLength(); ++i) {
2334
25.7k
    w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
2335
25.7k
  }
2336
427
      } else {
2337
  // otherwise, make a crude estimate
2338
427
  w = text2->getLength() * 0.5;
2339
427
      }
2340
2341
      // compute font autosize
2342
631
      if (fontSize == 0) {
2343
301
  fontSize = dy - 2 * border;
2344
301
  fontSize2 = (dx - 4 - 2 * border) / w;
2345
301
  if (fontSize2 < fontSize) {
2346
87
    fontSize = fontSize2;
2347
87
  }
2348
301
  fontSize = floor(fontSize);
2349
301
  if (fontSize > 10) {
2350
102
    fontSize = 10;
2351
102
  }
2352
301
  if (tfPos >= 0) {
2353
301
    tok = (GString *)daToks->get(tfPos + 1);
2354
301
    tok->clear();
2355
301
    tok->appendf("{0:.4f}", fontSize);
2356
301
  }
2357
301
      }
2358
2359
      // compute text start position
2360
631
      w *= fontSize;
2361
631
      switch (quadding) {
2362
251
      case acroFormQuadLeft:
2363
263
      default:
2364
263
  xx = border + 2;
2365
263
  break;
2366
324
      case acroFormQuadCenter:
2367
324
  xx = (dx - w) / 2;
2368
324
  break;
2369
44
      case acroFormQuadRight:
2370
44
  xx = dx - border - 2 - w;
2371
44
  break;
2372
631
      }
2373
631
      if (font) {
2374
204
  ascent = font->getDeclaredAscent() * fontSize;
2375
204
  descent = font->getDescent() * fontSize;
2376
427
      } else {
2377
427
  ascent = 0.75 * fontSize;
2378
427
  descent = -0.25 * fontSize;
2379
427
      }
2380
631
      switch (vAlign) {
2381
49
      case acroFormVAlignTop:
2382
49
      default:
2383
49
  yy = dy - ascent;
2384
49
  break;
2385
314
      case acroFormVAlignMiddle:
2386
314
  yy = 0.5 * (dy - ascent - descent);
2387
314
  break;
2388
268
      case acroFormVAlignMiddleNoDescender:
2389
268
  yy = 0.5 * (dy - ascent);
2390
268
  break;
2391
0
      case acroFormVAlignBottom:
2392
0
  yy = -descent;
2393
0
  break;
2394
631
      }
2395
2396
631
      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
631
      appearBuf->append("BT\n");
2403
2404
      // set the font matrix
2405
631
      if (tmPos >= 0) {
2406
18
  tok = (GString *)daToks->get(tmPos + 4);
2407
18
  tok->clear();
2408
18
  tok->appendf("{0:.4f}", x + xx);
2409
18
  tok = (GString *)daToks->get(tmPos + 5);
2410
18
  tok->clear();
2411
18
  tok->appendf("{0:.4f}", y + yy);
2412
18
      }
2413
2414
      // write the DA string
2415
631
      if (daToks) {
2416
21.2k
  for (i = 0; i < daToks->getLength(); ++i) {
2417
20.6k
    appearBuf->append((GString *)daToks->get(i))->append(' ');
2418
20.6k
  }
2419
631
      }
2420
2421
      // write the font matrix (if not part of the DA string)
2422
631
      if (tmPos < 0) {
2423
613
  appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
2424
613
      }
2425
2426
      // write the text string
2427
631
      appearBuf->append('(');
2428
76.5k
      for (i = 0; i < text2->getLength(); ++i) {
2429
75.9k
  c = text2->getChar(i) & 0xff;
2430
75.9k
  if (c == '(' || c == ')' || c == '\\') {
2431
417
    appearBuf->append('\\');
2432
417
    appearBuf->append((char)c);
2433
75.5k
  } else if (c < 0x20 || c >= 0x80) {
2434
28.5k
    appearBuf->appendf("\\{0:03o}", c);
2435
46.9k
  } else {
2436
46.9k
    appearBuf->append((char)c);
2437
46.9k
  }
2438
75.9k
      }
2439
631
      appearBuf->append(") Tj\n");
2440
631
    }
2441
2442
702
    appearBuf->append("ET\n");
2443
702
  }
2444
2445
  // cleanup
2446
802
  appearBuf->append("Q\n");
2447
802
  if (txField) {
2448
513
    appearBuf->append("EMC\n");
2449
513
  }
2450
2451
802
  if (daToks) {
2452
802
    deleteGList(daToks, GString);
2453
802
  }
2454
802
  if (text2 != text) {
2455
0
    delete text2;
2456
0
  }
2457
802
}
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
212
        GString *appearBuf) {
2466
212
  GList *daToks;
2467
212
  GString *tok;
2468
212
  GfxFont *font;
2469
212
  double fontSize, fontSize2, x, y, w, wMax;
2470
212
  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
212
  tfPos = tmPos = -1;
2478
212
  if (da) {
2479
178
    daToks = tokenize(da);
2480
15.1k
    for (i = 2; i < daToks->getLength(); ++i) {
2481
14.9k
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
2482
106
  tfPos = i - 2;
2483
14.8k
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
2484
20
  tmPos = i - 6;
2485
20
      }
2486
14.9k
    }
2487
178
  } else {
2488
34
    daToks = NULL;
2489
34
  }
2490
2491
  // get the font and font size
2492
212
  font = NULL;
2493
212
  fontSize = 0;
2494
212
  if (tfPos >= 0) {
2495
100
    tok = (GString *)daToks->get(tfPos);
2496
100
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2497
87
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2498
65
  error(errSyntaxError, -1, "Unknown font in field's DA string");
2499
65
  tok->clear();
2500
65
  tok->append("/xpdf_default_font");
2501
65
      }
2502
87
    } else {
2503
13
      error(errSyntaxError, -1,
2504
13
      "Invalid font name in 'Tf' operator in field's DA string");
2505
13
    }
2506
100
    tok = (GString *)daToks->get(tfPos + 1);
2507
100
    fontSize = atof(tok->getCString());
2508
112
  } else {
2509
112
    error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
2510
112
  }
2511
2512
  // compute font autosize
2513
212
  if (fontSize == 0) {
2514
140
    wMax = 0;
2515
1.65k
    for (i = 0; i < nOptions; ++i) {
2516
1.51k
      if (font && !font->isCIDFont()) {
2517
35
  w = 0;
2518
547
  for (j = 0; j < text[i]->getLength(); ++j) {
2519
512
    w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
2520
512
  }
2521
1.48k
      } else {
2522
  // otherwise, make a crude estimate
2523
1.48k
  w = text[i]->getLength() * 0.5;
2524
1.48k
      }
2525
1.51k
      if (w > wMax) {
2526
246
  wMax = w;
2527
246
      }
2528
1.51k
    }
2529
140
    fontSize = yMax - yMin - 2 * border;
2530
140
    fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
2531
140
    if (fontSize2 < fontSize) {
2532
99
      fontSize = fontSize2;
2533
99
    }
2534
140
    fontSize = floor(fontSize);
2535
140
    if (fontSize > 10) {
2536
25
      fontSize = 10;
2537
25
    }
2538
140
    if (tfPos >= 0) {
2539
28
      tok = (GString *)daToks->get(tfPos + 1);
2540
28
      tok->clear();
2541
28
      tok->appendf("{0:.4f}", fontSize);
2542
28
    }
2543
140
  }
2544
2545
  // draw the text
2546
212
  y = yMax - yMin - 1.1 * fontSize;
2547
2.94k
  for (i = topIdx; i < nOptions; ++i) {
2548
2549
    // setup
2550
2.73k
    appearBuf->append("q\n");
2551
2552
    // draw the background if selected
2553
2.73k
    if (selection[i]) {
2554
1.13k
      appearBuf->append("0 g f\n");
2555
1.13k
      appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
2556
1.13k
        border,
2557
1.13k
        y - 0.2 * fontSize,
2558
1.13k
        xMax - xMin - 2 * border,
2559
1.13k
        1.1 * fontSize);
2560
1.13k
    }
2561
2562
    // setup
2563
2.73k
    appearBuf->append("BT\n");
2564
2565
    // compute string width
2566
2.73k
    if (font && !font->isCIDFont()) {
2567
127
      w = 0;
2568
1.26k
      for (j = 0; j < text[i]->getLength(); ++j) {
2569
1.13k
  w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
2570
1.13k
      }
2571
2.61k
    } else {
2572
      // otherwise, make a crude estimate
2573
2.61k
      w = text[i]->getLength() * 0.5;
2574
2.61k
    }
2575
2576
    // compute text start position
2577
2.73k
    w *= fontSize;
2578
2.73k
    switch (quadding) {
2579
2.73k
    case acroFormQuadLeft:
2580
2.73k
    default:
2581
2.73k
      x = border + 2;
2582
2.73k
      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
2.73k
    }
2590
2591
    // set the font matrix
2592
2.73k
    if (tmPos >= 0) {
2593
176
      tok = (GString *)daToks->get(tmPos + 4);
2594
176
      tok->clear();
2595
176
      tok->appendf("{0:.4f}", x);
2596
176
      tok = (GString *)daToks->get(tmPos + 5);
2597
176
      tok->clear();
2598
176
      tok->appendf("{0:.4f}", y);
2599
176
    }
2600
2601
    // write the DA string
2602
2.73k
    if (daToks) {
2603
218k
      for (j = 0; j < daToks->getLength(); ++j) {
2604
215k
  appearBuf->append((GString *)daToks->get(j))->append(' ');
2605
215k
      }
2606
2.51k
    }
2607
2608
    // write the font matrix (if not part of the DA string)
2609
2.73k
    if (tmPos < 0) {
2610
2.56k
      appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y);
2611
2.56k
    }
2612
2613
    // change the text color if selected
2614
2.73k
    if (selection[i]) {
2615
1.13k
      appearBuf->append("1 g\n");
2616
1.13k
    }
2617
2618
    // write the text string
2619
2.73k
    appearBuf->append('(');
2620
29.3k
    for (j = 0; j < text[i]->getLength(); ++j) {
2621
26.5k
      c = text[i]->getChar(j) & 0xff;
2622
26.5k
      if (c == '(' || c == ')' || c == '\\') {
2623
181
  appearBuf->append('\\');
2624
181
  appearBuf->append((char)c);
2625
26.4k
      } else if (c < 0x20 || c >= 0x80) {
2626
11.0k
  appearBuf->appendf("\\{0:03o}", c);
2627
15.3k
      } else {
2628
15.3k
  appearBuf->append((char)c);
2629
15.3k
      }
2630
26.5k
    }
2631
2.73k
    appearBuf->append(") Tj\n");
2632
2633
    // cleanup
2634
2.73k
    appearBuf->append("ET\n");
2635
2.73k
    appearBuf->append("Q\n");
2636
2637
    // next line
2638
2.73k
    y -= 1.1 * fontSize;
2639
2.73k
  }
2640
2641
212
  if (daToks) {
2642
178
    deleteGList(daToks, GString);
2643
178
  }
2644
212
}
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
56.4k
        int *end, double *width, int *next) {
2653
56.4k
  double w, dw;
2654
56.4k
  int j, k, c;
2655
2656
  // figure out how much text will fit on the line
2657
  //~ what does Adobe do with tabs?
2658
56.4k
  w = 0;
2659
524k
  for (j = start; j < text->getLength() && w <= wMax; ++j) {
2660
490k
    c = text->getChar(j) & 0xff;
2661
490k
    if (c == 0x0a || c == 0x0d) {
2662
23.1k
      break;
2663
23.1k
    }
2664
467k
    if (font && !font->isCIDFont()) {
2665
5.30k
      dw = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
2666
462k
    } else {
2667
      // otherwise, make a crude estimate
2668
462k
      dw = 0.5 * fontSize;
2669
462k
    }
2670
467k
    w += dw;
2671
467k
  }
2672
56.4k
  if (w > wMax) {
2673
145k
    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
2674
37.7k
    for (; k > start && text->getChar(k-1) == ' '; --k) ;
2675
32.6k
    if (k > start) {
2676
3.81k
      j = k;
2677
3.81k
    }
2678
32.6k
    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
11.9k
      j = start + 1;
2682
11.9k
    }
2683
32.6k
  }
2684
56.4k
  *end = j;
2685
2686
  // compute the width
2687
56.4k
  w = 0;
2688
514k
  for (k = start; k < j; ++k) {
2689
458k
    if (font && !font->isCIDFont()) {
2690
5.08k
      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
2691
453k
    } else {
2692
      // otherwise, make a crude estimate
2693
453k
      dw = 0.5 * fontSize;
2694
453k
    }
2695
458k
    w += dw;
2696
458k
  }
2697
56.4k
  *width = w;
2698
2699
  // next line
2700
62.2k
  while (j < text->getLength() && text->getChar(j) == ' ') {
2701
5.77k
    ++j;
2702
5.77k
  }
2703
56.4k
  if (j < text->getLength() && text->getChar(j) == 0x0d) {
2704
164
    ++j;
2705
164
  }
2706
56.4k
  if (j < text->getLength() && text->getChar(j) == 0x0a) {
2707
23.6k
    ++j;
2708
23.6k
  }
2709
56.4k
  *next = j;
2710
56.4k
}
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
11
             const char *cmd, GString *appearBuf) {
2716
11
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2717
11
         cx + r, cy);
2718
11
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2719
11
         cx + r, cy + bezierCircle * r,
2720
11
         cx + bezierCircle * r, cy + r,
2721
11
         cx, cy + r);
2722
11
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2723
11
         cx - bezierCircle * r, cy + r,
2724
11
         cx - r, cy + bezierCircle * r,
2725
11
         cx - r, cy);
2726
11
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2727
11
         cx - r, cy - bezierCircle * r,
2728
11
         cx - bezierCircle * r, cy - r,
2729
11
         cx, cy - r);
2730
11
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2731
11
         cx + bezierCircle * r, cy - r,
2732
11
         cx + r, cy - bezierCircle * r,
2733
11
         cx + r, cy);
2734
11
  appearBuf->appendf("{0:s}\n", cmd);
2735
11
}
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
839
GList *AcroFormField::tokenize(GString *s) {
2987
839
  GList *toks;
2988
839
  int i, j;
2989
2990
839
  toks = new GList();
2991
839
  i = 0;
2992
38.8k
  while (i < s->getLength()) {
2993
107k
    while (i < s->getLength() && Lexer::isSpace(s->getChar(i))) {
2994
69.3k
      ++i;
2995
69.3k
    }
2996
38.0k
    if (i < s->getLength()) {
2997
38.0k
      for (j = i + 1;
2998
175k
     j < s->getLength() && !Lexer::isSpace(s->getChar(j));
2999
137k
     ++j) ;
3000
38.0k
      toks->append(new GString(s, i, j - i));
3001
38.0k
      i = j;
3002
38.0k
    }
3003
38.0k
  }
3004
839
  return toks;
3005
839
}
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.62k
Object *AcroFormField::getFieldRef(Object *ref) {
3042
3.62k
  return fieldRef.copy(ref);
3043
3.62k
}
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.36k
void AcroFormField::buildDefaultResourceDict(Object *dr) {
3104
2.36k
  Object formDR, fieldDR, resDict, newResDict, resObj;
3105
2.36k
  char *resType, *resName;
3106
2.36k
  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.36k
  dr->initDict(acroForm->doc->getXRef());
3112
3113
2.36k
  acroForm->acroFormObj.dictLookup("DR", &formDR);
3114
2.36k
  if (formDR.isDict()) {
3115
7.03k
    for (i = 0; i < formDR.dictGetLength(); ++i) {
3116
5.20k
      resType = formDR.dictGetKey(i);
3117
5.20k
      formDR.dictGetVal(i, &resDict);
3118
5.20k
      if (resDict.isDict()) {
3119
2.24k
  newResDict.initDict(acroForm->doc->getXRef());
3120
2.24k
  dr->dictAdd(copyString(resType), &newResDict);
3121
11.0k
  for (j = 0; j < resDict.dictGetLength(); ++j) {
3122
8.80k
    resName = resDict.dictGetKey(j);
3123
8.80k
    resDict.dictGetValNF(j, &resObj);
3124
8.80k
    newResDict.dictAdd(copyString(resName), &resObj);
3125
8.80k
  }
3126
2.24k
      }
3127
5.20k
      resDict.free();
3128
5.20k
    }
3129
1.83k
  }
3130
2.36k
  formDR.free();
3131
3132
2.36k
  fieldObj.dictLookup("DR", &fieldDR);
3133
2.36k
  if (fieldDR.isDict()) {
3134
3.45k
    for (i = 0; i < fieldDR.dictGetLength(); ++i) {
3135
2.50k
      resType = fieldDR.dictGetKey(i);
3136
2.50k
      fieldDR.dictGetVal(i, &resDict);
3137
2.50k
      if (resDict.isDict()) {
3138
843
  dr->dictLookup(resType, &newResDict);
3139
843
  if (!newResDict.isDict()) {
3140
504
    newResDict.free();
3141
504
    newResDict.initDict(acroForm->doc->getXRef());
3142
504
  }
3143
843
  dr->dictAdd(copyString(resType), &newResDict);
3144
2.66k
  for (j = 0; j < resDict.dictGetLength(); ++j) {
3145
1.82k
    resName = resDict.dictGetKey(j);
3146
1.82k
    resDict.dictGetValNF(j, &resObj);
3147
1.82k
    newResDict.dictAdd(copyString(resName), &resObj);
3148
1.82k
  }
3149
843
      }
3150
2.50k
      resDict.free();
3151
2.50k
    }
3152
950
  }
3153
2.36k
  fieldDR.free();
3154
2.36k
}
3155
3156
// Look up an inheritable field dictionary entry.
3157
7.72k
Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
3158
7.72k
  return fieldLookup(fieldObj.getDict(), key, obj);
3159
7.72k
}
3160
3161
7.72k
Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
3162
7.72k
  Object parent, parent2;
3163
7.72k
  int depth;
3164
3165
7.72k
  if (!dict->lookup(key, obj)->isNull()) {
3166
5.81k
    return obj;
3167
5.81k
  }
3168
1.90k
  obj->free();
3169
3170
1.90k
  dict->lookup("Parent", &parent)->isDict();
3171
1.90k
  depth = 0;
3172
5.04k
  while (parent.isDict() && depth < maxFieldObjectDepth) {
3173
3.25k
    if (!parent.dictLookup(key, obj)->isNull()) {
3174
113
      parent.free();
3175
113
      return obj;
3176
113
    }
3177
3.13k
    obj->free();
3178
3.13k
    parent.dictLookup("Parent", &parent2);
3179
3.13k
    parent.free();
3180
3.13k
    parent = parent2;
3181
3.13k
    ++depth;
3182
3.13k
  }
3183
1.79k
  parent.free();
3184
3185
  // some fields don't specify a parent, so we check the AcroForm
3186
  // dictionary just in case
3187
1.79k
  acroForm->acroFormObj.dictLookup(key, obj);
3188
1.79k
  return obj;
3189
1.90k
}
3190
3191
267
Unicode *AcroFormField::utf8ToUnicode(GString *s, int *unicodeLength) {
3192
267
  int n = 0;
3193
267
  int i = 0;
3194
267
  Unicode u;
3195
32.9k
  while (getUTF8(s, &i, &u)) {
3196
32.7k
    ++n;
3197
32.7k
  }
3198
267
  Unicode *uVec = (Unicode *)gmallocn(n, sizeof(Unicode));
3199
267
  n = 0;
3200
267
  i = 0;
3201
32.9k
  while (getUTF8(s, &i, &uVec[n])) {
3202
32.7k
    ++n;
3203
32.7k
  }
3204
267
  *unicodeLength = n;
3205
267
  return uVec;
3206
267
}
3207
3208
519
GString *AcroFormField::unicodeToLatin1(Unicode *u, int unicodeLength) {
3209
519
  GString *s = new GString();
3210
155k
  for (int i = 0; i < unicodeLength; ++i) {
3211
155k
    if (u[i] <= 0xff) {
3212
142k
      s->append((char)u[i]);
3213
142k
    }
3214
155k
  }
3215
519
  return s;
3216
519
}
3217
3218
GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
3219
2.73k
          GString *s) {
3220
2.73k
  if (s->getLength() != unicodeLength) {
3221
1.57k
    return gFalse;
3222
1.57k
  }
3223
1.26k
  for (int i = 0; i < unicodeLength; ++i) {
3224
129
    if ((Unicode)(s->getChar(i) & 0xff) != u[i]) {
3225
23
      return gFalse;
3226
23
    }
3227
129
  }
3228
1.13k
  return gTrue;
3229
1.16k
}
3230
3231
GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
3232
995
          const char *s) {
3233
1.28k
  for (int i = 0; i < unicodeLength; ++i) {
3234
1.18k
    if (!s[i] || (Unicode)(s[i] & 0xff) != u[i]) {
3235
887
      return gFalse;
3236
887
    }
3237
1.18k
  }
3238
108
  return gTrue;
3239
995
}
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
}