Skip to content

Commit

Permalink
Merge pull request #12879 from calixteman/xfa_parser
Browse files Browse the repository at this point in the history
XFA - Add a parser for XFA files
  • Loading branch information
brendandahl authored Feb 2, 2021
2 parents c92011e + 0ff5cd7 commit e16c99f
Show file tree
Hide file tree
Showing 14 changed files with 1,049 additions and 10 deletions.
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1431,7 +1431,7 @@ function buildLib(defines, dir) {
return merge([
gulp.src(
[
"src/{core,display,shared}/*.js",
"src/{core,display,shared}/**/*.js",
"!src/shared/{cffStandardStrings,fonts_utils}.js",
"src/{pdf,pdf.worker}.js",
],
Expand Down
148 changes: 148 additions & 0 deletions src/core/xfa/builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* 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 { $cleanup, $onChild, XFAObject } from "./xfa_object.js";
import { NamespaceSetUp } from "./setup.js";
import { UnknownNamespace } from "./unknown.js";
import { warn } from "../../shared/util.js";

class Root extends XFAObject {
constructor() {
super(-1, "root", Object.create(null));
this.element = null;
}

[$onChild](child) {
this.element = child;
}
}

class Empty extends XFAObject {
constructor() {
super(-1, "", Object.create(null));
}

[$onChild](_) {}
}

class Builder {
constructor() {
this._namespaceStack = [];

// Each prefix has its own stack
this._namespacePrefixes = new Map();
this._namespaces = new Map();
this._nextNsId = Math.max(
...Object.values(NamespaceIds).map(({ id }) => id)
);
this._currentNamespace = new UnknownNamespace(++this._nextNsId);
}

buildRoot() {
return new Root();
}

build({ nsPrefix, name, attributes, namespace, prefixes }) {
const hasNamespaceDef = namespace !== null;
if (hasNamespaceDef) {
// Define the current namespace to use.
this._namespaceStack.push(this._currentNamespace);
this._currentNamespace = this._searchNamespace(namespace);
}

if (prefixes) {
// The xml node may have namespace prefix definitions
this._addNamespacePrefix(prefixes);
}

const namespaceToUse = this._getNamespaceToUse(nsPrefix);
const node =
(namespaceToUse && namespaceToUse[$buildXFAObject](name, attributes)) ||
new Empty();

// In case the node has some namespace things,
// we must pop the different stacks.
if (hasNamespaceDef || prefixes) {
node[$cleanup] = {
hasNamespace: hasNamespaceDef,
prefixes,
};
}

return node;
}

_searchNamespace(nsName) {
let ns = this._namespaces.get(nsName);
if (ns) {
return ns;
}
for (const [name, { check }] of Object.entries(NamespaceIds)) {
if (check(nsName)) {
ns = NamespaceSetUp[name];
if (ns) {
this._namespaces.set(nsName, ns);
return ns;
}
// The namespace is known but not handled.
break;
}
}

ns = new UnknownNamespace(++this._nextNsId);
this._namespaces.set(nsName, ns);
return ns;
}

_addNamespacePrefix(prefixes) {
for (const { prefix, value } of prefixes) {
const namespace = this._searchNamespace(value);
let prefixStack = this._namespacePrefixes.get(prefix);
if (!prefixStack) {
prefixStack = [];
this._namespacePrefixes.set(prefix, prefixStack);
}
prefixStack.push(namespace);
}
}

_getNamespaceToUse(prefix) {
if (!prefix) {
return this._currentNamespace;
}
const prefixStack = this._namespacePrefixes.get(prefix);
if (prefixStack && prefixStack.length > 0) {
return prefixStack[prefixStack.length - 1];
}

warn(`Unknown namespace prefix: ${prefix}.`);
return null;
}

clean(data) {
const { hasNamespace, prefixes } = data;
if (hasNamespace) {
this._currentNamespace = this._namespaceStack.pop();
}
if (prefixes) {
prefixes.forEach(({ prefix }) => {
this._namespacePrefixes.get(prefix).pop();
});
}
}
}

export { Builder };
193 changes: 193 additions & 0 deletions src/core/xfa/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/* 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 {
IntegerObject,
OptionObject,
StringObject,
XFAObject,
} from "./xfa_object.js";

const CONFIG_NS_ID = NamespaceIds.config.id;

class Acrobat extends XFAObject {
constructor(attributes) {
super(CONFIG_NS_ID, "acrobat", /* hasChildren = */ true);
this.acrobat7 = null;
this.autoSave = null;
this.common = null;
this.validate = null;
this.validateApprovalSignatures = null;
this.submitUrl = [];
}
}

class Acrobat7 extends XFAObject {
constructor(attributes) {
super(CONFIG_NS_ID, "acrobat7", /* hasChildren = */ true);
this.dynamicRender = null;
}
}

class AdobeExtensionLevel extends IntegerObject {
constructor(attributes) {
super(CONFIG_NS_ID, "adobeExtensionLevel", 0, n => n >= 1 && n <= 8);
}
}

class AutoSave extends OptionObject {
constructor(attributes) {
super(CONFIG_NS_ID, "autoSave", ["disabled", "enabled"]);
}
}

class Config extends XFAObject {
constructor(attributes) {
super(CONFIG_NS_ID, "config", /* hasChildren = */ true);
this.acrobat = null;
this.present = null;
this.trace = null;
this.agent = [];
}
}

class DynamicRender extends OptionObject {
constructor(attributes) {
super(CONFIG_NS_ID, "dynamicRender", ["forbidden", "required"]);
}
}

class Present extends XFAObject {
constructor(attributes) {
super(CONFIG_NS_ID, "present", /* hasChildren = */ true);
this.behaviorOverride = null;
this.cache = null;
this.common = null;
this.copies = null;
this.destination = null;
this.incrementalMerge = null;
this.layout = null;
this.output = null;
this.overprint = null;
this.pagination = null;
this.paginationOverride = null;
this.script = null;
this.validate = null;
this.xdp = null;
this.driver = [];
this.labelPrinter = [];
this.pcl = [];
this.pdf = [];
this.ps = [];
this.submitUrl = [];
this.webClient = [];
this.zpl = [];
}
}

class Pdf extends XFAObject {
constructor(attributes) {
super(CONFIG_NS_ID, "pdf", /* hasChildren = */ true);
this.name = attributes.name || "";
this.adobeExtensionLevel = null;
this.batchOutput = null;
this.compression = null;
this.creator = null;
this.encryption = null;
this.fontInfo = null;
this.interactive = null;
this.linearized = null;
this.openAction = null;
this.pdfa = null;
this.producer = null;
this.renderPolicy = null;
this.scriptModel = null;
this.silentPrint = null;
this.submitFormat = null;
this.tagged = null;
this.version = null;
this.viewerPreferences = null;
this.xdc = null;
}
}

class SubmitUrl extends StringObject {
constructor(attributes) {
super(CONFIG_NS_ID, "submitUrl");
}
}

class Validate extends OptionObject {
constructor(attributes) {
super(CONFIG_NS_ID, "validate", [
"preSubmit",
"prePrint",
"preExecute",
"preSave",
]);
}
}

class ConfigNamespace {
static [$buildXFAObject](name, attributes) {
if (ConfigNamespace.hasOwnProperty(name)) {
return ConfigNamespace[name](attributes);
}
return undefined;
}

static acrobat(attrs) {
return new Acrobat(attrs);
}

static acrobat7(attrs) {
return new Acrobat7(attrs);
}

static adobeExtensionLevel(attrs) {
return new AdobeExtensionLevel(attrs);
}

static autoSave(attrs) {
return new AutoSave(attrs);
}

static config(attrs) {
return new Config(attrs);
}

static dynamicRender(attrs) {
return new DynamicRender(attrs);
}

static pdf(attrs) {
return new Pdf(attrs);
}

static present(attrs) {
return new Present(attrs);
}

static submitUrl(attrs) {
return new SubmitUrl(attrs);
}

static validate(attrs) {
return new Validate(attrs);
}
}

export { ConfigNamespace };
Loading

0 comments on commit e16c99f

Please sign in to comment.