Coverage Report

Created: 2026-04-12 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/AcroForm.cc
Line
Count
Source
1
//========================================================================
2
//
3
// AcroForm.cc
4
//
5
// Copyright 2012 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <math.h>
13
#include "gmem.h"
14
#include "gmempp.h"
15
#include "GString.h"
16
#include "GList.h"
17
#include "GlobalParams.h"
18
#include "Error.h"
19
#include "Object.h"
20
#include "PDFDoc.h"
21
#include "TextString.h"
22
#include "Gfx.h"
23
#include "GfxFont.h"
24
#include "OptionalContent.h"
25
#include "Annot.h"
26
#include "Lexer.h"
27
#include "XFAScanner.h"
28
#include "UTF8.h"
29
#include "PDF417Barcode.h"
30
#include "AcroForm.h"
31
32
//------------------------------------------------------------------------
33
34
#define acroFormFlagReadOnly           (1 <<  0)  // all
35
#define acroFormFlagRequired           (1 <<  1)  // all
36
#define acroFormFlagNoExport           (1 <<  2)  // all
37
3.72k
#define acroFormFlagMultiline          (1 << 12)  // text
38
#define acroFormFlagPassword           (1 << 13)  // text
39
#define acroFormFlagNoToggleToOff      (1 << 14)  // button
40
1.52k
#define acroFormFlagRadio              (1 << 15)  // button
41
1.44k
#define acroFormFlagPushbutton         (1 << 16)  // button
42
1.04k
#define acroFormFlagCombo              (1 << 17)  // choice
43
#define acroFormFlagEdit               (1 << 18)  // choice
44
#define acroFormFlagSort               (1 << 19)  // choice
45
2.75k
#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
485
#define acroFormFlagComb               (1 << 24)  // text
50
#define acroFormFlagRadiosInUnison     (1 << 25)  // button
51
#define acroFormFlagRichText           (1 << 25)  // text
52
#define acroFormFlagCommitOnSelChange  (1 << 26)  // choice
53
54
7.29k
#define acroFormQuadLeft   0
55
752
#define acroFormQuadCenter 1
56
133
#define acroFormQuadRight  2
57
58
262
#define acroFormVAlignTop               0
59
2.06k
#define acroFormVAlignMiddle            1
60
532
#define acroFormVAlignMiddleNoDescender 2
61
0
#define acroFormVAlignBottom            3
62
63
4.00k
#define annotFlagHidden    0x0002
64
1.66k
#define annotFlagPrint     0x0004
65
2.33k
#define annotFlagNoView    0x0020
66
67
// distance of Bezier control point from center for circle approximation
68
// = (4 * (sqrt(2) - 1) / 3) * r
69
208
#define bezierCircle 0.55228475
70
71
// limit recursive field-parent lookups to avoid infinite loops
72
13.2k
#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
90.3k
    { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
328
  int annotNum;
329
  int annotGen;
330
  int pageNum;
331
};
332
333
//------------------------------------------------------------------------
334
// AcroForm
335
//------------------------------------------------------------------------
336
337
16.3k
AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
338
16.3k
  Object acroFormObj2;
339
16.3k
  AcroForm *acroForm;
340
16.3k
  AcroFormField *field;
341
16.3k
  Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
342
16.3k
  char *touchedObjs;
343
16.3k
  int pageNum, i, j;
344
345
16.3k
  touchedObjs = (char *)gmalloc(docA->getXRef()->getNumObjects());
346
16.3k
  memset(touchedObjs, 0, docA->getXRef()->getNumObjects());
347
348
  // this is the normal case: acroFormObj is a dictionary, as expected
349
16.3k
  if (acroFormObjA->isDict()) {
350
1.52k
    acroForm = new AcroForm(docA, acroFormObjA);
351
352
1.52k
    if (globalParams->getEnableXFA()) {
353
1.52k
      if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
354
664
  acroForm->xfaScanner = XFAScanner::load(&xfaObj);
355
664
  if (!catalog->getNeedsRendering()) {
356
664
    acroForm->isStaticXFA = gTrue;
357
664
  }
358
664
      }
359
1.52k
      xfaObj.free();
360
1.52k
    }
361
362
1.52k
    if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
363
390
      acroForm->needAppearances = obj1.getBool();
364
390
    }
365
1.52k
    obj1.free();
366
367
1.52k
    acroForm->buildAnnotPageList(catalog);
368
369
1.52k
    if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
370
14
      if (!obj1.isNull()) {
371
2
  error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
372
2
      }
373
14
      obj1.free();
374
14
      delete acroForm;
375
14
      gfree(touchedObjs);
376
14
      return NULL;
377
14
    }
378
11.2k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
379
9.75k
      obj1.arrayGetNF(i, &obj2);
380
9.75k
      acroForm->scanField(&obj2, touchedObjs);
381
9.75k
      obj2.free();
382
9.75k
    }
383
1.50k
    obj1.free();
384
385
    // scan the annotations, looking for Widget-type annots that are
386
    // not attached to the AcroForm object
387
55.9k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
388
54.3k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
389
25.0k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
390
24.2k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
391
85.2k
      for (j = 0; j < acroForm->fields->getLength(); ++j) {
392
78.3k
        field = (AcroFormField *)acroForm->fields->get(j);
393
78.3k
        if (field->fieldRef.isRef()) {
394
73.8k
    if (field->fieldRef.getRefNum() == annotRef.getRefNum() &&
395
2.08k
        field->fieldRef.getRefGen() == annotRef.getRefGen()) {
396
2.06k
      break;
397
2.06k
    }
398
73.8k
        }
399
78.3k
      }
400
8.97k
      if (j == acroForm->fields->getLength()) {
401
6.91k
        annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
402
6.91k
        if (annotObj.isDict()) {
403
2.59k
    if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
404
1.10k
      acroForm->scanField(&annotRef, touchedObjs);
405
1.10k
    }
406
2.59k
    obj1.free();
407
2.59k
        }
408
6.91k
        annotObj.free();
409
6.91k
      }
410
8.97k
    }
411
24.2k
    annotRef.free();
412
24.2k
  }
413
854
      }
414
54.3k
      annotsObj.free();
415
54.3k
    }
416
417
  // if acroFormObjA is a null object, but there are Widget-type
418
  // annots, we still create an AcroForm
419
14.8k
  } else {
420
    // create an empty dict for acroFormObj
421
14.8k
    acroFormObj2.initDict(docA->getXRef());
422
14.8k
    acroForm = new AcroForm(docA, &acroFormObj2);
423
14.8k
    acroFormObj2.free();
424
425
14.8k
    acroForm->buildAnnotPageList(catalog);
426
427
    // scan the annotations, looking for any Widget-type annots
428
362k
    for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
429
347k
      if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
430
440k
  for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
431
431k
    if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
432
81.2k
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
433
81.2k
      if (annotObj.isDict()) {
434
13.0k
        if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
435
6.41k
    acroForm->scanField(&annotRef, touchedObjs);
436
6.41k
        }
437
13.0k
        obj1.free();
438
13.0k
      }
439
81.2k
      annotObj.free();
440
81.2k
    }
441
431k
    annotRef.free();
442
431k
  }
443
8.96k
      }
444
347k
      annotsObj.free();
445
347k
    }
446
447
14.8k
    if (acroForm->fields->getLength() == 0) {
448
14.7k
      delete acroForm;
449
14.7k
      acroForm = NULL;
450
14.7k
    }
451
14.8k
  }
452
453
16.3k
  gfree(touchedObjs);
454
455
16.3k
  return acroForm;
456
16.3k
}
457
458
16.3k
AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) {
459
16.3k
  doc = docA;
460
16.3k
  acroFormObjA->copy(&acroFormObj);
461
16.3k
  needAppearances = gFalse;
462
16.3k
  annotPages = new GList();
463
16.3k
  fields = new GList();
464
16.3k
  xfaScanner = NULL;
465
16.3k
  isStaticXFA = gFalse;
466
16.3k
}
467
468
16.3k
AcroForm::~AcroForm() {
469
16.3k
  acroFormObj.free();
470
16.3k
  deleteGList(annotPages, AcroFormAnnotPage);
471
16.3k
  deleteGList(fields, AcroFormField);
472
16.3k
  delete xfaScanner;
473
16.3k
}
474
475
0
const char *AcroForm::getType() {
476
0
  return isStaticXFA ? "static XFA" : "AcroForm";
477
0
}
478
479
16.3k
void AcroForm::buildAnnotPageList(Catalog *catalog) {
480
16.3k
  Object annotsObj, annotObj;
481
16.3k
  int pageNum, i;
482
483
418k
  for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
484
401k
    if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
485
466k
      for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
486
456k
  if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
487
90.3k
    annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
488
90.3k
               annotObj.getRefGen(),
489
90.3k
               pageNum));
490
90.3k
  }
491
456k
  annotObj.free();
492
456k
      }
493
9.82k
    }
494
401k
    annotsObj.free();
495
401k
  }
496
  //~ sort the list
497
16.3k
}
498
499
14.5k
int AcroForm::lookupAnnotPage(Object *annotRef) {
500
14.5k
  AcroFormAnnotPage *annotPage;
501
14.5k
  int num, gen, i;
502
503
14.5k
  if (!annotRef->isRef()) {
504
405
    return 0;
505
405
  }
506
14.1k
  num = annotRef->getRefNum();
507
14.1k
  gen = annotRef->getRefGen();
508
  //~ use bin search
509
210k
  for (i = 0; i < annotPages->getLength(); ++i) {
510
207k
    annotPage = (AcroFormAnnotPage *)annotPages->get(i);
511
207k
    if (annotPage->annotNum == num && annotPage->annotGen == gen) {
512
10.8k
      return annotPage->pageNum;
513
10.8k
    }
514
207k
  }
515
3.33k
  return 0;
516
14.1k
}
517
518
22.2k
void AcroForm::scanField(Object *fieldRef, char *touchedObjs) {
519
22.2k
  AcroFormField *field;
520
22.2k
  Object fieldObj, kidsRef, kidsObj, kidRef, kidObj, subtypeObj;
521
22.2k
  GBool isTerminal;
522
22.2k
  int i;
523
524
  // check for an object loop
525
22.2k
  if (fieldRef->isRef()) {
526
16.8k
    if (fieldRef->getRefNum() < 0 ||
527
16.8k
  fieldRef->getRefNum() >= doc->getXRef()->getNumObjects() ||
528
16.6k
  touchedObjs[fieldRef->getRefNum()]) {
529
6.08k
      return;
530
6.08k
    }
531
10.7k
    touchedObjs[fieldRef->getRefNum()] = 1;
532
10.7k
  }
533
534
16.2k
  fieldRef->fetch(doc->getXRef(), &fieldObj);
535
16.2k
  if (!fieldObj.isDict()) {
536
10.1k
    error(errSyntaxError, -1, "AcroForm field object is wrong type");
537
10.1k
    fieldObj.free();
538
10.1k
    return;
539
10.1k
  }
540
541
  // look for a Kids entry, and check for an object loop there
542
6.01k
  if (fieldObj.dictLookupNF("Kids", &kidsRef)->isRef()) {
543
2
    if (kidsRef.getRefNum() < 0 ||
544
2
  kidsRef.getRefNum() >= doc->getXRef()->getNumObjects() ||
545
2
  touchedObjs[kidsRef.getRefNum()]) {
546
0
      kidsRef.free();
547
0
      fieldObj.free();
548
0
      return;
549
0
    }
550
2
    touchedObjs[kidsRef.getRefNum()] = 1;
551
2
    kidsRef.fetch(doc->getXRef(), &kidsObj);
552
6.01k
  } else {
553
6.01k
    kidsRef.copy(&kidsObj);
554
6.01k
  }
555
6.01k
  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
6.01k
  isTerminal = gTrue;
562
6.01k
  if (kidsObj.isArray()) {
563
1.09k
    isTerminal = gFalse;
564
7.45k
    for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
565
6.35k
      kidsObj.arrayGet(i, &kidObj);
566
6.35k
      if (kidObj.isDict()) {
567
2.65k
  if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
568
411
    isTerminal = gTrue;
569
411
  }
570
2.65k
  subtypeObj.free();
571
2.65k
      }
572
6.35k
      kidObj.free();
573
6.35k
    }
574
1.09k
    if (!isTerminal) {
575
5.70k
      for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
576
5.02k
  kidsObj.arrayGetNF(i, &kidRef);
577
5.02k
  scanField(&kidRef, touchedObjs);
578
5.02k
  kidRef.free();
579
5.02k
      }
580
685
    }
581
1.09k
  }
582
6.01k
  kidsObj.free();
583
584
6.01k
  if (isTerminal) {
585
5.33k
    if ((field = AcroFormField::load(this, fieldRef))) {
586
4.29k
      fields->append(field);
587
4.29k
    }
588
5.33k
  }
589
590
6.01k
  fieldObj.free();
