diff --git a/src/core/calibri_factors.js b/src/core/calibri_factors.js index 6a7804a68..818c7b039 100644 --- a/src/core/calibri_factors.js +++ b/src/core/calibri_factors.js @@ -81,7 +81,7 @@ const CalibriBoldFactors = [ 0.95794, 0.95794, 0.82616, 0.86513, 0.85162, 0.85162, 0.85162, 0.85162, 0.91133, 0.85162, 0.79492, 0.79492, 0.79492, 0.79492, 0.91133, 0.79109, ]; -const CalibriBoldLineHeight = 1.2207; +const CalibriBoldMetrics = { lineHeight: 1.2207, lineGap: 0.2207 }; // Factors to rescale LiberationSans-BoldItalic.ttf to have the same // metrics as calibriz.ttf. @@ -153,7 +153,7 @@ const CalibriBoldItalicFactors = [ 0.84548, 0.84548, 0.91133, 0.84548, 0.79492, 0.79492, 0.79492, 0.79492, 0.91133, 0.74081, ]; -const CalibriBoldItalicLineHeight = 1.2207; +const CalibriBoldItalicMetrics = { lineHeight: 1.2207, lineGap: 0.2207 }; // Factors to rescale LiberationSans-Italic.ttf to have the same // metrics as calibrii.ttf. @@ -223,7 +223,7 @@ const CalibriItalicFactors = [ 0.84153, 0.89453, 0.89453, 0.89453, 0.89453, 0.91133, 0.89453, 0.79004, 0.79004, 0.79004, 0.79004, 0.91133, 0.75026, ]; -const CalibriItalicLineHeight = 1.2207; +const CalibriItalicMetrics = { lineHeight: 1.2207, lineGap: 0.2207 }; // Factors to rescale LiberationSans-Regular.ttf to have the same // metrics as calibri.ttf. @@ -294,15 +294,15 @@ const CalibriRegularFactors = [ 0.83969, 0.90527, 0.90527, 0.90527, 0.90527, 0.91133, 0.90527, 0.79004, 0.79004, 0.79004, 0.79004, 0.91133, 0.78848, ]; -const CalibriRegularLineHeight = 1.2207; +const CalibriRegularMetrics = { lineHeight: 1.2207, lineGap: 0.2207 }; export { CalibriBoldFactors, CalibriBoldItalicFactors, - CalibriBoldItalicLineHeight, - CalibriBoldLineHeight, + CalibriBoldItalicMetrics, + CalibriBoldMetrics, CalibriItalicFactors, - CalibriItalicLineHeight, + CalibriItalicMetrics, CalibriRegularFactors, - CalibriRegularLineHeight, + CalibriRegularMetrics, }; diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 517056d23..af0f3d465 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -3924,7 +3924,7 @@ class PartialEvaluator { const standardFontName = getXfaFontName(fontName.name); if (standardFontName) { cssFontInfo.fontFamily = `${cssFontInfo.fontFamily}-PdfJS-XFA`; - cssFontInfo.lineHeight = standardFontName.lineHeight || null; + cssFontInfo.metrics = standardFontName.metrics || null; glyphScaleFactors = standardFontName.factors || null; fontFile = await this.fetchStandardFontData(standardFontName.name); isInternalFont = !!fontFile; diff --git a/src/core/fonts.js b/src/core/fonts.js index 66d812acc..99d3afcb9 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -2550,7 +2550,8 @@ class Font { this.lineGap = metricsOverride.lineGap / metricsOverride.unitsPerEm; if (this.cssFontInfo && this.cssFontInfo.lineHeight) { - this.lineHeight = this.cssFontInfo.lineHeight; + this.lineHeight = this.cssFontInfo.metrics.lineHeight; + this.lineGap = this.cssFontInfo.metrics.lineGap; } else { this.lineHeight = this.ascent - this.descent + this.lineGap; } diff --git a/src/core/helvetica_factors.js b/src/core/helvetica_factors.js index 2bd4c42e4..207c63d76 100644 --- a/src/core/helvetica_factors.js +++ b/src/core/helvetica_factors.js @@ -94,7 +94,7 @@ const HelveticaBoldFactors = [ 1.00022, 1.00022, 0.99973, 0.9993, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1, 1, 1, 1, 0.99973, 0.99902, ]; -const HelveticaBoldLineHeight = 1.2; +const HelveticaBoldMetrics = { lineHeight: 1.2, lineGap: 0.2 }; // Factors to rescale LiberationSans-BoldItalic.ttf to have the same // metrics as NimbusSans-BoldItalic.otf. @@ -177,7 +177,7 @@ const HelveticaBoldItalicFactors = [ 0.99973, 1.00065, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1, 1, 1, 1, 0.99973, 1.00061, ]; -const HelveticaBoldItalicLineHeight = 1.35; +const HelveticaBoldItalicMetrics = { lineHeight: 1.35, lineGap: 0.2 }; // Factors to rescale LiberationSans-Italic.ttf to have the same // metrics as NimbusSans-Italic.otf. @@ -258,7 +258,7 @@ const HelveticaItalicFactors = [ 0.99973, 0.99973, 1, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 1, 1.0005, 1, 1, 1, 1, 0.99973, 1, 1, 1, 1, 1, 0.99973, 0.99918, ]; -const HelveticaItalicLineHeight = 1.35; +const HelveticaItalicMetrics = { lineHeight: 1.35, lineGap: 0.2 }; // Factors to rescale LiberationSans-Regular.ttf to have the same // metrics as NimbusSans-Regular.otf. @@ -339,15 +339,15 @@ const HelveticaRegularFactors = [ 0.99977, 0.99977, 0.99977, 1, 1.00055, 1, 1, 1, 1, 0.99973, 1, 1, 1, 1, 1, 0.99973, 1.00019, ]; -const HelveticaRegularLineHeight = 1.2; +const HelveticaRegularMetrics = { lineHeight: 1.2, lineGap: 0.2 }; export { HelveticaBoldFactors, HelveticaBoldItalicFactors, - HelveticaBoldItalicLineHeight, - HelveticaBoldLineHeight, + HelveticaBoldItalicMetrics, + HelveticaBoldMetrics, HelveticaItalicFactors, - HelveticaItalicLineHeight, + HelveticaItalicMetrics, HelveticaRegularFactors, - HelveticaRegularLineHeight, + HelveticaRegularMetrics, }; diff --git a/src/core/myriadpro_factors.js b/src/core/myriadpro_factors.js index da06cd4c7..e62567be5 100644 --- a/src/core/myriadpro_factors.js +++ b/src/core/myriadpro_factors.js @@ -77,7 +77,7 @@ const MyriadProBoldFactors = [ 0.97579, 0.97579, 0.97579, 0.9332, 1.05993, 0.94039, 0.94039, 0.94039, 0.94039, 0.99793, 0.94039, 0.938, 0.938, 0.938, 0.938, 0.99793, 0.95776, ]; -const MyriadProBoldLineHeight = 1.2; +const MyriadProBoldMetrics = { lineHeight: 1.2, lineGap: 0.2 }; // Factors to rescale LiberationSans-BoldItalic.ttf to have the same // metrics as MyriadPro-BoldIt.otf. @@ -144,7 +144,7 @@ const MyriadProBoldItalicFactors = [ 0.89544, 1.0051, 0.89364, 0.89364, 0.89364, 0.89364, 0.97276, 0.89364, 0.9, 0.9, 0.9, 0.9, 0.97276, 0.86842, ]; -const MyriadProBoldItalicLineHeight = 1.2; +const MyriadProBoldItalicMetrics = { lineHeight: 1.2, lineGap: 0.2 }; // Factors to rescale LiberationSans-Italic.ttf to have the same // metrics as MyriadPro-It.otf. @@ -210,7 +210,7 @@ const MyriadProItalicFactors = [ 0.979, 0.979, 0.979, 0.979, 0.882, 0.93559, 0.882, 0.882, 0.882, 0.882, 0.88465, 0.882, 0.83, 0.83, 0.83, 0.83, 0.88465, 0.84596, ]; -const MyriadProItalicLineHeight = 1.2; +const MyriadProItalicMetrics = { lineHeight: 1.2, lineGap: 0.2 }; // Factors to rescale LiberationSans-Regular.ttf to have the same // metrics as MyriadPro-Regular.otf. @@ -276,15 +276,15 @@ const MyriadProRegularFactors = [ 1.01915, 0.926, 0.96705, 0.942, 0.942, 0.942, 0.942, 0.92241, 0.942, 0.856, 0.856, 0.856, 0.856, 0.92241, 0.92761, ]; -const MyriadProRegularLineHeight = 1.2; +const MyriadProRegularMetrics = { lineHeight: 1.2, lineGap: 0.2 }; export { MyriadProBoldFactors, MyriadProBoldItalicFactors, - MyriadProBoldItalicLineHeight, - MyriadProBoldLineHeight, + MyriadProBoldItalicMetrics, + MyriadProBoldMetrics, MyriadProItalicFactors, - MyriadProItalicLineHeight, + MyriadProItalicMetrics, MyriadProRegularFactors, - MyriadProRegularLineHeight, + MyriadProRegularMetrics, }; diff --git a/src/core/segoeui_factors.js b/src/core/segoeui_factors.js index 278d92bd7..d6b509ac3 100644 --- a/src/core/segoeui_factors.js +++ b/src/core/segoeui_factors.js @@ -81,7 +81,7 @@ const SegoeuiBoldFactors = [ 1.02511, 0.99298, 1.07237, 0.96752, 0.96752, 0.96752, 0.96752, 1.03424, 0.96752, 0.95801, 0.95801, 0.95801, 0.95801, 1.03424, 1.0106, ]; -const SegoeuiBoldLineHeight = 1.33008; +const SegoeuiBoldMetrics = { lineHeight: 1.33008, lineGap: 0 }; // Factors to rescale LiberationSans-BoldItalic.ttf to have the same // metrics as segoeuiz.ttf. @@ -152,7 +152,7 @@ const SegoeuiBoldItalicFactors = [ 0.96752, 0.96752, 1.036, 0.96752, 0.97168, 0.97168, 0.97168, 0.97168, 1.036, 0.95134, ]; -const SegoeuiBoldItalicLineHeight = 1.33008; +const SegoeuiBoldItalicMetrics = { lineHeight: 1.33008, lineGap: 0 }; // Factors to rescale LiberationSans-Italic.ttf to have the same // metrics as segoeuii.ttf. @@ -223,7 +223,7 @@ const SegoeuiItalicFactors = [ 0.96777, 0.96777, 0.96777, 0.96927, 0.96777, 0.9043, 0.9043, 0.9043, 0.9043, 0.96927, 0.95364, ]; -const SegoeuiItalicLineHeight = 1.33008; +const SegoeuiItalicMetrics = { lineHeight: 1.33008, lineGap: 0 }; // Factors to rescale LiberationSans-Regular.ttf to have the same // metrics as segoeui.ttf. @@ -294,15 +294,15 @@ const SegoeuiRegularFactors = [ 1.00068, 0.91797, 0.99346, 0.96777, 0.96777, 0.96777, 0.96777, 0.96927, 0.96777, 0.9043, 0.9043, 0.9043, 0.9043, 0.96927, 1.00221, ]; -const SegoeuiRegularLineHeight = 1.33008; +const SegoeuiRegularMetrics = { lineHeight: 1.33008, lineGap: 0 }; export { SegoeuiBoldFactors, SegoeuiBoldItalicFactors, - SegoeuiBoldItalicLineHeight, - SegoeuiBoldLineHeight, + SegoeuiBoldItalicMetrics, + SegoeuiBoldMetrics, SegoeuiItalicFactors, - SegoeuiItalicLineHeight, + SegoeuiItalicMetrics, SegoeuiRegularFactors, - SegoeuiRegularLineHeight, + SegoeuiRegularMetrics, }; diff --git a/src/core/xfa/fonts.js b/src/core/xfa/fonts.js index 839febf80..94b41fdbd 100644 --- a/src/core/xfa/fonts.js +++ b/src/core/xfa/fonts.js @@ -13,6 +13,8 @@ * limitations under the License. */ +import { $globalData } from "./xfa_object.js"; +import { stripQuotes } from "./utils.js"; import { warn } from "../../shared/util.js"; class FontFinder { @@ -178,4 +180,32 @@ function selectFont(xfaFont, typeface) { return typeface.regular; } -export { FontFinder, selectFont }; +function getMetrics(xfaFont, real = false) { + let pdfFont = null; + if (xfaFont) { + const name = stripQuotes(xfaFont.typeface); + const typeface = xfaFont[$globalData].fontFinder.find(name); + pdfFont = selectFont(xfaFont, typeface); + } + + if (!pdfFont) { + return { + lineHeight: 12, + lineGap: 2, + lineNoGap: 10, + }; + } + + const size = xfaFont.size || 10; + const lineHeight = pdfFont.lineHeight + ? Math.max(real ? 0 : 1.2, pdfFont.lineHeight) + : 1.2; + const lineGap = pdfFont.lineGap === undefined ? 0.2 : pdfFont.lineGap; + return { + lineHeight: lineHeight * size, + lineGap: lineGap * size, + lineNoGap: Math.max(1, lineHeight - lineGap) * size, + }; +} + +export { FontFinder, getMetrics, selectFont }; diff --git a/src/core/xfa/html_utils.js b/src/core/xfa/html_utils.js index d7fa3ce0b..6e49ad7e9 100644 --- a/src/core/xfa/html_utils.js +++ b/src/core/xfa/html_utils.js @@ -247,7 +247,7 @@ function layoutNode(node, availableSpace) { } } - const maxWidth = !node.w ? availableSpace.width : node.w; + const maxWidth = (!node.w ? availableSpace.width : node.w) - marginH; const fontFinder = node[$globalData].fontFinder; if ( node.value.exData && @@ -614,10 +614,8 @@ function setFontFamily(xfaFont, fontFinder, style) { } const pdfFont = selectFont(xfaFont, typeface); - if (pdfFont && pdfFont.lineHeight > 0) { + if (pdfFont) { style.lineHeight = Math.max(1.2, pdfFont.lineHeight); - } else { - style.lineHeight = 1.2; } } } diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index c0ec58cfd..3640f8890 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -97,6 +97,7 @@ import { HTMLResult, } from "./utils.js"; import { stringToBytes, Util, warn } from "../../shared/util.js"; +import { getMetrics } from "./fonts.js"; import { searchNode } from "./som.js"; const TEMPLATE_NS_ID = NamespaceIds.template.id; @@ -115,6 +116,30 @@ const MAX_ATTEMPTS_FOR_LRTB_LAYOUT = 2; // the loop after having MAX_EMPTY_PAGES empty pages. const MAX_EMPTY_PAGES = 3; +function getBorderDims(node) { + if (!node || !node.border) { + return { w: 0, h: 0 }; + } + + const borderExtra = node.border[$getExtra](); + if (!borderExtra) { + return { w: 0, h: 0 }; + } + + return { + w: + borderExtra.widths[0] + + borderExtra.widths[2] + + borderExtra.insets[0] + + borderExtra.insets[2], + h: + borderExtra.widths[1] + + borderExtra.widths[3] + + borderExtra.insets[1] + + borderExtra.insets[3], + }; +} + function hasMargin(node) { return ( node.margin && @@ -773,33 +798,38 @@ class Border extends XFAObject { this.margin = null; } - [$toStyle]() { - // TODO: incomplete. - const edges = this.edge.children.slice(); - if (edges.length < 4) { - const defaultEdge = edges[edges.length - 1] || new Edge({}); - for (let i = edges.length; i < 4; i++) { - edges.push(defaultEdge); + [$getExtra]() { + if (!this[$extra]) { + const edges = this.edge.children.slice(); + if (edges.length < 4) { + const defaultEdge = edges[edges.length - 1] || new Edge({}); + for (let i = edges.length; i < 4; i++) { + edges.push(defaultEdge); + } } - } + const widths = edges.map(edge => edge.thickness); + const insets = [0, 0, 0, 0]; + if (this.margin) { + insets[0] = this.margin.topInset; + insets[1] = this.margin.rightInset; + insets[2] = this.margin.bottomInset; + insets[3] = this.margin.leftInset; + } + this[$extra] = { widths, insets, edges }; + } + return this[$extra]; + } + + [$toStyle]() { + // TODO: incomplete (hand). + const { edges } = this[$getExtra](); const edgeStyles = edges.map(node => { const style = node[$toStyle](); style.color = style.color || "#000000"; return style; }); - const widths = edges.map(edge => edge.thickness); - const insets = [0, 0, 0, 0]; - if (this.margin) { - insets[0] = this.margin.topInset; - insets[1] = this.margin.rightInset; - insets[2] = this.margin.bottomInset; - insets[3] = this.margin.leftInset; - } - this[$extra] = { widths, insets }; - // TODO: hand. - const style = Object.create(null); if (this.margin) { Object.assign(style, this.margin[$toStyle]()); @@ -2580,7 +2610,6 @@ class Field extends XFAObject { } setTabIndex(this); - if ( !this.ui || this.presence === "hidden" || @@ -2603,17 +2632,36 @@ class Field extends XFAObject { : null; const savedW = this.w; const savedH = this.h; - if (this.w === "" || this.h === "") { - let marginH = 0; - let marginV = 0; - if (this.margin) { - marginH = this.margin.leftInset + this.margin.rightInset; - marginV = this.margin.topInset + this.margin.bottomInset; - } + let marginH = 0; + let marginV = 0; + if (this.margin) { + marginH = this.margin.leftInset + this.margin.rightInset; + marginV = this.margin.topInset + this.margin.bottomInset; + } + let borderDims = null; + if (this.w === "" || this.h === "") { let width = null; let height = null; + let uiW = 0; + let uiH = 0; + if (this.ui.checkButton) { + uiW = uiH = this.ui.checkButton.size; + } else { + const { w, h } = layoutNode(this, availableSpace); + if (w !== null) { + uiW = w; + uiH = h; + } else { + uiH = getMetrics(this.font, /* real = */ true).lineNoGap; + } + } + + borderDims = getBorderDims(this.ui[$getExtra]()); + uiW += borderDims.w; + uiH += borderDims.h; + if (this.caption) { const { w, h, isBroken } = this.caption[$getExtra](availableSpace); // See comment in Draw::[$toHTML] to have an explanation @@ -2624,32 +2672,36 @@ class Field extends XFAObject { width = w; height = h; - if (this.ui.checkButton) { - switch (this.caption.placement) { - case "left": - case "right": - case "inline": - width += this.ui.checkButton.size; - break; - case "top": - case "bottom": - height += this.ui.checkButton.size; - break; - } + + switch (this.caption.placement) { + case "left": + case "right": + case "inline": + width += uiW; + break; + case "top": + case "bottom": + height += uiH; + break; } + } else { + width = uiW; + height = uiH; } if (width && this.w === "") { + width += marginH; this.w = Math.min( this.maxW <= 0 ? Infinity : this.maxW, - Math.max(this.minW, width + marginH) + this.minW + 1 < width ? width : this.minW ); } if (height && this.h === "") { + height += marginV; this.h = Math.min( this.maxH <= 0 ? Infinity : this.maxH, - Math.max(this.minH, height + marginV) + this.minH + 1 < height ? height : this.minH ); } } @@ -2718,7 +2770,6 @@ class Field extends XFAObject { } const borderStyle = this.border ? this.border[$toStyle]() : null; - const bbox = computeBbox(this, html, availableSpace); const ui = this.ui[$toHTML]().html; if (!ui) { @@ -2775,6 +2826,22 @@ class Field extends XFAObject { } } + if (!this.ui.imageEdit && ui.children && ui.children[0] && this.h) { + borderDims = borderDims || getBorderDims(this.ui[$getExtra]()); + + let captionHeight = 0; + if (this.caption && ["top", "bottom"].includes(this.caption.placement)) { + captionHeight = this.caption.reserve; + if (captionHeight <= 0) { + captionHeight = this.caption[$getExtra](availableSpace).h; + } + const inputHeight = this.h - captionHeight - marginV - borderDims.h; + ui.children[0].attributes.style.height = measureToString(inputHeight); + } else { + ui.children[0].attributes.style.height = "100%"; + } + } + if (!caption) { if (ui.attributes.class) { // Even if no caption this class will help to center the ui. @@ -2803,22 +2870,20 @@ class Field extends XFAObject { ui.attributes.class = []; } + ui.children.splice(0, 0, caption); + switch (this.caption.placement) { case "left": - ui.children.splice(0, 0, caption); ui.attributes.class.push("xfaLeft"); break; case "right": - ui.children.push(caption); - ui.attributes.class.push("xfaLeft"); + ui.attributes.class.push("xfaRight"); break; case "top": - ui.children.splice(0, 0, caption); ui.attributes.class.push("xfaTop"); break; case "bottom": - ui.children.push(caption); - ui.attributes.class.push("xfaTop"); + ui.attributes.class.push("xfaBottom"); break; case "inline": // TODO; @@ -2857,11 +2922,17 @@ class Fill extends XFAObject { [$toStyle]() { const parent = this[$getParent](); + const grandpa = parent[$getParent](); + const ggrandpa = grandpa[$getParent](); const style = Object.create(null); let propName = "color"; if (parent instanceof Border) { propName = "background"; + if (ggrandpa instanceof Ui) { + // The default fill color is white. + style.background = "white"; + } } if (parent instanceof Rectangle || parent instanceof Arc) { propName = "fill"; @@ -5515,7 +5586,11 @@ class TextEdit extends XFAObject { "on", ]); this.id = attributes.id || ""; - this.multiLine = attributes.multiLine || ""; + this.multiLine = getInteger({ + data: attributes.multiLine, + defaultValue: "", + validate: x => x === 0 || x === 1, + }); this.use = attributes.use || ""; this.usehref = attributes.usehref || ""; this.vScrollPolicy = getStringOption(attributes.vScrollPolicy, [ @@ -5529,22 +5604,14 @@ class TextEdit extends XFAObject { this.margin = null; } - [$clean](builder) { - super[$clean](builder); - const parent = this[$getParent](); - const defaultValue = parent instanceof Draw ? 1 : 0; - this.multiLine = getInteger({ - data: this.multiLine, - defaultValue, - validate: x => x === 0 || x === 1, - }); - } - [$toHTML](availableSpace) { // TODO: incomplete. const style = toStyle(this, "border", "font", "margin"); let html; const field = this[$getParent]()[$getParent](); + if (this.multiLine === "") { + this.multiLine = field instanceof Draw ? 1 : 0; + } if (this.multiLine === 1) { html = { name: "textarea", @@ -5683,17 +5750,29 @@ class Ui extends XFAObject { this.textEdit = null; } + [$getExtra]() { + if (this[$extra] === undefined) { + for (const name of Object.getOwnPropertyNames(this)) { + if (name === "extras" || name === "picture") { + continue; + } + const obj = this[name]; + if (!(obj instanceof XFAObject)) { + continue; + } + + this[$extra] = obj; + return obj; + } + this[$extra] = null; + } + return this[$extra]; + } + [$toHTML](availableSpace) { // TODO: picture. - for (const name of Object.getOwnPropertyNames(this)) { - if (name === "extras" || name === "picture") { - continue; - } - const obj = this[name]; - if (!(obj instanceof XFAObject)) { - continue; - } - + const obj = this[$getExtra](); + if (obj) { return obj[$toHTML](availableSpace); } return HTMLResult.EMPTY; diff --git a/src/core/xfa/text.js b/src/core/xfa/text.js index 1ae4de6ee..651f963cb 100644 --- a/src/core/xfa/text.js +++ b/src/core/xfa/text.js @@ -181,9 +181,12 @@ class TextMeasure { if (lastFont.pdfFont) { const letterSpacing = lastFont.xfaFont.letterSpacing; const pdfFont = lastFont.pdfFont; + const fontLineHeight = pdfFont.lineHeight || 1.2; const lineHeight = - lastFont.lineHeight || - Math.ceil(Math.max(1.2, pdfFont.lineHeight) * fontSize); + lastFont.lineHeight || Math.max(1.2, fontLineHeight) * fontSize; + const lineGap = pdfFont.lineGap === undefined ? 0.2 : pdfFont.lineGap; + const noGap = fontLineHeight - lineGap; + const firstLineHeight = Math.max(1, noGap) * fontSize; const scale = fontSize / 1000; for (const line of str.split(/[\u2029\n]/)) { @@ -194,12 +197,13 @@ class TextMeasure { this.glyphs.push([ glyph.width * scale + letterSpacing, lineHeight, + firstLineHeight, glyph.unicode === " ", false, ]); } - this.glyphs.push([0, 0, false, true]); + this.glyphs.push([0, 0, 0, false, true]); } this.glyphs.pop(); return; @@ -208,10 +212,16 @@ class TextMeasure { // When we have no font in the pdf, just use the font size as default width. for (const line of str.split(/[\u2029\n]/)) { for (const char of line.split("")) { - this.glyphs.push([fontSize, fontSize, char === " ", false]); + this.glyphs.push([ + fontSize, + 1.2 * fontSize, + fontSize, + char === " ", + false, + ]); } - this.glyphs.push([0, 0, false, true]); + this.glyphs.push([0, 0, 0, false, true]); } this.glyphs.pop(); } @@ -224,9 +234,12 @@ class TextMeasure { currentLineWidth = 0, currentLineHeight = 0; let isBroken = false; + let isFirstLine = true; for (let i = 0, ii = this.glyphs.length; i < ii; i++) { - const [glyphWidth, glyphHeight, isSpace, isEOL] = this.glyphs[i]; + const [glyphWidth, lineHeight, firstLineHeight, isSpace, isEOL] = + this.glyphs[i]; + const glyphHeight = isFirstLine ? firstLineHeight : lineHeight; if (isEOL) { width = Math.max(width, currentLineWidth); currentLineWidth = 0; @@ -234,6 +247,7 @@ class TextMeasure { currentLineHeight = glyphHeight; lastSpacePos = -1; lastSpaceWidth = 0; + isFirstLine = false; continue; } @@ -247,6 +261,7 @@ class TextMeasure { lastSpacePos = -1; lastSpaceWidth = 0; isBroken = true; + isFirstLine = false; } else { currentLineHeight = Math.max(glyphHeight, currentLineHeight); lastSpaceWidth = currentLineWidth; @@ -272,6 +287,7 @@ class TextMeasure { currentLineWidth = glyphWidth; } isBroken = true; + isFirstLine = false; continue; } diff --git a/src/core/xfa_fonts.js b/src/core/xfa_fonts.js index 9b52b62ce..5ee81af0f 100644 --- a/src/core/xfa_fonts.js +++ b/src/core/xfa_fonts.js @@ -16,22 +16,22 @@ import { CalibriBoldFactors, CalibriBoldItalicFactors, - CalibriBoldItalicLineHeight, - CalibriBoldLineHeight, + CalibriBoldItalicMetrics, + CalibriBoldMetrics, CalibriItalicFactors, - CalibriItalicLineHeight, + CalibriItalicMetrics, CalibriRegularFactors, - CalibriRegularLineHeight, + CalibriRegularMetrics, } from "./calibri_factors.js"; import { HelveticaBoldFactors, HelveticaBoldItalicFactors, - HelveticaBoldItalicLineHeight, - HelveticaBoldLineHeight, + HelveticaBoldItalicMetrics, + HelveticaBoldMetrics, HelveticaItalicFactors, - HelveticaItalicLineHeight, + HelveticaItalicMetrics, HelveticaRegularFactors, - HelveticaRegularLineHeight, + HelveticaRegularMetrics, } from "./helvetica_factors.js"; import { LiberationSansBoldItalicWidths, @@ -42,22 +42,22 @@ import { import { MyriadProBoldFactors, MyriadProBoldItalicFactors, - MyriadProBoldItalicLineHeight, - MyriadProBoldLineHeight, + MyriadProBoldItalicMetrics, + MyriadProBoldMetrics, MyriadProItalicFactors, - MyriadProItalicLineHeight, + MyriadProItalicMetrics, MyriadProRegularFactors, - MyriadProRegularLineHeight, + MyriadProRegularMetrics, } from "./myriadpro_factors.js"; import { SegoeuiBoldFactors, SegoeuiBoldItalicFactors, - SegoeuiBoldItalicLineHeight, - SegoeuiBoldLineHeight, + SegoeuiBoldItalicMetrics, + SegoeuiBoldMetrics, SegoeuiItalicFactors, - SegoeuiItalicLineHeight, + SegoeuiItalicMetrics, SegoeuiRegularFactors, - SegoeuiRegularLineHeight, + SegoeuiRegularMetrics, } from "./segoeui_factors.js"; import { getLookupTableFactory } from "./core_utils.js"; import { normalizeFontName } from "./fonts_utils.js"; @@ -67,13 +67,13 @@ const getXFAFontMap = getLookupTableFactory(function (t) { name: "LiberationSans-Regular", factors: MyriadProRegularFactors, baseWidths: LiberationSansRegularWidths, - lineHeight: MyriadProRegularLineHeight, + metrics: MyriadProRegularMetrics, }; t["MyriadPro-Bold"] = t["PdfJS-Fallback-Bold"] = { name: "LiberationSans-Bold", factors: MyriadProBoldFactors, baseWidths: LiberationSansBoldWidths, - lineHeight: MyriadProBoldLineHeight, + metrics: MyriadProBoldMetrics, }; t["MyriadPro-It"] = t["MyriadPro-Italic"] = @@ -82,7 +82,7 @@ const getXFAFontMap = getLookupTableFactory(function (t) { name: "LiberationSans-Italic", factors: MyriadProItalicFactors, baseWidths: LiberationSansItalicWidths, - lineHeight: MyriadProItalicLineHeight, + metrics: MyriadProItalicMetrics, }; t["MyriadPro-BoldIt"] = t["MyriadPro-BoldItalic"] = @@ -91,7 +91,7 @@ const getXFAFontMap = getLookupTableFactory(function (t) { name: "LiberationSans-BoldItalic", factors: MyriadProBoldItalicFactors, baseWidths: LiberationSansBoldItalicWidths, - lineHeight: MyriadProBoldItalicLineHeight, + metrics: MyriadProBoldItalicMetrics, }; t.ArialMT = t.Arial = @@ -116,73 +116,73 @@ const getXFAFontMap = getLookupTableFactory(function (t) { name: "LiberationSans-Regular", factors: CalibriRegularFactors, baseWidths: LiberationSansRegularWidths, - lineHeight: CalibriRegularLineHeight, + metrics: CalibriRegularMetrics, }; t["Calibri-Bold"] = { name: "LiberationSans-Bold", factors: CalibriBoldFactors, baseWidths: LiberationSansBoldWidths, - lineHeight: CalibriBoldLineHeight, + metrics: CalibriBoldMetrics, }; t["Calibri-Italic"] = { name: "LiberationSans-Italic", factors: CalibriItalicFactors, baseWidths: LiberationSansItalicWidths, - lineHeight: CalibriItalicLineHeight, + metrics: CalibriItalicMetrics, }; t["Calibri-BoldItalic"] = { name: "LiberationSans-BoldItalic", factors: CalibriBoldItalicFactors, baseWidths: LiberationSansBoldItalicWidths, - lineHeight: CalibriBoldItalicLineHeight, + metrics: CalibriBoldItalicMetrics, }; t["Segoeui-Regular"] = { name: "LiberationSans-Regular", factors: SegoeuiRegularFactors, baseWidths: LiberationSansRegularWidths, - lineHeight: SegoeuiRegularLineHeight, + metrics: SegoeuiRegularMetrics, }; t["Segoeui-Bold"] = { name: "LiberationSans-Bold", factors: SegoeuiBoldFactors, baseWidths: LiberationSansBoldWidths, - lineHeight: SegoeuiBoldLineHeight, + metrics: SegoeuiBoldMetrics, }; t["Segoeui-Italic"] = { name: "LiberationSans-Italic", factors: SegoeuiItalicFactors, baseWidths: LiberationSansItalicWidths, - lineHeight: SegoeuiItalicLineHeight, + metrics: SegoeuiItalicMetrics, }; t["Segoeui-BoldItalic"] = { name: "LiberationSans-BoldItalic", factors: SegoeuiBoldItalicFactors, baseWidths: LiberationSansBoldItalicWidths, - lineHeight: SegoeuiBoldItalicLineHeight, + metrics: SegoeuiBoldItalicMetrics, }; t["Helvetica-Regular"] = t.Helvetica = { name: "LiberationSans-Regular", factors: HelveticaRegularFactors, baseWidths: LiberationSansRegularWidths, - lineHeight: HelveticaRegularLineHeight, + metrics: HelveticaRegularMetrics, }; t["Helvetica-Bold"] = { name: "LiberationSans-Bold", factors: HelveticaBoldFactors, baseWidths: LiberationSansBoldWidths, - lineHeight: HelveticaBoldLineHeight, + metrics: HelveticaBoldMetrics, }; t["Helvetica-Italic"] = { name: "LiberationSans-Italic", factors: HelveticaItalicFactors, baseWidths: LiberationSansItalicWidths, - lineHeight: HelveticaItalicLineHeight, + metrics: HelveticaItalicMetrics, }; t["Helvetica-BoldItalic"] = { name: "LiberationSans-BoldItalic", factors: HelveticaBoldItalicFactors, baseWidths: LiberationSansBoldItalicWidths, - lineHeight: HelveticaBoldItalicLineHeight, + metrics: HelveticaBoldItalicMetrics, }; }); diff --git a/test/pdfs/xfa_bug1722030_1.pdf.link b/test/pdfs/xfa_bug1722030_1.pdf.link new file mode 100644 index 000000000..79807d2fa --- /dev/null +++ b/test/pdfs/xfa_bug1722030_1.pdf.link @@ -0,0 +1 @@ +https://bug1722030.bmoattachments.org/attachment.cgi?id=9232840 diff --git a/test/test_manifest.json b/test/test_manifest.json index 43d1858f2..2e29cc8bf 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -952,6 +952,15 @@ "enableXfa": true, "type": "eq" }, + { "id": "xfa_bug1722030_1", + "file": "pdfs/xfa_bug1722030_1.pdf", + "md5": "0baf149f21536ebb2bfc636a95e1d62d", + "link": true, + "rounds": 1, + "lastPage": 3, + "enableXfa": true, + "type": "eq" + }, { "id": "xfa_bug1722029", "file": "pdfs/xfa_bug1722029.pdf", "md5": "d8dd6bb20599fc777b4c7ff7b74dd5e3", diff --git a/web/xfa_layer_builder.css b/web/xfa_layer_builder.css index 97cd6c01b..38e7d21d8 100644 --- a/web/xfa_layer_builder.css +++ b/web/xfa_layer_builder.css @@ -84,7 +84,7 @@ .xfaCaption { overflow: hidden; - flex: 0 1 auto; + flex: 0 0 auto; } .xfaCaptionForCheckButton { @@ -103,8 +103,16 @@ align-items: center; } +.xfaRight { + display: flex; + flex-direction: row-reverse; + align-items: center; +} + .xfaLeft > .xfaCaption, -.xfaLeft > .xfaCaptionForCheckButton { +.xfaLeft > .xfaCaptionForCheckButton, +.xfaRight > .xfaCaption, +.xfaRight > .xfaCaptionForCheckButton { max-height: 100%; } @@ -114,8 +122,16 @@ align-items: flex-start; } +.xfaBottom { + display: flex; + flex-direction: column-reverse; + align-items: flex-start; +} + .xfaTop > .xfaCaption, -.xfaTop > .xfaCaptionForCheckButton { +.xfaTop > .xfaCaptionForCheckButton, +.xfaBottom > .xfaCaption, +.xfaBottom > .xfaCaptionForCheckButton { width: 100%; } @@ -130,24 +146,27 @@ height: 100%; } -.xfaTextfield, -.xfaSelect { - background-color: rgba(0, 54, 255, 0.13); -} - .xfaTextfield:focus, .xfaSelect:focus { - background-color: transparent; + background: transparent; outline: none; } .xfaTextfield, .xfaSelect { - width: 100%; height: 100%; - flex: 1 1 0; + width: 100%; + flex: 1 1 auto; border: none; resize: none; + background: rgba(0, 54, 255, 0.13); +} + +.xfaTop > .xfaTextfield, +.xfaTop > .xfaSelect, +.xfaBottom > .xfaTextfield, +.xfaBottom > .xfaSelect { + flex: 0 1 auto; } .xfaButton { @@ -158,10 +177,6 @@ text-align: center; } -.xfaButton:hover { - background: Highlight; -} - .xfaCheckbox, .xfaRadio { width: 100%; @@ -255,7 +270,7 @@ @media print { .xfaTextfield, .xfaSelect { - background-color: transparent; + background: transparent; } .xfaSelect {