/** * @licstart The following is the entire license notice for the * Javascript code in this page * * 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. * * @licend The above is the entire license notice for the * Javascript code in this page */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.computeBbox = computeBbox; exports.createWrapper = createWrapper; exports.fixDimensions = fixDimensions; exports.fixTextIndent = fixTextIndent; exports.fixURL = fixURL; exports.isPrintOnly = isPrintOnly; exports.layoutClass = layoutClass; exports.layoutNode = layoutNode; exports.measureToString = measureToString; exports.setAccess = setAccess; exports.setFontFamily = setFontFamily; exports.setMinMaxDimensions = setMinMaxDimensions; exports.setPara = setPara; exports.toStyle = toStyle; var _xfa_object = require("./xfa_object.js"); var _util = require("../../shared/util.js"); var _utils = require("./utils.js"); var _fonts = require("./fonts.js"); var _text = require("./text.js"); function measureToString(m) { if (typeof m === "string") { return "0px"; } return Number.isInteger(m) ? `${m}px` : `${m.toFixed(2)}px`; } const converters = { anchorType(node, style) { const parent = node[_xfa_object.$getSubformParent](); if (!parent || parent.layout && parent.layout !== "position") { return; } if (!("transform" in style)) { style.transform = ""; } switch (node.anchorType) { case "bottomCenter": style.transform += "translate(-50%, -100%)"; break; case "bottomLeft": style.transform += "translate(0,-100%)"; break; case "bottomRight": style.transform += "translate(-100%,-100%)"; break; case "middleCenter": style.transform += "translate(-50%,-50%)"; break; case "middleLeft": style.transform += "translate(0,-50%)"; break; case "middleRight": style.transform += "translate(-100%,-50%)"; break; case "topCenter": style.transform += "translate(-50%,0)"; break; case "topRight": style.transform += "translate(-100%,0)"; break; } }, dimensions(node, style) { const parent = node[_xfa_object.$getSubformParent](); let width = node.w; const height = node.h; if (parent.layout && parent.layout.includes("row")) { const extra = parent[_xfa_object.$extra]; const colSpan = node.colSpan; let w; if (colSpan === -1) { w = extra.columnWidths.slice(extra.currentColumn).reduce((a, x) => a + x, 0); extra.currentColumn = 0; } else { w = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, x) => a + x, 0); extra.currentColumn = (extra.currentColumn + node.colSpan) % extra.columnWidths.length; } if (!isNaN(w)) { width = node.w = w; } } if (width !== "") { style.width = measureToString(width); } else { style.width = "auto"; } if (height !== "") { style.height = measureToString(height); } else { style.height = "auto"; } }, position(node, style) { const parent = node[_xfa_object.$getSubformParent](); if (parent && parent.layout && parent.layout !== "position") { return; } style.position = "absolute"; style.left = measureToString(node.x); style.top = measureToString(node.y); }, rotate(node, style) { if (node.rotate) { if (!("transform" in style)) { style.transform = ""; } style.transform += `rotate(-${node.rotate}deg)`; style.transformOrigin = "top left"; } }, presence(node, style) { switch (node.presence) { case "invisible": style.visibility = "hidden"; break; case "hidden": case "inactive": style.display = "none"; break; } }, hAlign(node, style) { if (node[_xfa_object.$nodeName] === "para") { switch (node.hAlign) { case "justifyAll": style.textAlign = "justify-all"; break; case "radix": style.textAlign = "left"; break; default: style.textAlign = node.hAlign; } } else { switch (node.hAlign) { case "left": style.alignSelf = "start"; break; case "center": style.alignSelf = "center"; break; case "right": style.alignSelf = "end"; break; } } }, margin(node, style) { if (node.margin) { style.margin = node.margin[_xfa_object.$toStyle]().margin; } } }; function setMinMaxDimensions(node, style) { const parent = node[_xfa_object.$getSubformParent](); if (parent.layout === "position") { if (node.minW > 0) { style.minWidth = measureToString(node.minW); } if (node.maxW > 0) { style.maxWidth = measureToString(node.maxW); } if (node.minH > 0) { style.minHeight = measureToString(node.minH); } if (node.maxH > 0) { style.maxHeight = measureToString(node.maxH); } } } function layoutText(text, xfaFont, margin, lineHeight, fontFinder, width) { const measure = new _text.TextMeasure(xfaFont, margin, lineHeight, fontFinder); if (typeof text === "string") { measure.addString(text); } else { text[_xfa_object.$pushGlyphs](measure); } return measure.compute(width); } function layoutNode(node, availableSpace) { let height = null; let width = null; let isBroken = false; if ((!node.w || !node.h) && node.value) { let marginH = 0; let marginV = 0; if (node.margin) { marginH = node.margin.leftInset + node.margin.rightInset; marginV = node.margin.topInset + node.margin.bottomInset; } let lineHeight = null; let margin = null; if (node.para) { margin = Object.create(null); lineHeight = node.para.lineHeight === "" ? null : node.para.lineHeight; margin.top = node.para.spaceAbove === "" ? 0 : node.para.spaceAbove; margin.bottom = node.para.spaceBelow === "" ? 0 : node.para.spaceBelow; margin.left = node.para.marginLeft === "" ? 0 : node.para.marginLeft; margin.right = node.para.marginRight === "" ? 0 : node.para.marginRight; } let font = node.font; if (!font) { const root = node[_xfa_object.$getTemplateRoot](); let parent = node[_xfa_object.$getParent](); while (parent !== root) { if (parent.font) { font = parent.font; break; } parent = parent[_xfa_object.$getParent](); } } const maxWidth = (!node.w ? availableSpace.width : node.w) - marginH; const fontFinder = node[_xfa_object.$globalData].fontFinder; if (node.value.exData && node.value.exData[_xfa_object.$content] && node.value.exData.contentType === "text/html") { const res = layoutText(node.value.exData[_xfa_object.$content], font, margin, lineHeight, fontFinder, maxWidth); width = res.width; height = res.height; isBroken = res.isBroken; } else { const text = node.value[_xfa_object.$text](); if (text) { const res = layoutText(text, font, margin, lineHeight, fontFinder, maxWidth); width = res.width; height = res.height; isBroken = res.isBroken; } } if (width !== null && !node.w) { width += marginH; } if (height !== null && !node.h) { height += marginV; } } return { w: width, h: height, isBroken }; } function computeBbox(node, html, availableSpace) { let bbox; if (node.w !== "" && node.h !== "") { bbox = [node.x, node.y, node.w, node.h]; } else { if (!availableSpace) { return null; } let width = node.w; if (width === "") { if (node.maxW === 0) { const parent = node[_xfa_object.$getSubformParent](); if (parent.layout === "position" && parent.w !== "") { width = 0; } else { width = node.minW; } } else { width = Math.min(node.maxW, availableSpace.width); } html.attributes.style.width = measureToString(width); } let height = node.h; if (height === "") { if (node.maxH === 0) { const parent = node[_xfa_object.$getSubformParent](); if (parent.layout === "position" && parent.h !== "") { height = 0; } else { height = node.minH; } } else { height = Math.min(node.maxH, availableSpace.height); } html.attributes.style.height = measureToString(height); } bbox = [node.x, node.y, width, height]; } return bbox; } function fixDimensions(node) { const parent = node[_xfa_object.$getSubformParent](); if (parent.layout && parent.layout.includes("row")) { const extra = parent[_xfa_object.$extra]; const colSpan = node.colSpan; let width; if (colSpan === -1) { width = extra.columnWidths.slice(extra.currentColumn).reduce((a, w) => a + w, 0); } else { width = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, w) => a + w, 0); } if (!isNaN(width)) { node.w = width; } } if (parent.layout && parent.layout !== "position") { node.x = node.y = 0; } if (node.layout === "table") { if (node.w === "" && Array.isArray(node.columnWidths)) { node.w = node.columnWidths.reduce((a, x) => a + x, 0); } } } function layoutClass(node) { switch (node.layout) { case "position": return "xfaPosition"; case "lr-tb": return "xfaLrTb"; case "rl-row": return "xfaRlRow"; case "rl-tb": return "xfaRlTb"; case "row": return "xfaRow"; case "table": return "xfaTable"; case "tb": return "xfaTb"; default: return "xfaPosition"; } } function toStyle(node, ...names) { const style = Object.create(null); for (const name of names) { const value = node[name]; if (value === null) { continue; } if (converters.hasOwnProperty(name)) { converters[name](node, style); continue; } if (value instanceof _xfa_object.XFAObject) { const newStyle = value[_xfa_object.$toStyle](); if (newStyle) { Object.assign(style, newStyle); } else { (0, _util.warn)(`(DEBUG) - XFA - style for ${name} not implemented yet`); } } } return style; } function createWrapper(node, html) { const { attributes } = html; const { style } = attributes; const wrapper = { name: "div", attributes: { class: ["xfaWrapper"], style: Object.create(null) }, children: [] }; attributes.class.push("xfaWrapped"); if (node.border) { const { widths, insets } = node.border[_xfa_object.$extra]; let width, height; let top = insets[0]; let left = insets[3]; const insetsH = insets[0] + insets[2]; const insetsW = insets[1] + insets[3]; switch (node.border.hand) { case "even": top -= widths[0] / 2; left -= widths[3] / 2; width = `calc(100% + ${(widths[1] + widths[3]) / 2 - insetsW}px)`; height = `calc(100% + ${(widths[0] + widths[2]) / 2 - insetsH}px)`; break; case "left": top -= widths[0]; left -= widths[3]; width = `calc(100% + ${widths[1] + widths[3] - insetsW}px)`; height = `calc(100% + ${widths[0] + widths[2] - insetsH}px)`; break; case "right": width = insetsW ? `calc(100% - ${insetsW}px)` : "100%"; height = insetsH ? `calc(100% - ${insetsH}px)` : "100%"; break; } const classNames = ["xfaBorder"]; if (isPrintOnly(node.border)) { classNames.push("xfaPrintOnly"); } const border = { name: "div", attributes: { class: classNames, style: { top: `${top}px`, left: `${left}px`, width, height } }, children: [] }; for (const key of ["border", "borderWidth", "borderColor", "borderRadius", "borderStyle"]) { if (style[key] !== undefined) { border.attributes.style[key] = style[key]; delete style[key]; } } wrapper.children.push(border, html); } else { wrapper.children.push(html); } for (const key of ["background", "backgroundClip", "top", "left", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "transform", "transformOrigin", "visibility"]) { if (style[key] !== undefined) { wrapper.attributes.style[key] = style[key]; delete style[key]; } } if (style.position === "absolute") { wrapper.attributes.style.position = "absolute"; } else { wrapper.attributes.style.position = "relative"; } delete style.position; if (style.alignSelf) { wrapper.attributes.style.alignSelf = style.alignSelf; delete style.alignSelf; } return wrapper; } function fixTextIndent(styles) { const indent = (0, _utils.getMeasurement)(styles.textIndent, "0px"); if (indent >= 0) { return; } const align = styles.textAlign === "right" ? "right" : "left"; const name = "padding" + (align === "left" ? "Left" : "Right"); const padding = (0, _utils.getMeasurement)(styles[name], "0px"); styles[name] = `${padding - indent}px`; } function setAccess(node, classNames) { switch (node.access) { case "nonInteractive": classNames.push("xfaNonInteractive"); break; case "readOnly": classNames.push("xfaReadOnly"); break; case "protected": classNames.push("xfaDisabled"); break; } } function isPrintOnly(node) { return node.relevant.length > 0 && !node.relevant[0].excluded && node.relevant[0].viewname === "print"; } function getCurrentPara(node) { const stack = node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].paraStack; return stack.length ? stack[stack.length - 1] : null; } function setPara(node, nodeStyle, value) { if (value.attributes.class && value.attributes.class.includes("xfaRich")) { if (nodeStyle) { if (node.h === "") { nodeStyle.height = "auto"; } if (node.w === "") { nodeStyle.width = "auto"; } } const para = getCurrentPara(node); if (para) { const valueStyle = value.attributes.style; valueStyle.display = "flex"; valueStyle.flexDirection = "column"; switch (para.vAlign) { case "top": valueStyle.justifyContent = "start"; break; case "bottom": valueStyle.justifyContent = "end"; break; case "middle": valueStyle.justifyContent = "center"; break; } const paraStyle = para[_xfa_object.$toStyle](); for (const [key, val] of Object.entries(paraStyle)) { if (!(key in valueStyle)) { valueStyle[key] = val; } } } } } function setFontFamily(xfaFont, node, fontFinder, style) { const name = (0, _utils.stripQuotes)(xfaFont.typeface); const typeface = fontFinder.find(name); style.fontFamily = `"${name}"`; if (typeface) { const { fontFamily } = typeface.regular.cssFontInfo; if (fontFamily !== name) { style.fontFamily = `"${fontFamily}"`; } const para = getCurrentPara(node); if (para && para.lineHeight !== "") { return; } if (style.lineHeight) { return; } const pdfFont = (0, _fonts.selectFont)(xfaFont, typeface); if (pdfFont) { style.lineHeight = Math.max(1.2, pdfFont.lineHeight); } } } function fixURL(str) { const absoluteUrl = (0, _util.createValidAbsoluteUrl)(str, null, { addDefaultProtocol: true, tryConvertEncoding: true }); return absoluteUrl ? absoluteUrl.href : null; }