591
6.01k
}
592
593
56.7k
void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) {
594
56.7k
  int i;
595
596
70.0k
  for (i = 0; i < fields->getLength(); ++i) {
597
13.2k
    ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing);
598
13.2k
  }
599
56.7k
}
600
601
5.04k
int AcroForm::getNumFields() {
602
5.04k
  return fields->getLength();
603
5.04k
}
604
605
4.13k
AcroFormField *AcroForm::getField(int idx) {
606
4.13k
  return (AcroFormField *)fields->get(idx);
607
4.13k
}
608
609
0
AcroFormField *AcroForm::findField(int pg, double x, double y) {
610
0
  AcroFormField *field;
611
0
  double llx, lly, urx, ury;
612
0
  int i;
613
614
0
  for (i = 0; i < fields->getLength(); ++i) {
615
0
    field = (AcroFormField *)fields->get(i);
616
0
    if (field->getPageNum() == pg) {
617
0
      field->getBBox(&llx, &lly, &urx, &ury);
618
0
      if (llx <= x && x <= urx && lly <= y && y <= ury) {
619
0
  return field;
620
0
      }
621
0
    }
622
0
  }
623
0
  return NULL;
624
0
}
625
626
0
int AcroForm::findFieldIdx(int pg, double x, double y) {
627
0
  AcroFormField *field;
628
0
  double llx, lly, urx, ury;
629
0
  int i;
630
631
0
  for (i = 0; i < fields->getLength(); ++i) {
632
0
    field = (AcroFormField *)fields->get(i);
633
0
    if (field->getPageNum() == pg) {
634
0
      field->getBBox(&llx, &lly, &urx, &ury);
635
0
      if (llx <= x && x <= urx && lly <= y && y <= ury) {
636
0
  return i;
637
0
      }
638
0
    }
639
0
  }
640
0
  return -1;
641
0
}
642
643
//------------------------------------------------------------------------
644
// AcroFormField
645
//------------------------------------------------------------------------
646
647
5.33k
AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
648
5.33k
  GString *typeStr;
649
5.33k
  TextString *nameA;
650
5.33k
  GString *xfaName;
651
5.33k
  Guint flagsA;
652
5.33k
  GBool haveFlags, typeFromParentA;
653
5.33k
  Object fieldObjA, parentObj, parentObj2, obj1, obj2;
654
5.33k
  AcroFormFieldType typeA;
655
5.33k
  XFAField *xfaFieldA;
656
5.33k
  AcroFormField *field;
657
5.33k
  int depth, i0, i1;
658
659
5.33k
  fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
660
661
  //----- get field info
662
663
5.33k
  if (fieldObjA.dictLookup("T", &obj1)->isString()) {
664
4.10k
    nameA = new TextString(obj1.getString());
665
4.10k
  } else {
666
1.22k
    nameA = new TextString();
667
1.22k
  }
668
5.33k
  obj1.free();
669
670
5.33k
  if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
671
4.18k
    typeStr = new GString(obj1.getName());
672
4.18k
    typeFromParentA = gFalse;
673
4.18k
  } else {
674
1.15k
    typeStr = NULL;
675
1.15k
    typeFromParentA = gTrue;
676
1.15k
  }
677
5.33k
  obj1.free();
678
679
5.33k
  if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
680
2.56k
    flagsA = (Guint)obj1.getInt();
681
2.56k
    haveFlags = gTrue;
682
2.76k
  } else {
683
2.76k
    flagsA = 0;
684
2.76k
    haveFlags = gFalse;
685
2.76k
  }
686
5.33k
  obj1.free();
687
688
  //----- get info from parent non-terminal fields
689
690
5.33k
  fieldObjA.dictLookup("Parent", &parentObj);
691
5.33k
  depth = 0;
692
15.6k
  while (parentObj.isDict() && depth < maxFieldObjectDepth) {
693
694
10.3k
    if (parentObj.dictLookup("T", &obj1)->isString()) {
695
9.32k
      if (nameA->getLength()) {
696
9.01k
  nameA->insert(0, (Unicode)'.');
697
9.01k
      }
698
9.32k
      nameA->insert(0, obj1.getString());
699
9.32k
    }
700
10.3k
    obj1.free();
701
702
10.3k
    if (!typeStr) {
703
1.39k
      if (parentObj.dictLookup("FT", &obj1)->isName()) {
704
162
  typeStr = new GString(obj1.getName());
705
162
      }
706
1.39k
      obj1.free();
707
1.39k
    }
708
709
10.3k
    if (!haveFlags) {
710
3.32k
      if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
711
98
  flagsA = (Guint)obj1.getInt();
712
98
  haveFlags = gTrue;
713
98
      }
714
3.32k
      obj1.free();
715
3.32k
    }
716
717
10.3k
    parentObj.dictLookup("Parent", &parentObj2);
718
10.3k
    parentObj.free();
719
10.3k
    parentObj = parentObj2;
720
721
10.3k
    ++depth;
722
10.3k
  }
723
5.33k
  parentObj.free();
724
725
5.33k
  if (!typeStr) {
726
988
    error(errSyntaxError, -1, "Missing type in AcroForm field");
727
988
    goto err1;
728
988
  }
729
730
  //----- get static XFA info
731
732
4.34k
  xfaFieldA = NULL;
733
4.34k
  if (acroFormA->xfaScanner) {
734
    // convert field name to UTF-8, and remove segments that start
735
    // with '#' -- to match the XFA field name
736
866
    xfaName = nameA->toUTF8();
737
866
    i0 = 0;
738
4.14k
    while (i0 < xfaName->getLength()) {
739
3.27k
      i1 = i0;
740
134k
      while (i1 < xfaName->getLength()) {
741
133k
  if (xfaName->getChar(i1) == '.') {
742
2.43k
    ++i1;
743
2.43k
    break;
744
2.43k
  }
745
130k
  ++i1;
746
130k
      }
747
3.27k
      if (xfaName->getChar(i0) == '#') {
748
757
  xfaName->del(i0, i1 - i0);
749
2.52k
      } else {
750
2.52k
  i0 = i1;
751
2.52k
      }
752
3.27k
    }
753
866
    xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
754
866
    delete xfaName;
755
866
  }
756
757
  //----- check for a radio button
758
759
  // this is a kludge: if we see a Btn-type field with kids, and the
760
  // Ff entry is missing, assume the kids are radio buttons
761
4.34k
  if (typeFromParentA && !typeStr->cmp("Btn") && !haveFlags) {
762
41
    flagsA = acroFormFlagRadio;
763
41
  }
764
765
  //----- determine field type
766
767
4.34k
  if (!typeStr->cmp("Btn")) {
768
912
    if (flagsA & acroFormFlagPushbutton) {
769
146
      typeA = acroFormFieldPushbutton;
770
766
    } else if (flagsA & acroFormFlagRadio) {
771
157
      typeA = acroFormFieldRadioButton;
772
609
    } else {
773
609
      typeA = acroFormFieldCheckbox;
774
609
    }
775
3.43k
  } else if (!typeStr->cmp("Tx")) {
776
2.75k
    if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
777
0
      typeA = acroFormFieldBarcode;
778
2.75k
    } else if (flagsA & acroFormFlagFileSelect) {
779
3
      typeA = acroFormFieldFileSelect;
780
2.75k
    } else if (flagsA & acroFormFlagMultiline) {
781
173
      typeA = acroFormFieldMultilineText;
782
2.58k
    } else {
783
2.58k
      typeA = acroFormFieldText;
784
2.58k
    }
785
2.75k
  } else if (!typeStr->cmp("Ch")) {
786
623
    if (flagsA & acroFormFlagCombo) {
787
235
      typeA = acroFormFieldComboBox;
788
388
    } else {
789
388
      typeA = acroFormFieldListBox;
790
388
    }
791
623
  } else if (!typeStr->cmp("Sig")) {
792
0
    typeA = acroFormFieldSignature;
793
49
  } else {
794
49
    error(errSyntaxError, -1, "Invalid type in AcroForm field");
795
49
    goto err1;
796
49
  }
797
4.29k
  delete typeStr;
798
799
4.29k
  field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
800
4.29k
          typeA, nameA, flagsA, typeFromParentA, xfaFieldA);
801
4.29k
  fieldObjA.free();
802
4.29k
  return field;
803
804
1.03k
 err1:
805
1.03k
  delete typeStr;
806
1.03k
  delete nameA;
807
1.03k
  fieldObjA.free();
808
1.03k
  return NULL;
809
4.34k
}
810
811
AcroFormField::AcroFormField(AcroForm *acroFormA,
812
           Object *fieldRefA, Object *fieldObjA,
813
           AcroFormFieldType typeA, TextString *nameA,
814
           Guint flagsA, GBool typeFromParentA,
815
4.29k
           XFAField *xfaFieldA) {
816
4.29k
  acroForm = acroFormA;
817
4.29k
  fieldRefA->copy(&fieldRef);
818
4.29k
  fieldObjA->copy(&fieldObj);
819
4.29k
  type = typeA;
820
4.29k
  name = nameA;
821
4.29k
  flags = flagsA;
822
4.29k
  typeFromParent = typeFromParentA;
823
4.29k
  xfaField = xfaFieldA;
824
4.29k
}
825
826
4.29k
AcroFormField::~AcroFormField() {
827
4.29k
  fieldRef.free();
828
4.29k
  fieldObj.free();
829
4.29k
  delete name;
830
4.29k
}
831
832
0
int AcroFormField::getPageNum() {
833
0
  Object kidsObj, annotRef;
834
0
  int pageNum;
835
836
0
  pageNum = 0;
837
0
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
838
0
    if (kidsObj.arrayGetLength() > 0) {
839
0
      kidsObj.arrayGetNF(0, &annotRef);
840
0
      pageNum = acroForm->lookupAnnotPage(&annotRef);
841
0
      annotRef.free();
842
0
    }
843
0
  } else {
844
0
    pageNum = acroForm->lookupAnnotPage(&fieldRef);
845
0
  }
846
0
  kidsObj.free();
847
0
  return pageNum;
