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