1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-26 10:08:06 +02:00

XFA - Add a layer to display XFA forms (#13069)

- add an option to enable XFA rendering if any;
  - for now, let the canvas layer: it could be useful to implement XFAF forms (embedded pdf in xml stream for the background and xfa form for the foreground);
  - ui elements in template DOM are pretty close to their html counterpart so we generate a fake html DOM from template one:
    - it makes easier to translate template properties to html ones;
    - it makes faster the creation of the html element in the main thread.
This commit is contained in:
calixteman 2021-03-19 10:11:40 +01:00 committed by GitHub
parent a164941351
commit 24e598a895
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 760 additions and 27 deletions

View file

@ -15,8 +15,11 @@
import {
$appendChild,
$childrenToHTML,
$content,
$extra,
$finalize,
$getParent,
$hasItem,
$hasSettableValue,
$isTransparent,
@ -26,6 +29,8 @@ import {
$removeChild,
$setSetAttributes,
$setValue,
$toHTML,
$uid,
ContentObject,
Option01,
OptionObject,
@ -45,6 +50,7 @@ import {
getRelevant,
getStringOption,
} from "./utils.js";
import { measureToString, setPosition, setWidthHeight } from "./html_utils.js";
import { warn } from "../../shared/util.js";
const TEMPLATE_NS_ID = NamespaceIds.template.id;
@ -656,6 +662,29 @@ class ContentArea extends XFAObject {
this.desc = null;
this.extras = null;
}
[$toHTML]() {
// TODO: incomplete.
const left = measureToString(this.x);
const top = measureToString(this.y);
const style = {
position: "absolute",
left,
top,
width: measureToString(this.w),
height: measureToString(this.h),
};
return {
name: "div",
children: [],
attributes: {
style,
className: "xfa-contentarea",
id: this[$uid],
},
};
}
}
class Corner extends XFAObject {
@ -1946,6 +1975,41 @@ class PageArea extends XFAObject {
this.field = new XFAObjectArray();
this.subform = new XFAObjectArray();
}
[$toHTML]() {
// TODO: incomplete.
if (this.contentArea.children.length === 0) {
return null;
}
const children = this[$childrenToHTML]({
filter: new Set(["area", "draw", "field", "subform", "contentArea"]),
include: true,
});
// TODO: handle the case where there are several content areas.
const contentArea = children.find(
node => node.attributes.className === "xfa-contentarea"
);
const style = Object.create(null);
if (this.medium && this.medium.short.value && this.medium.long.value) {
style.width = measureToString(this.medium.short);
style.height = measureToString(this.medium.long);
} else {
// TODO: compute it from contentAreas
}
return {
name: "div",
children,
attributes: {
id: this[$uid],
style,
},
contentArea,
};
}
}
class PageSet extends XFAObject {
@ -1970,6 +2034,20 @@ class PageSet extends XFAObject {
this.pageArea = new XFAObjectArray();
this.pageSet = new XFAObjectArray();
}
[$toHTML]() {
// TODO: incomplete.
return {
name: "div",
children: this[$childrenToHTML]({
filter: new Set(["pageArea", "pageSet"]),
include: true,
}),
attributes: {
id: this[$uid],
},
};
}
}
class Para extends XFAObject {
@ -2465,6 +2543,64 @@ class Subform extends XFAObject {
this.subform = new XFAObjectArray();
this.subformSet = new XFAObjectArray();
}
[$toHTML]() {
// TODO: incomplete.
this[$extra] = Object.create(null);
const parent = this[$getParent]();
let page = null;
if (parent[$nodeName] === "template") {
// Root subform: should have page info.
if (this.pageSet !== null) {
this[$extra].pageNumber = 0;
} else {
// TODO
warn("XFA - No pageSet in root subform");
}
} else if (parent[$extra] && parent[$extra].pageNumber !== undefined) {
// This subform is a child of root subform
// so push it in a new page.
const pageNumber = parent[$extra].pageNumber;
const pageAreas = parent.pageSet.pageArea.children;
parent[$extra].pageNumber =
(parent[$extra].pageNumber + 1) % pageAreas.length;
page = pageAreas[pageNumber][$toHTML]();
}
const style = Object.create(null);
setWidthHeight(this, style);
setPosition(this, style);
const attributes = {
style,
id: this[$uid],
};
if (this.name) {
attributes["xfa-name"] = this.name;
}
const children = this[$childrenToHTML]({
// TODO: exObject & exclGroup
filter: new Set(["area", "draw", "field", "subform", "subformSet"]),
include: true,
});
const html = {
name: "div",
attributes,
children,
};
if (page) {
page.contentArea.children.push(html);
delete page.contentArea;
return page;
}
return html;
}
}
class SubformSet extends XFAObject {
@ -2580,8 +2716,32 @@ class Template extends XFAObject {
"interactiveForms",
]);
this.extras = null;
// Spec is unclear:
// A container element that describes a single subform capable of
// enclosing other containers.
// Can we have more than one subform ?
this.subform = new XFAObjectArray();
}
[$finalize]() {
if (this.subform.children.length === 0) {
warn("XFA - No subforms in template node.");
}
if (this.subform.children.length >= 2) {
warn("XFA - Several subforms in template node: please file a bug.");
}
}
[$toHTML]() {
if (this.subform.children.length > 0) {
return this.subform.children[0][$toHTML]();
}
return {
name: "div",
children: [],
};
}
}
class Text extends ContentObject {