848
0
}
849
850
0
const char *AcroFormField::getType() {
851
0
  switch (type) {
852
0
  case acroFormFieldPushbutton:    return "PushButton";
853
0
  case acroFormFieldRadioButton:   return "RadioButton";
854
0
  case acroFormFieldCheckbox:      return "Checkbox";
855
0
  case acroFormFieldFileSelect:    return "FileSelect";
856
0
  case acroFormFieldMultilineText: return "MultilineText";
857
0
  case acroFormFieldText:          return "Text";
858
0
  case acroFormFieldBarcode:       return "Barcode";
859
0
  case acroFormFieldComboBox:      return "ComboBox";
860
0
  case acroFormFieldListBox:       return "ListBox";
861
0
  case acroFormFieldSignature:     return "Signature";
862
0
  default:                         return NULL;
863
0
  }
864
0
}
865
866
0
Unicode *AcroFormField::getName(int *length) {
867
0
  Unicode *u, *ret;
868
0
  int n;
869
870
0
  u = name->getUnicode();
871
0
  n = name->getLength();
872
0
  ret = (Unicode *)gmallocn(n, sizeof(Unicode));
873
0
  memcpy(ret, u, n * sizeof(Unicode));
874
0
  *length = n;
875
0
  return ret;
876
0
}
877
878
2.70k
Unicode *AcroFormField::getValue(int *length) {
879
2.70k
  Object obj1, obj2;
880
2.70k
  Unicode *u;
881
2.70k
  char *s;
882
2.70k
  TextString *ts;
883
2.70k
  GString *gs;
884
2.70k
  int n, i;
885
886
2.70k
  u = NULL;
887
2.70k
  *length = 0;
888
889
  // if this field has a counterpart in the XFA form, take the value
890
  // from the XFA field (NB: an XFA field with no value overrides the
891
  // AcroForm value)
892
2.70k
  if (globalParams->getPreferXFAFieldValues() && xfaField) {
893
401
    if (xfaField->getValue()) {
894
326
      u = utf8ToUnicode(xfaField->getValue(), length);
895
326
    }
896
897
  // no XFA form - take the AcroForm value
898
2.30k
  } else {
899
2.30k
    fieldLookup("V", &obj1);
900
2.30k
    if (obj1.isName()) {
901
646
      s = obj1.getName();
902
646
      n = (int)strlen(s);
903
646
      u = (Unicode *)gmallocn(n, sizeof(Unicode));
904
2.02k
      for (i = 0; i < n; ++i) {
905
1.37k
        u[i] = s[i] & 0xff;
906
1.37k
      }
907
646
      *length = n;
908
1.66k
    } else if (obj1.isString()) {
909
457
      ts = new TextString(obj1.getString());
910
457
      n = ts->getLength();
911
457
      u = (Unicode *)gmallocn(n, sizeof(Unicode));
912
457
      memcpy(u, ts->getUnicode(), n * sizeof(Unicode));
913
457
      *length = n;
914
457
      delete ts;
915
1.20k
    } else if (obj1.isDict()) {
916
22
      obj1.dictLookup("Contents", &obj2);
917
22
      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
22
      obj2.free();
927
22
    }
928
2.30k
    obj1.free();
929
2.30k
  }
930
931
2.70k
  return u;
932
2.70k
}
933
934
void AcroFormField::getBBox(double *llx, double *lly,
935
0
          double *urx, double *ury) {
936
0
  Object annotObj, rectObj, numObj;
937
0
  double t;
938
939
0
  *llx = *lly = *urx = *ury = 0;
940
941
0
  if (getAnnotObj(&annotObj)->isDict()) {
942
0
    annotObj.dictLookup("Rect", &rectObj);
943
0
    if (rectObj.isArray() && rectObj.arrayGetLength() == 4) {
944
0
      rectObj.arrayGet(0, &numObj);
945
0
      if (numObj.isNum()) {
946
0
  *llx = numObj.getNum();
947
0
      }
948
0
      numObj.free();
949
0
      rectObj.arrayGet(1, &numObj);
950
0
      if (numObj.isNum()) {
951
0
  *lly = numObj.getNum();
952
0
      }
953
0
      numObj.free();
954
0
      rectObj.arrayGet(2, &numObj);
955
0
      if (numObj.isNum()) {
956
0
  *urx = numObj.getNum();
957
0
      }
958
0
      numObj.free();
959
0
      rectObj.arrayGet(3, &numObj);
960
0
      if (numObj.isNum()) {
961
0
  *ury = numObj.getNum();
962
0
      }
963
0
      numObj.free();
964
0
    }
965
0
    rectObj.free();
966
0
  }
967
0
  annotObj.free();
968
969
0
  if (*llx > *urx) {
970
0
    t = *llx;  *llx = *urx;  *urx = t;
971
0
  }
972
0
  if (*lly > *ury) {
973
0
    t = *lly;  *lly = *ury;  *ury = t;
974
0
  }
975
0
}
976
977
0
void AcroFormField::getFont(Ref *fontID, double *fontSize) {
978
0
  Object daObj;
979
0
  GList *daToks;
980
0
  char *fontTag;
981
0
  double tfSize, m2, m3;
982
0
  int tfPos, tmPos, i;
983
984
0
  fontID->num = fontID->gen = -1;
985
0
  *fontSize = 0;
986
987
0
  if (fieldLookup("DA", &daObj)->isString()) {
988
989
    // parse the default appearance string
990
0
    daToks = tokenize(daObj.getString());
991
0
    tfPos = tmPos = -1;
992
0
    for (i = 2; i < daToks->getLength(); ++i) {
993
0
      if (!((GString *)daToks->get(i))->cmp("Tf")) {
994
0
  tfPos = i - 2;
995
0
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
996
0
  tmPos = i - 6;
997
0
      }
998
0
    }
999
1000
    // handle the Tf operator
1001
0
    if (tfPos >= 0) {
1002
0
      fontTag = ((GString *)daToks->get(tfPos))->getCString();
1003
0
      if (*fontTag == '/') {
1004
0
  ++fontTag;
1005
0
      }
1006
0
      *fontID = findFontName(fontTag);
1007
0
      tfSize = atof(((GString *)daToks->get(tfPos + 1))->getCString());
1008
0
    } else {
1009
0
      tfSize = 1;
1010
0
    }
1011
1012
    // handle the Tm operator
1013
0
    if (tmPos >= 0) {
1014
      // transformed font size = sqrt(m[2]^2 + m[3]^2) * size
1015
0
      m2 = atof(((GString *)daToks->get(tfPos + 2))->getCString());
1016
0
      m3 = atof(((GString *)daToks->get(tfPos + 3))->getCString());
1017
0
      tfSize *= sqrt(m2*m2 + m3*m3);
1018
0
    }
1019
0
    *fontSize = tfSize;
1020
1021
0
    deleteGList(daToks, GString);
1022
0
  }
1023
1024
0
  daObj.free();
1025
0
}
1026
1027
0
Ref AcroFormField::findFontName(char *fontTag) {
1028
0
  Object drObj, fontDictObj, fontObj, baseFontObj;
1029
0
  Ref fontID;
1030
0
  GBool found;
1031
1032
0
  fontID.num = fontID.gen = -1;
1033
0
  found = gFalse;
1034
1035
0
  if (fieldObj.dictLookup("DR", &drObj)->isDict()) {
1036
0
    if (drObj.dictLookup("Font", &fontDictObj)->isDict()) {
1037
0
      if (fontDictObj.dictLookupNF(fontTag, &fontObj)->isRef()) {
1038
0
  fontID = fontObj.getRef();
1039
0
  found = gTrue;
1040
0
      }
1041
0
      fontObj.free();
1042
0
    }
1043
0
    fontDictObj.free();
1044
0
  }
1045
0
  drObj.free();
1046
0
  if (found) {
1047
0
    return fontID;
1048
0
  }
1049
1050
0
  if (acroForm->acroFormObj.dictLookup("DR", &drObj)->isDict()) {
1051
0
    if (drObj.dictLookup("Font", &fontDictObj)->isDict()) {
1052
0
      if (fontDictObj.dictLookupNF(fontTag, &fontObj)->isRef()) {
1053
0
  fontID = fontObj.getRef();
1054
0
      }
1055
0
      fontObj.free();
1056
0
    }
1057
0
    fontDictObj.free();
1058
0
  }
1059
0
  drObj.free();
1060
0
  return fontID;
1061
0
}
1062
1063
0
void AcroFormField::getColor(double *red, double *green, double *blue) {
1064
0
  Object daObj;
1065
0
  GList *daToks;
1066
0
  int i;
1067
1068
0
  *red = *green = *blue = 0;
1069
1070
0
  if (fieldLookup("DA", &daObj)->isString()) {
1071
1072
    // parse the default appearance string
1073
0
    daToks = tokenize(daObj.getString());
1074
0
    for (i = 1; i < daToks->getLength(); ++i) {
1075
1076
      // handle the g operator
1077
0
      if (!((GString *)daToks->get(i))->cmp("g")) {
1078
0
  *red = *green = *blue =
1079
0
      atof(((GString *)daToks->get(i - 1))->getCString());
1080
0
  break;
1081
1082
      // handle the rg operator
1083
0
      } else if (i >= 3 && !((GString *)daToks->get(i))->cmp("rg")) {
1084
0
  *red = atof(((GString *)daToks->get(i - 3))->getCString());
1085
0
  *green = atof(((GString *)daToks->get(i - 2))->getCString());
1086
0
  *blue = atof(((GString *)daToks->get(i - 1))->getCString());
1087
0
  break;
1088
0
      }
1089
0
    }
1090
1091
0
    deleteGList(daToks, GString);
1092
0
  }
1093
1094
0
  daObj.free();
1095
0
}
1096
1097
0
int AcroFormField::getMaxLen() {
1098
0
  Object obj;
1099
0
  int len;
1100
1101
0
  if (fieldLookup("MaxLen", &obj)->isInt()) {
1102
0
    len = obj.getInt();
1103
0
  } else {
1104
0
    len = -1;
1105
0
  }
1106
0
  obj.free();
1107
0
  return len;
1108
0
}
1109
1110
13.2k
void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) {
1111
13.2k
  Object kidsObj, annotRef, annotObj;
1112
13.2k
  int i;
1113
1114
  // find the annotation object(s)
1115
13.2k
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
1116
8.38k
    for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
1117
7.43k
      kidsObj.arrayGetNF(i, &annotRef);
1118
7.43k
      annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
1119
7.43k
      drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj);
1120
7.43k
      annotObj.free();
1121
7.43k
      annotRef.free();
1122
7.43k
    }
1123
12.3k
  } else {
1124
12.3k
    drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj);
1125
12.3k
  }
1126
13.2k
  kidsObj.free();
1127
13.2k
}
1128
1129
void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
1130
19.7k
            Object *annotRef, Object *annotObj) {
1131
19.7k
  Object obj1, obj2;
1132
19.7k
  double xMin, yMin, xMax, yMax, t;
1133
19.7k
  int annotFlags;
1134
19.7k
  GBool oc, render;
1135
1136
19.7k
  if (!annotObj->isDict()) {
1137
5.19k
    return;
1138
5.19k
  }
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
14.5k
  if (acroForm->lookupAnnotPage(annotRef) != pageNum) {
1145
10.5k
    return;
1146
10.5k
  }
1147
1148
  //----- check annotation flags
1149
1150
4.00k
  if (annotObj->dictLookup("F", &obj1)->isInt()) {
1151
3.78k
    annotFlags = obj1.getInt();
1152
3.78k
  } else {
1153
221
    annotFlags = 0;
1154
221
  }
1155
4.00k
  obj1.free();
1156
4.00k
  if ((annotFlags & annotFlagHidden) ||
1157
3.99k
      (printing && !(annotFlags & annotFlagPrint)) ||
1158
3.96k
      (!printing && (annotFlags & annotFlagNoView))) {
1159
67
    return;
1160
67
  }
1161
1162
  //----- check the optional content entry
1163
1164
3.93k
  annotObj->dictLookupNF("OC", &obj1);
1165
3.93k
  if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) {
1166
0
    obj1.free();
1167
0
    return;
1168
0
  }
1169
3.93k
  obj1.free();
1170
1171
  //----- get the bounding box
1172
1173
3.93k
  if (annotObj->dictLookup("Rect", &obj1)->isArray() &&
1174
3.64k
      obj1.arrayGetLength() == 4) {
1175
3.56k
    xMin = yMin = xMax = yMax = 0;
1176
3.56k
    if (obj1.arrayGet(0, &obj2)->isNum()) {
1177
3.43k
      xMin = obj2.getNum();
1178
3.43k
    }
1179
3.56k
    obj2.free();
1180
3.56k
    if (obj1.arrayGet(1, &obj2)->isNum()) {
1181
3.49k
      yMin = obj2.getNum();
1182
3.49k
    }
1183
3.56k
    obj2.free();
1184
3.56k
    if (obj1.arrayGet(2, &obj2)->isNum()) {
1185
3.27k
      xMax = obj2.getNum();
1186
3.27k
    }
1187
3.56k
    obj2.free();
1188
3.56k
    if (obj1.arrayGet(3, &obj2)->isNum()) {
1189
3.44k
      yMax = obj2.getNum();
1190
3.44k
    }
1191
3.56k
    obj2.free();
1192
3.56k
    if (xMin > xMax) {
1193
565
      t = xMin; xMin = xMax; xMax = t;
1194
565
    }
1195
3.56k
    if (yMin > yMax) {
1196
196
      t = yMin; yMin = yMax; yMax = t;
1197
196
    }
1198
3.56k
  } else {
1199
373
    error(errSyntaxError, -1, "Bad bounding box for annotation");
1200
373
    obj1.free();
1201
373
    return;
1202
373
  }
1203
3.56k
  obj1.free();
1204
1205
  //----- draw it
1206
1207
3.56k
  render = gFalse;
1208
3.56k
  if (acroForm->needAppearances) {
1209
1.12k
    render = gTrue;
1210
2.43k
  } else if (xfaField && xfaField->getValue()) {
1211
326
    render = gTrue;
1212
2.11k
  } else {
1213
2.11k
    if (!annotObj->dictLookup("AP", &obj1)->isDict()) {
1214
1.25k
      render = gTrue;
1215
1.25k
    }
1216
2.11k
    obj1.free();
1217
2.11k
  }
1218
3.56k
  if (render) {
1219
2.70k
    drawNewAppearance(gfx, annotObj->getDict(),
1220
2.70k
          xMin, yMin, xMax, yMax);
1221
2.70k
  } else {
1222
858
    drawExistingAppearance(gfx, annotObj->getDict(),
1223
858
         xMin, yMin, xMax, yMax);
1224
858
  }
1225
3.56k
}
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
858
             double xMax, double yMax) {
1232
858
  Object apObj, asObj, appearance, obj1;
1233
1234
  //----- get the appearance stream
1235
1236
858
  if (annot->lookup("AP", &apObj)->isDict()) {
1237
858
    apObj.dictLookup("N", &obj1);
1238
858
    if (obj1.isDict()) {
1239
216
      if (annot->lookup("AS", &asObj)->isName()) {
1240
184
  obj1.dictLookupNF(asObj.getName(), &appearance);
1241
184
      } else if (obj1.dictGetLength() == 1) {
1242
9
  obj1.dictGetValNF(0, &appearance);
1243
23
      } else {
1244
23
  obj1.dictLookupNF("Off", &appearance);
1245
23
      }
1246
216
      asObj.free();
1247
642
    } else {
1248
642
      apObj.dictLookupNF("N", &appearance);
1249
642
    }
1250
858
    obj1.free();
1251
858
  }
1252
858
  apObj.free();
1253
1254
  //----- draw it
1255
1256
858
  if (!appearance.isNone()) {
1257
858
    gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
1258
858
    appearance.free();
1259
858
  }
1260
858
}
1261
1262
// Regenerate the appearance for this field, and draw it.
1263
void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
1264
              double xMin, double yMin,
