Repository navigation
Expand file tree
/
Copy pathpdf-bionic-runtime.mjs
More file actions
112 lines (111 loc) · 5.3 KB
/
Copy pathpdf-bionic-runtime.mjs
File metadata and controls
112 lines (111 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// The upstream public API has no per-glyph paint hook. Extend this exact runtime
// at bundle time, and fail the build if its implementation changes. Two hooks
// share it: bionic glyph strokes and local font substitution.
export function extendPDFRuntime(source) {
if (!source.includes('const version = "6.3.289";'))
throw new Error('Unsupported pdf.js version: bionic glyph hook needs review');
function replace(before, after) {
if (source.split(before).length !== 2)
throw new Error('Unsupported pdf.js runtime: bionic glyph hook needs review');
source = source.replace(before, after);
}
replace(
' recordOperations = false,\n operationsFilter = null',
' recordOperations = false,\n bionicGlyphs = null,\n fontSubstitution = null,\n operationsFilter = null',
);
replace(
' transform,\n background\n },\n objs: this.objs,',
' transform,\n background,\n bionicGlyphs,\n fontSubstitution\n },\n objs: this.objs,',
);
replace(
' this.gfx.beginDrawing({',
' this.gfx.bionicGlyphs = this.params.bionicGlyphs;\n this.gfx.fontSubstitution = this.params.fontSubstitution;\n this.gfx.beginDrawing({',
);
replace(
' const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;',
' const bionic = this.bionicGlyphs?.get(opIdx);\n const simpleFillText = !bionic && current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;',
);
replace(
' if (this.contentVisible && (glyph.isInFont || font.missingFile)) {',
` const bionicStrength = bionic?.get(i) || 0;
const bionicPaint = bionicStrength > 0 &&
(current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK) === TextRenderingMode.FILL &&
!current.patternFill;
const originalTextMode = current.textRenderingMode;
if (bionicPaint) {
ctx.save();
current.textRenderingMode |= TextRenderingMode.FILL_STROKE;
ctx.strokeStyle = ctx.fillStyle;
ctx.lineWidth = fontSize / fontSizeScale * 0.035 * bionicStrength;
ctx.lineJoin = "round";
ctx.setLineDash([]);
}
if (this.contentVisible && (glyph.isInFont || font.missingFile)) {`,
);
replace(
' const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;',
` if (bionicPaint) {
current.textRenderingMode = originalTextMode;
ctx.restore();
}
const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;`,
);
// Font substitution: setFont records the local face a PDF font maps to, and
// showText draws that face's glyph for the text's Unicode at the original
// glyph's position, fitted to its width the way pdf.js fits missing fonts.
replace(
' this.current.font = fontObj;\n this.current.fontSize = size;',
' this.current.font = fontObj;\n this.current.fontSize = size;\n this.current.substituteFonts = null;',
);
replace(
' this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;\n }',
` this.ctx.font = \`\${italic} \${bold} \${browserFontSize}px \${typeface}\`;
const substitute = this.fontSubstitution?.face(fontObj);
if (substitute) {
const css = (family) => family ? \`\${substitute.style} \${substitute.weight} \${browserFontSize}px \${family}\` : null;
this.current.substituteFonts = { text: css(substitute.text), cjk: css(substitute.cjk) };
}
}`,
);
replace(
' let x = 0,\n i;\n for (i = 0; i < glyphsLength; ++i) {',
` const substituteFonts = !vertical && !font.disableFontFace && !current.patternFill && !current.patternStroke &&
!(current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) &&
fillStrokeMode !== TextRenderingMode.INVISIBLE ? current.substituteFonts : null;
const originalFont = ctx.font;
let activeFont = originalFont;
let x = 0,
i;
for (i = 0; i < glyphsLength; ++i) {`,
);
replace(
' const character = glyph.fontChar;\n const accent = glyph.accent;',
` const accent = glyph.accent;
const substituteFont = substituteFonts && !accent && glyph.unicode ? substituteFonts[this.fontSubstitution.kind(glyph.unicode)] : null;
const substituted = !!substituteFont;
const character = substituted ? glyph.unicode : glyph.fontChar;
const glyphFont = substituted ? substituteFont : originalFont;
if (glyphFont !== activeFont) {
ctx.font = activeFont = glyphFont;
}`,
);
replace(
' if (font.remeasure && width > 0) {',
' if ((font.remeasure || substituted) && width > 0) {',
);
replace(
' if (width < measuredWidth && this.isFontSubpixelAAEnabled) {',
' if (width < measuredWidth && (this.isFontSubpixelAAEnabled || substituted)) {',
);
return source;
}
export function pdfBionicRuntimePlugin() {
return {
name: 'pdf-bionic-glyphs',
enforce: 'pre',
transform(source, id) {
if (!id.replaceAll('\\', '/').endsWith('/pdfjs-dist/build/pdf.mjs')) return null;
return { code: extendPDFRuntime(source), map: null };
},
};
}