1 //========================================================================
2 //
3 // XFAForm.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 "GString.h"
17 #include "GList.h"
18 #include "GHash.h"
19 #include "Error.h"
20 #include "Object.h"
21 #include "PDFDoc.h"
22 #include "Gfx.h"
23 #include "GfxFont.h"
24 #include "Zoox.h"
25 #include "XFAForm.h"
26
27 #ifdef _WIN32
28 # define strcasecmp stricmp
29 # define strncasecmp strnicmp
30 #endif
31
32 //------------------------------------------------------------------------
33
34 // 5 bars + 5 spaces -- each can be wide (1) or narrow (0)
35 // (there are always exactly 3 wide elements;
36 // the last space is always narrow)
37 static Guchar code3Of9Data[128][10] = {
38 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00
39 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
40 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
41 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
42 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
43 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
44 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
45 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
46 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
47 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
48 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
49 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
50 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
51 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
52 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
53 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
54 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x10
55 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
56 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
57 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
58 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
59 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
60 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
61 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
62 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
63 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
64 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
65 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
66 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
67 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
68 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
69 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
70 { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20
71 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
72 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
73 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
74 { 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24
75 { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25
76 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
77 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
78 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
79 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
80 { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a
81 { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b
82 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
83 { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d
84 { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e
85 { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f
86 { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30
87 { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1'
88 { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2'
89 { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3'
90 { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4'
91 { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5'
92 { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6'
93 { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7'
94 { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8'
95 { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9'
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 },
101 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
102 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x40
103 { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41
104 { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B'
105 { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C'
106 { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D'
107 { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E'
108 { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F'
109 { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G'
110 { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H'
111 { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I'
112 { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J'
113 { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K'
114 { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L'
115 { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M'
116 { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N'
117 { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O'
118 { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50
119 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q'
120 { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R'
121 { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S'
122 { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T'
123 { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U'
124 { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V'
125 { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W'
126 { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X'
127 { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y'
128 { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z'
129 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
130 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
131 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
132 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
133 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
134 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x60
135 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
136 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
137 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
138 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
139 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
140 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
141 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
142 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
143 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
144 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
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 },
149 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
150 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x70
151 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
152 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
153 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
154 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
155 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
156 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
157 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
158 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
159 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
160 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
161 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
162 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
163 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
164 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
165 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
166 };
167
168 //------------------------------------------------------------------------
169 // XFAForm
170 //------------------------------------------------------------------------
171
load(PDFDoc * docA,Object * acroFormObj,Object * xfaObj)172 XFAForm *XFAForm::load(PDFDoc *docA, Object *acroFormObj, Object *xfaObj) {
173 XFAForm *xfaForm;
174 ZxDoc *xmlA;
175 ZxElement *tmpl;
176 Object catDict, resourceDictA, obj1;
177 GString *data;
178 GBool fullXFAA;
179 GString *name;
180 char buf[4096];
181 int n, i;
182
183 docA->getXRef()->getCatalog(&catDict);
184 catDict.dictLookup("NeedsRendering", &obj1);
185 fullXFAA = obj1.isBool() && obj1.getBool();
186 obj1.free();
187 catDict.free();
188
189 if (xfaObj->isStream()) {
190 data = new GString();
191 xfaObj->streamReset();
192 while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
193 data->append(buf, n);
194 }
195 } else if (xfaObj->isArray()) {
196 data = new GString();
197 for (i = 1; i < xfaObj->arrayGetLength(); i += 2) {
198 if (!xfaObj->arrayGet(i, &obj1)->isStream()) {
199 error(errSyntaxError, -1, "XFA array element is wrong type");
200 obj1.free();
201 delete data;
202 return NULL;
203 }
204 obj1.streamReset();
205 while ((n = obj1.getStream()->getBlock(buf, sizeof(buf))) > 0) {
206 data->append(buf, n);
207 }
208 obj1.free();
209 }
210 } else {
211 error(errSyntaxError, -1, "XFA object is wrong type");
212 return NULL;
213 }
214
215 xmlA = ZxDoc::loadMem(data->getCString(), data->getLength());
216 delete data;
217 if (!xmlA) {
218 error(errSyntaxError, -1, "Invalid XML in XFA form");
219 return NULL;
220 }
221
222 if (acroFormObj->isDict()) {
223 acroFormObj->dictLookup("DR", &resourceDictA);
224 }
225
226 xfaForm = new XFAForm(docA, xmlA, &resourceDictA, fullXFAA);
227
228 resourceDictA.free();
229
230 if (xfaForm->xml->getRoot()) {
231 if ((tmpl = xfaForm->xml->getRoot()->findFirstChildElement("template"))) {
232 name = new GString("form");
233 xfaForm->curPageNum = 1;
234 xfaForm->curXOffset = xfaForm->curYOffset = 0;
235 xfaForm->scanFields(tmpl, name, name);
236 delete name;
237 }
238 }
239
240 return xfaForm;
241 }
242
XFAForm(PDFDoc * docA,ZxDoc * xmlA,Object * resourceDictA,GBool fullXFAA)243 XFAForm::XFAForm(PDFDoc *docA, ZxDoc *xmlA, Object *resourceDictA,
244 GBool fullXFAA): Form(docA) {
245 xml = xmlA;
246 fields = new GList();
247 resourceDictA->copy(&resourceDict);
248 fullXFA = fullXFAA;
249 }
250
~XFAForm()251 XFAForm::~XFAForm() {
252 delete xml;
253 deleteGList(fields, XFAFormField);
254 resourceDict.free();
255 }
256
scanFields(ZxElement * elem,GString * name,GString * dataName)257 void XFAForm::scanFields(ZxElement *elem, GString *name, GString *dataName) {
258 ZxAttr *attr;
259 ZxNode *child;
260 ZxElement *bindElem;
261 GHash *names1, *names2;
262 GString *childName, *fullName, *fullDataName;
263 int i;
264
265 //~ need to handle subform
266
267 //~ need to handle exclGroup
268 //~ - fields in an exclGroup may/must(?) not have names
269 //~ - each field has an items element with the the value when that
270 //~ field is selected
271
272 if (elem->isElement("field")) {
273 fields->append(new XFAFormField(this, elem, name->copy(),
274 dataName->copy(), curPageNum,
275 curXOffset, curYOffset));
276 } else if (elem->isElement("breakBefore")) {
277 if ((attr = elem->findAttr("targetType")) &&
278 !attr->getValue()->cmp("pageArea") &&
279 (attr = elem->findAttr("startNew")) &&
280 !attr->getValue()->cmp("1")) {
281 ++curPageNum;
282 }
283 } else if (elem->isElement("break")) {
284 if ((attr = elem->findAttr("before")) &&
285 !attr->getValue()->cmp("pageArea") &&
286 (attr = elem->findAttr("startNew")) &&
287 !attr->getValue()->cmp("1")) {
288 ++curPageNum;
289 }
290 } else if (elem->isElement("contentArea")) {
291 curXOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0);
292 curYOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0);
293 } else {
294 names1 = new GHash();
295 for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
296 if (child->isElement() &&
297 (attr = ((ZxElement *)child)->findAttr("name"))) {
298 childName = attr->getValue();
299 names1->replace(childName, names1->lookupInt(childName) + 1);
300 }
301 }
302 names2 = new GHash();
303 for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
304 if (child->isElement()) {
305 if (!((bindElem = child->findFirstChildElement("bind")) &&
306 (attr = bindElem->findAttr("match")) &&
307 !attr->getValue()->cmp("none")) &&
308 (attr = ((ZxElement *)child)->findAttr("name"))) {
309 childName = attr->getValue();
310 if (names1->lookupInt(childName) > 1) {
311 i = names2->lookupInt(childName);
312 fullName = GString::format("{0:t}.{1:t}[{2:d}]",
313 name, childName, i);
314 fullDataName = GString::format("{0:t}.{1:t}[{2:d}]",
315 dataName, childName, i);
316 names2->replace(childName, i + 1);
317 } else {
318 fullName = GString::format("{0:t}.{1:t}", name, childName);
319 fullDataName = GString::format("{0:t}.{1:t}", dataName, childName);
320 }
321 } else {
322 fullName = name->copy();
323 fullDataName = dataName->copy();
324 }
325 scanFields((ZxElement *)child, fullName, fullDataName);
326 delete fullName;
327 delete fullDataName;
328 }
329 }
330 delete names1;
331 delete names2;
332 }
333 }
334
draw(int pageNum,Gfx * gfx,GBool printing)335 void XFAForm::draw(int pageNum, Gfx *gfx, GBool printing) {
336 GfxFontDict *fontDict;
337 Object obj1;
338 int i;
339
340 // build the font dictionary
341 if (resourceDict.isDict() &&
342 resourceDict.dictLookup("Font", &obj1)->isDict()) {
343 fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
344 } else {
345 fontDict = NULL;
346 }
347 obj1.free();
348
349 for (i = 0; i < fields->getLength(); ++i) {
350 ((XFAFormField *)fields->get(i))->draw(pageNum, gfx, printing, fontDict);
351 }
352
353 delete fontDict;
354 }
355
getNumFields()356 int XFAForm::getNumFields() {
357 return fields->getLength();
358 }
359
getField(int idx)360 FormField *XFAForm::getField(int idx) {
361 return (XFAFormField *)fields->get(idx);
362 }
363
364 //------------------------------------------------------------------------
365 // XFAFormField
366 //------------------------------------------------------------------------
367
XFAFormField(XFAForm * xfaFormA,ZxElement * xmlA,GString * nameA,GString * dataNameA,int pageNumA,double xOffsetA,double yOffsetA)368 XFAFormField::XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, GString *nameA,
369 GString *dataNameA, int pageNumA,
370 double xOffsetA, double yOffsetA) {
371 xfaForm = xfaFormA;
372 xml = xmlA;
373 name = nameA;
374 dataName = dataNameA;
375 pageNum = pageNumA;
376 xOffset = xOffsetA;
377 yOffset = yOffsetA;
378 }
379
~XFAFormField()380 XFAFormField::~XFAFormField() {
381 delete name;
382 delete dataName;
383 }
384
getType()385 const char *XFAFormField::getType() {
386 ZxElement *uiElem;
387 ZxNode *node;
388
389 if ((uiElem = xml->findFirstChildElement("ui"))) {
390 for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
391 if (node->isElement("textEdit")) {
392 return "Text";
393 } else if (node->isElement("barcode")) {
394 return "BarCode";
395 }
396 //~ other field types go here
397 }
398 }
399 return NULL;
400 }
401
getName(int * length)402 Unicode *XFAFormField::getName(int *length) {
403 //~ assumes name is UTF-8
404 return utf8ToUnicode(name, length);
405 }
406
getValue(int * length)407 Unicode *XFAFormField::getValue(int *length) {
408 ZxElement *uiElem;
409 ZxNode *node;
410 GString *s;
411
412 //~ assumes value is UTF-8
413 s = NULL;
414 if ((uiElem = xml->findFirstChildElement("ui"))) {
415 for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
416 if (node->isElement("textEdit")) {
417 s = getFieldValue("text");
418 } else if (node->isElement("barcode")) {
419 s = getFieldValue("text");
420 }
421 //~ other field types go here
422 }
423 }
424 if (!s) {
425 return NULL;
426 }
427 return utf8ToUnicode(s, length);
428 }
429
utf8ToUnicode(GString * s,int * length)430 Unicode *XFAFormField::utf8ToUnicode(GString *s, int *length) {
431 Unicode *u;
432 int n, size, c0, c1, c2, c3, c4, c5, i;
433
434 n = size = 0;
435 u = NULL;
436 i = 0;
437 while (i < s->getLength()) {
438 if (n == size) {
439 size = size ? size * 2 : 16;
440 u = (Unicode *)greallocn(u, size, sizeof(Unicode));
441 }
442 c0 = s->getChar(i++) & 0xff;
443 if (c0 <= 0x7f) {
444 u[n++] = c0;
445 } else if (c0 <= 0xdf && i < n) {
446 c1 = s->getChar(i++) & 0xff;
447 u[n++] = ((c0 & 0x1f) << 6) | (c1 & 0x3f);
448 } else if (c0 <= 0xef && i+1 < n) {
449 c1 = s->getChar(i++) & 0xff;
450 c2 = s->getChar(i++) & 0xff;
451 u[n++] = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f);
452 } else if (c0 <= 0xf7 && i+2 < n) {
453 c1 = s->getChar(i++) & 0xff;
454 c2 = s->getChar(i++) & 0xff;
455 c3 = s->getChar(i++) & 0xff;
456 u[n++] = ((c0 & 0x07) << 18) | ((c1 & 0x3f) << 12) | ((c2 & 0x3f) << 6)
457 | (c3 & 0x3f);
458 } else if (c0 <= 0xfb && i+3 < n) {
459 c1 = s->getChar(i++) & 0xff;
460 c2 = s->getChar(i++) & 0xff;
461 c3 = s->getChar(i++) & 0xff;
462 c4 = s->getChar(i++) & 0xff;
463 u[n++] = ((c0 & 0x03) << 24) | ((c1 & 0x3f) << 18) | ((c2 & 0x3f) << 12)
464 | ((c3 & 0x3f) << 6) | (c4 & 0x3f);
465 } else if (c0 <= 0xfd && i+4 < n) {
466 c1 = s->getChar(i++) & 0xff;
467 c2 = s->getChar(i++) & 0xff;
468 c3 = s->getChar(i++) & 0xff;
469 c4 = s->getChar(i++) & 0xff;
470 c5 = s->getChar(i++) & 0xff;
471 u[n++] = ((c0 & 0x01) << 30) | ((c1 & 0x3f) << 24) | ((c2 & 0x3f) << 18)
472 | ((c3 & 0x3f) << 12) | ((c4 & 0x3f) << 6) | (c5 & 0x3f);
473 } else {
474 u[n++] = '?';
475 }
476 }
477 *length = n;
478 return u;
479 }
480
draw(int pageNumA,Gfx * gfx,GBool printing,GfxFontDict * fontDict)481 void XFAFormField::draw(int pageNumA, Gfx *gfx, GBool printing,
482 GfxFontDict *fontDict) {
483 Page *page;
484 PDFRectangle *pageRect;
485 ZxElement *uiElem;
486 ZxNode *node;
487 ZxAttr *attr;
488 GString *appearBuf;
489 MemStream *appearStream;
490 Object appearDict, appearance, obj1, obj2;
491 double mat[6];
492 double x, y, w, h, x2, y2, w2, h2, x3, y3, w3, h3;
493 double anchorX, anchorY;
494 int pageRot, rot, rot3;
495
496 if (pageNumA != pageNum) {
497 return;
498 }
499
500 page = xfaForm->doc->getCatalog()->getPage(pageNum);
501 pageRect = page->getMediaBox();
502 pageRot = page->getRotate();
503
504 anchorX = 0;
505 anchorY = 0;
506 if ((attr = xml->findAttr("anchorType"))) {
507 if (!attr->getValue()->cmp("topLeft")) {
508 anchorX = 0;
509 anchorY = 0;
510 } else if (!attr->getValue()->cmp("topCenter")) {
511 anchorX = 0.5;
512 anchorY = 0;
513 } else if (!attr->getValue()->cmp("topRight")) {
514 anchorX = 1;
515 anchorY = 0;
516 } else if (!attr->getValue()->cmp("middleLeft")) {
517 anchorX = 0;
518 anchorY = 0.5;
519 } else if (!attr->getValue()->cmp("middleCenter")) {
520 anchorX = 0.5;
521 anchorY = 0.5;
522 } else if (!attr->getValue()->cmp("middleRight")) {
523 anchorX = 1;
524 anchorY = 0.5;
525 } else if (!attr->getValue()->cmp("bottomLeft")) {
526 anchorX = 0;
527 anchorY = 1;
528 } else if (!attr->getValue()->cmp("bottomCenter")) {
529 anchorX = 0.5;
530 anchorY = 1;
531 } else if (!attr->getValue()->cmp("bottomRight")) {
532 anchorX = 1;
533 anchorY = 1;
534 }
535 }
536 x = getMeasurement(xml->findAttr("x"), 0) + xOffset;
537 y = getMeasurement(xml->findAttr("y"), 0) + yOffset;
538 w = getMeasurement(xml->findAttr("w"), 0);
539 h = getMeasurement(xml->findAttr("h"), 0);
540 if ((attr = xml->findAttr("rotate"))) {
541 rot = atoi(attr->getValue()->getCString());
542 if ((rot %= 360) < 0) {
543 rot += 360;
544 }
545 } else {
546 rot = 0;
547 }
548
549 // get annot rect (UL corner, width, height) in XFA coords
550 // notes:
551 // - XFA coordinates are top-left origin, after page rotation
552 // - XFA coordinates are dependent on choice of anchor point
553 // and field rotation
554 switch (rot) {
555 case 0:
556 default:
557 x2 = x - anchorX * w;
558 y2 = y - anchorY * h;
559 w2 = w;
560 h2 = h;
561 break;
562 case 90:
563 x2 = x - anchorY * h;
564 y2 = y - (1 - anchorX) * w;
565 w2 = h;
566 h2 = w;
567 break;
568 case 180:
569 x2 = x - (1 - anchorX) * w;
570 y2 = y - (1 - anchorY) * h;
571 w2 = w;
572 h2 = h;
573 break;
574 case 270:
575 x2 = x - (1 - anchorY) * h;
576 y2 = y - anchorX * w;
577 w2 = h;
578 h2 = w;
579 break;
580 }
581
582 // convert annot rect to PDF coords (LL corner, width, height),
583 // taking page rotation into account
584 switch (pageRot) {
585 case 0:
586 default:
587 x3 = pageRect->x1 + x2;
588 y3 = pageRect->y2 - (y2 + h2);
589 w3 = w2;
590 h3 = h2;
591 break;
592 case 90:
593 x3 = pageRect->x1 + y2;
594 y3 = pageRect->y1 + x2;
595 w3 = h2;
596 h3 = w2;
597 break;
598 case 180:
599 x3 = pageRect->x2 - (x2 + w2);
600 y3 = pageRect->y1 + y2;
601 w3 = w2;
602 h3 = h2;
603 break;
604 case 270:
605 x3 = pageRect->x2 - (y2 + h2);
606 y3 = pageRect->y1 + (x2 + w2);
607 w3 = h2;
608 h3 = w2;
609 break;
610 }
611 rot3 = (rot + pageRot) % 360;
612
613 // generate transform matrix
614 switch (rot3) {
615 case 0:
616 default:
617 mat[0] = 1; mat[1] = 0;
618 mat[2] = 0; mat[3] = 1;
619 mat[4] = 0; mat[5] = 0;
620 break;
621 case 90:
622 mat[0] = 0; mat[1] = 1;
623 mat[2] = -1; mat[3] = 0;
624 mat[4] = h; mat[5] = 0;
625 break;
626 case 180:
627 mat[0] = -1; mat[1] = 0;
628 mat[2] = 0; mat[3] = -1;
629 mat[4] = w; mat[5] = h;
630 break;
631 case 270:
632 mat[0] = 0; mat[1] = -1;
633 mat[2] = 1; mat[3] = 0;
634 mat[4] = 0; mat[5] = w;
635 break;
636 }
637
638 // get the appearance stream data
639 appearBuf = new GString();
640 #if 0 //~ for debugging
641 appearBuf->appendf("q 1 1 0 rg 0 0 {0:.4f} {1:.4f} re f Q\n", w, h);
642 #endif
643 if ((uiElem = xml->findFirstChildElement("ui"))) {
644 for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
645 if (node->isElement("textEdit")) {
646 drawTextEdit(fontDict, w, h, rot3, appearBuf);
647 break;
648 } else if (node->isElement("barcode")) {
649 drawBarCode(fontDict, w, h, rot3, appearBuf);
650 break;
651 }
652 //~ other field types go here
653 }
654 }
655
656 // create the appearance stream
657 appearDict.initDict(xfaForm->doc->getXRef());
658 appearDict.dictAdd(copyString("Length"),
659 obj1.initInt(appearBuf->getLength()));
660 appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
661 obj1.initArray(xfaForm->doc->getXRef());
662 obj1.arrayAdd(obj2.initReal(0));
663 obj1.arrayAdd(obj2.initReal(0));
664 obj1.arrayAdd(obj2.initReal(w));
665 obj1.arrayAdd(obj2.initReal(h));
666 appearDict.dictAdd(copyString("BBox"), &obj1);
667 obj1.initArray(xfaForm->doc->getXRef());
668 obj1.arrayAdd(obj2.initReal(mat[0]));
669 obj1.arrayAdd(obj2.initReal(mat[1]));
670 obj1.arrayAdd(obj2.initReal(mat[2]));
671 obj1.arrayAdd(obj2.initReal(mat[3]));
672 obj1.arrayAdd(obj2.initReal(mat[4]));
673 obj1.arrayAdd(obj2.initReal(mat[5]));
674 appearDict.dictAdd(copyString("Matrix"), &obj1);
675 if (xfaForm->resourceDict.isDict()) {
676 appearDict.dictAdd(copyString("Resources"),
677 xfaForm->resourceDict.copy(&obj1));
678 }
679 appearStream = new MemStream(appearBuf->getCString(), 0,
680 appearBuf->getLength(), &appearDict);
681 appearance.initStream(appearStream);
682 gfx->drawAnnot(&appearance, NULL, x3, y3, x3 + w3, y3 + h3);
683 appearance.free();
684 delete appearBuf;
685 }
686
drawTextEdit(GfxFontDict * fontDict,double w,double h,int rot,GString * appearBuf)687 void XFAFormField::drawTextEdit(GfxFontDict *fontDict,
688 double w, double h, int rot,
689 GString *appearBuf) {
690 ZxElement *valueElem, *textElem, *uiElem, *textEditElem, *combElem;
691 ZxElement *fontElem, *paraElem;
692 ZxAttr *attr;
693 GString *value, *fontName;
694 double fontSize;
695 int maxChars, combCells;
696 GBool multiLine, bold, italic;
697 XFAHorizAlign hAlign;
698 XFAVertAlign vAlign;
699
700 if (!(value = getFieldValue("text"))) {
701 return;
702 }
703
704 maxChars = 0;
705 if ((valueElem = xml->findFirstChildElement("value")) &&
706 (textElem = valueElem->findFirstChildElement("text")) &&
707 (attr = textElem->findAttr("maxChars"))) {
708 maxChars = atoi(attr->getValue()->getCString());
709 }
710
711 multiLine = gFalse;
712 combCells = 0;
713 if ((uiElem = xml->findFirstChildElement("ui")) &&
714 (textEditElem = uiElem->findFirstChildElement("textEdit"))) {
715 if ((attr = textEditElem->findAttr("multiLine")) &&
716 !attr->getValue()->cmp("1")) {
717 multiLine = gTrue;
718 }
719 if ((combElem = textEditElem->findFirstChildElement("comb"))) {
720 if ((attr = combElem->findAttr("numberOfCells"))) {
721 combCells = atoi(attr->getValue()->getCString());
722 } else {
723 combCells = maxChars;
724 }
725 }
726 }
727
728 fontName = NULL;
729 fontSize = 10;
730 bold = gFalse;
731 italic = gFalse;
732 if ((fontElem = xml->findFirstChildElement("font"))) {
733 if ((attr = fontElem->findAttr("typeface"))) {
734 fontName = attr->getValue()->copy();
735 }
736 if ((attr = fontElem->findAttr("weight"))) {
737 if (!attr->getValue()->cmp("bold")) {
738 bold = gTrue;
739 }
740 }
741 if ((attr = fontElem->findAttr("posture"))) {
742 if (!attr->getValue()->cmp("italic")) {
743 italic = gTrue;
744 }
745 }
746 if ((attr = fontElem->findAttr("size"))) {
747 fontSize = getMeasurement(attr, fontSize);
748 }
749 }
750 if (!fontName) {
751 fontName = new GString("Courier");
752 }
753
754 hAlign = xfaHAlignLeft;
755 vAlign = xfaVAlignTop;
756 if ((paraElem = xml->findFirstChildElement("para"))) {
757 if ((attr = paraElem->findAttr("hAlign"))) {
758 if (!attr->getValue()->cmp("left")) {
759 hAlign = xfaHAlignLeft;
760 } else if (!attr->getValue()->cmp("center")) {
761 hAlign = xfaHAlignCenter;
762 } else if (!attr->getValue()->cmp("right")) {
763 hAlign = xfaHAlignRight;
764 }
765 //~ other hAlign values (justify, justifyAll, radix) are
766 //~ currently unsupported
767 }
768 if ((attr = paraElem->findAttr("vAlign"))) {
769 if (!attr->getValue()->cmp("top")) {
770 vAlign = xfaVAlignTop;
771 } else if (!attr->getValue()->cmp("bottom")) {
772 vAlign = xfaVAlignBottom;
773 } else if (!attr->getValue()->cmp("middle")) {
774 vAlign = xfaVAlignMiddle;
775 }
776 }
777 }
778
779 drawText(value, multiLine, combCells,
780 fontName, bold, italic, fontSize,
781 hAlign, vAlign, 0, 0, w, h, gFalse, fontDict, appearBuf);
782 delete fontName;
783 }
784
drawBarCode(GfxFontDict * fontDict,double w,double h,int rot,GString * appearBuf)785 void XFAFormField::drawBarCode(GfxFontDict *fontDict,
786 double w, double h, int rot,
787 GString *appearBuf) {
788 ZxElement *uiElem, *barcodeElem, *fontElem;
789 ZxAttr *attr;
790 GString *value, *value2, *barcodeType, *textLocation, *fontName, *s1, *s2;
791 XFAVertAlign textAlign;
792 double wideNarrowRatio, fontSize;
793 double yText, wText, yBarcode, hBarcode, wNarrow, xx;
794 GBool doText;
795 int dataLength;
796 GBool bold, italic;
797 char *p;
798 int i, j, c;
799
800 //--- get field value
801 if (!(value = getFieldValue("text"))) {
802 return;
803 }
804
805 //--- get field attributes
806 barcodeType = NULL;
807 wideNarrowRatio = 3;
808 dataLength = 0;
809 textLocation = NULL;
810 if ((uiElem = xml->findFirstChildElement("ui")) &&
811 (barcodeElem = uiElem->findFirstChildElement("barcode"))) {
812 if ((attr = barcodeElem->findAttr("type"))) {
813 barcodeType = attr->getValue();
814 }
815 if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
816 s1 = attr->getValue();
817 if ((p = strchr(s1->getCString(), ':'))) {
818 s2 = new GString(s1, 0, p - s1->getCString());
819 wideNarrowRatio = atof(p + 1);
820 if (wideNarrowRatio == 0) {
821 wideNarrowRatio = 1;
822 }
823 wideNarrowRatio = atof(s2->getCString()) / wideNarrowRatio;
824 delete s2;
825 } else {
826 wideNarrowRatio = atof(s1->getCString());
827 }
828 }
829 if ((attr = barcodeElem->findAttr("dataLength"))) {
830 dataLength = atoi(attr->getValue()->getCString());
831 }
832 if ((attr = barcodeElem->findAttr("textLocation"))) {
833 textLocation = attr->getValue();
834 }
835 }
836 if (!barcodeType) {
837 error(errSyntaxError, -1, "Missing 'type' attribute in XFA barcode field");
838 return;
839 }
840 if (!dataLength) {
841 error(errSyntaxError, -1,
842 "Missing 'dataLength' attribute in XFA barcode field");
843 return;
844 }
845
846 //--- get font
847 fontName = NULL;
848 fontSize = 0.2 * h;
849 bold = gFalse;
850 italic = gFalse;
851 if ((fontElem = xml->findFirstChildElement("font"))) {
852 if ((attr = fontElem->findAttr("typeface"))) {
853 fontName = attr->getValue()->copy();
854 }
855 if ((attr = fontElem->findAttr("weight"))) {
856 if (!attr->getValue()->cmp("bold")) {
857 bold = gTrue;
858 }
859 }
860 if ((attr = fontElem->findAttr("posture"))) {
861 if (!attr->getValue()->cmp("italic")) {
862 italic = gTrue;
863 }
864 }
865 if ((attr = fontElem->findAttr("size"))) {
866 fontSize = getMeasurement(attr, fontSize);
867 }
868 }
869 if (!fontName) {
870 fontName = new GString("Courier");
871 }
872
873 //--- compute the embedded text type position
874 doText = gTrue;
875 yText = yBarcode = hBarcode = 0;
876 if (textLocation && !textLocation->cmp("above")) {
877 textAlign = xfaVAlignTop;
878 yText = h;
879 yBarcode = 0;
880 hBarcode = h - fontSize;
881 } else if (textLocation && !textLocation->cmp("belowEmbedded")) {
882 textAlign = xfaVAlignBottom;
883 yText = 0;
884 yBarcode = 0;
885 hBarcode = h;
886 } else if (textLocation && !textLocation->cmp("aboveEmbedded")) {
887 textAlign = xfaVAlignTop;
888 yText = h;
889 yBarcode = 0;
890 hBarcode = h;
891 } else if (textLocation && !textLocation->cmp("none")) {
892 textAlign = xfaVAlignBottom; // make gcc happy
893 doText = gFalse;
894 } else { // default is "below"
895 textAlign = xfaVAlignBottom;
896 yText = 0;
897 yBarcode = fontSize;
898 hBarcode = h - fontSize;
899 }
900 wText = w;
901
902 //--- remove extraneous start/stop chars
903 //~ this may depend on barcode type
904 value2 = value->copy();
905 if (value2->getLength() >= 1 && value2->getChar(0) == '*') {
906 value2->del(0);
907 }
908 if (value2->getLength() >= 1 &&
909 value2->getChar(value2->getLength() - 1) == '*') {
910 value2->del(value2->getLength() - 1);
911 }
912
913 //--- draw the bar code
914 if (!barcodeType->cmp("code3Of9")) {
915 appearBuf->append("0 g\n");
916 wNarrow = w / ((7 + 3 * wideNarrowRatio) * (dataLength + 2));
917 xx = 0;
918 for (i = -1; i <= value2->getLength(); ++i) {
919 if (i < 0 || i >= value2->getLength()) {
920 c = '*';
921 } else {
922 c = value2->getChar(i) & 0x7f;
923 }
924 for (j = 0; j < 10; j += 2) {
925 appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
926 xx, yBarcode,
927 (code3Of9Data[c][j] ? wideNarrowRatio : 1) * wNarrow,
928 hBarcode);
929 xx += ((code3Of9Data[c][j] ? wideNarrowRatio : 1) +
930 (code3Of9Data[c][j+1] ? wideNarrowRatio : 1)) * wNarrow;
931 }
932 }
933 // center the text on the drawn barcode (not the max length barcode)
934 wText = (value2->getLength() + 2) * (7 + 3 * wideNarrowRatio) * wNarrow;
935 } else {
936 error(errSyntaxError, -1,
937 "Unimplemented barcode type in XFA barcode field");
938 }
939 //~ add other barcode types here
940
941 //--- draw the embedded text
942 if (doText) {
943 appearBuf->append("0 g\n");
944 drawText(value2, gFalse, 0,
945 fontName, bold, italic, fontSize,
946 xfaHAlignCenter, textAlign, 0, yText, wText, h, gTrue,
947 fontDict, appearBuf);
948 }
949 delete fontName;
950 delete value2;
951 }
952
getResources(Object * res)953 Object *XFAFormField::getResources(Object *res) {
954 return xfaForm->resourceDict.copy(res);
955 }
956
getMeasurement(ZxAttr * attr,double defaultVal)957 double XFAFormField::getMeasurement(ZxAttr *attr, double defaultVal) {
958 GString *s;
959 double val, mul;
960 GBool neg;
961 int i;
962
963 if (!attr) {
964 return defaultVal;
965 }
966 s = attr->getValue();
967 i = 0;
968 neg = gFalse;
969 if (i < s->getLength() && s->getChar(i) == '+') {
970 ++i;
971 } else if (i < s->getLength() && s->getChar(i) == '-') {
972 neg = gTrue;
973 ++i;
974 }
975 val = 0;
976 while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
977 val = val * 10 + s->getChar(i) - '0';
978 ++i;
979 }
980 if (i < s->getLength() && s->getChar(i) == '.') {
981 ++i;
982 mul = 0.1;
983 while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
984 val += mul * (s->getChar(i) - '0');
985 mul *= 0.1;
986 ++i;
987 }
988 }
989 if (neg) {
990 val = -val;
991 }
992 if (i+1 < s->getLength()) {
993 if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
994 val *= 72;
995 } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
996 // no change
997 } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
998 val *= 72 / 2.54;
999 } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
1000 val *= 72 / 25.4;
1001 } else {
1002 // default to inches
1003 val *= 72;
1004 }
1005 } else {
1006 // default to inches
1007 val *= 72;
1008 }
1009 return val;
1010 }
1011
getFieldValue(const char * valueChildType)1012 GString *XFAFormField::getFieldValue(const char *valueChildType) {
1013 ZxElement *valueElem, *datasets, *data, *elem;
1014 char *p;
1015
1016 // check the <value> element within the field
1017 if ((valueElem = xml->findFirstChildElement("value")) &&
1018 (elem = valueElem->findFirstChildElement(valueChildType))) {
1019 if (elem->getFirstChild() &&
1020 elem->getFirstChild()->isCharData() &&
1021 ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
1022 return ((ZxCharData *)elem->getFirstChild())->getData();
1023 }
1024 }
1025
1026 // check the <datasets> packet
1027 if (!xfaForm->xml->getRoot() ||
1028 !(datasets =
1029 xfaForm->xml->getRoot()->findFirstChildElement("xfa:datasets")) ||
1030 !(data = datasets->findFirstChildElement("xfa:data"))) {
1031 return NULL;
1032 }
1033 p = name->getCString();
1034 if (!strncmp(p, "form.", 5)) {
1035 p += 5;
1036 } else {
1037 return NULL;
1038 }
1039 elem = findFieldData(data, p);
1040 if (elem &&
1041 elem->getFirstChild() &&
1042 elem->getFirstChild()->isCharData() &&
1043 ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
1044 return ((ZxCharData *)elem->getFirstChild())->getData();
1045 }
1046
1047 return NULL;
1048 }
1049
findFieldData(ZxElement * elem,char * partName)1050 ZxElement *XFAFormField::findFieldData(ZxElement *elem, char *partName) {
1051 ZxNode *node;
1052 GString *nodeName;
1053 int curIdx, idx, n;
1054
1055 curIdx = 0;
1056 for (node = elem->getFirstChild(); node; node = node->getNextChild()) {
1057 if (node->isElement()) {
1058 nodeName = ((ZxElement *)node)->getType();
1059 n = nodeName->getLength();
1060 if (!strncmp(partName, nodeName->getCString(), n)) {
1061 if (partName[n] == '[') {
1062 idx = atoi(partName + n + 1);
1063 if (idx == curIdx) {
1064 for (++n; partName[n] && partName[n-1] != ']'; ++n) ;
1065 } else {
1066 ++curIdx;
1067 continue;
1068 }
1069 }
1070 if (!partName[n]) {
1071 return (ZxElement *)node;
1072 } else if (partName[n] == '.') {
1073 return findFieldData((ZxElement *)node, partName + n + 1);
1074 }
1075 }
1076 }
1077 }
1078 return NULL;
1079 }
1080
transform(int rot,double w,double h,double * wNew,double * hNew,GString * appearBuf)1081 void XFAFormField::transform(int rot, double w, double h,
1082 double *wNew, double *hNew, GString *appearBuf) {
1083 switch (rot) {
1084 case 0:
1085 default:
1086 appearBuf->appendf("1 0 0 1 0 {0:.4f} cm\n", -h);
1087 break;
1088 case 90:
1089 appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", w);
1090 *wNew = h;
1091 *hNew = w;
1092 break;
1093 case 180:
1094 appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", w, h);
1095 *wNew = w;
1096 *hNew = h;
1097 break;
1098 case 270:
1099 appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", h);
1100 *wNew = h;
1101 *hNew = w;
1102 break;
1103 }
1104 }
1105
drawText(GString * text,GBool multiLine,int combCells,GString * fontName,GBool bold,GBool italic,double fontSize,XFAHorizAlign hAlign,XFAVertAlign vAlign,double x,double y,double w,double h,GBool whiteBackground,GfxFontDict * fontDict,GString * appearBuf)1106 void XFAFormField::drawText(GString *text, GBool multiLine, int combCells,
1107 GString *fontName, GBool bold,
1108 GBool italic, double fontSize,
1109 XFAHorizAlign hAlign, XFAVertAlign vAlign,
1110 double x, double y, double w, double h,
1111 GBool whiteBackground,
1112 GfxFontDict *fontDict, GString *appearBuf) {
1113 GfxFont *font;
1114 GString *s;
1115 double xx, yy, tw, charWidth, lineHeight;
1116 double rectX, rectY, rectW, rectH;
1117 int line, i, j, k, c, rectI;
1118
1119 //~ deal with Unicode text (is it UTF-8?)
1120
1121 // find the font
1122 if (!(font = findFont(fontDict, fontName, bold, italic))) {
1123 error(errSyntaxError, -1, "Couldn't find a font for '{0:t}', {1:s}, {2:s} used in XFA field",
1124 fontName, bold ? "bold" : "non-bold",
1125 italic ? "italic" : "non-italic");
1126 return;
1127 }
1128
1129 // setup
1130 rectW = rectH = 0;
1131 rectI = appearBuf->getLength();
1132 appearBuf->append("BT\n");
1133 appearBuf->appendf("/{0:t} {1:.2f} Tf\n", font->getTag(), fontSize);
1134
1135 // multi-line text
1136 if (multiLine) {
1137
1138 // figure out how many lines will fit
1139 lineHeight = 1.2 * fontSize;
1140
1141 // write a series of lines of text
1142 line = 0;
1143 i = 0;
1144 while (i < text->getLength()) {
1145
1146 getNextLine(text, i, font, fontSize, w, &j, &tw, &k);
1147 if (tw > rectW) {
1148 rectW = tw;
1149 }
1150
1151 // compute text start position
1152 switch (hAlign) {
1153 case xfaHAlignLeft:
1154 default:
1155 xx = x;
1156 break;
1157 case xfaHAlignCenter:
1158 xx = x + 0.5 * (w - tw);
1159 break;
1160 case xfaHAlignRight:
1161 xx = x + w - tw;
1162 break;
1163 }
1164 yy = y + h - fontSize * font->getAscent() - line * lineHeight;
1165
1166 // draw the line
1167 appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", xx, yy);
1168 appearBuf->append('(');
1169 for (; i < j; ++i) {
1170 c = text->getChar(i) & 0xff;
1171 if (c == '(' || c == ')' || c == '\\') {
1172 appearBuf->append('\\');
1173 appearBuf->append(c);
1174 } else if (c < 0x20 || c >= 0x80) {
1175 appearBuf->appendf("\\{0:03o}", c);
1176 } else {
1177 appearBuf->append(c);
1178 }
1179 }
1180 appearBuf->append(") Tj\n");
1181
1182 // next line
1183 i = k;
1184 ++line;
1185 }
1186 rectH = line * lineHeight;
1187 rectY = y + h - rectH;
1188
1189 // comb formatting
1190 } else if (combCells > 0) {
1191
1192 // compute comb spacing
1193 tw = w / combCells;
1194
1195 // compute text start position
1196 switch (hAlign) {
1197 case xfaHAlignLeft:
1198 default:
1199 xx = x;
1200 break;
1201 case xfaHAlignCenter:
1202 xx = x + (int)(0.5 * (combCells - text->getLength())) * tw;
1203 break;
1204 case xfaHAlignRight:
1205 xx = x + w - text->getLength() * tw;
1206 break;
1207 }
1208 rectW = text->getLength() * tw;
1209 switch (vAlign) {
1210 case xfaVAlignTop:
1211 default:
1212 yy = y + h - fontSize * font->getAscent();
1213 break;
1214 case xfaVAlignMiddle:
1215 yy = y + 0.5 * (h - fontSize * (font->getAscent() +
1216 font->getDescent()));
1217 break;
1218 case xfaVAlignBottom:
1219 yy = y - fontSize * font->getDescent();
1220 break;
1221 }
1222 rectY = yy + fontSize * font->getDescent();
1223 rectH = fontSize * (font->getAscent() - font->getDescent());
1224
1225 // write the text string
1226 for (i = 0; i < text->getLength(); ++i) {
1227 c = text->getChar(i) & 0xff;
1228 if (!font->isCIDFont()) {
1229 charWidth = fontSize * ((Gfx8BitFont *)font)->getWidth(c);
1230 appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n",
1231 xx + i * tw + 0.5 * (tw - charWidth), yy);
1232 } else {
1233 appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n",
1234 xx + i * tw, yy);
1235 }
1236 appearBuf->append('(');
1237 if (c == '(' || c == ')' || c == '\\') {
1238 appearBuf->append('\\');
1239 appearBuf->append(c);
1240 } else if (c < 0x20 || c >= 0x80) {
1241 appearBuf->appendf("{0:.4f} 0 Td\n", w);
1242 } else {
1243 appearBuf->append(c);
1244 }
1245 appearBuf->append(") Tj\n");
1246 }
1247
1248 // regular (non-comb) formatting
1249 } else {
1250
1251 // compute string width
1252 if (!font->isCIDFont()) {
1253 tw = 0;
1254 for (i = 0; i < text->getLength(); ++i) {
1255 tw += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
1256 }
1257 } else {
1258 // otherwise, make a crude estimate
1259 tw = text->getLength() * 0.5;
1260 }
1261 tw *= fontSize;
1262 rectW = tw;
1263
1264 // compute text start position
1265 switch (hAlign) {
1266 case xfaHAlignLeft:
1267 default:
1268 xx = x;
1269 break;
1270 case xfaHAlignCenter:
1271 xx = x + 0.5 * (w - tw);
1272 break;
1273 case xfaHAlignRight:
1274 xx = x + w - tw;
1275 break;
1276 }
1277 switch (vAlign) {
1278 case xfaVAlignTop:
1279 default:
1280 yy = y + h - fontSize * font->getAscent();
1281 break;
1282 case xfaVAlignMiddle:
1283 yy = y + 0.5 * (h - fontSize * (font->getAscent() +
1284 font->getDescent()));
1285 break;
1286 case xfaVAlignBottom:
1287 yy = y - fontSize * font->getDescent();
1288 break;
1289 }
1290 rectY = yy + fontSize * font->getDescent();
1291 rectH = fontSize * (font->getAscent() - font->getDescent());
1292 appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx, yy);
1293
1294 // write the text string
1295 appearBuf->append('(');
1296 for (i = 0; i < text->getLength(); ++i) {
1297 c = text->getChar(i) & 0xff;
1298 if (c == '(' || c == ')' || c == '\\') {
1299 appearBuf->append('\\');
1300 appearBuf->append(c);
1301 } else if (c < 0x20 || c >= 0x80) {
1302 appearBuf->appendf("\\{0:03o}", c);
1303 } else {
1304 appearBuf->append(c);
1305 }
1306 }
1307 appearBuf->append(") Tj\n");
1308 }
1309
1310 // cleanup
1311 appearBuf->append("ET\n");
1312
1313 // draw a white rectangle behind the text
1314 if (whiteBackground) {
1315 switch (hAlign) {
1316 case xfaHAlignLeft:
1317 default:
1318 rectX = x;
1319 break;
1320 case xfaHAlignCenter:
1321 rectX = x + 0.5 * (w - rectW);
1322 break;
1323 case xfaHAlignRight:
1324 rectX = x + w - rectW;
1325 break;
1326 }
1327 rectX -= 0.25 * fontSize;
1328 rectW += 0.5 * fontSize;
1329 s = GString::format("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n",
1330 rectX, rectY, rectW, rectH);
1331 appearBuf->insert(rectI, s);
1332 delete s;
1333 }
1334 }
1335
1336 // Searches <fontDict> for a font matching(<fontName>, <bold>,
1337 // <italic>).
findFont(GfxFontDict * fontDict,GString * fontName,GBool bold,GBool italic)1338 GfxFont *XFAFormField::findFont(GfxFontDict *fontDict, GString *fontName,
1339 GBool bold, GBool italic) {
1340 GString *reqName, *testName;
1341 GfxFont *font;
1342 GBool foundName, foundBold, foundItalic;
1343 char *p;
1344 char c;
1345 int i, j;
1346
1347 if (!fontDict) {
1348 return NULL;
1349 }
1350
1351 reqName = new GString();
1352 for (i = 0; i < fontName->getLength(); ++i) {
1353 c = fontName->getChar(i);
1354 if (c != ' ') {
1355 reqName->append(c);
1356 }
1357 }
1358
1359 for (i = 0; i < fontDict->getNumFonts(); ++i) {
1360 font = fontDict->getFont(i);
1361 if (!font || !font->getName()) {
1362 continue;
1363 }
1364 testName = new GString();
1365 for (j = 0; j < font->getName()->getLength(); ++j) {
1366 c = font->getName()->getChar(j);
1367 if (c != ' ') {
1368 testName->append(c);
1369 }
1370 }
1371 foundName = foundBold = foundItalic = gFalse;
1372 for (p = testName->getCString(); *p; ++p) {
1373 if (!strncasecmp(p, reqName->getCString(), reqName->getLength())) {
1374 foundName = gTrue;
1375 }
1376 if (!strncasecmp(p, "bold", 4)) {
1377 foundBold = gTrue;
1378 }
1379 if (!strncasecmp(p, "italic", 6) || !strncasecmp(p, "oblique", 7)) {
1380 foundItalic = gTrue;
1381 }
1382 }
1383 delete testName;
1384 if (foundName && foundBold == bold && foundItalic == italic) {
1385 delete reqName;
1386 return font;
1387 }
1388 }
1389
1390 delete reqName;
1391 return NULL;
1392 }
1393
1394 // Figure out how much text will fit on the next line. Returns:
1395 // *end = one past the last character to be included
1396 // *width = width of the characters start .. end-1
1397 // *next = index of first character on the following line
getNextLine(GString * text,int start,GfxFont * font,double fontSize,double wMax,int * end,double * width,int * next)1398 void XFAFormField::getNextLine(GString *text, int start,
1399 GfxFont *font, double fontSize, double wMax,
1400 int *end, double *width, int *next) {
1401 double w, dw;
1402 int j, k, c;
1403
1404 // figure out how much text will fit on the line
1405 //~ what does Adobe do with tabs?
1406 w = 0;
1407 for (j = start; j < text->getLength() && w <= wMax; ++j) {
1408 c = text->getChar(j) & 0xff;
1409 if (c == 0x0a || c == 0x0d) {
1410 break;
1411 }
1412 if (font && !font->isCIDFont()) {
1413 dw = ((Gfx8BitFont *)font)->getWidth(c) * fontSize;
1414 } else {
1415 // otherwise, make a crude estimate
1416 dw = 0.5 * fontSize;
1417 }
1418 w += dw;
1419 }
1420 if (w > wMax) {
1421 for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
1422 for (; k > start && text->getChar(k-1) == ' '; --k) ;
1423 if (k > start) {
1424 j = k;
1425 }
1426 if (j == start) {
1427 // handle the pathological case where the first character is
1428 // too wide to fit on the line all by itself
1429 j = start + 1;
1430 }
1431 }
1432 *end = j;
1433
1434 // compute the width
1435 w = 0;
1436 for (k = start; k < j; ++k) {
1437 if (font && !font->isCIDFont()) {
1438 dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
1439 } else {
1440 // otherwise, make a crude estimate
1441 dw = 0.5 * fontSize;
1442 }
1443 w += dw;
1444 }
1445 *width = w;
1446
1447 // next line
1448 while (j < text->getLength() && text->getChar(j) == ' ') {
1449 ++j;
1450 }
1451 if (j < text->getLength() && text->getChar(j) == 0x0d) {
1452 ++j;
1453 }
1454 if (j < text->getLength() && text->getChar(j) == 0x0a) {
1455 ++j;
1456 }
1457 *next = j;
1458 }
1459