Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

676 lines
18 KiB
C++

//========================================================================
//
// XFAScanner.cc
//
// Copyright 2020 Glyph & Cog, LLC
//
//========================================================================
#include <aconf.h>
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include "GString.h"
#include "GHash.h"
#include "Object.h"
#include "Error.h"
#include "Zoox.h"
#include "XFAScanner.h"
//------------------------------------------------------------------------
// fields have two names:
//
// name:
// - nodes with bind=global set the index to 0 ("foo[0]") regardless
// of the number of nodes with the same name
// - nodes with bind=none are dropped from the name
// - <area> nodes are dropped from the name
// - used for field value lookup in <xfa:datasets>
//
// fullName:
// - all named nodes are treated the same, regardless of bind=global
// or bind=none
// - <area> nodes are included in the name, but don't reset the
// numbering (i.e., <area> nodes are "transparent" with respect to
// node numbering)
// - used for field value lookup in <form>
// - used for matching with AcroForm names
//
// Both names use indexes on all nodes, even if there's only one node
// with the name -- this isn't correct for XFA naming, but it matches
// the AcroForm behavior.
//------------------------------------------------------------------------
XFAFieldLayoutInfo::XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,
XFAFieldLayoutVAlign vAlignA) {
hAlign = hAlignA;
vAlign = vAlignA;
}
//------------------------------------------------------------------------
XFAFieldPictureInfo::XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA,
GString *formatA) {
subtype = subtypeA;
format = formatA;
}
XFAFieldPictureInfo::~XFAFieldPictureInfo() {
delete format;
}
//------------------------------------------------------------------------
XFAFieldBarcodeInfo::XFAFieldBarcodeInfo(GString *barcodeTypeA,
double wideNarrowRatioA,
double moduleWidthA,
double moduleHeightA,
int dataLengthA,
int errorCorrectionLevelA,
GString *textLocationA) {
barcodeType = barcodeTypeA;
wideNarrowRatio = wideNarrowRatioA;
moduleWidth = moduleWidthA;
moduleHeight = moduleHeightA;
dataLength = dataLengthA;
errorCorrectionLevel = errorCorrectionLevelA;
textLocation = textLocationA;
}
XFAFieldBarcodeInfo::~XFAFieldBarcodeInfo() {
delete barcodeType;
delete textLocation;
}
//------------------------------------------------------------------------
XFAField::XFAField(GString *nameA, GString *fullNameA, GString *valueA,
XFAFieldLayoutInfo *layoutInfoA,
XFAFieldPictureInfo *pictureInfoA,
XFAFieldBarcodeInfo *barcodeInfoA)
: name(nameA)
, fullName(fullNameA)
, value(valueA)
, layoutInfo(layoutInfoA)
, pictureInfo(pictureInfoA)
, barcodeInfo(barcodeInfoA)
{
}
XFAField::~XFAField() {
delete name;
delete fullName;
delete value;
delete layoutInfo;
delete pictureInfo;
delete barcodeInfo;
}
//------------------------------------------------------------------------
XFAScanner *XFAScanner::load(Object *xfaObj) {
GString *xfaData = readXFAStreams(xfaObj);
if (!xfaData) {
return NULL;
}
ZxDoc *xml = ZxDoc::loadMem(xfaData->getCString(), xfaData->getLength());
delete xfaData;
if (!xml) {
error(errSyntaxError, -1, "Invalid XML in XFA form");
return NULL;
}
XFAScanner *scanner = new XFAScanner();
if (xml->getRoot()) {
GHash *formValues = scanner->scanFormValues(xml->getRoot());
ZxElement *dataElem = NULL;
ZxElement *datasets =
xml->getRoot()->findFirstChildElement("xfa:datasets");
if (datasets) {
dataElem = datasets->findFirstChildElement("xfa:data");
}
ZxElement *tmpl = xml->getRoot()->findFirstChildElement("template");
if (tmpl) {
scanner->scanNode(tmpl, NULL, NULL, NULL, NULL, NULL,
dataElem, formValues);
}
deleteGHash(formValues, GString);
}
delete xml;
return scanner;
}
XFAScanner::XFAScanner() {
fields = new GHash();
}
XFAScanner::~XFAScanner() {
deleteGHash(fields, XFAField);
}
XFAField *XFAScanner::findField(GString *acroFormFieldName) {
return (XFAField *)fields->lookup(acroFormFieldName);
}
GString *XFAScanner::readXFAStreams(Object *xfaObj) {
GString *data = new GString();
char buf[4096];
int n;
if (xfaObj->isStream()) {
xfaObj->streamReset();
while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
data->append(buf, n);
}
} else if (xfaObj->isArray()) {
for (int i = 1; i < xfaObj->arrayGetLength(); i += 2) {
Object obj;
if (!xfaObj->arrayGet(i, &obj)->isStream()) {
error(errSyntaxError, -1, "XFA array element is wrong type");
obj.free();
delete data;
return NULL;
}
obj.streamReset();
while ((n = obj.getStream()->getBlock(buf, sizeof(buf))) > 0) {
data->append(buf, n);
}
obj.free();
}
} else {
error(errSyntaxError, -1, "XFA object is wrong type");
return NULL;
}
return data;
}
GHash *XFAScanner::scanFormValues(ZxElement *xmlRoot) {
GHash *formValues = new GHash(gTrue);
ZxElement *formElem = xmlRoot->findFirstChildElement("form");
if (formElem) {
scanFormNode(formElem, NULL, formValues);
}
return formValues;
}
void XFAScanner::scanFormNode(ZxElement *elem, GString *fullName,
GHash *formValues) {
GHash *fullNameIdx = new GHash();
for (ZxNode *node = elem->getFirstChild();
node;
node = node->getNextChild()) {
if (node->isElement("value")) {
if (fullName) {
ZxNode *child1Node = ((ZxElement *)node)->getFirstChild();
if (child1Node && child1Node->isElement()) {
ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
if (child2Node && child2Node->isCharData()) {
formValues->add(fullName->copy(),
((ZxCharData *)child2Node)->getData()->copy());
}
}
}
} else if (node->isElement()) {
ZxAttr *nameAttr = ((ZxElement *)node)->findAttr("name");
if (nameAttr && (node->isElement("subform") ||
node->isElement("field"))) {
GString *nodeName = nameAttr->getValue();
GString *childFullName;
if (fullName) {
childFullName = GString::format("{0:t}.{1:t}", fullName, nodeName);
} else {
childFullName = nodeName->copy();
}
int idx = fullNameIdx->lookupInt(nodeName);
childFullName->appendf("[{0:d}]", idx);
fullNameIdx->replace(nodeName, idx + 1);
scanFormNode((ZxElement *)node, childFullName, formValues);
delete childFullName;
} else if (node->isElement("subform")) {
scanFormNode((ZxElement *)node, fullName, formValues);
}
}
}
delete fullNameIdx;
}
void XFAScanner::scanNode(ZxElement *elem,
GString *parentName, GString *parentFullName,
GHash *nameIdx, GHash *fullNameIdx,
GString *exclGroupName, ZxElement *dataElem,
GHash *formValues) {
GString *nodeName = getNodeName(elem);
GHash *childNameIdx;
if (!nameIdx || nodeName) {
childNameIdx = new GHash();
} else {
childNameIdx = nameIdx;
}
GString *nodeFullName = getNodeFullName(elem);
GHash *childFullNameIdx;
if (!fullNameIdx || (nodeFullName && !elem->isElement("area"))) {
childFullNameIdx = new GHash();
} else {
childFullNameIdx = fullNameIdx;
}
GString *childName;
if (nodeName) {
if (parentName) {
childName = GString::format("{0:t}.{1:t}", parentName, nodeName);
} else {
childName = nodeName->copy();
}
int idx = nameIdx->lookupInt(nodeName);
nameIdx->replace(nodeName, idx + 1);
if (nodeIsBindGlobal(elem)) {
childName->appendf("[0]");
} else {
childName->appendf("[{0:d}]", idx);
}
} else {
childName = parentName;
}
GString *childFullName;
if (nodeFullName) {
if (parentFullName) {
childFullName = GString::format("{0:t}.{1:t}",
parentFullName, nodeFullName);
} else {
childFullName = nodeFullName->copy();
}
int idx = fullNameIdx->lookupInt(nodeFullName);
fullNameIdx->replace(nodeFullName, idx + 1);
childFullName->appendf("[{0:d}]", idx);
} else {
childFullName = parentFullName;
}
if (elem->isElement("field")) {
scanField(elem, childName, childFullName, exclGroupName,
dataElem, formValues);
} else {
GString *childExclGroupName;
if (elem->isElement("exclGroup")) {
childExclGroupName = childName;
} else {
childExclGroupName = NULL;
}
for (ZxNode *child = elem->getFirstChild();
child;
child = child->getNextChild()) {
if (child->isElement()) {
scanNode((ZxElement *)child, childName, childFullName,
childNameIdx, childFullNameIdx, childExclGroupName,
dataElem, formValues);
}
}
}
if (childName != parentName) {
delete childName;
}
if (childFullName != parentFullName) {
delete childFullName;
}
if (childNameIdx != nameIdx) {
delete childNameIdx;
}
if (childFullNameIdx != fullNameIdx) {
delete childFullNameIdx;
}
}
void XFAScanner::scanField(ZxElement *elem, GString *name, GString *fullName,
GString *exclGroupName, ZxElement *dataElem,
GHash *formValues) {
GString *value = getFieldValue(elem, name, fullName, exclGroupName,
dataElem, formValues);
XFAFieldLayoutInfo *layoutInfo = getFieldLayoutInfo(elem);
XFAFieldPictureInfo *pictureInfo = getFieldPictureInfo(elem);
XFAFieldBarcodeInfo *barcodeInfo = getFieldBarcodeInfo(elem);
XFAField *field = new XFAField(name->copy(), fullName->copy(), value,
layoutInfo, pictureInfo, barcodeInfo);
fields->add(field->fullName, field);
}
GString *XFAScanner::getFieldValue(ZxElement *elem, GString *name,
GString *fullName, GString *exclGroupName,
ZxElement *dataElem, GHash *formValues) {
GString *val = NULL;
//--- check the <xfa:datasets> packet
val = getDatasetsValue(name->getCString(), dataElem);
if (!val && exclGroupName) {
val = (GString *)getDatasetsValue(exclGroupName->getCString(), dataElem);
}
//--- check the <form> element
if (!val) {
val = (GString *)formValues->lookup(fullName);
}
//--- check the <value> element within the field
if (!val) {
ZxElement *valueElem = elem->findFirstChildElement("value");
if (valueElem) {
ZxNode *child1Node = valueElem->getFirstChild();
if (child1Node && child1Node->isElement()) {
ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
if (child2Node && child2Node->isCharData()) {
val = ((ZxCharData *)child2Node)->getData();
}
}
}
}
//--- get the checkbutton item value
GString *checkbuttonItem = NULL;
ZxElement *uiElem = elem->findFirstChildElement("ui");
if (uiElem) {
ZxNode *uiChild = uiElem->getFirstChild();
if (uiChild && uiChild->isElement("checkButton")) {
ZxElement *itemsElem = elem->findFirstChildElement("items");
if (itemsElem) {
ZxNode *node1 = itemsElem->getFirstChild();
if (node1 && node1->isElement()) {
ZxNode *node2 = ((ZxElement *)node1)->getFirstChild();
if (node2 && node2->isCharData()) {
checkbuttonItem = ((ZxCharData *)node2)->getData();
}
}
}
}
}
// convert XFA checkbutton value to AcroForm-style On/Off value
if (checkbuttonItem && val) {
if (val->cmp(checkbuttonItem)) {
val = new GString("Off");
} else {
val = new GString("On");
}
} else if (val) {
val = val->copy();
}
return val;
}
GString *XFAScanner::getDatasetsValue(char *partName, ZxElement *elem) {
if (!elem) {
return NULL;
}
// partName = xxxx[nn].yyyy----
char *p = strchr(partName, '[');
if (!p) {
return NULL;
}
int partLen = (int)(p - partName);
int idx = atoi(p + 1);
p = strchr(p + 1, '.');
if (p) {
++p;
}
int curIdx = 0;
for (ZxNode *node = elem->getFirstChild();
node;
node = node->getNextChild()) {
if (!node->isElement()) {
continue;
}
GString *nodeName = ((ZxElement *)node)->getType();
if (nodeName->getLength() != partLen ||
strncmp(nodeName->getCString(), partName, partLen)) {
continue;
}
if (curIdx != idx) {
++curIdx;
continue;
}
if (p) {
GString *val = getDatasetsValue(p, (ZxElement *)node);
if (val) {
return val;
}
break;
} else {
ZxNode *child = ((ZxElement *)node)->getFirstChild();
if (!child || !child->isCharData()) {
return NULL;
}
return ((ZxCharData *)child)->getData();
}
}
// search for an 'ancestor match'
if (p) {
return getDatasetsValue(p, elem);
}
return NULL;
}
XFAFieldLayoutInfo *XFAScanner::getFieldLayoutInfo(ZxElement *elem) {
ZxElement *paraElem = elem->findFirstChildElement("para");
if (!paraElem) {
return NULL;
}
XFAFieldLayoutHAlign hAlign = xfaFieldLayoutHAlignLeft;
ZxAttr *hAlignAttr = paraElem->findAttr("hAlign");
if (hAlignAttr) {
if (!hAlignAttr->getValue()->cmp("left")) {
hAlign = xfaFieldLayoutHAlignLeft;
} else if (!hAlignAttr->getValue()->cmp("center")) {
hAlign = xfaFieldLayoutHAlignCenter;
} else if (!hAlignAttr->getValue()->cmp("right")) {
hAlign = xfaFieldLayoutHAlignRight;
}
}
XFAFieldLayoutVAlign vAlign = xfaFieldLayoutVAlignTop;
ZxAttr *vAlignAttr = paraElem->findAttr("vAlign");
if (vAlignAttr) {
if (!vAlignAttr->getValue()->cmp("top")) {
vAlign = xfaFieldLayoutVAlignTop;
} else if (!vAlignAttr->getValue()->cmp("middle")) {
vAlign = xfaFieldLayoutVAlignMiddle;
} else if (!vAlignAttr->getValue()->cmp("bottom")) {
vAlign = xfaFieldLayoutVAlignBottom;
}
}
return new XFAFieldLayoutInfo(hAlign, vAlign);
}
XFAFieldPictureInfo *XFAScanner::getFieldPictureInfo(ZxElement *elem) {
ZxElement *uiElem = elem->findFirstChildElement("ui");
if (!uiElem) {
return NULL;
}
XFAFieldPictureSubtype subtype;
if (uiElem->findFirstChildElement("dateTimeEdit")) {
subtype = xfaFieldPictureDateTime;
} else if (uiElem->findFirstChildElement("numericEdit")) {
subtype = xfaFieldPictureNumeric;
} else if (uiElem->findFirstChildElement("textEdit")) {
subtype = xfaFieldPictureText;
} else {
return NULL;
}
ZxElement *formatElem, *pictureElem;
ZxNode *pictureChildNode;
if (!(formatElem = elem->findFirstChildElement("format")) ||
!(pictureElem = formatElem->findFirstChildElement("picture")) ||
!(pictureChildNode = pictureElem->getFirstChild()) ||
!pictureChildNode->isCharData()) {
return NULL;
}
GString *format = ((ZxCharData *)pictureChildNode)->getData()->copy();
return new XFAFieldPictureInfo(subtype, format);
}
XFAFieldBarcodeInfo *XFAScanner::getFieldBarcodeInfo(ZxElement *elem) {
ZxElement *uiElem, *barcodeElem;
if (!(uiElem = elem->findFirstChildElement("ui")) ||
!(barcodeElem = uiElem->findFirstChildElement("barcode"))) {
return NULL;
}
ZxAttr *attr;
if (!(attr = barcodeElem->findAttr("type"))) {
return NULL;
}
GString *barcodeType = attr->getValue()->copy();
double wideNarrowRatio = 3;
if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
char *s = attr->getValue()->getCString();
char *colon = strchr(s, ':');
if (colon) {
GString *numStr = new GString(s, (int)(colon - s));
double num = atof(numStr->getCString());
delete numStr;
double den = atof(colon + 1);
if (den == 0) {
wideNarrowRatio = num;
} else {
wideNarrowRatio = num / den;
}
} else {
wideNarrowRatio = atof(s);
}
}
double moduleWidth = (0.25 / 25.4) * 72.0; // 0.25mm
if ((attr = barcodeElem->findAttr("moduleWidth"))) {
moduleWidth = getMeasurement(attr->getValue());
}
double moduleHeight = (5.0 / 25.4) * 72.0; // 5mm
if ((attr = barcodeElem->findAttr("moduleHeight"))) {
moduleHeight = getMeasurement(attr->getValue());
}
int dataLength = 0;
if ((attr = barcodeElem->findAttr("dataLength"))) {
dataLength = atoi(attr->getValue()->getCString());
}
int errorCorrectionLevel = 0;
if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) {
errorCorrectionLevel = atoi(attr->getValue()->getCString());
}
GString *textLocation;
if ((attr = barcodeElem->findAttr("textLocation"))) {
textLocation = attr->getValue()->copy();
} else {
textLocation = new GString("below");
}
return new XFAFieldBarcodeInfo(barcodeType, wideNarrowRatio,
moduleWidth, moduleHeight, dataLength,
errorCorrectionLevel, textLocation);
}
double XFAScanner::getMeasurement(GString *s) {
int i = 0;
GBool neg = gFalse;
if (i < s->getLength() && s->getChar(i) == '+') {
++i;
} else if (i < s->getLength() && s->getChar(i) == '-') {
neg = gTrue;
++i;
}
double val = 0;
while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
val = val * 10 + s->getChar(i) - '0';
++i;
}
if (i < s->getLength() && s->getChar(i) == '.') {
++i;
double mul = 0.1;
while (i < s->getLength() &&
s->getChar(i) >= '0' && s->getChar(i) <= '9') {
val += mul * (s->getChar(i) - '0');
mul *= 0.1;
++i;
}
}
if (neg) {
val = -val;
}
if (i+1 < s->getLength()) {
if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
val *= 72;
} else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
// no change
} else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
val *= 72 / 2.54;
} else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
val *= 72 / 25.4;
} else {
// default to inches
val *= 72;
}
} else {
// default to inches
val *= 72;
}
return val;
}
GString *XFAScanner::getNodeName(ZxElement *elem) {
if (elem->isElement("template") ||
elem->isElement("area") ||
elem->isElement("draw")) {
return NULL;
}
if (!elem->isElement("field") && nodeIsBindNone(elem)) {
return NULL;
}
ZxAttr *nameAttr = elem->findAttr("name");
if (!nameAttr) {
return NULL;
}
return nameAttr->getValue();
}
GString *XFAScanner::getNodeFullName(ZxElement *elem) {
if (elem->isElement("template") ||
elem->isElement("draw")) {
return NULL;
}
ZxAttr *nameAttr = elem->findAttr("name");
if (!nameAttr) {
return NULL;
}
return nameAttr->getValue();
}
GBool XFAScanner::nodeIsBindGlobal(ZxElement *elem) {
ZxElement *bindElem = elem->findFirstChildElement("bind");
if (!bindElem) {
return gFalse;
}
ZxAttr *attr = bindElem->findAttr("match");
return attr && !attr->getValue()->cmp("global");
}
GBool XFAScanner::nodeIsBindNone(ZxElement *elem) {
ZxElement *bindElem = elem->findFirstChildElement("bind");
if (!bindElem) {
return gFalse;
}
ZxAttr *attr = bindElem->findAttr("match");
return attr && !attr->getValue()->cmp("none");
}