1265
2.70k
              double xMax, double yMax) {
1266
2.70k
  GString *appearBuf;
1267
2.70k
  Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj;
1268
2.70k
  Object resources, fontResources, defaultFont, gfxStateDict;
1269
2.70k
  Object obj1, obj2, obj3, obj4;
1270
2.70k
  Dict *mkDict;
1271
2.70k
  MemStream *appearStream;
1272
2.70k
  GfxFontDict *fontDict;
1273
2.70k
  GBool hasCaption;
1274
2.70k
  double dx, dy, r;
1275
2.70k
  GString *val, *caption, *da;
1276
2.70k
  GString **text;
1277
2.70k
  GBool done;
1278
2.70k
  GBool *selection;
1279
2.70k
  AnnotBorderType borderType;
1280
2.70k
  double borderWidth;
1281
2.70k
  double *borderDash;
1282
2.70k
  GString *appearanceState;
1283
2.70k
  int borderDashLength, rot, quadding, vAlign, comb, nOptions, topIdx, i;
1284
1285
2.70k
  appearBuf = new GString();
1286
#if 0 //~debug
1287
  appearBuf->appendf("1 1 0 rg 0 0 {0:.4f} {1:.4f} re f\n",
1288
         xMax - xMin, yMax - yMin);
1289
#endif
1290
#if 0 //~debug
1291
  appearBuf->appendf("1 1 0 RG 0 0 {0:.4f} {1:.4f} re s\n",
1292
         xMax - xMin, yMax - yMin);
1293
#endif
1294
1295
  // get the appearance characteristics (MK) dictionary
1296
2.70k
  if (annot->lookup("MK", &mkObj)->isDict()) {
1297
1.86k
    mkDict = mkObj.getDict();
1298
1.86k
  } else {
1299
846
    mkDict = NULL;
1300
846
  }
1301
1302
  // draw the background
1303
2.70k
  if (mkDict) {
1304
1.86k
    if (mkDict->lookup("BG", &obj1)->isArray() &&
1305
87
  obj1.arrayGetLength() > 0) {
1306
87
      setColor(obj1.getArray(), gTrue, 0, appearBuf);
1307
87
      appearBuf->appendf("0 0 {0:.4f} {1:.4f} re f\n",
1308
87
       xMax - xMin, yMax - yMin);
1309
87
    }
1310
1.86k
    obj1.free();
1311
1.86k
  }
1312
1313
  // get the field type
1314
2.70k
  fieldLookup("FT", &ftObj);
1315
1316
  // draw the border
1317
2.70k
  borderType = annotBorderSolid;
1318
2.70k
  borderWidth = 1;
1319
2.70k
  borderDash = NULL;
1320
2.70k
  borderDashLength = 0;
1321
2.70k
  if (annot->lookup("BS", &obj1)->isDict()) {
1322
609
    if (obj1.dictLookup("S", &obj2)->isName()) {
1323
509
      if (obj2.isName("S")) {
1324
321
  borderType = annotBorderSolid;
1325
321
      } else if (obj2.isName("D")) {
1326
4
  borderType = annotBorderDashed;
1327
184
      } else if (obj2.isName("B")) {
1328
37
  borderType = annotBorderBeveled;
1329
147
      } else if (obj2.isName("I")) {
1330
28
  borderType = annotBorderInset;
1331
119
      } else if (obj2.isName("U")) {
1332
61
  borderType = annotBorderUnderlined;
1333
61
      }
1334
509
    }
1335
609
    obj2.free();
1336
609
    if (obj1.dictLookup("W", &obj2)->isNum()) {
1337
529
      borderWidth = obj2.getNum();
1338
529
    }
1339
609
    obj2.free();
1340
609
    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
609
    obj2.free();
1353
2.09k
  } else {
1354
2.09k
    obj1.free();
1355
2.09k
    if (annot->lookup("Border", &obj1)->isArray()) {
1356
42
      if (obj1.arrayGetLength() >= 3) {
1357
37
  if (obj1.arrayGet(2, &obj2)->isNum()) {
1358
16
    borderWidth = obj2.getNum();
1359
16
  }
1360
37
  obj2.free();
1361
37
  if (obj1.arrayGetLength() >= 4) {
1362
25
    if (obj1.arrayGet(3, &obj2)->isArray()) {
1363
18
      borderType = annotBorderDashed;
1364
18
      borderDashLength = obj2.arrayGetLength();
1365
18
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
1366
312
      for (i = 0; i < borderDashLength; ++i) {
1367
294
        if (obj2.arrayGet(i, &obj3)->isNum()) {
1368
89
    borderDash[i] = obj3.getNum();
1369
205
        } else {
1370
205
    borderDash[i] = 1;
1371
205
        }
1372
294
        obj3.free();
1373
294
      }
1374
18
    } else {
1375
      // Adobe draws no border at all if the last element is of
1376
      // the wrong type.
1377
7
      borderWidth = 0;
1378
7
    }
1379
25
    obj2.free();
1380
25
  }
1381
37
      }
1382
42
    }
1383
2.09k
  }
1384
2.70k
  obj1.free();
1385
2.70k
  if (mkDict) {
1386
1.86k
    if (borderWidth > 0) {
1387
1.84k
      mkDict->lookup("BC", &obj1);
1388
1.84k
      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
1389
1.64k
  obj1.free();
1390
1.64k
  mkDict->lookup("BG", &obj1);
1391
1.64k
      }
1392
1.84k
      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
1393
263
  dx = xMax - xMin;
1394
263
  dy = yMax - yMin;
1395
1396
  // radio buttons with no caption have a round border
1397
263
  hasCaption = mkDict->lookup("CA", &obj2)->isString();
1398
263
  obj2.free();
1399
263
  if (ftObj.isName("Btn") && (flags & acroFormFlagRadio) && !hasCaption) {
1400
10
    r = 0.5 * (dx < dy ? dx : dy);
1401
10
    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
8
    case annotBorderInset:
1418
8
      appearBuf->appendf("{0:.4f} w\n", 0.5 * borderWidth);
1419
8
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1420
8
      drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * borderWidth, "s",
1421
8
           appearBuf);
1422
8
      setColor(obj1.getArray(), gFalse,
1423
8
         borderType == annotBorderBeveled ? 1 : -1,
1424
8
         appearBuf);
1425
8
      drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth,
1426
8
            appearBuf);
1427
8
      setColor(obj1.getArray(), gFalse,
1428
8
         borderType == annotBorderBeveled ? -1 : 1,
1429
8
         appearBuf);
1430
8
      drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth,
1431
8
          appearBuf);
1432
8
      break;
1433
10
    }
1434
1435
253
  } else {
1436
253
    switch (borderType) {
1437
4
    case annotBorderDashed:
1438
4
      appearBuf->append("[");
1439
4
      for (i = 0; i < borderDashLength; ++i) {
1440
0
        appearBuf->appendf(" {0:.4f}", borderDash[i]);
1441
0
      }
1442
4
      appearBuf->append("] 0 d\n");
1443
      // fall through to the solid case
1444
250
    case annotBorderSolid:
1445
250
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1446
250
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1447
250
      appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n",
1448
250
             0.5 * borderWidth,
1449
250
             dx - borderWidth, dy - borderWidth);
1450
250
      break;
1451
2
    case annotBorderBeveled:
1452
2
    case annotBorderInset:
1453
2
      setColor(obj1.getArray(), gTrue,
1454
2
         borderType == annotBorderBeveled ? 1 : -1,
1455
2
         appearBuf);
1456
2
      appearBuf->append("0 0 m\n");
1457
2
      appearBuf->appendf("0 {0:.4f} l\n", dy);
1458
2
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
1459
2
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1460
2
             dx - borderWidth, dy - borderWidth);
1461
2
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1462
2
             borderWidth, dy - borderWidth);
1463
2
      appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
1464
2
      appearBuf->append("f\n");
1465
2
      setColor(obj1.getArray(), gTrue,
1466
2
         borderType == annotBorderBeveled ? -1 : 1,
1467
2
         appearBuf);
1468
2
      appearBuf->append("0 0 m\n");
1469
2
      appearBuf->appendf("{0:.4f} 0 l\n", dx);
1470
2
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
1471
2
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1472
2
             dx - borderWidth, dy - borderWidth);
1473
2
      appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1474
2
             dx - borderWidth, borderWidth);
1475
2
      appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
1476
2
      appearBuf->append("f\n");
1477
2
      break;
1478
1
    case annotBorderUnderlined:
1479
1
      appearBuf->appendf("{0:.4f} w\n", borderWidth);
1480
1
      setColor(obj1.getArray(), gFalse, 0, appearBuf);
1481
1
      appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx);
1482
1
      break;
1483
253
    }
1484
1485
    // clip to the inside of the border
1486
253
    appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n",
1487
253
           borderWidth,
1488
253
           dx - 2 * borderWidth, dy - 2 * borderWidth);
1489
253
  }
1490
263
      }
1491
1.84k
      obj1.free();
1492
1.84k
    }
1493
1.86k
  }
1494
2.70k
  gfree(borderDash);
1495
1496
  // get the resource dictionary
1497
2.70k
  buildDefaultResourceDict(&drObj);
1498
1499
  // build the font dictionary
1500
2.70k
  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
1501
1.81k
    fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict());
1502
1.81k
  } else {
1503
891
    fontDict = NULL;
1504
891
  }
1505
2.70k
  obj1.free();
1506
1507
  // get the default appearance string
1508
2.70k
  if (fieldLookup("DA", &obj1)->isString()) {
1509
2.38k
    da = obj1.getString()->copy();
1510
2.38k
  } else {
1511
324
    da = NULL;
1512
324
  }
1513
2.70k
  obj1.free();
1514
1515
  // get the rotation value
1516
2.70k
  rot = 0;
1517
2.70k
  if (mkDict) {
1518
1.86k
    if (mkDict->lookup("R", &obj1)->isInt()) {
1519
0
      rot = obj1.getInt();
1520
0
    }
1521
1.86k
    obj1.free();
1522
1.86k
  }
1523
1524
  // get the appearance state
1525
2.70k
  annot->lookup("AP", &apObj);
1526
2.70k
  annot->lookup("AS", &asObj);
1527
2.70k
  appearanceState = NULL;
1528
2.70k
  if (asObj.isName()) {
1529
266
    appearanceState = new GString(asObj.getName());
1530
2.44k
  } else if (apObj.isDict()) {
1531
524
    apObj.dictLookup("N", &obj1);
1532
524
    if (obj1.isDict() && obj1.dictGetLength() == 1) {
1533
15
      appearanceState = new GString(obj1.dictGetKey(0));
1534
15
    }
1535
524
    obj1.free();
1536
524
  }
1537
2.70k
  if (!appearanceState) {
1538
2.42k
    appearanceState = new GString("Off");
1539
2.42k
  }
1540
2.70k
  asObj.free();
1541
2.70k
  apObj.free();
1542
1543
2.70k
  int valueLength;
1544
2.70k
  Unicode *value = getValue(&valueLength);
1545
1546
  // draw the field contents
1547
2.70k
  if (ftObj.isName("Btn")) {
1548
634
    caption = NULL;
1549
634
    if (mkDict) {
1550
257
      if (mkDict->lookup("CA", &obj1)->isString()) {
1551
159
  caption = obj1.getString()->copy();
1552
159
      }
1553
257
      obj1.free();
1554
257
    }
1555
    // radio button
1556
634
    if (flags & acroFormFlagRadio) {
1557
      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
1558
98
      if (value && unicodeStringEqual(value, valueLength,
1559
61
              appearanceState->getCString())) {
1560
18
  if (caption) {
1561
7
    drawText(caption, da, fontDict, gFalse, 0,
1562
7
       acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
1563
7
       gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
1564
7
       borderWidth, gFalse, appearBuf);
1565
11
  } else {
1566
11
    if (mkDict) {
1567
1
      if (mkDict->lookup("BC", &obj2)->isArray() &&
1568
0
    obj2.arrayGetLength() > 0) {
1569
0
        dx = xMax - xMin;
1570
0
        dy = yMax - yMin;
1571
0
        setColor(obj2.getArray(), gTrue, 0, appearBuf);
1572
0
        drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), "f",
1573
0
       appearBuf);
1574
0
      }
1575
1
      obj2.free();
1576
1
    }
1577
11
  }
1578
18
      }
1579
    // pushbutton
