Skip to content

Commit edbe969

Browse files
authored
Merge pull request #13815 from calixteman/xfa_fonts2
XFA - Fix font scale factors (bug 1720888)
2 parents ac5c4b7 + 4a4591b commit edbe969

17 files changed

+1605
-1239
lines changed

src/core/calibri_factors.js

+252-251
Large diffs are not rendered by default.

src/core/document.js

+3-11
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ import {
5353
XRefEntryException,
5454
XRefParseException,
5555
} from "./core_utils.js";
56+
import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js";
5657
import { NullStream, Stream } from "./stream.js";
5758
import { AnnotationFactory } from "./annotation.js";
5859
import { BaseStream } from "./base_stream.js";
5960
import { calculateMD5 } from "./crypto.js";
6061
import { Catalog } from "./catalog.js";
61-
import { getXfaFontWidths } from "./xfa_fonts.js";
6262
import { Linearization } from "./parser.js";
6363
import { ObjectLoader } from "./object_loader.js";
6464
import { OperatorList } from "./operator_list.js";
@@ -1019,7 +1019,7 @@ class PDFDocument {
10191019

10201020
const reallyMissingFonts = new Set();
10211021
for (const missing of missingFonts) {
1022-
if (!getXfaFontWidths(`${missing}-Regular`)) {
1022+
if (!getXfaFontName(`${missing}-Regular`)) {
10231023
// No substitution available: we'll fallback on Myriad.
10241024
reallyMissingFonts.add(missing);
10251025
}
@@ -1040,15 +1040,7 @@ class PDFDocument {
10401040
{ name: "BoldItalic", fontWeight: 700, italicAngle: 12 },
10411041
]) {
10421042
const name = `${missing}-${fontInfo.name}`;
1043-
const widths = getXfaFontWidths(name);
1044-
const dict = new Dict(null);
1045-
dict.set("BaseFont", Name.get(name));
1046-
dict.set("Type", Name.get("Font"));
1047-
dict.set("Subtype", Name.get("TrueType"));
1048-
dict.set("Encoding", Name.get("WinAnsiEncoding"));
1049-
const descriptor = new Dict(null);
1050-
descriptor.set("Widths", widths);
1051-
dict.set("FontDescriptor", descriptor);
1043+
const dict = getXfaFontDict(name);
10521044

10531045
promises.push(
10541046
partialEvaluator

src/core/evaluator.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import {
7070
reverseIfRtl,
7171
} from "./unicode.js";
7272
import { getTilingPatternIR, Pattern } from "./pattern.js";
73+
import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js";
7374
import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js";
7475
import { isPDFFunction, PDFFunctionFactory } from "./function.js";
7576
import { Lexer, Parser } from "./parser.js";
@@ -86,7 +87,6 @@ import { DecodeStream } from "./decode_stream.js";
8687
import { getGlyphsUnicode } from "./glyphlist.js";
8788
import { getLookupTableFactory } from "./core_utils.js";
8889
import { getMetrics } from "./metrics.js";
89-
import { getXfaFontName } from "./xfa_fonts.js";
9090
import { MurmurHash3_64 } from "./murmurhash3.js";
9191
import { OperatorList } from "./operator_list.js";
9292
import { PDFImage } from "./image.js";
@@ -3928,7 +3928,13 @@ class PartialEvaluator {
39283928
glyphScaleFactors = standardFontName.factors || null;
39293929
fontFile = await this.fetchStandardFontData(standardFontName.name);
39303930
isInternalFont = !!fontFile;
3931-
type = "TrueType";
3931+
3932+
// We're using a substitution font but for example widths (if any)
3933+
// are related to the glyph positions in the font.
3934+
// So we overwrite everything here to be sure that widths are
3935+
// correct.
3936+
baseDict = dict = getXfaFontDict(fontName.name);
3937+
composite = true;
39323938
}
39333939
} else if (!isType3Font) {
39343940
const standardFontName = getStandardFontName(fontName.name);

src/core/fonts.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -3200,15 +3200,19 @@ class Font {
32003200
// and currentBuf contains non-encoded chars.
32013201
const hasCurrentBufErrors = () => buffers.length % 2 === 1;
32023202

3203+
const getCharCode =
3204+
this.toUnicode instanceof IdentityToUnicodeMap
3205+
? unicode => this.toUnicode.charCodeOf(unicode)
3206+
: unicode => this.toUnicode.charCodeOf(String.fromCodePoint(unicode));
3207+
32033208
for (let i = 0, ii = str.length; i < ii; i++) {
32043209
const unicode = str.codePointAt(i);
32053210
if (unicode > 0xd7ff && (unicode < 0xe000 || unicode > 0xfffd)) {
32063211
// unicode is represented by two uint16
32073212
i++;
32083213
}
32093214
if (this.toUnicode) {
3210-
const char = String.fromCodePoint(unicode);
3211-
const charCode = this.toUnicode.charCodeOf(char);
3215+
const charCode = getCharCode(unicode);
32123216
if (charCode !== -1) {
32133217
if (hasCurrentBufErrors()) {
32143218
buffers.push(currentBuf.join(""));

src/core/helvetica_factors.js

+291-292
Large diffs are not rendered by default.

src/core/liberationsans_widths.js

+388-180
Large diffs are not rendered by default.

src/core/myriadpro_factors.js

+226-225
Large diffs are not rendered by default.

src/core/segoeui_factors.js

+251-251
Large diffs are not rendered by default.

src/core/xfa/html_utils.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,11 @@ function isPrintOnly(node) {
560560
);
561561
}
562562

563+
function getCurrentPara(node) {
564+
const stack = node[$getTemplateRoot]()[$extra].paraStack;
565+
return stack.length ? stack[stack.length - 1] : null;
566+
}
567+
563568
function setPara(node, nodeStyle, value) {
564569
if (value.attributes.class && value.attributes.class.includes("xfaRich")) {
565570
if (nodeStyle) {
@@ -570,13 +575,15 @@ function setPara(node, nodeStyle, value) {
570575
nodeStyle.width = "auto";
571576
}
572577
}
573-
if (node.para) {
578+
579+
const para = getCurrentPara(node);
580+
if (para) {
574581
// By definition exData are external data so para
575582
// has no effect on it.
576583
const valueStyle = value.attributes.style;
577584
valueStyle.display = "flex";
578585
valueStyle.flexDirection = "column";
579-
switch (node.para.vAlign) {
586+
switch (para.vAlign) {
580587
case "top":
581588
valueStyle.justifyContent = "start";
582589
break;
@@ -588,7 +595,7 @@ function setPara(node, nodeStyle, value) {
588595
break;
589596
}
590597

591-
const paraStyle = node.para[$toStyle]();
598+
const paraStyle = para[$toStyle]();
592599
for (const [key, val] of Object.entries(paraStyle)) {
593600
if (!(key in valueStyle)) {
594601
valueStyle[key] = val;
@@ -598,7 +605,7 @@ function setPara(node, nodeStyle, value) {
598605
}
599606
}
600607

601-
function setFontFamily(xfaFont, fontFinder, style) {
608+
function setFontFamily(xfaFont, node, fontFinder, style) {
602609
const name = stripQuotes(xfaFont.typeface);
603610
const typeface = fontFinder.find(name);
604611

@@ -608,6 +615,12 @@ function setFontFamily(xfaFont, fontFinder, style) {
608615
if (fontFamily !== name) {
609616
style.fontFamily = `"${fontFamily}"`;
610617
}
618+
619+
const para = getCurrentPara(node);
620+
if (para && para.lineHeight !== "") {
621+
return;
622+
}
623+
611624
if (style.lineHeight) {
612625
// Already something so don't overwrite.
613626
return;

src/core/xfa/template.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import {
4646
$nodeName,
4747
$onChild,
4848
$onText,
49+
$popPara,
50+
$pushPara,
4951
$removeChild,
5052
$searchNode,
5153
$setSetAttributes,
@@ -1062,8 +1064,11 @@ class Caption extends XFAObject {
10621064
return HTMLResult.EMPTY;
10631065
}
10641066

1067+
this[$pushPara]();
10651068
const value = this.value[$toHTML](availableSpace).html;
1069+
10661070
if (!value) {
1071+
this[$popPara]();
10671072
return HTMLResult.EMPTY;
10681073
}
10691074

@@ -1110,6 +1115,7 @@ class Caption extends XFAObject {
11101115
}
11111116

11121117
setPara(this, null, value);
1118+
this[$popPara]();
11131119

11141120
this.reserve = savedReserve;
11151121

@@ -1730,6 +1736,7 @@ class Draw extends XFAObject {
17301736
}
17311737

17321738
fixDimensions(this);
1739+
this[$pushPara]();
17331740

17341741
// If at least one dimension is missing and we've a text
17351742
// then we can guess it in laying out the text.
@@ -1744,6 +1751,7 @@ class Draw extends XFAObject {
17441751
// So if we've potentially more width to provide in some parent containers
17451752
// let's increase it to give a chance to have a better rendering.
17461753
if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) {
1754+
this[$popPara]();
17471755
return HTMLResult.FAILURE;
17481756
}
17491757

@@ -1757,6 +1765,7 @@ class Draw extends XFAObject {
17571765
if (!checkDimensions(this, availableSpace)) {
17581766
this.w = savedW;
17591767
this.h = savedH;
1768+
this[$popPara]();
17601769
return HTMLResult.FAILURE;
17611770
}
17621771
unsetFirstUnsplittable(this);
@@ -1816,6 +1825,7 @@ class Draw extends XFAObject {
18161825
if (value === null) {
18171826
this.w = savedW;
18181827
this.h = savedH;
1828+
this[$popPara]();
18191829
return HTMLResult.success(createWrapper(this, html), bbox);
18201830
}
18211831

@@ -1825,6 +1835,7 @@ class Draw extends XFAObject {
18251835
this.w = savedW;
18261836
this.h = savedH;
18271837

1838+
this[$popPara]();
18281839
return HTMLResult.success(createWrapper(this, html), bbox);
18291840
}
18301841
}
@@ -2364,6 +2375,7 @@ class ExclGroup extends XFAObject {
23642375
attributes.xfaName = this.name;
23652376
}
23662377

2378+
this[$pushPara]();
23672379
const isLrTb = this.layout === "lr-tb" || this.layout === "rl-tb";
23682380
const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1;
23692381
for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {
@@ -2381,6 +2393,7 @@ class ExclGroup extends XFAObject {
23812393
break;
23822394
}
23832395
if (result.isBreak()) {
2396+
this[$popPara]();
23842397
return result;
23852398
}
23862399
if (
@@ -2395,6 +2408,8 @@ class ExclGroup extends XFAObject {
23952408
}
23962409
}
23972410

2411+
this[$popPara]();
2412+
23982413
if (!isSplittable) {
23992414
unsetFirstUnsplittable(this);
24002415
}
@@ -2627,6 +2642,8 @@ class Field extends XFAObject {
26272642
delete this.caption[$extra];
26282643
}
26292644

2645+
this[$pushPara]();
2646+
26302647
const caption = this.caption
26312648
? this.caption[$toHTML](availableSpace).html
26322649
: null;
@@ -2667,6 +2684,7 @@ class Field extends XFAObject {
26672684
// See comment in Draw::[$toHTML] to have an explanation
26682685
// about this line.
26692686
if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) {
2687+
this[$popPara]();
26702688
return HTMLResult.FAILURE;
26712689
}
26722690

@@ -2706,12 +2724,15 @@ class Field extends XFAObject {
27062724
}
27072725
}
27082726

2727+
this[$popPara]();
2728+
27092729
fixDimensions(this);
27102730

27112731
setFirstUnsplittable(this);
27122732
if (!checkDimensions(this, availableSpace)) {
27132733
this.w = savedW;
27142734
this.h = savedH;
2735+
this[$popPara]();
27152736
return HTMLResult.FAILURE;
27162737
}
27172738
unsetFirstUnsplittable(this);
@@ -3131,7 +3152,7 @@ class Font extends XFAObject {
31313152
style.fontStyle = this.posture;
31323153
style.fontSize = measureToString(0.99 * this.size);
31333154

3134-
setFontFamily(this, this[$globalData].fontFinder, style);
3155+
setFontFamily(this, this, this[$globalData].fontFinder, style);
31353156

31363157
if (this.underline !== 0) {
31373158
style.textDecoration = "underline";
@@ -4957,6 +4978,7 @@ class Subform extends XFAObject {
49574978
}
49584979
}
49594980

4981+
this[$pushPara]();
49604982
const isLrTb = this.layout === "lr-tb" || this.layout === "rl-tb";
49614983
const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1;
49624984
for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {
@@ -4974,6 +4996,7 @@ class Subform extends XFAObject {
49744996
break;
49754997
}
49764998
if (result.isBreak()) {
4999+
this[$popPara]();
49775000
return result;
49785001
}
49795002
if (
@@ -4995,6 +5018,7 @@ class Subform extends XFAObject {
49955018
}
49965019
}
49975020

5021+
this[$popPara]();
49985022
if (!isSplittable) {
49995023
unsetFirstUnsplittable(this);
50005024
}
@@ -5242,6 +5266,7 @@ class Template extends XFAObject {
52425266
pagePosition: "first",
52435267
oddOrEven: "odd",
52445268
blankOrNotBlank: "nonBlank",
5269+
paraStack: [],
52455270
};
52465271

52475272
const root = this.subform.children[0];

src/core/xfa/text.js

+11-13
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import { selectFont } from "./fonts.js";
1717

18-
const WIDTH_FACTOR = 1.01;
18+
const WIDTH_FACTOR = 1.02;
1919

2020
class FontInfo {
2121
constructor(xfaFont, margin, lineHeight, fontFinder) {
@@ -188,22 +188,25 @@ class TextMeasure {
188188
const noGap = fontLineHeight - lineGap;
189189
const firstLineHeight = Math.max(1, noGap) * fontSize;
190190
const scale = fontSize / 1000;
191+
const fallbackWidth =
192+
pdfFont.defaultWidth || pdfFont.charsToGlyphs(" ")[0].width;
191193

192194
for (const line of str.split(/[\u2029\n]/)) {
193195
const encodedLine = pdfFont.encodeString(line).join("");
194196
const glyphs = pdfFont.charsToGlyphs(encodedLine);
195197

196198
for (const glyph of glyphs) {
199+
const width = glyph.width || fallbackWidth;
197200
this.glyphs.push([
198-
glyph.width * scale + letterSpacing,
201+
width * scale + letterSpacing,
199202
lineHeight,
200203
firstLineHeight,
201-
glyph.unicode === " ",
204+
glyph.unicode,
202205
false,
203206
]);
204207
}
205208

206-
this.glyphs.push([0, 0, 0, false, true]);
209+
this.glyphs.push([0, 0, 0, "\n", true]);
207210
}
208211
this.glyphs.pop();
209212
return;
@@ -212,16 +215,10 @@ class TextMeasure {
212215
// When we have no font in the pdf, just use the font size as default width.
213216
for (const line of str.split(/[\u2029\n]/)) {
214217
for (const char of line.split("")) {
215-
this.glyphs.push([
216-
fontSize,
217-
1.2 * fontSize,
218-
fontSize,
219-
char === " ",
220-
false,
221-
]);
218+
this.glyphs.push([fontSize, 1.2 * fontSize, fontSize, char, false]);
222219
}
223220

224-
this.glyphs.push([0, 0, 0, false, true]);
221+
this.glyphs.push([0, 0, 0, "\n", true]);
225222
}
226223
this.glyphs.pop();
227224
}
@@ -237,8 +234,9 @@ class TextMeasure {
237234
let isFirstLine = true;
238235

239236
for (let i = 0, ii = this.glyphs.length; i < ii; i++) {
240-
const [glyphWidth, lineHeight, firstLineHeight, isSpace, isEOL] =
237+
const [glyphWidth, lineHeight, firstLineHeight, char, isEOL] =
241238
this.glyphs[i];
239+
const isSpace = char === " ";
242240
const glyphHeight = isFirstLine ? firstLineHeight : lineHeight;
243241
if (isEOL) {
244242
width = Math.max(width, currentLineWidth);

0 commit comments

Comments
 (0)