1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-28 23:28:16 +02:00

XFA - Improve text layout

- support paragraph margins, line height, letter spacing, ...
  - compute missing dimensions from fields based almost on the dimensions of caption contents.
This commit is contained in:
Calixte Denizet 2021-07-02 17:53:27 +02:00
parent d80651e572
commit f7d3b22480
5 changed files with 359 additions and 99 deletions

View file

@ -15,17 +15,30 @@
import { selectFont } from "./fonts.js";
const WIDTH_FACTOR = 1.2;
const HEIGHT_FACTOR = 1.2;
const WIDTH_FACTOR = 1.05;
class FontInfo {
constructor(xfaFont, fontFinder) {
constructor(xfaFont, margin, lineHeight, fontFinder) {
this.lineHeight = lineHeight;
this.paraMargin = margin || {
top: 0,
bottom: 0,
left: 0,
right: 0,
};
if (!xfaFont) {
[this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
return;
}
this.xfaFont = xfaFont;
this.xfaFont = {
typeface: xfaFont.typeface,
posture: xfaFont.posture,
weight: xfaFont.weight,
size: xfaFont.size,
letterSpacing: xfaFont.letterSpacing,
};
const typeface = fontFinder.find(xfaFont.typeface);
if (!typeface) {
[this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
@ -54,6 +67,7 @@ class FontInfo {
posture: "normal",
weight: "normal",
size: 10,
letterSpacing: 0,
};
return [pdfFont, xfaFont];
}
@ -63,29 +77,60 @@ class FontInfo {
posture: "normal",
weight: "normal",
size: 10,
letterSpacing: 0,
};
return [null, xfaFont];
}
}
class FontSelector {
constructor(defaultXfaFont, fontFinder) {
constructor(
defaultXfaFont,
defaultParaMargin,
defaultLineHeight,
fontFinder
) {
this.fontFinder = fontFinder;
this.stack = [new FontInfo(defaultXfaFont, fontFinder)];
this.stack = [
new FontInfo(
defaultXfaFont,
defaultParaMargin,
defaultLineHeight,
fontFinder
),
];
}
pushFont(xfaFont) {
pushData(xfaFont, margin, lineHeight) {
const lastFont = this.stack[this.stack.length - 1];
for (const name of ["typeface", "posture", "weight", "size"]) {
for (const name of [
"typeface",
"posture",
"weight",
"size",
"letterSpacing",
]) {
if (!xfaFont[name]) {
xfaFont[name] = lastFont.xfaFont[name];
}
}
const fontInfo = new FontInfo(xfaFont, this.fontFinder);
for (const name of ["top", "bottom", "left", "right"]) {
if (isNaN(margin[name])) {
margin[name] = lastFont.paraMargin[name];
}
}
const fontInfo = new FontInfo(
xfaFont,
margin,
lineHeight || lastFont.lineHeight,
this.fontFinder
);
if (!fontInfo.pdfFont) {
fontInfo.pdfFont = lastFont.pdfFont;
}
this.stack.push(fontInfo);
}
@ -102,19 +147,30 @@ class FontSelector {
* Compute a text area dimensions based on font metrics.
*/
class TextMeasure {
constructor(defaultXfaFont, fonts) {
constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts) {
this.glyphs = [];
this.fontSelector = new FontSelector(defaultXfaFont, fonts);
this.fontSelector = new FontSelector(
defaultXfaFont,
defaultParaMargin,
defaultLineHeight,
fonts
);
this.extraHeight = 0;
}
pushFont(xfaFont) {
return this.fontSelector.pushFont(xfaFont);
pushData(xfaFont, margin, lineHeight) {
this.fontSelector.pushData(xfaFont, margin, lineHeight);
}
popFont(xfaFont) {
return this.fontSelector.popFont();
}
addPara() {
const lastFont = this.fontSelector.topFont();
this.extraHeight += lastFont.paraMargin.top + lastFont.paraMargin.bottom;
}
addString(str) {
if (!str) {
return;
@ -123,8 +179,11 @@ class TextMeasure {
const lastFont = this.fontSelector.topFont();
const fontSize = lastFont.xfaFont.size;
if (lastFont.pdfFont) {
const letterSpacing = lastFont.xfaFont.letterSpacing;
const pdfFont = lastFont.pdfFont;
const lineHeight = Math.round(Math.max(1, pdfFont.lineHeight) * fontSize);
const lineHeight =
lastFont.lineHeight ||
Math.round(Math.max(1, pdfFont.lineHeight) * fontSize);
const scale = fontSize / 1000;
for (const line of str.split(/[\u2029\n]/)) {
@ -133,7 +192,7 @@ class TextMeasure {
for (const glyph of glyphs) {
this.glyphs.push([
glyph.width * scale,
glyph.width * scale + letterSpacing,
lineHeight,
glyph.unicode === " ",
false,
@ -218,9 +277,9 @@ class TextMeasure {
}
width = Math.max(width, currentLineWidth);
height += currentLineHeight;
height += currentLineHeight + this.extraHeight;
return { width: WIDTH_FACTOR * width, height: HEIGHT_FACTOR * height };
return { width: WIDTH_FACTOR * width, height };
}
}