1580
536
    } else if (flags & acroFormFlagPushbutton) {
1581
139
      if (caption) {
1582
28
  drawText(caption, da, fontDict, gFalse, 0,
1583
28
     acroFormQuadCenter, acroFormVAlignMiddle,
1584
28
     gFalse, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
1585
28
     borderWidth, gFalse, appearBuf);
1586
28
      }
1587
    // checkbox
1588
397
    } else {
1589
397
      if (value && !(unicodeStringEqual(value, valueLength, "Off") ||
1590
275
         unicodeStringEqual(value, valueLength, "No") ||
1591
265
         unicodeStringEqual(value, valueLength, "0") ||
1592
259
         valueLength == 0)) {
1593
259
  if (!caption) {
1594
207
    caption = new GString("3"); // ZapfDingbats checkmark
1595
207
  }
1596
259
  drawText(caption, da, fontDict, gFalse, 0,
1597
259
     acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
1598
259
     gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
1599
259
     borderWidth, gFalse, appearBuf);
1600
259
      }
1601
397
    }
1602
634
    if (caption) {
1603
366
      delete caption;
1604
366
    }
1605
2.07k
  } else if (ftObj.isName("Tx")) {
1606
1.65k
    XFAFieldBarcodeInfo *barcodeInfo = xfaField ? xfaField->getBarcodeInfo()
1607
1.65k
                                                : (XFAFieldBarcodeInfo *)NULL;
1608
1.65k
    if (value) {
1609
      //~ value strings can be Unicode
1610
485
      GString *valueLatin1 = unicodeToLatin1(value, valueLength);
1611
485
      if (barcodeInfo) {
1612
0
  drawBarcode(valueLatin1, da, fontDict, rot, xMin, yMin, xMax, yMax,
1613
0
        barcodeInfo, appearBuf);
1614
485
      } else {
1615
485
  if (fieldLookup("Q", &obj2)->isInt()) {
1616
327
    quadding = obj2.getInt();
1617
327
  } else {
1618
158
    quadding = acroFormQuadLeft;
1619
158
  }
1620
485
  obj2.free();
1621
485
  vAlign = (flags & acroFormFlagMultiline) ? acroFormVAlignTop
1622
485
                                           : acroFormVAlignMiddle;
1623
485
  XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
1624
485
                                            : (XFAFieldLayoutInfo *)NULL;
1625
485
  if (layoutInfo) {
1626
317
    switch (layoutInfo->hAlign) {
1627
213
    case xfaFieldLayoutHAlignLeft:
1628
213
    default:
1629
213
      quadding = acroFormQuadLeft;
1630
213
      break;
1631
55
    case xfaFieldLayoutHAlignCenter:
1632
55
      quadding = acroFormQuadCenter;
1633
55
      break;
1634
49
    case xfaFieldLayoutHAlignRight:
1635
49
      quadding = acroFormQuadRight;
1636
49
      break;
1637
317
    }
1638
317
    switch (layoutInfo->vAlign) {
1639
51
    case xfaFieldLayoutVAlignTop:
1640
51
    default:
1641
51
      vAlign = acroFormVAlignTop;
1642
51
      break;
1643
266
    case xfaFieldLayoutVAlignMiddle:
1644
266
      vAlign = acroFormVAlignMiddle;
1645
266
      break;
1646
0
    case xfaFieldLayoutVAlignBottom:
1647
0
      vAlign = acroFormVAlignBottom;
1648
0
      break;
1649
317
    }
1650
317
  }
1651
485
  comb = 0;
1652
485
  if (flags & acroFormFlagComb) {
1653
86
    if (fieldLookup("MaxLen", &obj2)->isInt()) {
1654
80
      comb = obj2.getInt();
1655
80
    }
1656
86
    obj2.free();
1657
86
  }
1658
485
  XFAFieldPictureInfo *pictureInfo =
1659
485
      xfaField ? xfaField->getPictureInfo()
1660
485
               : (XFAFieldPictureInfo *)NULL;
1661
485
  GString *value2 = valueLatin1;
1662
485
  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
485
  drawText(value2, da, fontDict,
1676
485
     flags & acroFormFlagMultiline, comb, quadding, vAlign,
1677
485
     gTrue, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
1678
485
     borderWidth, gFalse, appearBuf);
1679
485
  if (value2 != valueLatin1) {
1680
0
    delete value2;
1681
0
  }
1682
485
      }
1683
485
      delete valueLatin1;
1684
485
    }
1685
1.65k
  } else if (ftObj.isName("Ch")) {
1686
    //~ value/option strings can be Unicode
1687
420
    if (fieldLookup("Q", &obj1)->isInt()) {
1688
10
      quadding = obj1.getInt();
1689
410
    } else {
1690
410
      quadding = acroFormQuadLeft;
1691
410
    }
1692
420
    obj1.free();
1693
420
    vAlign = acroFormVAlignMiddle;
1694
420
    XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
1695
420
                                              : (XFAFieldLayoutInfo *)NULL;
1696
420
    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
420
    if (flags & acroFormFlagCombo) {
1724
93
      if (value) {
1725
80
  val = unicodeToLatin1(value, valueLength);
1726
80
  if (fieldObj.dictLookup("Opt", &obj2)->isArray()) {
1727
919
    for (i = 0, done = false; i < obj2.arrayGetLength() && !done; ++i) {
1728
850
      obj2.arrayGet(i, &obj3);
1729
850
      if (obj3.isArray() && obj3.arrayGetLength() == 2) {
1730
22
        if (obj3.arrayGet(0, &obj4)->isString() &&
1731
11
      obj4.getString()->cmp(val) == 0) {
1732
11
    obj4.free();
1733
11
    if (obj3.arrayGet(1, &obj4)->isString()) {
1734
11
      delete val;
1735
11
      val = obj4.getString()->copy();
1736
11
    }
1737
11
    done = gTrue;
1738
11
        }
1739
22
        obj4.free();
1740
22
      }
1741
850
      obj3.free();
1742
850
    }
1743
69
  }
1744
80
  obj2.free();
1745
80
  drawText(val, da, fontDict,
1746
80
     gFalse, 0, quadding, vAlign, gTrue, gFalse, rot,
1747
80
     0, 0, xMax - xMin, yMax - yMin, borderWidth,
1748
80
     gFalse, appearBuf);
1749
80
  delete val;
1750
  //~ Acrobat draws a popup icon on the right side
1751
80
      }
1752
    // list box
1753
327
    } else {
1754
327
      if (fieldObj.dictLookup("Opt", &obj1)->isArray()) {
1755
201
  nOptions = obj1.arrayGetLength();
1756
  // get the option text
1757
201
  text = (GString **)gmallocn(nOptions, sizeof(GString *));
1758
3.80k
  for (i = 0; i < nOptions; ++i) {
1759
3.60k
    text[i] = NULL;
1760
3.60k
    obj1.arrayGet(i, &obj2);
1761
3.60k
    if (obj2.isString()) {
1762
730
      text[i] = obj2.getString()->copy();
1763
2.87k
    } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
1764
145
      if (obj2.arrayGet(1, &obj3)->isString()) {
1765
138
        text[i] = obj3.getString()->copy();
1766
138
      }
1767
145
      obj3.free();
1768
145
    }
1769
3.60k
    obj2.free();
1770
3.60k
    if (!text[i]) {
1771
2.73k
      text[i] = new GString();
1772
2.73k
    }
1773
3.60k
  }
1774
  // get the selected option(s)
1775
201
  selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
1776
  //~ need to use the I field in addition to the V field
1777
3.80k
  for (i = 0; i < nOptions; ++i) {
1778
3.60k
    selection[i] = unicodeStringEqual(value, valueLength, text[i]);
1779
3.60k
  }
1780
  // get the top index
1781
201
  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
201
  } else {
1787
201
    topIdx = 0;
1788
201
  }
1789
201
  obj2.free();
1790
  // draw the text
1791
201
  drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding,
1792
201
        xMin, yMin, xMax, yMax, borderWidth, appearBuf);
1793
3.80k
  for (i = 0; i < nOptions; ++i) {
1794
3.60k
    delete text[i];
1795
3.60k
  }
1796
201
  gfree(text);
1797
201
  gfree(selection);
1798
201
      }
1799
327
      obj1.free();
1800
327
    }
1801
420
  } else if (ftObj.isName("Sig")) {
1802
    //~ check to see if background is already drawn
1803
0
    gfxStateDict.initDict(acroForm->doc->getXRef());
1804
0
    obj1.initReal(0.5);
1805
0
    gfxStateDict.dictAdd(copyString("ca"), &obj1);
1806
0
    appearBuf->append("/GS1 gs\n");
1807
0
    appearBuf->appendf("0.7 0.7 1 rg 0 0 {0:.2f} {1:.2f} re f\n",
1808
0
           xMax - xMin, yMax - yMin);
1809
0
    caption = new GString("SIGN HERE");
1810
0
    if (da) {
1811
0
      delete da;
1812
0
    }
1813
0
    da = new GString("/Helv 10 Tf 1 0 0 rg");
1814
0
    drawText(caption, da, fontDict, gFalse, 0,
1815
0
       acroFormQuadLeft, acroFormVAlignMiddle, gFalse, gFalse, rot,
1816
0
       0, 0, xMax - xMin, yMax - yMin, borderWidth, gFalse, appearBuf);
1817
0
    delete caption;
1818
1
  } else {
1819
1
    error(errSyntaxError, -1, "Unknown field type");
1820
1
  }
1821
1822
2.70k
  gfree(value);
1823
1824
2.70k
  delete appearanceState;
1825
2.70k
  if (da) {
1826
2.38k
    delete da;
1827
2.38k
  }
1828
1829
  // build the appearance stream dictionary
1830
2.70k
  appearDict.initDict(acroForm->doc->getXRef());
1831
2.70k
  appearDict.dictAdd(copyString("Length"),
1832
2.70k
         obj1.initInt(appearBuf->getLength()));
1833
2.70k
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
1834
2.70k
  obj1.initArray(acroForm->doc->getXRef());
1835
2.70k
  obj1.arrayAdd(obj2.initReal(0));
1836
2.70k
  obj1.arrayAdd(obj2.initReal(0));
1837
2.70k
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
1838
2.70k
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
1839
2.70k
  appearDict.dictAdd(copyString("BBox"), &obj1);
1840
1841
  // set the resource dictionary; add a default font
1842
2.70k
  if (drObj.isDict()) {
1843
2.70k
    drObj.copy(&resources);
1844
2.70k
  } else {
1845
0
    resources.initDict(acroForm->doc->getXRef());
1846
0
  }
1847
2.70k
  drObj.free();
1848
2.70k
  fontResources.initDict(acroForm->doc->getXRef());
1849
2.70k
  if (resources.dictLookup("Font", &obj1)->isDict()) {
1850
7.39k
    for (i = 0; i < obj1.dictGetLength(); ++i) {
1851
5.58k
      obj1.dictGetValNF(i, &obj2);
1852
5.58k
      fontResources.dictAdd(copyString(obj1.dictGetKey(i)), &obj2);
1853
5.58k
    }
1854
1.81k
  }
1855
2.70k
  obj1.free();
1856
2.70k
  defaultFont.initDict(acroForm->doc->getXRef());
1857
2.70k
  defaultFont.dictAdd(copyString("Type"), obj1.initName("Font"));
1858
2.70k
  defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1"));
1859
2.70k
  defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica"));
1860
2.70k
  defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding"));
1861
2.70k
  fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
1862
2.70k
  resources.dictAdd(copyString("Font"), &fontResources);
1863
2.70k
  if (gfxStateDict.isDict()) {
1864
0
    obj1.initDict(acroForm->doc->getXRef());
1865
0
    obj1.dictAdd(copyString("GS1"), &gfxStateDict);
1866
0
    resources.dictAdd(copyString("ExtGState"), &obj1);
1867
0
  }
1868
2.70k
  appearDict.dictAdd(copyString("Resources"), &resources);
1869
1870
  // build the appearance stream
1871
2.70k
  appearStream = new MemStream(appearBuf->getCString(), 0,
1872
2.70k
             appearBuf->getLength(), &appearDict);
1873
2.70k
  appearance.initStream(appearStream);
1874
1875
  // draw it
1876
2.70k
  gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
1877
1878
2.70k
  appearance.free();
1879
2.70k
  delete appearBuf;
1880
2.70k
  appearBuf = NULL;
1881
2.70k
  if (fontDict) {
1882
1.81k
    delete fontDict;
1883
1.81k
  }
1884
2.70k
  ftObj.free();
1885
2.70k
  mkObj.free();
