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