/* Copyright 2020 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 { $dump, $getChildren, $getChildrenByClass, $getChildrenByName, $text, } from "../../src/core/xfa/xfa_object.js"; import { searchNode } from "../../src/core/xfa/som.js"; import { XFAParser } from "../../src/core/xfa/parser.js"; describe("XFAParser", function () { describe("Parse XFA", function () { it("should parse a xfa document and create an object to represent it", function () { const xml = ` 7 foobar http://a.b.c forbidden enabled http://d.e.f http://g.h.i foobar 1.23 1 314 2.71 `; const attributes = { id: "", name: "", use: "", usehref: "", }; const mediumAttributes = { id: "", long: { value: 0, unit: "" }, orientation: "portrait", short: { value: 0, unit: "" }, stock: "", trayIn: "auto", trayOut: "auto", use: "", usehref: "", }; const colorAttributes = { cSpace: "SRGB", id: "", use: "", usehref: "", }; const root = new XFAParser().parse(xml); const expected = { uuid: "1234", timeStamp: "", template: { baseProfile: "full", extras: { ...attributes, float: [ { ...attributes, $content: 1.23 }, { ...attributes, $content: 2.71 }, ], boolean: { ...attributes, $content: 1 }, integer: { ...attributes, $content: 314 }, }, subform: { access: "open", allowMacro: 0, anchorType: "topLeft", colSpan: 1, columnWidths: [{ value: 0, unit: "" }], h: { value: 0, unit: "" }, hAlign: "left", id: "", layout: "position", locale: "", maxH: { value: 0, unit: "" }, maxW: { value: 0, unit: "" }, mergeMode: "consumeData", minH: { value: 0, unit: "" }, minW: { value: 0, unit: "" }, name: "", presence: "visible", relevant: [], restoreState: "manual", scope: "name", use: "", usehref: "", w: { value: 0, unit: "" }, x: { value: 0, unit: "" }, y: { value: 0, unit: "" }, proto: { area: { ...attributes, colSpan: 1, x: { value: 0, unit: "" }, y: { value: -3.14, unit: "in" }, relevant: [ { excluded: true, viewname: "foo" }, { excluded: false, viewname: "bar" }, ], }, color: [ { ...colorAttributes, value: { r: 111, g: 222, b: 123 }, }, { ...colorAttributes, value: { r: 111, g: 0, b: 123 }, }, ], medium: [ { ...mediumAttributes, imagingBBox: { x: { value: 1, unit: "" }, y: { value: 2, unit: "in" }, width: { value: 3.4, unit: "cm" }, height: { value: 5.67, unit: "px" }, }, }, { ...mediumAttributes, imagingBBox: { x: { value: -1, unit: "" }, y: { value: -1, unit: "" }, width: { value: -1, unit: "" }, height: { value: -1, unit: "" }, }, }, ], }, }, }, config: { acrobat: { acrobat7: { dynamicRender: { $content: "forbidden", }, }, autoSave: { $content: "enabled", }, validate: { $content: "preSubmit", }, submitUrl: [ { $content: "http://a.b.c", }, { $content: "http://d.e.f", }, { $content: "http://g.h.i", }, ], }, present: { pdf: { name: "hello", adobeExtensionLevel: { $content: 7, }, }, }, }, }; expect(root[$dump]()).toEqual(expected); }); it("should parse a xfa document and check namespaces", function () { const xml = ` 7 http://a.b.c http://c.b.a 1.23 `; const root = new XFAParser().parse(xml); const expected = { uuid: "", timeStamp: "", config: { acrobat: { submitUrl: { $content: "http://c.b.a" }, }, }, }; expect(root[$dump]()).toEqual(expected); }); it("should parse a xfa document and apply some prototypes", function () { const xml = ` `; const root = new XFAParser().parse(xml)[$dump](); let font = root.template.subform.field[0].font; expect(font.typeface).toEqual("Foo"); expect(font.overline).toEqual(0); expect(font.size).toEqual({ value: 123, unit: "pt" }); expect(font.weight).toEqual("bold"); expect(font.posture).toEqual("italic"); expect(font.fill.color.value).toEqual({ r: 1, g: 2, b: 3 }); expect(font.extras).toEqual(undefined); font = root.template.subform.field[1].font; expect(font.typeface).toEqual("Foo"); expect(font.overline).toEqual(0); expect(font.size).toEqual({ value: 456, unit: "pt" }); expect(font.weight).toEqual("bold"); expect(font.posture).toEqual("normal"); expect(font.fill.color.value).toEqual({ r: 4, g: 5, b: 6 }); expect(font.extras.id).toEqual("id2"); }); it("should parse a xfa document with xhtml", function () { const xml = ` The first line of this paragraph is indented a half-inch. Successive lines are not indented. This is the last line of the paragraph. `; 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("") ); }); it("should parse a xfa document and apply some prototypes with cycle", function () { const xml = ` `; const root = new XFAParser().parse(xml)[$dump](); const subform = root.template.subform[1]; expect(subform.id).toEqual("id1"); expect(subform.subform.id).toEqual("id1"); }); it("should parse a xfa document and apply some nested prototypes", function () { const xml = ` `; const root = new XFAParser().parse(xml)[$dump](); const font = root.template.subform.field.font; expect(font.typeface).toEqual("helvetica"); expect(font.overline).toEqual(0); expect(font.size).toEqual({ value: 31, unit: "pt" }); expect(font.weight).toEqual("normal"); expect(font.posture).toEqual("italic"); expect(font.fill.color.value).toEqual({ r: 7, g: 8, b: 9 }); }); it("should parse a xfa document and apply a prototype with content", function () { const xml = ` default TEXT Overriding text `; const root = new XFAParser().parse(xml)[$dump](); let field = root.template.subform.field[0]; expect(field.value.text.$content).toEqual("default TEXT"); field = root.template.subform.field[1]; expect(field.value.text.$content).toEqual("Overriding text"); }); }); describe("Search in XFA", function () { it("should search some nodes in a template object", function () { const xml = ` `; const root = new XFAParser().parse(xml); let found = root[$getChildrenByName]("subform", true); expect(found.map(x => x.id)).toEqual(["l", "m", "n", "o"]); found = root[$getChildrenByName]("Total_Price", true); expect(found.map(x => x.id)).toEqual(["d", "h", "s", "k"]); found = root.template[$getChildrenByName]("Receipt", false); const receipt = found[0]; found = receipt[$getChildrenByName]("Total_Price", false); expect(found.map(x => x.id)).toEqual(["d", "h", "k"]); expect(receipt[$getChildrenByClass]("name")).toEqual("Receipt"); const subforms = receipt[$getChildrenByClass]("subform"); expect(subforms.children.map(x => x.id)).toEqual(["m", "n", "o"]); }); it("should search some nodes in a template object using SOM", function () { const xml = ` `; const root = new XFAParser().parse(xml); expect(searchNode(root, null, "$template..Description.id")[$text]()).toBe( "a" ); expect(searchNode(root, null, "$template..Description.id")[$text]()).toBe( "a" ); expect( searchNode(root, null, "$template..Description[0].id")[$text]() ).toBe("a"); expect( searchNode(root, null, "$template..Description[1].id")[$text]() ).toBe("e"); expect( searchNode(root, null, "$template..Description[2].id")[$text]() ).toBe("p"); expect(searchNode(root, null, "$template.Receipt.id")[$text]()).toBe("l"); expect( searchNode(root, null, "$template.Receipt.Description[1].id")[$text]() ).toBe("e"); expect(searchNode(root, null, "$template.Receipt.Description[2]")).toBe( null ); expect( searchNode(root, null, "$template.Receipt.foo.Description.id")[$text]() ).toBe("p"); expect( searchNode(root, null, "$template.#subform.Sub_Total.id")[$text]() ).toBe("i"); expect( searchNode(root, null, "$template.#subform.Units.id")[$text]() ).toBe("b"); expect( searchNode(root, null, "$template.#subform.Units.parent.id")[$text]() ).toBe("m"); }); it("should search some nodes in a datasets object", function () { const xml = ` 1 Giant Slingshot 1 250.00 250.00 2 Road Runner Bait, large bag 5 12.00 60.00 310.00 24.80 334.80 `; const root = new XFAParser().parse(xml); const data = root.datasets.data; let found = data[$getChildrenByName]("Description", true); expect(found.map(x => x[$text]())).toEqual([ "Giant Slingshot", "Road Runner Bait, large bag", ]); found = data[$getChildrenByName]("Total_Price", true); expect(found.map(x => x[$text]())).toEqual(["250.00", "60.00", "334.80"]); }); it("should search some nodes using SOM from a non-root node", function () { const xml = ` 1 Giant Slingshot 1 250.00 250.00 2 Road Runner Bait, large bag 5 12.00 60.00 310.00 24.80 334.80 `; const root = new XFAParser().parse(xml); const [receipt] = root.datasets.data[$getChildren]("Receipt"); expect( searchNode(root, receipt, "Detail[*].Total_Price").map(x => x[$text]()) ).toEqual(["250.00", "60.00"]); const units = searchNode(root, receipt, "Detail[1].Units"); expect(units[$text]()).toBe("5"); let found = searchNode(root, units, "Total_Price"); expect(found[$text]()).toBe("60.00"); found = searchNode(root, units, "Total_Pric"); expect(found).toEqual(null); }); it("should search some nodes in a datasets object using SOM", function () { const xml = ` foo bar `; const root = new XFAParser().parse(xml); expect(searchNode(root, null, "$data.Receipt.Detail")[$text]()).toBe( "Acme" ); expect(searchNode(root, null, "$data.Receipt.Detail[0]")[$text]()).toBe( "Acme" ); expect(searchNode(root, null, "$data.Receipt.Detail[1]")[$text]()).toBe( "foo" ); expect(searchNode(root, null, "$data.Receipt.Detail[2]")[$text]()).toBe( "bar" ); }); }); });
The first line of this paragraph is indented a half-inch. Successive lines are not indented. This is the last line of the paragraph.