1886
2.70k
}
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
368
           GString *appearBuf) {
1894
368
  Object obj1;
1895
368
  double color[4];
1896
368
  int nComps, i;
1897
1898
368
  nComps = a->getLength();
1899
368
  if (nComps > 4) {
1900
6
    nComps = 4;
1901
6
  }
1902
1.16k
  for (i = 0; i < nComps && i < 4; ++i) {
1903
801
    if (a->get(i, &obj1)->isNum()) {
1904
756
      color[i] = obj1.getNum();
1905
756
    } else {
1906
45
      color[i] = 0;
1907
45
    }
1908
801
    obj1.free();
1909
801
  }
1910
368
  if (nComps == 4) {
1911
26
    adjust = -adjust;
1912
26
  }
1913
368
  if (adjust > 0) {
1914
27
    for (i = 0; i < nComps; ++i) {
1915
17
      color[i] = 0.5 * color[i] + 0.5;
1916
17
    }
1917
358
  } else if (adjust < 0) {
1918
27
    for (i = 0; i < nComps; ++i) {
1919
17
      color[i] = 0.5 * color[i];
1920
17
    }
1921
10
  }
1922
368
  if (nComps == 4) {
1923
26
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1924
26
           color[0], color[1], color[2], color[3],
1925
26
           fill ? 'k' : 'K');
1926
342
  } else if (nComps == 3) {
1927
173
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1928
173
           color[0], color[1], color[2],
1929
173
           fill ? "rg" : "RG");
1930
173
  } else {
1931
169
    appearBuf->appendf("{0:.2f} {1:c}\n",
1932
169
           color[0],
1933
169
           fill ? 'g' : 'G');
1934
169
  }
1935
368
}
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
859
           GString *appearBuf) {
1945
859
  GString *text2;
1946
859
  GList *daToks;
1947
859
  GString *tok;
1948
859
  GfxFont *font;
1949
859
  double dx, dy;
1950
859
  double fontSize, fontSize2, topBorder, xx, xPrev, yy, w, wMax;
1951
859
  double offset, offset2, charWidth, ascent, descent;
1952
859
  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
859
  if (text->getLength() >= 2 &&
1961
597
      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
859
  } else {
1972
859
    text2 = text;
1973
859
  }
1974
859
  if (text2->getLength() == 0) {
1975
5
    if (text2 != text) {
1976
0
      delete text2;
1977
0
    }
1978
5
    return;
1979
5
  }
1980
1981
  // parse the default appearance string
1982
854
  tfPos = tmPos = -1;
1983
854
  if (da) {
1984
681
    daToks = tokenize(da);
1985
26.4k
    for (i = 2; i < daToks->getLength(); ++i) {
1986
25.8k
      if (!((GString *)daToks->get(i))->cmp("Tf")) {
1987
552
  tfPos = i - 2;
1988
25.2k
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
1989
13
  tmPos = i - 6;
1990
13
      }
1991
25.8k
    }
1992
681
  } else {
1993
173
    daToks = NULL;
1994
173
  }
1995
1996
  // force ZapfDingbats
1997
  //~ this should create the font if needed (?)
1998
854
  if (forceZapfDingbats) {
1999
266
    if (tfPos >= 0) {
2000
96
      tok = (GString *)daToks->get(tfPos);
2001
96
      if (tok->cmp("/ZaDb")) {
2002
96
  tok->clear();
2003
96
  tok->append("/ZaDb");
2004
96
      }
2005
96
    }
2006
266
  }
2007
2008
  // get the font and font size
2009
854
  font = NULL;
2010
854
  fontSize = 0;
2011
854
  if (tfPos >= 0) {
2012
552
    tok = (GString *)daToks->get(tfPos);
2013
552
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2014
493
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2015
146
  error(errSyntaxError, -1, "Unknown font in field's DA string");
2016
146
  tok->clear();
2017
146
  tok->append("/xpdf_default_font");
2018
146
      }
2019
493
    } else {
2020
59
      error(errSyntaxError, -1,
2021
59
      "Invalid font name in 'Tf' operator in field's DA string");
2022
59
    }
2023
552
    tok = (GString *)daToks->get(tfPos + 1);
2024
552
    fontSize = atof(tok->getCString());
2025
552
  } else {
2026
302
    error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
2027
302
    fontSize = 0;
2028
302
    if (!daToks) {
2029
173
      daToks = new GList();
2030
173
    }
2031
302
    tfPos = daToks->getLength();
2032
302
    daToks->append(new GString("/xpdf_default_font"));
2033
302
    daToks->append(new GString("10"));
2034
302
    daToks->append(new GString("Tf"));
2035
302
  }
2036
2037
  // setup
2038
854
  if (txField) {
2039
560
    appearBuf->append("/Tx BMC\n");
2040
560
  }
2041
854
  appearBuf->append("q\n");
2042
854
  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
854
  } 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
854
  } 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
854
  } else { // assume rot == 0
2055
854
    dx = width;
2056
854
    dy = height;
2057
854
  }
2058
2059
  // multi-line text
2060
854
  if (multiline) {
2061
    // note: the comb flag is ignored in multiline mode
2062
2063
93
    wMax = dx - 2 * border - 4;
2064
2065
    // this is a kludge that appears to match Adobe's behavior
2066
93
    if (height > 15) {
2067
71
      topBorder = 5;
2068
71
    } else {
2069
22
      topBorder = 2;
2070
22
    }
2071
2072
    // compute font autosize
2073
93
    if (fontSize == 0) {
2074
381
      for (fontSize = 10; fontSize > 1; --fontSize) {
2075
348
  yy = dy - topBorder;
2076
348
  w = 0;
2077
348
  i = 0;
2078
61.6k
  while (i < text2->getLength()) {
2079
61.3k
    getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2080
61.3k
    i = k;
2081
61.3k
    yy -= fontSize;
2082
61.3k
  }
2083
  // approximate the descender for the last line
2084
348
  if (yy >= 0.25 * fontSize && w <= wMax) {
2085
18
    break;
2086
18
  }
2087
348
      }
2088
51
      if (tfPos >= 0) {
2089
51
  tok = (GString *)daToks->get(tfPos + 1);
2090
51
  tok->clear();
2091
51
  tok->appendf("{0:.2f}", fontSize);
2092
51
      }
2093
51
    }
2094
2095
    // starting y coordinate
2096
93
    nLines = 0;
2097
93
    i = 0;
2098
2.73k
    while (i < text2->getLength()) {
2099
2.64k
      getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2100
2.64k
      i = k;
2101
2.64k
      ++nLines;
2102
2.64k
    }
2103
93
    if (font) {
2104
31
      ascent = font->getDeclaredAscent() * fontSize;
2105
31
      descent = font->getDescent() * fontSize;
2106
62
    } else {
2107
62
      ascent = 0.75 * fontSize;
2108
62
      descent = -0.25 * fontSize;
2109
62
    }
2110
93
    switch (vAlign) {
2111
67
    case acroFormVAlignTop:
2112
67
    default:
2113
67
      yy = dy - ascent - topBorder;
2114
67
      break;
2115
26
    case acroFormVAlignMiddle:
2116
26
      yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize - descent;
2117
26
      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
93
    }
2125
    // if the field is shorter than a line of text, Acrobat positions
2126
    // the text relative to the bottom edge
2127
93
    if (dy < fontSize + topBorder) {
2128
6
      yy = 2 - descent;
2129
6
    }
2130
    // each line of text starts with a Td operator that moves down a
2131
    // line -- so move up a line here
2132
93
    yy += fontSize;
2133
2134
93
    appearBuf->append("BT\n");
2135
2136
    // set the font matrix
2137
93
    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
93
    if (daToks) {
2148
600
      for (i = 0; i < daToks->getLength(); ++i) {
2149
507
  appearBuf->append((GString *)daToks->get(i))->append(' ');
2150
507
      }
2151
93
    }
2152
2153
    // write the font matrix (if not part of the DA string)
2154
93
    if (tmPos < 0) {
2155
93
      appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y + yy);
2156
93
    }
2157
2158
    // write a series of lines of text
2159
93
    i = 0;
2160
93
    xPrev = 0;
2161
2.73k
    while (i < text2->getLength()) {
2162
2163
2.64k
      getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
2164
2165
      // compute text start position
2166
2.64k
      switch (quadding) {
2167
2.53k
      case acroFormQuadLeft:
2168
2.53k
      default:
2169
2.53k
  xx = border + 2;
2170
2.53k
  break;
2171
66
      case acroFormQuadCenter:
2172
66
  xx = (dx - w) / 2;
2173
66
  break;
2174
42
      case acroFormQuadRight:
2175
42
  xx = dx - border - 2 - w;
2176
42
  break;
2177
2.64k
      }
2178
2179
      // draw the line
2180
2.64k
      appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx - xPrev, -fontSize);
2181
2.64k
      appearBuf->append('(');
2182
36.2k
      for (; i < j; ++i) {
2183
33.6k
  c = text2->getChar(i) & 0xff;
2184
33.6k
  if (c == '(' || c == ')' || c == '\\') {
2185
411
    appearBuf->append('\\');
2186
411
    appearBuf->append((char)c);
2187
33.2k
  } else if (c < 0x20 || c >= 0x80) {
2188
7.94k
    appearBuf->appendf("\\{0:03o}", c);
2189
25.2k
  } else {
2190
25.2k
    appearBuf->append((char)c);
2191
25.2k
  }
2192
33.6k
      }
2193
2.64k
      appearBuf->append(") Tj\n");
2194
2195
      // next line
2196
2.64k
      i = k;
2197
2.64k
      xPrev = xx;
2198
2.64k
    }
2199
2200
93
    appearBuf->append("ET\n");
2201
2202
  // single-line text
