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