2021-02-11 20:30:37 +09:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
import {
|
|
|
|
$acceptWhitespace,
|
|
|
|
$childrenToHTML,
|
|
|
|
$content,
|
2021-05-19 18:09:21 +09:00
|
|
|
$extra,
|
2021-03-26 00:54:36 +09:00
|
|
|
$nodeName,
|
|
|
|
$onText,
|
|
|
|
$text,
|
|
|
|
$toHTML,
|
|
|
|
XmlObject,
|
|
|
|
} from "./xfa_object.js";
|
2021-02-11 20:30:37 +09:00
|
|
|
import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
|
2021-05-19 18:09:21 +09:00
|
|
|
import { fixTextIndent, getFonts, measureToString } from "./html_utils.js";
|
|
|
|
import { getMeasurement, HTMLResult } from "./utils.js";
|
2021-02-11 20:30:37 +09:00
|
|
|
|
|
|
|
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",
|
2021-03-26 00:54:36 +09:00
|
|
|
"text-align",
|
2021-02-11 20:30:37 +09:00
|
|
|
"text-decoration",
|
|
|
|
"text-indent",
|
|
|
|
"vertical-align",
|
|
|
|
"widows",
|
|
|
|
"kerning-mode",
|
|
|
|
"xfa-font-horizontal-scale",
|
|
|
|
"xfa-font-vertical-scale",
|
2021-03-26 00:54:36 +09:00
|
|
|
"xfa-spacerun",
|
2021-02-11 20:30:37 +09:00
|
|
|
"xfa-tab-stops",
|
|
|
|
]);
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
const StyleMapping = new Map([
|
|
|
|
["page-break-after", "breakAfter"],
|
|
|
|
["page-break-before", "breakBefore"],
|
|
|
|
["page-break-inside", "breakInside"],
|
|
|
|
["kerning-mode", value => (value === "none" ? "none" : "normal")],
|
|
|
|
[
|
|
|
|
"xfa-font-horizontal-scale",
|
|
|
|
value =>
|
|
|
|
`scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`,
|
|
|
|
],
|
|
|
|
[
|
|
|
|
"xfa-font-vertical-scale",
|
|
|
|
value =>
|
|
|
|
`scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`,
|
|
|
|
],
|
|
|
|
["xfa-spacerun", ""],
|
|
|
|
["xfa-tab-stops", ""],
|
2021-06-03 02:14:41 +09:00
|
|
|
["font-size", value => measureToString(getMeasurement(value))],
|
2021-03-26 00:54:36 +09:00
|
|
|
["letter-spacing", value => measureToString(getMeasurement(value))],
|
2021-06-03 02:14:41 +09:00
|
|
|
["line-height", value => measureToString(getMeasurement(value))],
|
2021-03-26 00:54:36 +09:00
|
|
|
["margin", value => measureToString(getMeasurement(value))],
|
|
|
|
["margin-bottom", value => measureToString(getMeasurement(value))],
|
|
|
|
["margin-left", value => measureToString(getMeasurement(value))],
|
|
|
|
["margin-right", value => measureToString(getMeasurement(value))],
|
|
|
|
["margin-top", value => measureToString(getMeasurement(value))],
|
2021-05-19 18:09:21 +09:00
|
|
|
["text-indent", value => measureToString(getMeasurement(value))],
|
|
|
|
["font-family", value => getFonts(value)],
|
2021-03-26 00:54:36 +09:00
|
|
|
]);
|
|
|
|
|
|
|
|
const spacesRegExp = /\s+/g;
|
|
|
|
const crlfRegExp = /[\r\n]+/g;
|
|
|
|
|
|
|
|
function mapStyle(styleStr) {
|
|
|
|
const style = Object.create(null);
|
|
|
|
if (!styleStr) {
|
|
|
|
return style;
|
|
|
|
}
|
|
|
|
for (const [key, value] of styleStr.split(";").map(s => s.split(":", 2))) {
|
|
|
|
const mapping = StyleMapping.get(key);
|
|
|
|
if (mapping === "") {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let newValue = value;
|
|
|
|
if (mapping) {
|
|
|
|
if (typeof mapping === "string") {
|
|
|
|
newValue = mapping;
|
|
|
|
} else {
|
|
|
|
newValue = mapping(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (key.endsWith("scale")) {
|
|
|
|
if (style.transform) {
|
|
|
|
style.transform = `${style[key]} ${newValue}`;
|
|
|
|
} else {
|
|
|
|
style.transform = newValue;
|
|
|
|
}
|
|
|
|
} else {
|
2021-05-16 17:58:34 +09:00
|
|
|
style[key.replaceAll(/-([a-zA-Z])/g, (_, x) => x.toUpperCase())] =
|
|
|
|
newValue;
|
2021-03-26 00:54:36 +09:00
|
|
|
}
|
|
|
|
}
|
2021-05-19 18:09:21 +09:00
|
|
|
|
|
|
|
fixTextIndent(style);
|
2021-03-26 00:54:36 +09:00
|
|
|
return style;
|
|
|
|
}
|
|
|
|
|
2021-02-11 20:30:37 +09:00
|
|
|
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(";");
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
const NoWhites = new Set(["body", "html"]);
|
|
|
|
|
|
|
|
class XhtmlObject extends XmlObject {
|
|
|
|
constructor(attributes, name) {
|
|
|
|
super(XHTML_NS_ID, name);
|
|
|
|
this.style = checkStyle(attributes.style);
|
|
|
|
}
|
|
|
|
|
|
|
|
[$acceptWhitespace]() {
|
|
|
|
return !NoWhites.has(this[$nodeName]);
|
|
|
|
}
|
|
|
|
|
|
|
|
[$onText](str) {
|
|
|
|
str = str.replace(crlfRegExp, "");
|
|
|
|
if (!this.style.includes("xfa-spacerun:yes")) {
|
|
|
|
str = str.replace(spacesRegExp, " ");
|
|
|
|
}
|
|
|
|
if (str) {
|
|
|
|
this[$content] += str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:09:21 +09:00
|
|
|
[$toHTML](availableSpace) {
|
|
|
|
const children = [];
|
|
|
|
this[$extra] = {
|
|
|
|
children,
|
|
|
|
};
|
|
|
|
|
|
|
|
this[$childrenToHTML]({});
|
|
|
|
|
|
|
|
if (children.length === 0 && !this[$content]) {
|
|
|
|
return HTMLResult.EMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HTMLResult.success({
|
2021-03-26 00:54:36 +09:00
|
|
|
name: this[$nodeName],
|
|
|
|
attributes: {
|
|
|
|
href: this.href,
|
|
|
|
style: mapStyle(this.style),
|
|
|
|
},
|
2021-05-19 18:09:21 +09:00
|
|
|
children,
|
2021-03-26 00:54:36 +09:00
|
|
|
value: this[$content] || "",
|
2021-05-19 18:09:21 +09:00
|
|
|
});
|
2021-03-26 00:54:36 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "a");
|
2021-02-11 20:30:37 +09:00
|
|
|
this.href = attributes.href || "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class B extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "b");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Body extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "body");
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:09:21 +09:00
|
|
|
[$toHTML](availableSpace) {
|
|
|
|
const res = super[$toHTML](availableSpace);
|
|
|
|
const { html } = res;
|
|
|
|
if (!html) {
|
|
|
|
return HTMLResult.EMPTY;
|
|
|
|
}
|
|
|
|
html.name = "div";
|
2021-06-03 02:14:41 +09:00
|
|
|
html.attributes.class = ["xfaRich"];
|
2021-05-19 18:09:21 +09:00
|
|
|
return res;
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Br extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "br");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
[$text]() {
|
|
|
|
return "\n";
|
|
|
|
}
|
2021-03-26 00:54:36 +09:00
|
|
|
|
2021-05-19 18:09:21 +09:00
|
|
|
[$toHTML](availableSpace) {
|
|
|
|
return HTMLResult.success({
|
2021-03-26 00:54:36 +09:00
|
|
|
name: "br",
|
2021-05-19 18:09:21 +09:00
|
|
|
});
|
2021-03-26 00:54:36 +09:00
|
|
|
}
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Html extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "html");
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:09:21 +09:00
|
|
|
[$toHTML](availableSpace) {
|
|
|
|
const children = [];
|
|
|
|
this[$extra] = {
|
|
|
|
children,
|
|
|
|
};
|
|
|
|
|
|
|
|
this[$childrenToHTML]({});
|
2021-03-26 00:54:36 +09:00
|
|
|
if (children.length === 0) {
|
2021-05-19 18:09:21 +09:00
|
|
|
return HTMLResult.success({
|
2021-03-26 00:54:36 +09:00
|
|
|
name: "div",
|
|
|
|
attributes: {
|
2021-06-03 02:14:41 +09:00
|
|
|
class: ["xfaRich"],
|
2021-03-26 00:54:36 +09:00
|
|
|
style: {},
|
|
|
|
},
|
|
|
|
value: this[$content] || "",
|
2021-05-19 18:09:21 +09:00
|
|
|
});
|
2021-03-26 00:54:36 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if (children.length === 1) {
|
|
|
|
const child = children[0];
|
2021-06-03 02:14:41 +09:00
|
|
|
if (child.attributes && child.attributes.class.includes("xfaRich")) {
|
2021-05-19 18:09:21 +09:00
|
|
|
return HTMLResult.success(child);
|
2021-03-26 00:54:36 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:09:21 +09:00
|
|
|
return HTMLResult.success({
|
2021-03-26 00:54:36 +09:00
|
|
|
name: "div",
|
|
|
|
attributes: {
|
2021-06-03 02:14:41 +09:00
|
|
|
class: ["xfaRich"],
|
2021-03-26 00:54:36 +09:00
|
|
|
style: {},
|
|
|
|
},
|
|
|
|
children,
|
2021-05-19 18:09:21 +09:00
|
|
|
});
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class I extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "i");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Li extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "li");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Ol extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "ol");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class P extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "p");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
2021-05-19 18:09:21 +09:00
|
|
|
|
|
|
|
[$text]() {
|
|
|
|
return super[$text]() + "\n";
|
|
|
|
}
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Span extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "span");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Sub extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "sub");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Sup extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "sup");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 00:54:36 +09:00
|
|
|
class Ul extends XhtmlObject {
|
2021-02-11 20:30:37 +09:00
|
|
|
constructor(attributes) {
|
2021-03-26 00:54:36 +09:00
|
|
|
super(attributes, "ul");
|
2021-02-11 20:30:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 };
|