2203
761
  } else {
2204
    //~ replace newlines with spaces? - what does Acrobat do?
2205
2206
    // comb formatting
2207
761
    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
79
      if (xfaField) {
2215
45
  XFAFieldLayoutInfo *layoutInfo = xfaField->getLayoutInfo();
2216
45
  if (layoutInfo) {
2217
44
    dx -= layoutInfo->marginLeft + layoutInfo->marginRight;
2218
44
  }
2219
45
      }
2220
2221
      // compute comb spacing
2222
79
      w = dx / comb;
2223
2224
      // compute font autosize
2225
79
      if (fontSize == 0) {
2226
31
  fontSize = dy - 2 * border;
2227
31
  if (w < fontSize) {
2228
30
    fontSize = w;
2229
30
  }
2230
31
  fontSize = floor(fontSize);
2231
31
  if (fontSize > 10) {
2232
2
    fontSize = 10;
2233
2
  }
2234
31
  if (tfPos >= 0) {
2235
31
    tok = (GString *)daToks->get(tfPos + 1);
2236
31
    tok->clear();
2237
31
    tok->appendf("{0:.4f}", fontSize);
2238
31
  }
2239
31
      }
2240
2241
      // compute text start position
2242
79
      switch (quadding) {
2243
75
      case acroFormQuadLeft:
2244
75
      default:
2245
75
  xx = 0;
2246
75
  break;
2247
1
      case acroFormQuadCenter:
2248
1
  xx = ((comb - text2->getLength()) / 2) * w;
2249
1
  break;
2250
3
      case acroFormQuadRight:
2251
3
  xx = (comb - text2->getLength()) * w;
2252
3
  break;
2253
79
      }
2254
79
      if (font) {
2255
44
  ascent = font->getDeclaredAscent() * fontSize;
2256
44
  descent = font->getDescent() * fontSize;
2257
44
      } else {
2258
35
  ascent = 0.75 * fontSize;
2259
35
  descent = -0.25 * fontSize;
2260
35
      }
2261
79
      switch (vAlign) {
2262
13
      case acroFormVAlignTop:
2263
13
      default:
2264
13
  yy = dy - ascent;
2265
13
  break;
2266
66
      case acroFormVAlignMiddle:
2267
66
  yy = 0.5 * (dy - ascent - descent);
2268
66
  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
79
      }
2276
2277
79
      appearBuf->append("BT\n");
2278
2279
      // set the font matrix
2280
79
      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
79
      if (daToks) {
2291
527
  for (i = 0; i < daToks->getLength(); ++i) {
2292
448
    appearBuf->append((GString *)daToks->get(i))->append(' ');
2293
448
  }
2294
79
      }
2295
2296
      // write the font matrix (if not part of the DA string)
2297
79
      if (tmPos < 0) {
2298
79
  appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
2299
79
      }
2300
2301
      // write the text string
2302
79
      offset = 0;
2303
37.5k
      for (i = 0; i < text2->getLength(); ++i) {
2304
37.4k
  c = text2->getChar(i) & 0xff;
2305
37.4k
  if (c >= 0x20 && c < 0x80) {
2306
27.9k
    if (font && !font->isCIDFont()) {
2307
976
      charWidth = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
2308
26.9k
    } else {
2309
      // otherwise, make a crude estimate
2310
26.9k
      charWidth = 0.5 * fontSize;
2311
26.9k
    }
2312
27.9k
    offset2 = 0.5 * (w - charWidth);
2313
27.9k
    appearBuf->appendf("{0:.4f} 0 Td\n", offset + offset2);
2314
27.9k
    if (c == '(' || c == ')' || c == '\\') {
2315
281
      appearBuf->appendf("(\\{0:c}) Tj\n", c);
2316
27.6k
    } else {
2317
27.6k
      appearBuf->appendf("({0:c}) Tj\n", c);
2318
27.6k
    }
2319
27.9k
    offset = w - offset2;
2320
27.9k
  } else {
2321
9.49k
    offset += w;
2322
9.49k
  }
2323
37.4k
      }
2324
2325
79
      appearBuf->append("ET\n");
2326
2327
    // regular (non-comb) formatting
2328
682
    } else {
2329
2330
      // compute string width
2331
682
      if (font && !font->isCIDFont()) {
2332
272
  w = 0;
2333
6.85k
  for (i = 0; i < text2->getLength(); ++i) {
2334
6.57k
    w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
2335
6.57k
  }
2336
410
      } else {
2337
  // otherwise, make a crude estimate
2338
410
  w = text2->getLength() * 0.5;
2339
410
      }
2340
2341
      // compute font autosize
2342
682
      if (fontSize == 0) {
2343
315
  fontSize = dy - 2 * border;
2344
315
  fontSize2 = (dx - 4 - 2 * border) / w;
2345
315
  if (fontSize2 < fontSize) {
2346
78
    fontSize = fontSize2;
2347
78
  }
2348
315
  fontSize = floor(fontSize);
2349
315
  if (fontSize > 10) {
2350
159
    fontSize = 10;
2351
159
  }
2352
315
  if (tfPos >= 0) {
2353
315
    tok = (GString *)daToks->get(tfPos + 1);
2354
315
    tok->clear();
2355
315
    tok->appendf("{0:.4f}", fontSize);
2356
315
  }
2357
315
      }
2358
2359
      // compute text start position
2360
682
      w *= fontSize;
2361
682
      switch (quadding) {
2362
297
      case acroFormQuadLeft:
2363
307
      default:
2364
307
  xx = border + 2;
2365
307
  break;
2366
336
      case acroFormQuadCenter:
2367
336
  xx = (dx - w) / 2;
2368
336
  break;
2369
39
      case acroFormQuadRight:
2370
39
  xx = dx - border - 2 - w;
2371
39
  break;
2372
682
      }
2373
682
      if (font) {
2374
272
  ascent = font->getDeclaredAscent() * fontSize;
2375
272
  descent = font->getDescent() * fontSize;
2376
410
      } else {
2377
410
  ascent = 0.75 * fontSize;
2378
410
  descent = -0.25 * fontSize;
2379
410
      }
2380
682
      switch (vAlign) {
2381
36
      case acroFormVAlignTop:
2382
36
      default:
2383
36
  yy = dy - ascent;
2384
36
  break;
2385
380
      case acroFormVAlignMiddle:
2386
380
  yy = 0.5 * (dy - ascent - descent);
2387
380
  break;
2388
266
      case acroFormVAlignMiddleNoDescender:
2389
266
  yy = 0.5 * (dy - ascent);
2390
266
  break;
2391
0
      case acroFormVAlignBottom:
2392
0
  yy = -descent;
2393
0
  break;
2394
682
      }
2395
2396
682
      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
682
      appearBuf->append("BT\n");
2403
2404
      // set the font matrix
2405
682
      if (tmPos >= 0) {
2406
11
  tok = (GString *)daToks->get(tmPos + 4);
2407
11
  tok->clear();
2408
11
  tok->appendf("{0:.4f}", x + xx);
2409
11
  tok = (GString *)daToks->get(tmPos + 5);
2410
11
  tok->clear();
2411
11
  tok->appendf("{0:.4f}", y + yy);
2412
11
      }
2413
2414
      // write the DA string
2415
682
      if (daToks) {
2416
27.7k
  for (i = 0; i < daToks->getLength(); ++i) {
2417
27.1k
    appearBuf->append((GString *)daToks->get(i))->append(' ');
2418
27.1k
  }
2419
682
      }
2420
2421
      // write the font matrix (if not part of the DA string)
2422
682
      if (tmPos < 0) {
2423
671
  appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
2424
671
      }
2425
2426
      // write the text string
2427
682
      appearBuf->append('(');
2428
29.7k
      for (i = 0; i < text2->getLength(); ++i) {
2429
29.0k
  c = text2->getChar(i) & 0xff;
2430
29.0k
  if (c == '(' || c == ')' || c == '\\') {
2431
148
    appearBuf->append('\\');
2432
148
    appearBuf->append((char)c);
2433
28.9k
  } else if (c < 0x20 || c >= 0x80) {
2434
5.32k
    appearBuf->appendf("\\{0:03o}", c);
2435
23.5k
  } else {
2436
23.5k
    appearBuf->append((char)c);
2437
23.5k
  }
2438
29.0k
      }
2439
682
      appearBuf->append(") Tj\n");
2440
682
    }
2441
2442
761
    appearBuf->append("ET\n");
2443
761
  }
2444
2445
  // cleanup
2446
854
  appearBuf->append("Q\n");
2447
854
  if (txField) {
2448
560
    appearBuf->append("EMC\n");
2449
560
  }
2450
2451
854
  if (daToks) {
2452
854
    deleteGList(daToks, GString);
2453
854
  }
2454
854
  if (text2 != text) {
2455
0
    delete text2;
2456
0
  }
2457
854
}
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
201
        GString *appearBuf) {
2466
201
  GList *daToks;
2467
201
  GString *tok;
2468
201
  GfxFont *font;
2469
201
  double fontSize, fontSize2, x, y, w, wMax;
2470
201
  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
201
  tfPos = tmPos = -1;
2478
201
  if (da) {
2479
170
    daToks = tokenize(da);
2480
12.3k
    for (i = 2; i < daToks->getLength(); ++i) {
2481
12.1k
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
2482
124
  tfPos = i - 2;
2483
12.0k
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
2484
15
  tmPos = i - 6;
2485
15
      }
2486
12.1k
    }
2487
170
  } else {
2488
31
    daToks = NULL;
2489
31
  }
2490
2491
  // get the font and font size
2492
201
  font = NULL;
2493
201
  fontSize = 0;
2494
201
  if (tfPos >= 0) {
2495
124
    tok = (GString *)daToks->get(tfPos);
2496
124
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2497
95
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2498
61
  error(errSyntaxError, -1, "Unknown font in field's DA string");
2499
61
  tok->clear();
2500
61
  tok->append("/xpdf_default_font");
2501
61
      }
2502
95
    } else {
2503
29
      error(errSyntaxError, -1,
2504
29
      "Invalid font name in 'Tf' operator in field's DA string");
2505
29
    }
2506
124
    tok = (GString *)daToks->get(tfPos + 1);
2507
124
    fontSize = atof(tok->getCString());
2508
124
  } else {
2509
77
    error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
2510
77
  }
2511
2512
  // compute font autosize
2513
201
  if (fontSize == 0) {
2514
126
    wMax = 0;
2515
1.87k
    for (i = 0; i < nOptions; ++i) {
2516
1.75k
      if (font && !font->isCIDFont()) {
2517
86
  w = 0;
2518
1.30k
  for (j = 0; j < text[i]->getLength(); ++j) {
2519
1.22k
    w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
2520
1.22k
  }
2521
1.66k
      } else {
2522
  // otherwise, make a crude estimate
2523
1.66k
  w = text[i]->getLength() * 0.5;
2524
1.66k
      }
2525
1.75k
      if (w > wMax) {
2526
270
  wMax = w;
2527
270
      }
2528
1.75k
    }
2529
126
    fontSize = yMax - yMin - 2 * border;
2530
126
    fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
2531
126
    if (fontSize2 < fontSize) {
2532
103
      fontSize = fontSize2;
2533
103
    }
2534
126
    fontSize = floor(fontSize);
2535
126
    if (fontSize > 10) {
2536
8
      fontSize = 10;
2537
8
    }
2538
126
    if (tfPos >= 0) {
2539
49
      tok = (GString *)daToks->get(tfPos + 1);
2540
49
      tok->clear();
2541
49
      tok->appendf("{0:.4f}", fontSize);
2542
49
    }
2543
126
  }
2544
2545
  // draw the text
2546
201
  y = yMax - yMin - 1.1 * fontSize;
2547
3.80k
  for (i = topIdx; i < nOptions; ++i) {
2548
2549
    // setup
2550
3.60k
    appearBuf->append("q\n");
2551
2552
    // draw the background if selected
2553
3.60k
    if (selection[i]) {
2554
1.61k
      appearBuf->append("0 g f\n");
2555
1.61k
      appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
2556
1.61k
        border,
2557
1.61k
        y - 0.2 * fontSize,
2558
1.61k
        xMax - xMin - 2 * border,
2559
1.61k
        1.1 * fontSize);
2560
1.61k
    }
2561
2562
    // setup
2563
3.60k
    appearBuf->append("BT\n");
2564
2565
    // compute string width
2566
3.60k
    if (font && !font->isCIDFont()) {
2567
227
      w = 0;
2568
2.12k
      for (j = 0; j < text[i]->getLength(); ++j) {
2569
1.89k
  w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
2570
1.89k
      }
2571
3.37k
    } else {
2572
      // otherwise, make a crude estimate
2573
3.37k
      w = text[i]->getLength() * 0.5;
2574
3.37k
    }
2575
2576
    // compute text start position
2577
3.60k
    w *= fontSize;
2578
3.60k
    switch (quadding) {
2579
3.60k
    case acroFormQuadLeft:
2580
3.60k
    default:
2581
3.60k
      x = border + 2;
2582
3.60k
      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.60k
    }
2590
2591
    // set the font matrix
2592
3.60k
    if (tmPos >= 0) {
2593
427
      tok = (GString *)daToks->get(tmPos + 4);
2594
427
      tok->clear();
2595
427
      tok->appendf("{0:.4f}", x);
2596
427
      tok = (GString *)daToks->get(tmPos + 5);
2597
427
      tok->clear();
2598
427
      tok->appendf("{0:.4f}", y);
2599
427
    }
2600
2601
    // write the DA string
2602
3.60k
    if (daToks) {
2603
335k
      for (j = 0; j < daToks->getLength(); ++j) {
2604
332k
  appearBuf->append((GString *)daToks->get(j))->append(' ');
2605
332k
      }
2606
3.20k
    }
2607
2608
    // write the font matrix (if not part of the DA string)
2609
3.60k
    if (tmPos < 0) {
2610
3.17k
      appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y);
2611
3.17k
    }
2612
2613
    // change the text color if selected
2614
3.60k
    if (selection[i]) {
2615
1.61k
      appearBuf->append("1 g\n");
2616
1.61k
    }
2617
2618
    // write the text string
2619
3.60k
    appearBuf->append('(');
2620
63.8k
    for (j = 0; j < text[i]->getLength(); ++j) {
2621
60.2k
      c = text[i]->getChar(j) & 0xff;
2622
60.2k
      if (c == '(' || c == ')' || c == '\\') {
2623
315
  appearBuf->append('\\');
2624
315
  appearBuf->append((char)c);
2625
59.9k
      } else if (c < 0x20 || c >= 0x80) {
2626
22.5k
  appearBuf->appendf("\\{0:03o}", c);
2627
37.3k
      } else {
2628
37.3k
  appearBuf->append((char)c);
2629
37.3k
      }
2630
60.2k
    }
2631
3.60k
    appearBuf->append(") Tj\n");
2632
2633
    // cleanup
2634
3.60k
    appearBuf->append("ET\n");
2635
3.60k
    appearBuf->append("Q\n");
2636
2637
    // next line
2638
3.60k
    y -= 1.1 * fontSize;
2639
3.60k
  }
2640
2641
201
  if (daToks) {
2642
170
    deleteGList(daToks, GString);
2643
170
  }
2644
201
}
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
66.6k
        int *end, double *width, int *next) {
2653
66.6k
  double w, dw;
2654
66.6k
  int j, k, c;
2655
2656
  // figure out how much text will fit on the line
2657
  //~ what does Adobe do with tabs?
2658
66.6k
  w = 0;
2659
437k
  for (j = start; j < text->getLength() && w <= wMax; ++j) {
2660
378k
    c = text->getChar(j) & 0xff;
2661
378k
    if (c == 0x0a || c == 0x0d) {
2662
8.32k
      break;
2663
8.32k
    }
2664
370k
    if (font && !font->isCIDFont()) {
2665
1.89k
      dw = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
2666
368k
    } else {
2667
      // otherwise, make a crude estimate
2668
368k
      dw = 0.5 * fontSize;
2669
368k
    }
2670
370k
    w += dw;
2671
370k
  }
2672
66.6k
  if (w > wMax) {
2673
299k
    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
2674
67.8k
    for (; k > start && text->getChar(k-1) == ' '; --k) ;
2675
57.8k
    if (k > start) {
2676
8.88k
      j = k;
2677
8.88k
    }
2678
57.8k
    if (j == start) {
2679
      // handle the pathological case where the first character is
2680
      // too wide to fit on the line all by itself
2681
1.63k
      j = start + 1;
2682
1.63k
    }
2683
57.8k
  }
2684
66.6k
  *end = j;
2685
2686
  // compute the width
2687
66.6k
  w = 0;
2688
411k
  for (k = start; k < j; ++k) {
2689
345k
    if (font && !font->isCIDFont()) {
2690
1.75k
      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
2691
343k
    } else {
2692
      // otherwise, make a crude estimate
2693
343k
      dw = 0.5 * fontSize;
2694
343k
    }
2695
345k
    w += dw;
2696
345k
  }
2697
66.6k
  *width = w;
2698
2699
  // next line
2700
77.5k
  while (j < text->getLength() && text->getChar(j) == ' ') {
2701
10.9k
    ++j;
2702
10.9k
  }
2703
66.6k
  if (j < text->getLength() && text->getChar(j) == 0x0d) {
2704
294
    ++j;
2705
294
  }
2706
66.6k
  if (j < text->getLength() && text->getChar(j) == 0x0a) {
2707
8.97k
    ++j;
2708
8.97k
  }
2709
66.6k
  *next = j;
2710
66.6k
}
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
10
             const char *cmd, GString *appearBuf) {
2716
10
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2717
10
         cx + r, cy);
