From 8d0d14f74fc154a161b2da9240eaf638624d7785 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Tue, 2 Feb 2021 14:05:10 +0100 Subject: [PATCH] XFA -- Add other objects - connectionSet: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.364.2157&rep=rep1&type=pdf#page=969 - datasets: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.364.2157&rep=rep1&type=pdf#page=1038 - signature: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.364.2157&rep=rep1&type=pdf#page=1040 - stylesheet: the same - xhtml: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.364.2157&rep=rep1&type=pdf#page=1187 --- src/core/xfa/connection_set.js | 202 +++++++++++++++++++++++++++++ src/core/xfa/datasets.js | 69 ++++++++++ src/core/xfa/parser.js | 2 +- src/core/xfa/setup.js | 10 ++ src/core/xfa/signature.js | 40 ++++++ src/core/xfa/stylesheet.js | 40 ++++++ src/core/xfa/xfa_object.js | 2 + src/core/xfa/xhtml.js | 225 +++++++++++++++++++++++++++++++++ test/unit/xfa_parser_spec.js | 35 ++++- 9 files changed, 623 insertions(+), 2 deletions(-) create mode 100644 src/core/xfa/connection_set.js create mode 100644 src/core/xfa/datasets.js create mode 100644 src/core/xfa/signature.js create mode 100644 src/core/xfa/stylesheet.js create mode 100644 src/core/xfa/xhtml.js diff --git a/src/core/xfa/connection_set.js b/src/core/xfa/connection_set.js new file mode 100644 index 0000000000000..7e14c5f61f323 --- /dev/null +++ b/src/core/xfa/connection_set.js @@ -0,0 +1,202 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { $buildXFAObject, NamespaceIds } from "./namespaces.js"; +import { StringObject, XFAObject, XFAObjectArray } from "./xfa_object.js"; + +const CONNECTION_SET_NS_ID = NamespaceIds.connectionSet.id; + +class ConnectionSet extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "connectionSet", /* hasChildren = */ true); + this.wsdlConnection = new XFAObjectArray(); + this.xmlConnection = new XFAObjectArray(); + this.xsdConnection = new XFAObjectArray(); + } +} + +class EffectiveInputPolicy extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "effectiveInputPolicy"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class EffectiveOutputPolicy extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "effectiveOutputPolicy"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class Operation extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "operation"); + this.id = attributes.id || ""; + this.input = attributes.input || ""; + this.name = attributes.name || ""; + this.output = attributes.output || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class RootElement extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "rootElement"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class SoapAction extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "soapAction"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class SoapAddress extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "soapAddress"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class Uri extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "uri"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class WsdlAddress extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "wsdlAddress"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} + +class WsdlConnection extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "wsdlConnection", /* hasChildren = */ true); + this.dataDescription = attributes.dataDescription || ""; + this.name = attributes.name || ""; + this.effectiveInputPolicy = null; + this.effectiveOutputPolicy = null; + this.operation = null; + this.soapAction = null; + this.soapAddress = null; + this.wsdlAddress = null; + } +} + +class XmlConnection extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "xmlConnection", /* hasChildren = */ true); + this.dataDescription = attributes.dataDescription || ""; + this.name = attributes.name || ""; + this.uri = null; + } +} + +class XsdConnection extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "xsdConnection", /* hasChildren = */ true); + this.dataDescription = attributes.dataDescription || ""; + this.name = attributes.name || ""; + this.rootElement = null; + this.uri = null; + } +} + +class ConnectionSetNamespace { + static [$buildXFAObject](name, attributes) { + if (ConnectionSetNamespace.hasOwnProperty(name)) { + return ConnectionSetNamespace[name](attributes); + } + return undefined; + } + + static connectionSet(attrs) { + return new ConnectionSet(attrs); + } + + static effectiveInputPolicy(attrs) { + return new EffectiveInputPolicy(attrs); + } + + static effectiveOutputPolicy(attrs) { + return new EffectiveOutputPolicy(attrs); + } + + static operation(attrs) { + return new Operation(attrs); + } + + static rootElement(attrs) { + return new RootElement(attrs); + } + + static soapAction(attrs) { + return new SoapAction(attrs); + } + + static soapAddress(attrs) { + return new SoapAddress(attrs); + } + + static uri(attrs) { + return new Uri(attrs); + } + + static wsdlAddress(attrs) { + return new WsdlAddress(attrs); + } + + static wsdlConnection(attrs) { + return new WsdlConnection(attrs); + } + + static xmlConnection(attrs) { + return new XmlConnection(attrs); + } + + static xsdConnection(attrs) { + return new XsdConnection(attrs); + } +} + +export { ConnectionSetNamespace }; diff --git a/src/core/xfa/datasets.js b/src/core/xfa/datasets.js new file mode 100644 index 0000000000000..b7ad096991049 --- /dev/null +++ b/src/core/xfa/datasets.js @@ -0,0 +1,69 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { $buildXFAObject, NamespaceIds } from "./namespaces.js"; +import { + $namespaceId, + $nodeName, + $onChildCheck, + XFAObject, + XmlObject, +} from "./xfa_object.js"; + +const DATASETS_NS_ID = NamespaceIds.datasets.id; + +class Data extends XmlObject { + constructor(attributes) { + super(DATASETS_NS_ID, "data", attributes); + } +} + +class Datasets extends XFAObject { + constructor(attributes) { + super(DATASETS_NS_ID, "datasets", /* hasChildren = */ true); + this.data = null; + this.Signature = null; + } + + [$onChildCheck](child) { + const name = child[$nodeName]; + if (name === "data") { + return child[$namespaceId] === DATASETS_NS_ID; + } + if (name === "Signature") { + return child[$namespaceId] === NamespaceIds.signature.id; + } + return false; + } +} + +class DatasetsNamespace { + static [$buildXFAObject](name, attributes) { + if (DatasetsNamespace.hasOwnProperty(name)) { + return DatasetsNamespace[name](attributes); + } + return undefined; + } + + static datasets(attributes) { + return new Datasets(attributes); + } + + static data(attributes) { + return new Data(attributes); + } +} + +export { DatasetsNamespace }; diff --git a/src/core/xfa/parser.js b/src/core/xfa/parser.js index 39fd6ec39796f..c0dddc0031dfd 100644 --- a/src/core/xfa/parser.js +++ b/src/core/xfa/parser.js @@ -42,7 +42,7 @@ class XFAParser extends XMLParserBase { if (this._whiteRegex.test(text)) { return; } - this._current[$onText](text); + this._current[$onText](text.trim()); } onCdata(text) { diff --git a/src/core/xfa/setup.js b/src/core/xfa/setup.js index b2ee3691a39b3..0fab11b284915 100644 --- a/src/core/xfa/setup.js +++ b/src/core/xfa/setup.js @@ -14,15 +14,25 @@ */ import { ConfigNamespace } from "./config.js"; +import { ConnectionSetNamespace } from "./connection_set.js"; +import { DatasetsNamespace } from "./datasets.js"; import { LocaleSetNamespace } from "./locale_set.js"; +import { SignatureNamespace } from "./signature.js"; +import { StylesheetNamespace } from "./stylesheet.js"; import { TemplateNamespace } from "./template.js"; import { XdpNamespace } from "./xdp.js"; +import { XhtmlNamespace } from "./xhtml.js"; const NamespaceSetUp = { config: ConfigNamespace, + connection: ConnectionSetNamespace, + datasets: DatasetsNamespace, localeSet: LocaleSetNamespace, + signature: SignatureNamespace, + stylesheet: StylesheetNamespace, template: TemplateNamespace, xdp: XdpNamespace, + xhtml: XhtmlNamespace, }; export { NamespaceSetUp }; diff --git a/src/core/xfa/signature.js b/src/core/xfa/signature.js new file mode 100644 index 0000000000000..c13b788879702 --- /dev/null +++ b/src/core/xfa/signature.js @@ -0,0 +1,40 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { $buildXFAObject, NamespaceIds } from "./namespaces.js"; +import { XFAObject } from "./xfa_object.js"; + +const SIGNATURE_NS_ID = NamespaceIds.signature.id; + +class Signature extends XFAObject { + constructor(attributes) { + super(SIGNATURE_NS_ID, "signature", /* hasChildren = */ true); + } +} + +class SignatureNamespace { + static [$buildXFAObject](name, attributes) { + if (SignatureNamespace.hasOwnProperty(name)) { + return SignatureNamespace[name](attributes); + } + return undefined; + } + + static signature(attributes) { + return new Signature(attributes); + } +} + +export { SignatureNamespace }; diff --git a/src/core/xfa/stylesheet.js b/src/core/xfa/stylesheet.js new file mode 100644 index 0000000000000..175b58f4d5410 --- /dev/null +++ b/src/core/xfa/stylesheet.js @@ -0,0 +1,40 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { $buildXFAObject, NamespaceIds } from "./namespaces.js"; +import { XFAObject } from "./xfa_object.js"; + +const STYLESHEET_NS_ID = NamespaceIds.stylesheet.id; + +class Stylesheet extends XFAObject { + constructor(attributes) { + super(STYLESHEET_NS_ID, "stylesheet", /* hasChildren = */ true); + } +} + +class StylesheetNamespace { + static [$buildXFAObject](name, attributes) { + if (StylesheetNamespace.hasOwnProperty(name)) { + return StylesheetNamespace[name](attributes); + } + return undefined; + } + + static stylesheet(attributes) { + return new Stylesheet(attributes); + } +} + +export { StylesheetNamespace }; diff --git a/src/core/xfa/xfa_object.js b/src/core/xfa/xfa_object.js index 71eb255bdf712..0a3bf49eccdd5 100644 --- a/src/core/xfa/xfa_object.js +++ b/src/core/xfa/xfa_object.js @@ -301,12 +301,14 @@ export { $content, $dump, $finalize, + $getChildren, $isTransparent, $namespaceId, $nodeName, $onChild, $onChildCheck, $onText, + $text, ContentObject, IntegerObject, Option01, diff --git a/src/core/xfa/xhtml.js b/src/core/xfa/xhtml.js new file mode 100644 index 0000000000000..9615448778c50 --- /dev/null +++ b/src/core/xfa/xhtml.js @@ -0,0 +1,225 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { $buildXFAObject, NamespaceIds } from "./namespaces.js"; +import { $text, XmlObject } from "./xfa_object.js"; + +const XHTML_NS_ID = NamespaceIds.xhtml.id; + +const VALID_STYLES = new Set([ + "color", + "font", + "font-family", + "font-size", + "font-stretch", + "font-style", + "font-weight", + "margin", + "margin-bottom", + "margin-left", + "margin-right", + "margin-top", + "letter-spacing", + "line-height", + "orphans", + "page-break-after", + "page-break-before", + "page-break-inside", + "tab-interval", + "tab-stop", + "text-decoration", + "text-indent", + "vertical-align", + "widows", + "kerning-mode", + "xfa-font-horizontal-scale", + "xfa-font-vertical-scale", + "xfa-tab-stops", +]); + +function checkStyle(style) { + if (!style) { + return ""; + } + + // Remove any non-allowed keys. + return style + .trim() + .split(/\s*;\s*/) + .filter(s => !!s) + .map(s => s.split(/\s*:\s*/, 2)) + .filter(([key]) => VALID_STYLES.has(key)) + .map(kv => kv.join(":")) + .join(";"); +} + +class A extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "a"); + this.href = attributes.href || ""; + this.style = checkStyle(attributes.style); + } +} + +class B extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "b"); + this.style = checkStyle(attributes.style); + } +} + +class Body extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "body"); + this.style = checkStyle(attributes.style); + } +} + +class Br extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "br"); + this.style = checkStyle(attributes.style); + } + + [$text]() { + return "\n"; + } +} + +class Html extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "html"); + this.style = checkStyle(attributes.style); + } +} + +class I extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "i"); + this.style = checkStyle(attributes.style); + } +} + +class Li extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "li"); + this.style = checkStyle(attributes.style); + } +} + +class Ol extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "ol"); + this.style = checkStyle(attributes.style); + } +} + +class P extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "p"); + this.style = checkStyle(attributes.style); + } +} + +class Span extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "span"); + this.style = checkStyle(attributes.style); + } +} + +class Sub extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "sub"); + this.style = checkStyle(attributes.style); + } +} + +class Sup extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "sup"); + this.style = checkStyle(attributes.style); + } +} + +class Ul extends XmlObject { + constructor(attributes) { + super(XHTML_NS_ID, "ul"); + this.style = checkStyle(attributes.style); + } +} + +class XhtmlNamespace { + static [$buildXFAObject](name, attributes) { + if (XhtmlNamespace.hasOwnProperty(name)) { + return XhtmlNamespace[name](attributes); + } + return undefined; + } + + static a(attributes) { + return new A(attributes); + } + + static b(attributes) { + return new B(attributes); + } + + static body(attributes) { + return new Body(attributes); + } + + static br(attributes) { + return new Br(attributes); + } + + static html(attributes) { + return new Html(attributes); + } + + static i(attributes) { + return new I(attributes); + } + + static li(attributes) { + return new Li(attributes); + } + + static ol(attributes) { + return new Ol(attributes); + } + + static p(attributes) { + return new P(attributes); + } + + static span(attributes) { + return new Span(attributes); + } + + static sub(attributes) { + return new Sub(attributes); + } + + static sup(attributes) { + return new Sup(attributes); + } + + static ul(attributes) { + return new Ul(attributes); + } +} + +export { XhtmlNamespace }; diff --git a/test/unit/xfa_parser_spec.js b/test/unit/xfa_parser_spec.js index 21fce1193de08..3257e29031b9a 100644 --- a/test/unit/xfa_parser_spec.js +++ b/test/unit/xfa_parser_spec.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { $dump } from "../../src/core/xfa/xfa_object.js"; +import { $dump, $getChildren, $text } from "../../src/core/xfa/xfa_object.js"; import { XFAParser } from "../../src/core/xfa/parser.js"; describe("XFAParser", function () { @@ -247,5 +247,38 @@ describe("XFAParser", function () { }; expect(root[$dump]()).toEqual(expected); }); + + it("should parse a xfa document with xhtml", function () { + const xml = ` + + + + + `; + const root = new XFAParser().parse(xml)[$dump](); + const p = root.template.extras.text.$content[$getChildren]()[0]; + expect(p.style).toEqual( + "text-indent:0.5in;line-height:11px;tab-stop:left 0.5in" + ); + expect(p[$text]()).toEqual( + [ + "The first line of this paragraph is indented a half-inch.\n", + "Successive lines are not indented.\n", + "This is the last line of the paragraph.\n", + ].join("") + ); + }); }); });