postext 0.3.6 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canvas-backend/blockRender.d.ts +3 -0
- package/dist/canvas-backend/blockRender.d.ts.map +1 -0
- package/dist/canvas-backend/blockRender.js +176 -0
- package/dist/canvas-backend/blockRender.js.map +1 -0
- package/dist/canvas-backend/decorations.d.ts +6 -0
- package/dist/canvas-backend/decorations.d.ts.map +1 -0
- package/dist/canvas-backend/decorations.js +106 -0
- package/dist/canvas-backend/decorations.js.map +1 -0
- package/dist/{canvas-backend.d.ts → canvas-backend/index.d.ts} +2 -2
- package/dist/canvas-backend/index.d.ts.map +1 -0
- package/dist/canvas-backend/index.js +66 -0
- package/dist/canvas-backend/index.js.map +1 -0
- package/dist/knuthPlass/breakpoints.d.ts +6 -0
- package/dist/knuthPlass/breakpoints.d.ts.map +1 -0
- package/dist/knuthPlass/breakpoints.js +284 -0
- package/dist/knuthPlass/breakpoints.js.map +1 -0
- package/dist/knuthPlass/constants.d.ts +8 -0
- package/dist/knuthPlass/constants.d.ts.map +1 -0
- package/dist/knuthPlass/constants.js +8 -0
- package/dist/knuthPlass/constants.js.map +1 -0
- package/dist/knuthPlass/index.d.ts +12 -0
- package/dist/knuthPlass/index.d.ts.map +1 -0
- package/dist/knuthPlass/index.js +11 -0
- package/dist/knuthPlass/index.js.map +1 -0
- package/dist/knuthPlass/pretextAdapter.d.ts +12 -0
- package/dist/knuthPlass/pretextAdapter.d.ts.map +1 -0
- package/dist/knuthPlass/pretextAdapter.js +178 -0
- package/dist/knuthPlass/pretextAdapter.js.map +1 -0
- package/dist/knuthPlass/richAdapter.d.ts +26 -0
- package/dist/knuthPlass/richAdapter.d.ts.map +1 -0
- package/dist/knuthPlass/richAdapter.js +189 -0
- package/dist/knuthPlass/richAdapter.js.map +1 -0
- package/dist/{knuthPlass.d.ts → knuthPlass/types.d.ts} +1 -31
- package/dist/knuthPlass/types.d.ts.map +1 -0
- package/dist/knuthPlass/types.js +5 -0
- package/dist/knuthPlass/types.js.map +1 -0
- package/dist/knuthPlass/utils.d.ts +2 -0
- package/dist/knuthPlass/utils.d.ts.map +1 -0
- package/dist/knuthPlass/utils.js +4 -0
- package/dist/knuthPlass/utils.js.map +1 -0
- package/dist/measure/cache.d.ts +5 -0
- package/dist/measure/cache.d.ts.map +1 -0
- package/dist/measure/cache.js +38 -0
- package/dist/measure/cache.js.map +1 -0
- package/dist/measure/canvas.d.ts +8 -0
- package/dist/measure/canvas.d.ts.map +1 -0
- package/dist/measure/canvas.js +30 -0
- package/dist/measure/canvas.js.map +1 -0
- package/dist/measure/font.d.ts +19 -0
- package/dist/measure/font.d.ts.map +1 -0
- package/dist/measure/font.js +34 -0
- package/dist/measure/font.js.map +1 -0
- package/dist/measure/index.d.ts +7 -0
- package/dist/measure/index.d.ts.map +1 -0
- package/dist/measure/index.js +6 -0
- package/dist/measure/index.js.map +1 -0
- package/dist/measure/plain.d.ts +13 -0
- package/dist/measure/plain.d.ts.map +1 -0
- package/dist/measure/plain.js +167 -0
- package/dist/measure/plain.js.map +1 -0
- package/dist/measure/rich.d.ts +25 -0
- package/dist/measure/rich.d.ts.map +1 -0
- package/dist/measure/rich.js +246 -0
- package/dist/measure/rich.js.map +1 -0
- package/dist/measure/types.d.ts +28 -0
- package/dist/measure/types.d.ts.map +1 -0
- package/dist/measure/types.js +2 -0
- package/dist/measure/types.js.map +1 -0
- package/dist/parse/blockParser.d.ts +28 -0
- package/dist/parse/blockParser.d.ts.map +1 -0
- package/dist/parse/blockParser.js +302 -0
- package/dist/parse/blockParser.js.map +1 -0
- package/dist/parse/index.d.ts +4 -0
- package/dist/parse/index.d.ts.map +1 -0
- package/dist/parse/index.js +3 -0
- package/dist/parse/index.js.map +1 -0
- package/dist/parse/inlineFormatting.d.ts +12 -0
- package/dist/parse/inlineFormatting.d.ts.map +1 -0
- package/dist/parse/inlineFormatting.js +90 -0
- package/dist/parse/inlineFormatting.js.map +1 -0
- package/dist/parse/inlineMath.d.ts +29 -0
- package/dist/parse/inlineMath.d.ts.map +1 -0
- package/dist/parse/inlineMath.js +141 -0
- package/dist/parse/inlineMath.js.map +1 -0
- package/dist/parse/sourceMapping.d.ts +12 -0
- package/dist/parse/sourceMapping.d.ts.map +1 -0
- package/dist/parse/sourceMapping.js +130 -0
- package/dist/parse/sourceMapping.js.map +1 -0
- package/dist/{parse.d.ts → parse/types.d.ts} +2 -25
- package/dist/parse/types.d.ts.map +1 -0
- package/dist/parse/types.js +2 -0
- package/dist/parse/types.js.map +1 -0
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +34 -276
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/buildBlockKind.d.ts +33 -0
- package/dist/pipeline/buildBlockKind.d.ts.map +1 -0
- package/dist/pipeline/buildBlockKind.js +82 -0
- package/dist/pipeline/buildBlockKind.js.map +1 -0
- package/dist/pipeline/buildHelpers.d.ts +47 -0
- package/dist/pipeline/buildHelpers.d.ts.map +1 -0
- package/dist/pipeline/buildHelpers.js +169 -0
- package/dist/pipeline/buildHelpers.js.map +1 -0
- package/dist/pipeline/buildMeasurement.d.ts +27 -0
- package/dist/pipeline/buildMeasurement.d.ts.map +1 -0
- package/dist/pipeline/buildMeasurement.js +49 -0
- package/dist/pipeline/buildMeasurement.js.map +1 -0
- package/package.json +1 -1
- package/dist/canvas-backend.d.ts.map +0 -1
- package/dist/canvas-backend.js +0 -370
- package/dist/canvas-backend.js.map +0 -1
- package/dist/knuthPlass.d.ts.map +0 -1
- package/dist/knuthPlass.js +0 -665
- package/dist/knuthPlass.js.map +0 -1
- package/dist/measure.d.ts +0 -61
- package/dist/measure.d.ts.map +0 -1
- package/dist/measure.js +0 -512
- package/dist/measure.js.map +0 -1
- package/dist/parse.d.ts.map +0 -1
- package/dist/parse.js +0 -653
- package/dist/parse.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourceMapping.d.ts","sourceRoot":"","sources":["../../src/parse/sourceMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA+F1C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,UAAU,EAAE,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B5D"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { MATH_PLACEHOLDER } from './inlineMath';
|
|
2
|
+
/**
|
|
3
|
+
* Build a per-character map from plain text to absolute source offsets.
|
|
4
|
+
* Greedy matches each plain char against the raw source (delimited by
|
|
5
|
+
* [blockSrcStart, blockSrcEnd)), skipping markdown markers and treating
|
|
6
|
+
* newlines/tabs as spaces for paragraph line joins.
|
|
7
|
+
*/
|
|
8
|
+
function computeSourceMap(markdown, blockSrcStart, blockSrcEnd, plainText) {
|
|
9
|
+
const map = new Array(plainText.length);
|
|
10
|
+
let r = blockSrcStart;
|
|
11
|
+
for (let p = 0; p < plainText.length; p++) {
|
|
12
|
+
const ch = plainText[p];
|
|
13
|
+
// Math placeholder: the plain char represents `$...$` in the markdown.
|
|
14
|
+
// Advance to the opening `$`, map to it, then skip past the closing `$`
|
|
15
|
+
// so subsequent plain chars can keep aligning with the source.
|
|
16
|
+
if (ch === MATH_PLACEHOLDER) {
|
|
17
|
+
while (r < blockSrcEnd && markdown[r] !== '$')
|
|
18
|
+
r++;
|
|
19
|
+
if (r >= blockSrcEnd) {
|
|
20
|
+
map[p] = blockSrcEnd;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
map[p] = r;
|
|
24
|
+
let j = r + 1;
|
|
25
|
+
while (j < blockSrcEnd) {
|
|
26
|
+
if (markdown[j] === '\\' && markdown[j + 1] === '$') {
|
|
27
|
+
j += 2;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (markdown[j] === '$') {
|
|
31
|
+
j++;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if (markdown[j] === '\n')
|
|
35
|
+
break;
|
|
36
|
+
j++;
|
|
37
|
+
}
|
|
38
|
+
r = j;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const isSpace = ch === ' ';
|
|
42
|
+
while (r < blockSrcEnd) {
|
|
43
|
+
const rc = markdown[r];
|
|
44
|
+
if (rc === ch)
|
|
45
|
+
break;
|
|
46
|
+
if (isSpace && (rc === '\n' || rc === '\t'))
|
|
47
|
+
break;
|
|
48
|
+
r++;
|
|
49
|
+
}
|
|
50
|
+
if (r >= blockSrcEnd) {
|
|
51
|
+
map[p] = blockSrcEnd;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
map[p] = r;
|
|
55
|
+
r++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return map;
|
|
59
|
+
}
|
|
60
|
+
const COLLAPSIBLE_WS_RE = /[ \t\n\r\f]/;
|
|
61
|
+
/**
|
|
62
|
+
* Collapse runs of `[ \t\n\r\f]` to a single space and strip leading/trailing
|
|
63
|
+
* whitespace across spans. Mirrors pretext's `normalizeWhitespaceNormal` so
|
|
64
|
+
* that `spans` and the block's plain text stay aligned with what the layout
|
|
65
|
+
* engine actually renders — otherwise cursor/selection mapping drifts by one
|
|
66
|
+
* character per collapsed whitespace character.
|
|
67
|
+
*/
|
|
68
|
+
function normalizeWhitespaceInSpans(spans) {
|
|
69
|
+
const out = [];
|
|
70
|
+
let inSpace = true; // start true to strip leading whitespace
|
|
71
|
+
for (const span of spans) {
|
|
72
|
+
let result = '';
|
|
73
|
+
for (const ch of span.text) {
|
|
74
|
+
if (COLLAPSIBLE_WS_RE.test(ch)) {
|
|
75
|
+
if (!inSpace) {
|
|
76
|
+
result += ' ';
|
|
77
|
+
inSpace = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
result += ch;
|
|
82
|
+
inSpace = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
out.push({ ...span, text: result });
|
|
86
|
+
}
|
|
87
|
+
// Strip trailing space from the last span that contributed content
|
|
88
|
+
for (let i = out.length - 1; i >= 0; i--) {
|
|
89
|
+
const text = out[i].text;
|
|
90
|
+
if (text.length === 0)
|
|
91
|
+
continue;
|
|
92
|
+
if (text.endsWith(' ')) {
|
|
93
|
+
out[i] = { ...out[i], text: text.slice(0, -1) };
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
return out.filter((s) => s.text.length > 0);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build normalized text, spans, and sourceMap for a block. The plain text is
|
|
101
|
+
* whitespace-normalized to match pretext's internal normalization so that the
|
|
102
|
+
* per-character `sourceMap` aligns with rendered line segments.
|
|
103
|
+
*/
|
|
104
|
+
export function buildBlockMapping(markdown, blockSrcStart, blockSrcEnd, rawSpans) {
|
|
105
|
+
const rawText = rawSpans.map((s) => s.text).join('');
|
|
106
|
+
const rawSourceMap = computeSourceMap(markdown, blockSrcStart, blockSrcEnd, rawText);
|
|
107
|
+
const spans = normalizeWhitespaceInSpans(rawSpans);
|
|
108
|
+
const text = spans.map((s) => s.text).join('');
|
|
109
|
+
// Walk the raw text building the same normalization, and project each kept
|
|
110
|
+
// normalized character onto the rawSourceMap to obtain the source offset.
|
|
111
|
+
const sourceMap = [];
|
|
112
|
+
let inSpace = true;
|
|
113
|
+
for (let i = 0; i < rawText.length; i++) {
|
|
114
|
+
const ch = rawText[i];
|
|
115
|
+
if (COLLAPSIBLE_WS_RE.test(ch)) {
|
|
116
|
+
if (!inSpace) {
|
|
117
|
+
sourceMap.push(rawSourceMap[i] ?? blockSrcEnd);
|
|
118
|
+
inSpace = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
sourceMap.push(rawSourceMap[i] ?? blockSrcEnd);
|
|
123
|
+
inSpace = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (sourceMap.length > text.length)
|
|
127
|
+
sourceMap.length = text.length;
|
|
128
|
+
return { text, spans, sourceMap };
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=sourceMapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourceMapping.js","sourceRoot":"","sources":["../../src/parse/sourceMapping.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ export interface InlineSpan {
|
|
|
18
18
|
math?: MathMeta;
|
|
19
19
|
/** Resolved math render — populated by the pipeline before measurement
|
|
20
20
|
* so the parser remains free of MathJax dependencies. */
|
|
21
|
-
mathRender?: import('
|
|
21
|
+
mathRender?: import('../math/types').MathRender;
|
|
22
22
|
}
|
|
23
23
|
/** Convenience discriminants for inline span iteration. */
|
|
24
24
|
export type TextSpan = InlineSpan & {
|
|
@@ -66,27 +66,4 @@ export interface ContentBlock {
|
|
|
66
66
|
*/
|
|
67
67
|
sourceMap: number[];
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
* span. One code unit per formula so `sourceMap` stays 1-to-1. */
|
|
71
|
-
export declare const MATH_PLACEHOLDER = "\uFFFC";
|
|
72
|
-
/**
|
|
73
|
-
* Memoized wrapper around parseMarkdown: returns the cached result when the
|
|
74
|
-
* input string is byte-for-byte identical to the previous call. This avoids
|
|
75
|
-
* reparsing the whole document on each keystroke when upstream recomputes
|
|
76
|
-
* only because a sibling state changed.
|
|
77
|
-
*/
|
|
78
|
-
export declare function parseMarkdownMemo(markdown: string): ContentBlock[];
|
|
79
|
-
export declare function parseMarkdownWithIssuesMemo(markdown: string): {
|
|
80
|
-
blocks: ContentBlock[];
|
|
81
|
-
issues: ParseIssue[];
|
|
82
|
-
};
|
|
83
|
-
export declare function parseMarkdown(markdown: string): ContentBlock[];
|
|
84
|
-
/**
|
|
85
|
-
* Merge consecutive blockquote lines into a single block, and consecutive
|
|
86
|
-
* non-blank, non-special lines into paragraphs.
|
|
87
|
-
*/
|
|
88
|
-
export declare function parseMarkdownWithIssues(markdown: string): {
|
|
89
|
-
blocks: ContentBlock[];
|
|
90
|
-
issues: ParseIssue[];
|
|
91
|
-
};
|
|
92
|
-
//# sourceMappingURL=parse.d.ts.map
|
|
69
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/parse/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,YAAY,GACZ,UAAU,GACV,aAAa,CAAC;AAElB;;sEAEsE;AACtE,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB;uEACmE;IACnE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;8DAC0D;IAC1D,UAAU,CAAC,EAAE,OAAO,eAAe,EAAE,UAAU,CAAC;CACjD;AAED,2DAA2D;AAC3D,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB;mCAC+B;IAC/B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/parse/types.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/pipeline/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9D,OAAO,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/pipeline/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9D,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,QAAQ,CAAC;AAKhB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA4BnD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,aAAa,EACtB,KAAK,CAAC,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,WAAW,CAifb"}
|
package/dist/pipeline/build.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { dimensionToPx } from '../units';
|
|
2
|
-
import {
|
|
2
|
+
import { createVDTDocument, createVDTBlock, } from '../vdt';
|
|
3
3
|
import { parseMarkdownMemo } from '../parse';
|
|
4
4
|
import { computeHeadingNumbers } from '../numbering';
|
|
5
5
|
import { extractFrontmatter } from '../frontmatter';
|
|
6
|
-
import {
|
|
6
|
+
import { initHyphenator } from '../measure';
|
|
7
7
|
import { resolveAllConfig, computeBaselineGrid } from './config';
|
|
8
|
-
import { resolveBodyStyle,
|
|
9
|
-
import {
|
|
10
|
-
import { computeLevelIndentsPx, computeOrderedLevelIndentsPx, computeOrderedListRunMetrics, resolveUnorderedListItemStyle, resolveOrderedListItemStyle, } from './lists';
|
|
8
|
+
import { resolveBodyStyle, resolveBlockquoteStyle } from './styles';
|
|
9
|
+
import { computeLevelIndentsPx, computeOrderedLevelIndentsPx, computeOrderedListRunMetrics, } from './lists';
|
|
11
10
|
import { resetLinePositions, createPageWithColumns, currentColumn, advanceToNextColumn, placeBlockInColumn, } from './placement';
|
|
12
11
|
import { chooseParagraphSplit } from './orphanWidow';
|
|
12
|
+
import { applyStyleAttrs, computeMeasureViewport, computePageMetrics, enrichMathSpans, rollbackTrailingBlocks, stampSourceRanges, } from './buildHelpers';
|
|
13
|
+
import { resolveBlockKind } from './buildBlockKind';
|
|
14
|
+
import { runMeasurement } from './buildMeasurement';
|
|
13
15
|
export class BuildCancelledError extends Error {
|
|
14
16
|
constructor() {
|
|
15
17
|
super('Build cancelled');
|
|
@@ -27,26 +29,7 @@ export function buildDocument(content, config, cache, options) {
|
|
|
27
29
|
const baselineGrid = computeBaselineGrid(resolved);
|
|
28
30
|
// Create document
|
|
29
31
|
const doc = createVDTDocument(resolved, baselineGrid);
|
|
30
|
-
|
|
31
|
-
const trimWidthPx = dimensionToPx(resolved.page.width, dpi);
|
|
32
|
-
const trimHeightPx = dimensionToPx(resolved.page.height, dpi);
|
|
33
|
-
// Cut lines expansion: canvas grows to fit bleed + mark offset + mark length
|
|
34
|
-
let trimOffset = 0;
|
|
35
|
-
if (resolved.page.cutLines.enabled) {
|
|
36
|
-
const bleedPx = dimensionToPx(resolved.page.cutLines.bleed, dpi);
|
|
37
|
-
const markOffsetPx = dimensionToPx(resolved.page.cutLines.markOffset, dpi);
|
|
38
|
-
const markLengthPx = dimensionToPx(resolved.page.cutLines.markLength, dpi);
|
|
39
|
-
trimOffset = bleedPx + markOffsetPx + markLengthPx;
|
|
40
|
-
}
|
|
41
|
-
const pageWidthPx = trimWidthPx + trimOffset * 2;
|
|
42
|
-
const pageHeightPx = trimHeightPx + trimOffset * 2;
|
|
43
|
-
// Margins in px
|
|
44
|
-
const marginTop = dimensionToPx(resolved.page.margins.top, dpi);
|
|
45
|
-
const marginBottom = dimensionToPx(resolved.page.margins.bottom, dpi);
|
|
46
|
-
const marginLeft = dimensionToPx(resolved.page.margins.left, dpi);
|
|
47
|
-
const marginRight = dimensionToPx(resolved.page.margins.right, dpi);
|
|
48
|
-
// Content area (offset by trimOffset so content sits inside the trim area)
|
|
49
|
-
const contentArea = createBoundingBox(marginLeft + trimOffset, marginTop + trimOffset, trimWidthPx - marginLeft - marginRight, trimHeightPx - marginTop - marginBottom);
|
|
32
|
+
const { pageWidthPx, pageHeightPx, trimOffset, contentArea } = computePageMetrics(resolved);
|
|
50
33
|
doc.trimOffset = trimOffset;
|
|
51
34
|
// Create first page
|
|
52
35
|
const firstPage = createPageWithColumns(0, resolved, contentArea, pageWidthPx, pageHeightPx);
|
|
@@ -77,110 +60,26 @@ export function buildDocument(content, config, cache, options) {
|
|
|
77
60
|
throw new BuildCancelledError();
|
|
78
61
|
const rawBlock = contentBlocks[blockIdx];
|
|
79
62
|
const id = `block-${blockIdCounter++}`;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
style = resolveHeadingStyle(rawBlock.level ?? 1, resolved);
|
|
93
|
-
vdtType = 'heading';
|
|
94
|
-
headingLevel = rawBlock.level;
|
|
95
|
-
numberPrefix = headingPrefixes[blockIdx];
|
|
96
|
-
if (numberPrefix) {
|
|
97
|
-
const sep = `${numberPrefix} `;
|
|
98
|
-
const firstSpan = rawBlock.spans[0];
|
|
99
|
-
const newSpans = firstSpan
|
|
100
|
-
? [{ text: sep + firstSpan.text, bold: firstSpan.bold, italic: firstSpan.italic }, ...rawBlock.spans.slice(1)]
|
|
101
|
-
: [{ text: sep, bold: false, italic: false }];
|
|
102
|
-
contentBlock = { ...rawBlock, text: sep + rawBlock.text, spans: newSpans };
|
|
103
|
-
}
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
case 'blockquote':
|
|
107
|
-
style = blockquoteStyle;
|
|
108
|
-
vdtType = 'blockquote';
|
|
109
|
-
break;
|
|
110
|
-
case 'mathDisplay': {
|
|
111
|
-
style = resolveMathDisplayStyle(resolved);
|
|
112
|
-
vdtType = 'mathDisplay';
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
case 'listItem': {
|
|
116
|
-
const depth = rawBlock.depth ?? 1;
|
|
117
|
-
const kind = rawBlock.listKind ?? 'unordered';
|
|
118
|
-
let resolvedList;
|
|
119
|
-
if (kind === 'ordered') {
|
|
120
|
-
const metric = orderedMetrics.perBlock.get(blockIdx) ??
|
|
121
|
-
{ numberText: '', numberWidthPx: 0, maxNumberWidthPx: 0 };
|
|
122
|
-
resolvedList = resolveOrderedListItemStyle(depth, resolved, orderedLevelIndentsPx, metric);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
resolvedList = resolveUnorderedListItemStyle(depth, resolved, listLevelIndentsPx, rawBlock.checked ?? false, kind === 'task');
|
|
126
|
-
}
|
|
127
|
-
style = resolvedList.text;
|
|
128
|
-
listBullet = resolvedList.bullet;
|
|
129
|
-
listDepth = depth;
|
|
130
|
-
listKind = kind;
|
|
131
|
-
bulletXOffsetInColumn = resolvedList.bulletXOffsetInColumn;
|
|
132
|
-
strikethroughText = resolvedList.strikethroughText;
|
|
133
|
-
vdtType = 'listItem';
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
default:
|
|
137
|
-
style = bodyStyle;
|
|
138
|
-
vdtType = 'paragraph';
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
63
|
+
const kind = resolveBlockKind(rawBlock, {
|
|
64
|
+
resolved,
|
|
65
|
+
bodyStyle,
|
|
66
|
+
blockquoteStyle,
|
|
67
|
+
headingPrefixes,
|
|
68
|
+
blockIdx,
|
|
69
|
+
listLevelIndentsPx,
|
|
70
|
+
orderedLevelIndentsPx,
|
|
71
|
+
orderedMetrics,
|
|
72
|
+
});
|
|
73
|
+
const { style, vdtType, headingLevel, numberPrefix, listBullet, listDepth, listKind, bulletXOffsetInColumn, strikethroughText } = kind;
|
|
74
|
+
let contentBlock = kind.contentBlock;
|
|
141
75
|
// Measure text — use rich measurement for blocks with bold spans
|
|
142
76
|
const col = currentColumn(doc, cursor);
|
|
143
|
-
// Resolve inline math
|
|
144
|
-
// When math is disabled, drop the math metadata so spans fall back to
|
|
145
|
-
// the raw TeX (visible as literal `$...$`).
|
|
77
|
+
// Resolve inline math on spans (no-op when the block has no math).
|
|
146
78
|
const mathEnabled = resolved.math.enabled;
|
|
147
|
-
|
|
148
|
-
const mathFontSizePx = style.fontSizePx * resolved.math.fontSizeScale;
|
|
149
|
-
const mathColor = resolved.math.color?.hex ?? style.color;
|
|
150
|
-
const enrichedSpans = contentBlock.spans.map((s) => {
|
|
151
|
-
if (!s.math)
|
|
152
|
-
return s;
|
|
153
|
-
if (!mathEnabled) {
|
|
154
|
-
return { text: `$${s.math.tex}$`, bold: s.bold, italic: s.italic };
|
|
155
|
-
}
|
|
156
|
-
const render = isMathReady()
|
|
157
|
-
? renderMath(s.math.tex, false, mathFontSizePx, { lineBoxPx: style.lineHeightPx, color: mathColor })
|
|
158
|
-
: undefined;
|
|
159
|
-
return { ...s, mathRender: render };
|
|
160
|
-
});
|
|
161
|
-
contentBlock = { ...contentBlock, spans: enrichedSpans };
|
|
162
|
-
}
|
|
79
|
+
contentBlock = enrichMathSpans(contentBlock, style, resolved);
|
|
163
80
|
const hasRichSpans = contentBlock.spans.some((s) => s.bold || s.italic || s.mathRender);
|
|
164
81
|
// List items reserve horizontal space for indent + bullet + gap.
|
|
165
|
-
|
|
166
|
-
let lineXShift = 0;
|
|
167
|
-
let measureFirstLineIndent = style.firstLineIndentPx;
|
|
168
|
-
let measureHangingIndent = style.hangingIndent;
|
|
169
|
-
if (listBullet) {
|
|
170
|
-
const textGap = listBullet.bulletWidthPx + listBullet.gapPx;
|
|
171
|
-
if (listBullet.hangingIndent) {
|
|
172
|
-
measureMaxWidth = Math.max(1, col.bbox.width - listBullet.indentPx - textGap);
|
|
173
|
-
lineXShift = listBullet.indentPx + textGap;
|
|
174
|
-
measureFirstLineIndent = 0;
|
|
175
|
-
measureHangingIndent = false;
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
measureMaxWidth = Math.max(1, col.bbox.width - listBullet.indentPx);
|
|
179
|
-
lineXShift = listBullet.indentPx;
|
|
180
|
-
measureFirstLineIndent = textGap;
|
|
181
|
-
measureHangingIndent = false;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
82
|
+
const { measureMaxWidth, lineXShift, measureFirstLineIndent, measureHangingIndent, } = computeMeasureViewport(col.bbox.width, style, listBullet);
|
|
184
83
|
const runtActive = resolved.bodyText.avoidRunts
|
|
185
84
|
&& (vdtType === 'paragraph'
|
|
186
85
|
|| (vdtType === 'listItem' && resolved.bodyText.avoidRuntsInLists));
|
|
@@ -195,50 +94,10 @@ export function buildDocument(content, config, cache, options) {
|
|
|
195
94
|
runtPenalty: runtActive ? resolved.bodyText.runtPenalty : 0,
|
|
196
95
|
runtMinCharacters: runtActive ? resolved.bodyText.runtMinCharacters : 0,
|
|
197
96
|
};
|
|
198
|
-
const useRich = hasRichSpans && style.boldFontString && style.italicFontString && style.boldItalicFontString;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
let measured;
|
|
203
|
-
if (vdtType === 'mathDisplay') {
|
|
204
|
-
const tex = rawBlock.tex ?? '';
|
|
205
|
-
if (!mathEnabled) {
|
|
206
|
-
// Fallback: render the literal TeX as a paragraph-like run.
|
|
207
|
-
measured = useRich
|
|
208
|
-
? measureRichBlock([{ text: `$$${tex}$$`, bold: false, italic: false }], style.fontString, style.fontString, style.fontString, style.fontString, measureMaxWidth, style.lineHeightPx, measureOptions)
|
|
209
|
-
: measureBlock(`$$${tex}$$`, style.fontString, measureMaxWidth, style.lineHeightPx, measureOptions);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
// `renderMath` internally returns a cheap placeholder when MathJax
|
|
213
|
-
// isn't initialised yet — no need to gate the call here. When the
|
|
214
|
-
// real engine lands later, `CanvasPreview` bumps `resizeKey` and the
|
|
215
|
-
// pipeline rebuilds with the genuine render.
|
|
216
|
-
const render = renderMath(tex, true, style.fontSizePx, { color: style.color });
|
|
217
|
-
mathDisplayRender = render;
|
|
218
|
-
const width = Math.min(render.widthPx, measureMaxWidth);
|
|
219
|
-
const height = render.heightPx;
|
|
220
|
-
measured = {
|
|
221
|
-
lines: [{
|
|
222
|
-
text: '',
|
|
223
|
-
bbox: { x: 0, y: 0, width, height },
|
|
224
|
-
baseline: render.ascentPx,
|
|
225
|
-
hyphenated: false,
|
|
226
|
-
segments: [{ kind: 'math', text: '\uFFFC', width, mathRender: render }],
|
|
227
|
-
isLastLine: true,
|
|
228
|
-
}],
|
|
229
|
-
totalHeight: height,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
measured = cache
|
|
235
|
-
? (useRich
|
|
236
|
-
? cachedMeasureRichBlock(contentBlock.spans, style.fontString, style.boldFontString, style.italicFontString, style.boldItalicFontString, measureMaxWidth, style.lineHeightPx, measureOptions, cache)
|
|
237
|
-
: cachedMeasureBlock(contentBlock.text, style.fontString, measureMaxWidth, style.lineHeightPx, measureOptions, cache))
|
|
238
|
-
: (useRich
|
|
239
|
-
? measureRichBlock(contentBlock.spans, style.fontString, style.boldFontString, style.italicFontString, style.boldItalicFontString, measureMaxWidth, style.lineHeightPx, measureOptions)
|
|
240
|
-
: measureBlock(contentBlock.text, style.fontString, measureMaxWidth, style.lineHeightPx, measureOptions));
|
|
241
|
-
}
|
|
97
|
+
const useRich = !!(hasRichSpans && style.boldFontString && style.italicFontString && style.boldItalicFontString);
|
|
98
|
+
const { measured, mathDisplayRender } = runMeasurement({
|
|
99
|
+
vdtType, rawBlock, contentBlock, style, measureMaxWidth, measureOptions, mathEnabled, useRich, cache,
|
|
100
|
+
});
|
|
242
101
|
if (measured.lines.length === 0)
|
|
243
102
|
continue;
|
|
244
103
|
if (lineXShift > 0) {
|
|
@@ -248,46 +107,7 @@ export function buildDocument(content, config, cache, options) {
|
|
|
248
107
|
}
|
|
249
108
|
// Per-line source-range mapping using the block's plain→source map.
|
|
250
109
|
// Accounts for heading numbering prefix which prepends chars with no source.
|
|
251
|
-
const
|
|
252
|
-
const blockSrcEnd = rawBlock.sourceEnd + bodyOffset;
|
|
253
|
-
const srcMap = rawBlock.sourceMap;
|
|
254
|
-
const prefixLen = contentBlock.text.length - rawBlock.text.length;
|
|
255
|
-
const plainToSrc = (p) => {
|
|
256
|
-
const idx = p - prefixLen;
|
|
257
|
-
if (idx <= 0)
|
|
258
|
-
return blockSrcStart;
|
|
259
|
-
if (idx >= srcMap.length)
|
|
260
|
-
return blockSrcEnd;
|
|
261
|
-
return srcMap[idx] + bodyOffset;
|
|
262
|
-
};
|
|
263
|
-
let cumPlain = 0;
|
|
264
|
-
const lastLineIdx = measured.lines.length - 1;
|
|
265
|
-
for (let li = 0; li < measured.lines.length; li++) {
|
|
266
|
-
const line = measured.lines[li];
|
|
267
|
-
// If segments are present, prefer their aggregate text length for a more
|
|
268
|
-
// accurate plain-char count (excludes trailing hyphen for hyphenated lines).
|
|
269
|
-
let lineLen;
|
|
270
|
-
if (line.segments && line.segments.length > 0) {
|
|
271
|
-
lineLen = line.segments.reduce((s, seg) => s + seg.text.length, 0);
|
|
272
|
-
if (line.hyphenated) {
|
|
273
|
-
const last = line.segments[line.segments.length - 1];
|
|
274
|
-
if (last.text.endsWith('-'))
|
|
275
|
-
lineLen -= 1;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
lineLen = line.text.length - (line.hyphenated ? 1 : 0);
|
|
280
|
-
}
|
|
281
|
-
line.plainStart = cumPlain;
|
|
282
|
-
line.plainEnd = cumPlain + lineLen;
|
|
283
|
-
// Advance past the separator space that was consumed to break the line
|
|
284
|
-
// (skip when hyphenated — break was at a soft hyphen — or on the last line).
|
|
285
|
-
const skipSeparator = !line.hyphenated && li !== lastLineIdx ? 1 : 0;
|
|
286
|
-
cumPlain = line.plainEnd + skipSeparator;
|
|
287
|
-
line.sourceStart = plainToSrc(line.plainStart);
|
|
288
|
-
line.sourceEnd = plainToSrc(line.plainEnd);
|
|
289
|
-
}
|
|
290
|
-
const absoluteSourceMap = srcMap.map((o) => o + bodyOffset);
|
|
110
|
+
const { prefixLen, absoluteSourceMap } = stampSourceRanges(measured, rawBlock, contentBlock, bodyOffset);
|
|
291
111
|
const finalizeListItem = (blk, isFirstPart) => {
|
|
292
112
|
if (!listBullet)
|
|
293
113
|
return;
|
|
@@ -395,16 +215,7 @@ export function buildDocument(content, config, cache, options) {
|
|
|
395
215
|
curCol.availableHeight -= spacingBefore;
|
|
396
216
|
const splitLines = remainingLines.slice(0, splitAt);
|
|
397
217
|
const blk = createVDTBlock(id, vdtType, style.fontString, style.color, style.textAlign);
|
|
398
|
-
|
|
399
|
-
blk.boldFontString = style.boldFontString;
|
|
400
|
-
if (style.italicFontString)
|
|
401
|
-
blk.italicFontString = style.italicFontString;
|
|
402
|
-
if (style.boldItalicFontString)
|
|
403
|
-
blk.boldItalicFontString = style.boldItalicFontString;
|
|
404
|
-
if (style.boldColor)
|
|
405
|
-
blk.boldColor = style.boldColor;
|
|
406
|
-
if (style.italicColor)
|
|
407
|
-
blk.italicColor = style.italicColor;
|
|
218
|
+
applyStyleAttrs(blk, style);
|
|
408
219
|
blk.headingLevel = headingLevel;
|
|
409
220
|
if (numberPrefix)
|
|
410
221
|
blk.numberPrefix = numberPrefix;
|
|
@@ -491,21 +302,8 @@ export function buildDocument(content, config, cache, options) {
|
|
|
491
302
|
if (remainAfterHeading < minSpaceAfter) {
|
|
492
303
|
// Roll back any immediately-preceding heading blocks in this
|
|
493
304
|
// column so they travel with this one.
|
|
494
|
-
|
|
495
|
-
for (let j = curCol.blocks.length - 1; j >= 0; j--) {
|
|
496
|
-
if (curCol.blocks[j].type === 'heading')
|
|
497
|
-
rollbackCount++;
|
|
498
|
-
else
|
|
499
|
-
break;
|
|
500
|
-
}
|
|
305
|
+
const rollbackCount = rollbackTrailingBlocks(curCol, doc.blocks, (b) => b.type === 'heading');
|
|
501
306
|
if (rollbackCount > 0) {
|
|
502
|
-
const popped = curCol.blocks.splice(curCol.blocks.length - rollbackCount);
|
|
503
|
-
for (const p of popped) {
|
|
504
|
-
const idx = doc.blocks.indexOf(p);
|
|
505
|
-
if (idx !== -1)
|
|
506
|
-
doc.blocks.splice(idx, 1);
|
|
507
|
-
curCol.availableHeight += p.bbox.height;
|
|
508
|
-
}
|
|
509
307
|
// Rewind so the for-loop's blockIdx++ lands on the first
|
|
510
308
|
// rolled-back heading.
|
|
511
309
|
blockIdx -= rollbackCount + 1;
|
|
@@ -524,16 +322,7 @@ export function buildDocument(content, config, cache, options) {
|
|
|
524
322
|
}
|
|
525
323
|
const partId = partIndex === 0 ? id : `${id}-cont-${partIndex}`;
|
|
526
324
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
527
|
-
|
|
528
|
-
blk.boldFontString = style.boldFontString;
|
|
529
|
-
if (style.italicFontString)
|
|
530
|
-
blk.italicFontString = style.italicFontString;
|
|
531
|
-
if (style.boldItalicFontString)
|
|
532
|
-
blk.boldItalicFontString = style.boldItalicFontString;
|
|
533
|
-
if (style.boldColor)
|
|
534
|
-
blk.boldColor = style.boldColor;
|
|
535
|
-
if (style.italicColor)
|
|
536
|
-
blk.italicColor = style.italicColor;
|
|
325
|
+
applyStyleAttrs(blk, style);
|
|
537
326
|
if (partIndex === 0) {
|
|
538
327
|
blk.headingLevel = headingLevel;
|
|
539
328
|
if (numberPrefix)
|
|
@@ -613,16 +402,7 @@ export function buildDocument(content, config, cache, options) {
|
|
|
613
402
|
const partId = partIndex === 0 ? id : `${id}-cont-${partIndex}`;
|
|
614
403
|
const splitLines = remainingLines.slice(0, choice.splitAt);
|
|
615
404
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
616
|
-
|
|
617
|
-
blk.boldFontString = style.boldFontString;
|
|
618
|
-
if (style.italicFontString)
|
|
619
|
-
blk.italicFontString = style.italicFontString;
|
|
620
|
-
if (style.boldItalicFontString)
|
|
621
|
-
blk.boldItalicFontString = style.boldItalicFontString;
|
|
622
|
-
if (style.boldColor)
|
|
623
|
-
blk.boldColor = style.boldColor;
|
|
624
|
-
if (style.italicColor)
|
|
625
|
-
blk.italicColor = style.italicColor;
|
|
405
|
+
applyStyleAttrs(blk, style);
|
|
626
406
|
if (partIndex === 0) {
|
|
627
407
|
blk.headingLevel = headingLevel;
|
|
628
408
|
if (numberPrefix)
|
|
@@ -656,21 +436,8 @@ export function buildDocument(content, config, cache, options) {
|
|
|
656
436
|
// pull those headings along so they don't remain stranded as orphans
|
|
657
437
|
// at the column's bottom. Mirrors the rollback inside the "fits" path.
|
|
658
438
|
if (vdtType === 'heading' && resolved.headings.keepWithNext) {
|
|
659
|
-
|
|
660
|
-
for (let j = curCol.blocks.length - 1; j >= 0; j--) {
|
|
661
|
-
if (curCol.blocks[j].type === 'heading')
|
|
662
|
-
rollbackCount++;
|
|
663
|
-
else
|
|
664
|
-
break;
|
|
665
|
-
}
|
|
439
|
+
const rollbackCount = rollbackTrailingBlocks(curCol, doc.blocks, (b) => b.type === 'heading');
|
|
666
440
|
if (rollbackCount > 0) {
|
|
667
|
-
const popped = curCol.blocks.splice(curCol.blocks.length - rollbackCount);
|
|
668
|
-
for (const p of popped) {
|
|
669
|
-
const idx = doc.blocks.indexOf(p);
|
|
670
|
-
if (idx !== -1)
|
|
671
|
-
doc.blocks.splice(idx, 1);
|
|
672
|
-
curCol.availableHeight += p.bbox.height;
|
|
673
|
-
}
|
|
674
441
|
blockIdx -= rollbackCount + 1;
|
|
675
442
|
pendingSpacing = 0;
|
|
676
443
|
advanceToNextColumn(doc, cursor, resolved, contentArea, pageWidthPx, pageHeightPx);
|
|
@@ -684,16 +451,7 @@ export function buildDocument(content, config, cache, options) {
|
|
|
684
451
|
// Empty column but block still doesn't fit (block taller than page) — place anyway
|
|
685
452
|
const partId = partIndex === 0 ? id : `${id}-cont-${partIndex}`;
|
|
686
453
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
687
|
-
|
|
688
|
-
blk.boldFontString = style.boldFontString;
|
|
689
|
-
if (style.italicFontString)
|
|
690
|
-
blk.italicFontString = style.italicFontString;
|
|
691
|
-
if (style.boldItalicFontString)
|
|
692
|
-
blk.boldItalicFontString = style.boldItalicFontString;
|
|
693
|
-
if (style.boldColor)
|
|
694
|
-
blk.boldColor = style.boldColor;
|
|
695
|
-
if (style.italicColor)
|
|
696
|
-
blk.italicColor = style.italicColor;
|
|
454
|
+
applyStyleAttrs(blk, style);
|
|
697
455
|
if (partIndex === 0)
|
|
698
456
|
blk.headingLevel = headingLevel;
|
|
699
457
|
blk.lines = resetLinePositions(remainingLines, style.lineHeightPx);
|