2718
10
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2719
10
         cx + r, cy + bezierCircle * r,
2720
10
         cx + bezierCircle * r, cy + r,
2721
10
         cx, cy + r);
2722
10
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2723
10
         cx - bezierCircle * r, cy + r,
2724
10
         cx - r, cy + bezierCircle * r,
2725
10
         cx - r, cy);
2726
10
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2727
10
         cx - r, cy - bezierCircle * r,
2728
10
         cx - bezierCircle * r, cy - r,
2729
10
         cx, cy - r);
2730
10
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2731
10
         cx + bezierCircle * r, cy - r,
2732
10
         cx + r, cy - bezierCircle * r,
2733
10
         cx + r, cy);
2734
10
  appearBuf->appendf("{0:s}\n", cmd);
2735
10
}
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
8
              GString *appearBuf) {
2741
8
  double r2;
2742
2743
8
  r2 = r / sqrt(2.0);
2744
8
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2745
8
         cx + r2, cy + r2);
2746
8
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2747
8
         cx + (1 - bezierCircle) * r2,
2748
8
         cy + (1 + bezierCircle) * r2,
2749
8
         cx - (1 - bezierCircle) * r2,
2750
8
         cy + (1 + bezierCircle) * r2,
2751
8
         cx - r2,
2752
8
         cy + r2);
2753
8
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2754
8
         cx - (1 + bezierCircle) * r2,
2755
8
         cy + (1 - bezierCircle) * r2,
2756
8
         cx - (1 + bezierCircle) * r2,
2757
8
         cy - (1 - bezierCircle) * r2,
2758
8
         cx - r2,
2759
8
         cy - r2);
2760
8
  appearBuf->append("S\n");
2761
8
}
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
8
            GString *appearBuf) {
2767
8
  double r2;
2768
2769
8
  r2 = r / sqrt(2.0);
2770
8
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
2771
8
         cx - r2, cy - r2);
2772
8
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2773
8
         cx - (1 - bezierCircle) * r2,
2774
8
         cy - (1 + bezierCircle) * r2,
2775
8
         cx + (1 - bezierCircle) * r2,
2776
8
         cy - (1 + bezierCircle) * r2,
2777
8
         cx + r2,
2778
8
         cy - r2);
2779
8
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
2780
8
         cx + (1 + bezierCircle) * r2,
2781
8
         cy - (1 - bezierCircle) * r2,
2782
8
         cx + (1 + bezierCircle) * r2,
2783
8
         cy + (1 - bezierCircle) * r2,
2784
8
         cx + r2,
2785
8
         cy + r2);
2786
8
  appearBuf->append("S\n");
2787
8
}
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
851
GList *AcroFormField::tokenize(GString *s) {
2987
851
  GList *toks;
2988
851
  int i, j;
2989
2990
851
  toks = new GList();
2991
851
  i = 0;
2992
40.5k
  while (i < s->getLength()) {
2993
111k
    while (i < s->getLength() && Lexer::isSpace(s->getChar(i))) {
2994
71.6k
      ++i;
2995
71.6k
    }
2996
39.6k
    if (i < s->getLength()) {
2997
39.6k
      for (j = i + 1;
2998
192k
     j < s->getLength() && !Lexer::isSpace(s->getChar(j));
2999
153k
     ++j) ;
3000
39.6k
      toks->append(new GString(s, i, j - i));
3001
39.6k
      i = j;
3002
39.6k
    }
3003
39.6k
  }
3004
851
  return toks;
3005
851
}
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.13k
Object *AcroFormField::getFieldRef(Object *ref) {
3042
4.13k
  return fieldRef.copy(ref);
3043
4.13k
}
3044
3045
0
Object *AcroFormField::getValueObj(Object *val) {
3046
0
  return fieldLookup("V", val);
3047
0
}
3048
3049
0
Object *AcroFormField::getParentRef(Object *parent) {
3050
0
  return fieldObj.dictLookupNF("Parent", parent);
3051
0
}
3052
3053
// Get the first annotation object associated with this field.
3054
0
Object *AcroFormField::getAnnotObj(Object *annotObj) {
3055
0
  Object kidsObj;
3056
3057
0
  if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
3058
0
    if (kidsObj.arrayGetLength() > 0) {
3059
0
      kidsObj.arrayGet(0, annotObj);
3060
0
    } else {
3061
0
      annotObj->initNull();
3062
0
    }
3063
0
  } else {
3064
0
    fieldObj.copy(annotObj);
3065
0
  }
3066
0
  kidsObj.free();
3067
0
  return annotObj;
3068
0
}
3069
3070
0
Object *AcroFormField::getAnnotResources(Dict *annot, Object *res) {
3071
0
  Object apObj, asObj, appearance, obj1;
3072
3073
  // get the appearance stream
3074
0
  if (annot->lookup("AP", &apObj)->isDict()) {
3075
0
    apObj.dictLookup("N", &obj1);
3076
0
    if (obj1.isDict()) {
3077
0
      if (annot->lookup("AS", &asObj)->isName()) {
3078
0
  obj1.dictLookup(asObj.getName(), &appearance);
3079
0
      } else if (obj1.dictGetLength() == 1) {
3080
0
  obj1.dictGetVal(0, &appearance);
3081
0
      } else {
3082
0
  obj1.dictLookup("Off", &appearance);
3083
0
      }
3084
0
      asObj.free();
3085
0
    } else {
3086
0
      obj1.copy(&appearance);
3087
0
    }
3088
0
    obj1.free();
3089
0
  }
3090
0
  apObj.free();
3091
3092
0
  if (appearance.isStream()) {
3093
0
    appearance.streamGetDict()->lookup("Resources", res);
3094
0
  } else {
3095
0
    res->initNull();
3096
0
  }
3097
0
  appearance.free();
3098
3099
0
  return res;
3100
0
}
3101
3102
// Merge the field and AcroForm DR objects.
3103
2.70k
void AcroFormField::buildDefaultResourceDict(Object *dr) {
3104
2.70k
  Object formDR, fieldDR, resDict, newResDict, resObj;
3105
2.70k
  char *resType, *resName;
3106
2.70k
  int i, j;
3107
3108
  // NB: we need to deep-copy the dictionaries here, because multiple
3109
  // threads can be sharing these objects.
3110
3111
2.70k
  dr->initDict(acroForm->doc->getXRef());
3112
3113
2.70k
  acroForm->acroFormObj.dictLookup("DR", &formDR);
3114
2.70k
  if (formDR.isDict()) {
3115
11.5k
    for (i = 0; i < formDR.dictGetLength(); ++i) {
3116
9.32k
      resType = formDR.dictGetKey(i);
3117
9.32k
      formDR.dictGetVal(i, &resDict);
3118
9.32k
      if (resDict.isDict()) {
3119
3.58k
  newResDict.initDict(acroForm->doc->getXRef());
3120
3.58k
  dr->dictAdd(copyString(resType), &newResDict);
3121
15.4k
  for (j = 0; j < resDict.dictGetLength(); ++j) {
3122
11.8k
    resName = resDict.dictGetKey(j);
3123
11.8k
    resDict.dictGetValNF(j, &resObj);
3124
11.8k
    newResDict.dictAdd(copyString(resName), &resObj);
3125
11.8k
  }
3126
3.58k
      }
3127
9.32k
      resDict.free();
3128
9.32k
    }
3129
2.18k
  }
3130
2.70k
  formDR.free();
3131
3132
2.70k
  fieldObj.dictLookup("DR", &fieldDR);
3133
2.70k
  if (fieldDR.isDict()) {
3134
2.41k
    for (i = 0; i < fieldDR.dictGetLength(); ++i) {
3135
1.56k
      resType = fieldDR.dictGetKey(i);
3136
1.56k
      fieldDR.dictGetVal(i, &resDict);
3137
1.56k
      if (resDict.isDict()) {
3138
486
  dr->dictLookup(resType, &newResDict);
3139
486
  if (!newResDict.isDict()) {
3140
358
    newResDict.free();
3141
358
    newResDict.initDict(acroForm->doc->getXRef());
3142
358
  }
3143
486
  dr->dictAdd(copyString(resType), &newResDict);
3144
1.57k
  for (j = 0; j < resDict.dictGetLength(); ++j) {
3145
1.08k
    resName = resDict.dictGetKey(j);
3146
1.08k
    resDict.dictGetValNF(j, &resObj);
3147
1.08k
    newResDict.dictAdd(copyString(resName), &resObj);
3148
1.08k
  }
3149
486
      }
3150
1.56k
      resDict.free();
3151
1.56k
    }
3152
849
  }
3153
2.70k
  fieldDR.free();
3154
2.70k
}
3155
3156
// Look up an inheritable field dictionary entry.
3157
8.71k
Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
3158
8.71k
  return fieldLookup(fieldObj.getDict(), key, obj);
3159
8.71k
}
3160
3161
8.71k
Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
3162
8.71k
  Object parent, parent2;
3163
8.71k
  int depth;
3164
3165
8.71k
  if (!dict->lookup(key, obj)->isNull()) {
3166
6.60k
    return obj;
3167
6.60k
  }
3168
2.11k
  obj->free();
3169
3170
2.11k
  dict->lookup("Parent", &parent)->isDict();
3171
2.11k
  depth = 0;
3172
4.81k
  while (parent.isDict() && depth < maxFieldObjectDepth) {
3173
2.74k
    if (!parent.dictLookup(key, obj)->isNull()) {
3174
34
      parent.free();
3175
34
      return obj;
3176
34
    }
3177
2.70k
    obj->free();
3178
2.70k
    parent.dictLookup("Parent", &parent2);
3179
2.70k
    parent.free();
3180
2.70k
    parent = parent2;
3181
2.70k
    ++depth;
3182
2.70k
  }
3183
2.07k
  parent.free();
3184
3185
  // some fields don't specify a parent, so we check the AcroForm
3186
  // dictionary just in case
3187
2.07k
  acroForm->acroFormObj.dictLookup(key, obj);
3188
2.07k
  return obj;
3189
2.11k
}
3190
3191
326
Unicode *AcroFormField::utf8ToUnicode(GString *s, int *unicodeLength) {
3192
326
  int n = 0;
3193
326
  int i = 0;
3194
326
  Unicode u;
3195
8.72k
  while (getUTF8(s, &i, &u)) {
3196
8.39k
    ++n;
3197
8.39k
  }
3198
326
  Unicode *uVec = (Unicode *)gmallocn(n, sizeof(Unicode));
3199
326
  n = 0;
3200
326
  i = 0;
3201
8.72k
  while (getUTF8(s, &i, &uVec[n])) {
3202
8.39k
    ++n;
3203
8.39k
  }
3204
326
  *unicodeLength = n;
3205
326
  return uVec;
3206
326
}
3207
3208
565
GString *AcroFormField::unicodeToLatin1(Unicode *u, int unicodeLength) {
3209
565
  GString *s = new GString();
3210
96.9k
  for (int i = 0; i < unicodeLength; ++i) {
3211
96.4k
    if (u[i] <= 0xff) {
3212
89.9k
      s->append((char)u[i]);
3213
89.9k
    }
3214
96.4k
  }
3215
565
  return s;
3216
565
}
3217
3218
GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
3219
3.60k
          GString *s) {
3220
3.60k
  if (s->getLength() != unicodeLength) {
3221
1.95k
    return gFalse;
3222
1.95k
  }
3223
1.65k
  for (int i = 0; i < unicodeLength; ++i) {
3224
43
    if ((Unicode)(s->getChar(i) & 0xff) != u[i]) {
3225
33
      return gFalse;
3226
33
    }
3227
43
  }
3228
1.61k
  return gTrue;
3229
1.64k
}
3230
3231
GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
3232
944
          const char *s) {
3233
1.17k
  for (int i = 0; i < unicodeLength; ++i) {
3234
1.06k
    if (!s[i] || (Unicode)(s[i] & 0xff) != u[i]) {
3235
842
      return gFalse;
3236
842
    }
3237
1.06k
  }
3238
102
  return gTrue;
3239
944
}
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
}