Coverage Report

Created: 2026-07-04 07:15

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