1 //========================================================================
2 //
3 // XFAScanner.cc
4 //
5 // Copyright 2020 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include "GString.h"
16 #include "GHash.h"
17 #include "Object.h"
18 #include "Error.h"
19 #include "Zoox.h"
20 #include "XFAScanner.h"
21
22 //------------------------------------------------------------------------
23
24 // fields have two names:
25 //
26 // name:
27 // - nodes with bind=global set the index to 0 ("foo[0]") regardless
28 // of the number of nodes with the same name
29 // - nodes with bind=none are dropped from the name
30 // - <area> nodes are dropped from the name
31 // - used for field value lookup in <xfa:datasets>
32 //
33 // fullName:
34 // - all named nodes are treated the same, regardless of bind=global
35 // or bind=none
36 // - <area> nodes are included in the name, but don't reset the
37 // numbering (i.e., <area> nodes are "transparent" with respect to
38 // node numbering)
39 // - used for field value lookup in <form>
40 // - used for matching with AcroForm names
41 //
42 // Both names use indexes on all nodes, even if there's only one node
43 // with the name -- this isn't correct for XFA naming, but it matches
44 // the AcroForm behavior.
45
46 //------------------------------------------------------------------------
47
XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,XFAFieldLayoutVAlign vAlignA)48 XFAFieldLayoutInfo::XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,
49 XFAFieldLayoutVAlign vAlignA) {
50 hAlign = hAlignA;
51 vAlign = vAlignA;
52 }
53
54 //------------------------------------------------------------------------
55
XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA,GString * formatA)56 XFAFieldPictureInfo::XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA,
57 GString *formatA) {
58 subtype = subtypeA;
59 format = formatA;
60 }
61
~XFAFieldPictureInfo()62 XFAFieldPictureInfo::~XFAFieldPictureInfo() {
63 delete format;
64 }
65
66 //------------------------------------------------------------------------
67
XFAFieldBarcodeInfo(GString * barcodeTypeA,double wideNarrowRatioA,double moduleWidthA,double moduleHeightA,int dataLengthA,int errorCorrectionLevelA,GString * textLocationA)68 XFAFieldBarcodeInfo::XFAFieldBarcodeInfo(GString *barcodeTypeA,
69 double wideNarrowRatioA,
70 double moduleWidthA,
71 double moduleHeightA,
72 int dataLengthA,
73 int errorCorrectionLevelA,
74 GString *textLocationA) {
75 barcodeType = barcodeTypeA;
76 wideNarrowRatio = wideNarrowRatioA;
77 moduleWidth = moduleWidthA;
78 moduleHeight = moduleHeightA;
79 dataLength = dataLengthA;
80 errorCorrectionLevel = errorCorrectionLevelA;
81 textLocation = textLocationA;
82 }
83
~XFAFieldBarcodeInfo()84 XFAFieldBarcodeInfo::~XFAFieldBarcodeInfo() {
85 delete barcodeType;
86 delete textLocation;
87 }
88
89 //------------------------------------------------------------------------
90
XFAField(GString * nameA,GString * fullNameA,GString * valueA,XFAFieldLayoutInfo * layoutInfoA,XFAFieldPictureInfo * pictureInfoA,XFAFieldBarcodeInfo * barcodeInfoA)91 XFAField::XFAField(GString *nameA, GString *fullNameA, GString *valueA,
92 XFAFieldLayoutInfo *layoutInfoA,
93 XFAFieldPictureInfo *pictureInfoA,
94 XFAFieldBarcodeInfo *barcodeInfoA)
95 : name(nameA)
96 , fullName(fullNameA)
97 , value(valueA)
98 , layoutInfo(layoutInfoA)
99 , pictureInfo(pictureInfoA)
100 , barcodeInfo(barcodeInfoA)
101 {
102 }
103
~XFAField()104 XFAField::~XFAField() {
105 delete name;
106 delete fullName;
107 delete value;
108 delete layoutInfo;
109 delete pictureInfo;
110 delete barcodeInfo;
111 }
112
113 //------------------------------------------------------------------------
114
load(Object * xfaObj)115 XFAScanner *XFAScanner::load(Object *xfaObj) {
116 GString *xfaData = readXFAStreams(xfaObj);
117 if (!xfaData) {
118 return NULL;
119 }
120 ZxDoc *xml = ZxDoc::loadMem(xfaData->getCString(), xfaData->getLength());
121 delete xfaData;
122 if (!xml) {
123 error(errSyntaxError, -1, "Invalid XML in XFA form");
124 return NULL;
125 }
126
127 XFAScanner *scanner = new XFAScanner();
128
129 if (xml->getRoot()) {
130 GHash *formValues = scanner->scanFormValues(xml->getRoot());
131 ZxElement *dataElem = NULL;
132 ZxElement *datasets =
133 xml->getRoot()->findFirstChildElement("xfa:datasets");
134 if (datasets) {
135 dataElem = datasets->findFirstChildElement("xfa:data");
136 }
137 ZxElement *tmpl = xml->getRoot()->findFirstChildElement("template");
138 if (tmpl) {
139 scanner->scanNode(tmpl, NULL, NULL, NULL, NULL, NULL,
140 dataElem, formValues);
141 }
142 deleteGHash(formValues, GString);
143 }
144
145 delete xml;
146
147 return scanner;
148 }
149
XFAScanner()150 XFAScanner::XFAScanner() {
151 fields = new GHash();
152 }
153
~XFAScanner()154 XFAScanner::~XFAScanner() {
155 deleteGHash(fields, XFAField);
156 }
157
findField(GString * acroFormFieldName)158 XFAField *XFAScanner::findField(GString *acroFormFieldName) {
159 return (XFAField *)fields->lookup(acroFormFieldName);
160 }
161
readXFAStreams(Object * xfaObj)162 GString *XFAScanner::readXFAStreams(Object *xfaObj) {
163 GString *data = new GString();
164 char buf[4096];
165 int n;
166 if (xfaObj->isStream()) {
167 xfaObj->streamReset();
168 while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
169 data->append(buf, n);
170 }
171 } else if (xfaObj->isArray()) {
172 for (int i = 1; i < xfaObj->arrayGetLength(); i += 2) {
173 Object obj;
174 if (!xfaObj->arrayGet(i, &obj)->isStream()) {
175 error(errSyntaxError, -1, "XFA array element is wrong type");
176 obj.free();
177 delete data;
178 return NULL;
179 }
180 obj.streamReset();
181 while ((n = obj.getStream()->getBlock(buf, sizeof(buf))) > 0) {
182 data->append(buf, n);
183 }
184 obj.free();
185 }
186 } else {
187 error(errSyntaxError, -1, "XFA object is wrong type");
188 return NULL;
189 }
190 return data;
191 }
192
scanFormValues(ZxElement * xmlRoot)193 GHash *XFAScanner::scanFormValues(ZxElement *xmlRoot) {
194 GHash *formValues = new GHash(gTrue);
195 ZxElement *formElem = xmlRoot->findFirstChildElement("form");
196 if (formElem) {
197 scanFormNode(formElem, NULL, formValues);
198 }
199 return formValues;
200 }
201
scanFormNode(ZxElement * elem,GString * fullName,GHash * formValues)202 void XFAScanner::scanFormNode(ZxElement *elem, GString *fullName,
203 GHash *formValues) {
204 GHash *fullNameIdx = new GHash();
205 for (ZxNode *node = elem->getFirstChild();
206 node;
207 node = node->getNextChild()) {
208 if (node->isElement("value")) {
209 if (fullName) {
210 ZxNode *child1Node = ((ZxElement *)node)->getFirstChild();
211 if (child1Node && child1Node->isElement()) {
212 ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
213 if (child2Node && child2Node->isCharData()) {
214 formValues->add(fullName->copy(),
215 ((ZxCharData *)child2Node)->getData()->copy());
216 }
217 }
218 }
219 } else if (node->isElement()) {
220 ZxAttr *nameAttr = ((ZxElement *)node)->findAttr("name");
221 if (nameAttr && (node->isElement("subform") ||
222 node->isElement("field"))) {
223 GString *nodeName = nameAttr->getValue();
224 GString *childFullName;
225 if (fullName) {
226 childFullName = GString::format("{0:t}.{1:t}", fullName, nodeName);
227 } else {
228 childFullName = nodeName->copy();
229 }
230 int idx = fullNameIdx->lookupInt(nodeName);
231 childFullName->appendf("[{0:d}]", idx);
232 fullNameIdx->replace(nodeName, idx + 1);
233 scanFormNode((ZxElement *)node, childFullName, formValues);
234 delete childFullName;
235 } else if (node->isElement("subform")) {
236 scanFormNode((ZxElement *)node, fullName, formValues);
237 }
238 }
239 }
240 delete fullNameIdx;
241 }
242
scanNode(ZxElement * elem,GString * parentName,GString * parentFullName,GHash * nameIdx,GHash * fullNameIdx,GString * exclGroupName,ZxElement * dataElem,GHash * formValues)243 void XFAScanner::scanNode(ZxElement *elem,
244 GString *parentName, GString *parentFullName,
245 GHash *nameIdx, GHash *fullNameIdx,
246 GString *exclGroupName, ZxElement *dataElem,
247 GHash *formValues) {
248 GString *nodeName = getNodeName(elem);
249 GHash *childNameIdx;
250 if (!nameIdx || nodeName) {
251 childNameIdx = new GHash();
252 } else {
253 childNameIdx = nameIdx;
254 }
255 GString *nodeFullName = getNodeFullName(elem);
256 GHash *childFullNameIdx;
257 if (!fullNameIdx || (nodeFullName && !elem->isElement("area"))) {
258 childFullNameIdx = new GHash();
259 } else {
260 childFullNameIdx = fullNameIdx;
261 }
262
263 GString *childName;
264 if (nodeName) {
265 if (parentName) {
266 childName = GString::format("{0:t}.{1:t}", parentName, nodeName);
267 } else {
268 childName = nodeName->copy();
269 }
270 int idx = nameIdx->lookupInt(nodeName);
271 nameIdx->replace(nodeName, idx + 1);
272 if (nodeIsBindGlobal(elem)) {
273 childName->appendf("[0]");
274 } else {
275 childName->appendf("[{0:d}]", idx);
276 }
277 } else {
278 childName = parentName;
279 }
280 GString *childFullName;
281 if (nodeFullName) {
282 if (parentFullName) {
283 childFullName = GString::format("{0:t}.{1:t}",
284 parentFullName, nodeFullName);
285 } else {
286 childFullName = nodeFullName->copy();
287 }
288 int idx = fullNameIdx->lookupInt(nodeFullName);
289 fullNameIdx->replace(nodeFullName, idx + 1);
290 childFullName->appendf("[{0:d}]", idx);
291 } else {
292 childFullName = parentFullName;
293 }
294
295 if (elem->isElement("field")) {
296 scanField(elem, childName, childFullName, exclGroupName,
297 dataElem, formValues);
298 } else {
299 GString *childExclGroupName;
300 if (elem->isElement("exclGroup")) {
301 childExclGroupName = childName;
302 } else {
303 childExclGroupName = NULL;
304 }
305 for (ZxNode *child = elem->getFirstChild();
306 child;
307 child = child->getNextChild()) {
308 if (child->isElement()) {
309 scanNode((ZxElement *)child, childName, childFullName,
310 childNameIdx, childFullNameIdx, childExclGroupName,
311 dataElem, formValues);
312 }
313 }
314 }
315
316 if (childName != parentName) {
317 delete childName;
318 }
319 if (childFullName != parentFullName) {
320 delete childFullName;
321 }
322 if (childNameIdx != nameIdx) {
323 delete childNameIdx;
324 }
325 if (childFullNameIdx != fullNameIdx) {
326 delete childFullNameIdx;
327 }
328 }
329
scanField(ZxElement * elem,GString * name,GString * fullName,GString * exclGroupName,ZxElement * dataElem,GHash * formValues)330 void XFAScanner::scanField(ZxElement *elem, GString *name, GString *fullName,
331 GString *exclGroupName, ZxElement *dataElem,
332 GHash *formValues) {
333 GString *value = getFieldValue(elem, name, fullName, exclGroupName,
334 dataElem, formValues);
335 XFAFieldLayoutInfo *layoutInfo = getFieldLayoutInfo(elem);
336 XFAFieldPictureInfo *pictureInfo = getFieldPictureInfo(elem);
337 XFAFieldBarcodeInfo *barcodeInfo = getFieldBarcodeInfo(elem);
338 XFAField *field = new XFAField(name->copy(), fullName->copy(), value,
339 layoutInfo, pictureInfo, barcodeInfo);
340 fields->add(field->fullName, field);
341 }
342
getFieldValue(ZxElement * elem,GString * name,GString * fullName,GString * exclGroupName,ZxElement * dataElem,GHash * formValues)343 GString *XFAScanner::getFieldValue(ZxElement *elem, GString *name,
344 GString *fullName, GString *exclGroupName,
345 ZxElement *dataElem, GHash *formValues) {
346 GString *val = NULL;
347
348 //--- check the <xfa:datasets> packet
349 val = getDatasetsValue(name->getCString(), dataElem);
350 if (!val && exclGroupName) {
351 val = (GString *)getDatasetsValue(exclGroupName->getCString(), dataElem);
352 }
353
354 //--- check the <form> element
355 if (!val) {
356 val = (GString *)formValues->lookup(fullName);
357 }
358
359 //--- check the <value> element within the field
360 if (!val) {
361 ZxElement *valueElem = elem->findFirstChildElement("value");
362 if (valueElem) {
363 ZxNode *child1Node = valueElem->getFirstChild();
364 if (child1Node && child1Node->isElement()) {
365 ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
366 if (child2Node && child2Node->isCharData()) {
367 val = ((ZxCharData *)child2Node)->getData();
368 }
369 }
370 }
371 }
372
373 //--- get the checkbutton item value
374 GString *checkbuttonItem = NULL;
375 ZxElement *uiElem = elem->findFirstChildElement("ui");
376 if (uiElem) {
377 ZxNode *uiChild = uiElem->getFirstChild();
378 if (uiChild && uiChild->isElement("checkButton")) {
379 ZxElement *itemsElem = elem->findFirstChildElement("items");
380 if (itemsElem) {
381 ZxNode *node1 = itemsElem->getFirstChild();
382 if (node1 && node1->isElement()) {
383 ZxNode *node2 = ((ZxElement *)node1)->getFirstChild();
384 if (node2 && node2->isCharData()) {
385 checkbuttonItem = ((ZxCharData *)node2)->getData();
386 }
387 }
388 }
389 }
390 }
391 // convert XFA checkbutton value to AcroForm-style On/Off value
392 if (checkbuttonItem && val) {
393 if (val->cmp(checkbuttonItem)) {
394 val = new GString("Off");
395 } else {
396 val = new GString("On");
397 }
398 } else if (val) {
399 val = val->copy();
400 }
401
402 return val;
403 }
404
getDatasetsValue(char * partName,ZxElement * elem)405 GString *XFAScanner::getDatasetsValue(char *partName, ZxElement *elem) {
406 if (!elem) {
407 return NULL;
408 }
409
410 // partName = xxxx[nn].yyyy----
411 char *p = strchr(partName, '[');
412 if (!p) {
413 return NULL;
414 }
415 int partLen = (int)(p - partName);
416 int idx = atoi(p + 1);
417 p = strchr(p + 1, '.');
418 if (p) {
419 ++p;
420 }
421
422 int curIdx = 0;
423 for (ZxNode *node = elem->getFirstChild();
424 node;
425 node = node->getNextChild()) {
426 if (!node->isElement()) {
427 continue;
428 }
429 GString *nodeName = ((ZxElement *)node)->getType();
430 if (nodeName->getLength() != partLen ||
431 strncmp(nodeName->getCString(), partName, partLen)) {
432 continue;
433 }
434 if (curIdx != idx) {
435 ++curIdx;
436 continue;
437 }
438 if (p) {
439 GString *val = getDatasetsValue(p, (ZxElement *)node);
440 if (val) {
441 return val;
442 }
443 break;
444 } else {
445 ZxNode *child = ((ZxElement *)node)->getFirstChild();
446 if (!child || !child->isCharData()) {
447 return NULL;
448 }
449 return ((ZxCharData *)child)->getData();
450 }
451 }
452
453 // search for an 'ancestor match'
454 if (p) {
455 return getDatasetsValue(p, elem);
456 }
457
458 return NULL;
459 }
460
getFieldLayoutInfo(ZxElement * elem)461 XFAFieldLayoutInfo *XFAScanner::getFieldLayoutInfo(ZxElement *elem) {
462 ZxElement *paraElem = elem->findFirstChildElement("para");
463 if (!paraElem) {
464 return NULL;
465 }
466 XFAFieldLayoutHAlign hAlign = xfaFieldLayoutHAlignLeft;
467 ZxAttr *hAlignAttr = paraElem->findAttr("hAlign");
468 if (hAlignAttr) {
469 if (!hAlignAttr->getValue()->cmp("left")) {
470 hAlign = xfaFieldLayoutHAlignLeft;
471 } else if (!hAlignAttr->getValue()->cmp("center")) {
472 hAlign = xfaFieldLayoutHAlignCenter;
473 } else if (!hAlignAttr->getValue()->cmp("right")) {
474 hAlign = xfaFieldLayoutHAlignRight;
475 }
476 }
477 XFAFieldLayoutVAlign vAlign = xfaFieldLayoutVAlignTop;
478 ZxAttr *vAlignAttr = paraElem->findAttr("vAlign");
479 if (vAlignAttr) {
480 if (!vAlignAttr->getValue()->cmp("top")) {
481 vAlign = xfaFieldLayoutVAlignTop;
482 } else if (!vAlignAttr->getValue()->cmp("middle")) {
483 vAlign = xfaFieldLayoutVAlignMiddle;
484 } else if (!vAlignAttr->getValue()->cmp("bottom")) {
485 vAlign = xfaFieldLayoutVAlignBottom;
486 }
487 }
488 return new XFAFieldLayoutInfo(hAlign, vAlign);
489 }
490
getFieldPictureInfo(ZxElement * elem)491 XFAFieldPictureInfo *XFAScanner::getFieldPictureInfo(ZxElement *elem) {
492 ZxElement *uiElem = elem->findFirstChildElement("ui");
493 if (!uiElem) {
494 return NULL;
495 }
496 XFAFieldPictureSubtype subtype;
497 if (uiElem->findFirstChildElement("dateTimeEdit")) {
498 subtype = xfaFieldPictureDateTime;
499 } else if (uiElem->findFirstChildElement("numericEdit")) {
500 subtype = xfaFieldPictureNumeric;
501 } else if (uiElem->findFirstChildElement("textEdit")) {
502 subtype = xfaFieldPictureText;
503 } else {
504 return NULL;
505 }
506
507 ZxElement *formatElem, *pictureElem;
508 ZxNode *pictureChildNode;
509 if (!(formatElem = elem->findFirstChildElement("format")) ||
510 !(pictureElem = formatElem->findFirstChildElement("picture")) ||
511 !(pictureChildNode = pictureElem->getFirstChild()) ||
512 !pictureChildNode->isCharData()) {
513 return NULL;
514 }
515 GString *format = ((ZxCharData *)pictureChildNode)->getData()->copy();
516
517 return new XFAFieldPictureInfo(subtype, format);
518 }
519
getFieldBarcodeInfo(ZxElement * elem)520 XFAFieldBarcodeInfo *XFAScanner::getFieldBarcodeInfo(ZxElement *elem) {
521 ZxElement *uiElem, *barcodeElem;
522 if (!(uiElem = elem->findFirstChildElement("ui")) ||
523 !(barcodeElem = uiElem->findFirstChildElement("barcode"))) {
524 return NULL;
525 }
526
527 ZxAttr *attr;
528 if (!(attr = barcodeElem->findAttr("type"))) {
529 return NULL;
530 }
531 GString *barcodeType = attr->getValue()->copy();
532
533 double wideNarrowRatio = 3;
534 if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
535 char *s = attr->getValue()->getCString();
536 char *colon = strchr(s, ':');
537 if (colon) {
538 GString *numStr = new GString(s, (int)(colon - s));
539 double num = atof(numStr->getCString());
540 delete numStr;
541 double den = atof(colon + 1);
542 if (den == 0) {
543 wideNarrowRatio = num;
544 } else {
545 wideNarrowRatio = num / den;
546 }
547 } else {
548 wideNarrowRatio = atof(s);
549 }
550 }
551
552 double moduleWidth = (0.25 / 25.4) * 72.0; // 0.25mm
553 if ((attr = barcodeElem->findAttr("moduleWidth"))) {
554 moduleWidth = getMeasurement(attr->getValue());
555 }
556
557 double moduleHeight = (5.0 / 25.4) * 72.0; // 5mm
558 if ((attr = barcodeElem->findAttr("moduleHeight"))) {
559 moduleHeight = getMeasurement(attr->getValue());
560 }
561
562 int dataLength = 0;
563 if ((attr = barcodeElem->findAttr("dataLength"))) {
564 dataLength = atoi(attr->getValue()->getCString());
565 }
566
567 int errorCorrectionLevel = 0;
568 if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) {
569 errorCorrectionLevel = atoi(attr->getValue()->getCString());
570 }
571
572 GString *textLocation;
573 if ((attr = barcodeElem->findAttr("textLocation"))) {
574 textLocation = attr->getValue()->copy();
575 } else {
576 textLocation = new GString("below");
577 }
578
579 return new XFAFieldBarcodeInfo(barcodeType, wideNarrowRatio,
580 moduleWidth, moduleHeight, dataLength,
581 errorCorrectionLevel, textLocation);
582 }
583
getMeasurement(GString * s)584 double XFAScanner::getMeasurement(GString *s) {
585 int i = 0;
586 GBool neg = gFalse;
587 if (i < s->getLength() && s->getChar(i) == '+') {
588 ++i;
589 } else if (i < s->getLength() && s->getChar(i) == '-') {
590 neg = gTrue;
591 ++i;
592 }
593 double val = 0;
594 while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
595 val = val * 10 + s->getChar(i) - '0';
596 ++i;
597 }
598 if (i < s->getLength() && s->getChar(i) == '.') {
599 ++i;
600 double mul = 0.1;
601 while (i < s->getLength() &&
602 s->getChar(i) >= '0' && s->getChar(i) <= '9') {
603 val += mul * (s->getChar(i) - '0');
604 mul *= 0.1;
605 ++i;
606 }
607 }
608 if (neg) {
609 val = -val;
610 }
611 if (i+1 < s->getLength()) {
612 if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
613 val *= 72;
614 } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
615 // no change
616 } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
617 val *= 72 / 2.54;
618 } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
619 val *= 72 / 25.4;
620 } else {
621 // default to inches
622 val *= 72;
623 }
624 } else {
625 // default to inches
626 val *= 72;
627 }
628 return val;
629 }
630
getNodeName(ZxElement * elem)631 GString *XFAScanner::getNodeName(ZxElement *elem) {
632 if (elem->isElement("template") ||
633 elem->isElement("area") ||
634 elem->isElement("draw")) {
635 return NULL;
636 }
637 if (!elem->isElement("field") && nodeIsBindNone(elem)) {
638 return NULL;
639 }
640 ZxAttr *nameAttr = elem->findAttr("name");
641 if (!nameAttr) {
642 return NULL;
643 }
644 return nameAttr->getValue();
645 }
646
getNodeFullName(ZxElement * elem)647 GString *XFAScanner::getNodeFullName(ZxElement *elem) {
648 if (elem->isElement("template") ||
649 elem->isElement("draw")) {
650 return NULL;
651 }
652 ZxAttr *nameAttr = elem->findAttr("name");
653 if (!nameAttr) {
654 return NULL;
655 }
656 return nameAttr->getValue();
657 }
658
nodeIsBindGlobal(ZxElement * elem)659 GBool XFAScanner::nodeIsBindGlobal(ZxElement *elem) {
660 ZxElement *bindElem = elem->findFirstChildElement("bind");
661 if (!bindElem) {
662 return gFalse;
663 }
664 ZxAttr *attr = bindElem->findAttr("match");
665 return attr && !attr->getValue()->cmp("global");
666 }
667
nodeIsBindNone(ZxElement * elem)668 GBool XFAScanner::nodeIsBindNone(ZxElement *elem) {
669 ZxElement *bindElem = elem->findFirstChildElement("bind");
670 if (!bindElem) {
671 return gFalse;
672 }
673 ZxAttr *attr = bindElem->findAttr("match");
674 return attr && !attr->getValue()->cmp("none");
675 }
676