postext 0.1.7 → 0.1.9
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/README.md +48 -21
- package/dist/__tests__/exports.test.js +4 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/frontmatter.test.d.ts +2 -0
- package/dist/__tests__/frontmatter.test.d.ts.map +1 -0
- package/dist/__tests__/frontmatter.test.js +41 -0
- package/dist/__tests__/frontmatter.test.js.map +1 -0
- package/dist/__tests__/numbering.test.d.ts +2 -0
- package/dist/__tests__/numbering.test.d.ts.map +1 -0
- package/dist/__tests__/numbering.test.js +95 -0
- package/dist/__tests__/numbering.test.js.map +1 -0
- package/dist/canvas-backend.d.ts.map +1 -1
- package/dist/canvas-backend.js +16 -7
- package/dist/canvas-backend.js.map +1 -1
- package/dist/defaults.d.ts +4 -1
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +80 -6
- package/dist/defaults.js.map +1 -1
- package/dist/frontmatter.d.ts +9 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +15 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/measure.d.ts +1 -1
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +34 -36
- package/dist/measure.js.map +1 -1
- package/dist/numbering.d.ts +16 -0
- package/dist/numbering.d.ts.map +1 -0
- package/dist/numbering.js +169 -0
- package/dist/numbering.js.map +1 -0
- package/dist/parse.d.ts +11 -0
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +188 -41
- package/dist/parse.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +128 -16
- package/dist/pipeline.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +22 -1
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js +1 -0
- package/dist/vdt.js.map +1 -1
- package/package.json +3 -2
package/dist/parse.js
CHANGED
|
@@ -18,69 +18,178 @@ function stripInlineFormatting(text) {
|
|
|
18
18
|
.trim();
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Strip non-
|
|
21
|
+
* Strip non-emphasis inline formatting (images, links, code) but keep bold/italic markers.
|
|
22
22
|
*/
|
|
23
|
-
function
|
|
23
|
+
function stripNonEmphasisFormatting(text) {
|
|
24
24
|
return text
|
|
25
25
|
.replace(/!\[.*?\]\(.*?\)/g, '') // images
|
|
26
26
|
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // links
|
|
27
27
|
.replace(/`(.+?)`/g, '$1'); // inline code
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
|
|
30
|
+
* Split a text region into italic/non-italic spans, carrying an ambient bold flag.
|
|
31
|
+
*/
|
|
32
|
+
function splitItalicSpans(text, bold, forcedItalic, out) {
|
|
33
|
+
if (forcedItalic) {
|
|
34
|
+
if (text.length > 0)
|
|
35
|
+
out.push({ text, bold, italic: true });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const italicRe = /\*(.+?)\*|_(.+?)_/g;
|
|
39
|
+
let last = 0;
|
|
40
|
+
let m;
|
|
41
|
+
while ((m = italicRe.exec(text)) !== null) {
|
|
42
|
+
if (m.index > last) {
|
|
43
|
+
const before = text.slice(last, m.index);
|
|
44
|
+
if (before.length > 0)
|
|
45
|
+
out.push({ text: before, bold, italic: false });
|
|
46
|
+
}
|
|
47
|
+
const inner = m[1] ?? m[2];
|
|
48
|
+
if (inner.length > 0)
|
|
49
|
+
out.push({ text: inner, bold, italic: true });
|
|
50
|
+
last = m.index + m[0].length;
|
|
51
|
+
}
|
|
52
|
+
if (last < text.length) {
|
|
53
|
+
const rest = text.slice(last);
|
|
54
|
+
if (rest.length > 0)
|
|
55
|
+
out.push({ text: rest, bold, italic: false });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse inline formatting to produce spans with bold and italic flags.
|
|
60
|
+
* Recognizes ***bold italic***, **bold**, *italic* (and underscore equivalents).
|
|
32
61
|
*/
|
|
33
62
|
function parseInlineFormatting(text) {
|
|
34
|
-
const cleaned =
|
|
63
|
+
const cleaned = stripNonEmphasisFormatting(text);
|
|
35
64
|
const spans = [];
|
|
36
|
-
//
|
|
37
|
-
const boldRe =
|
|
65
|
+
// Triple markers (bold+italic) first, then double (bold) — longest first.
|
|
66
|
+
const boldRe = /\*\*\*(.+?)\*\*\*|___(.+?)___|\*\*(.+?)\*\*|__(.+?)__/g;
|
|
38
67
|
let lastIndex = 0;
|
|
39
68
|
let match;
|
|
40
69
|
while ((match = boldRe.exec(cleaned)) !== null) {
|
|
41
|
-
// Text before the bold marker
|
|
42
70
|
if (match.index > lastIndex) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
splitItalicSpans(cleaned.slice(lastIndex, match.index), false, false, spans);
|
|
72
|
+
}
|
|
73
|
+
const triple = match[1] ?? match[2];
|
|
74
|
+
if (triple !== undefined) {
|
|
75
|
+
splitItalicSpans(triple, true, true, spans);
|
|
47
76
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
spans.push({ text: boldText, bold: true });
|
|
77
|
+
else {
|
|
78
|
+
const inner = match[3] ?? match[4];
|
|
79
|
+
splitItalicSpans(inner, true, false, spans);
|
|
52
80
|
}
|
|
53
81
|
lastIndex = match.index + match[0].length;
|
|
54
82
|
}
|
|
55
|
-
// Remaining text after last bold
|
|
56
83
|
if (lastIndex < cleaned.length) {
|
|
57
|
-
|
|
58
|
-
if (remaining.length > 0) {
|
|
59
|
-
spans.push({ text: remaining, bold: false });
|
|
60
|
-
}
|
|
84
|
+
splitItalicSpans(cleaned.slice(lastIndex), false, false, spans);
|
|
61
85
|
}
|
|
62
|
-
// If no spans were produced (empty text), return a single empty span
|
|
63
86
|
if (spans.length === 0) {
|
|
64
|
-
const trimmed =
|
|
87
|
+
const trimmed = cleaned.trim();
|
|
65
88
|
if (trimmed.length > 0) {
|
|
66
|
-
|
|
89
|
+
splitItalicSpans(trimmed, false, false, spans);
|
|
67
90
|
}
|
|
68
91
|
}
|
|
69
92
|
return spans;
|
|
70
93
|
}
|
|
71
94
|
/**
|
|
72
|
-
*
|
|
95
|
+
* Build a per-character map from plain text to absolute source offsets.
|
|
96
|
+
* Greedy matches each plain char against the raw source (delimited by
|
|
97
|
+
* [blockSrcStart, blockSrcEnd)), skipping markdown markers and treating
|
|
98
|
+
* newlines/tabs as spaces for paragraph line joins.
|
|
73
99
|
*/
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
100
|
+
function computeSourceMap(markdown, blockSrcStart, blockSrcEnd, plainText) {
|
|
101
|
+
const map = new Array(plainText.length);
|
|
102
|
+
let r = blockSrcStart;
|
|
103
|
+
for (let p = 0; p < plainText.length; p++) {
|
|
104
|
+
const ch = plainText[p];
|
|
105
|
+
const isSpace = ch === ' ';
|
|
106
|
+
while (r < blockSrcEnd) {
|
|
107
|
+
const rc = markdown[r];
|
|
108
|
+
if (rc === ch)
|
|
109
|
+
break;
|
|
110
|
+
if (isSpace && (rc === '\n' || rc === '\t'))
|
|
111
|
+
break;
|
|
112
|
+
r++;
|
|
113
|
+
}
|
|
114
|
+
if (r >= blockSrcEnd) {
|
|
115
|
+
map[p] = blockSrcEnd;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
map[p] = r;
|
|
119
|
+
r++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return map;
|
|
78
123
|
}
|
|
124
|
+
const COLLAPSIBLE_WS_RE = /[ \t\n\r\f]/;
|
|
79
125
|
/**
|
|
80
|
-
*
|
|
126
|
+
* Collapse runs of `[ \t\n\r\f]` to a single space and strip leading/trailing
|
|
127
|
+
* whitespace across spans. Mirrors pretext's `normalizeWhitespaceNormal` so
|
|
128
|
+
* that `spans` and the block's plain text stay aligned with what the layout
|
|
129
|
+
* engine actually renders — otherwise cursor/selection mapping drifts by one
|
|
130
|
+
* character per collapsed whitespace character.
|
|
81
131
|
*/
|
|
82
|
-
function
|
|
83
|
-
|
|
132
|
+
function normalizeWhitespaceInSpans(spans) {
|
|
133
|
+
const out = [];
|
|
134
|
+
let inSpace = true; // start true to strip leading whitespace
|
|
135
|
+
for (const span of spans) {
|
|
136
|
+
let result = '';
|
|
137
|
+
for (const ch of span.text) {
|
|
138
|
+
if (COLLAPSIBLE_WS_RE.test(ch)) {
|
|
139
|
+
if (!inSpace) {
|
|
140
|
+
result += ' ';
|
|
141
|
+
inSpace = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
result += ch;
|
|
146
|
+
inSpace = false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
out.push({ ...span, text: result });
|
|
150
|
+
}
|
|
151
|
+
// Strip trailing space from the last span that contributed content
|
|
152
|
+
for (let i = out.length - 1; i >= 0; i--) {
|
|
153
|
+
const text = out[i].text;
|
|
154
|
+
if (text.length === 0)
|
|
155
|
+
continue;
|
|
156
|
+
if (text.endsWith(' ')) {
|
|
157
|
+
out[i] = { ...out[i], text: text.slice(0, -1) };
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
return out.filter((s) => s.text.length > 0);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Build normalized text, spans, and sourceMap for a block. The plain text is
|
|
165
|
+
* whitespace-normalized to match pretext's internal normalization so that the
|
|
166
|
+
* per-character `sourceMap` aligns with rendered line segments.
|
|
167
|
+
*/
|
|
168
|
+
function buildBlockMapping(markdown, blockSrcStart, blockSrcEnd, rawSpans) {
|
|
169
|
+
const rawText = rawSpans.map((s) => s.text).join('');
|
|
170
|
+
const rawSourceMap = computeSourceMap(markdown, blockSrcStart, blockSrcEnd, rawText);
|
|
171
|
+
const spans = normalizeWhitespaceInSpans(rawSpans);
|
|
172
|
+
const text = spans.map((s) => s.text).join('');
|
|
173
|
+
// Walk the raw text building the same normalization, and project each kept
|
|
174
|
+
// normalized character onto the rawSourceMap to obtain the source offset.
|
|
175
|
+
const sourceMap = [];
|
|
176
|
+
let inSpace = true;
|
|
177
|
+
for (let i = 0; i < rawText.length; i++) {
|
|
178
|
+
const ch = rawText[i];
|
|
179
|
+
if (COLLAPSIBLE_WS_RE.test(ch)) {
|
|
180
|
+
if (!inSpace) {
|
|
181
|
+
sourceMap.push(rawSourceMap[i] ?? blockSrcEnd);
|
|
182
|
+
inSpace = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
sourceMap.push(rawSourceMap[i] ?? blockSrcEnd);
|
|
187
|
+
inSpace = false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (sourceMap.length > text.length)
|
|
191
|
+
sourceMap.length = text.length;
|
|
192
|
+
return { text, spans, sourceMap };
|
|
84
193
|
}
|
|
85
194
|
/**
|
|
86
195
|
* Merge consecutive blockquote lines into a single block, and consecutive
|
|
@@ -89,6 +198,18 @@ function spansToText(spans) {
|
|
|
89
198
|
export function parseMarkdown(markdown) {
|
|
90
199
|
const blocks = [];
|
|
91
200
|
const rawLines = markdown.split('\n');
|
|
201
|
+
// Precompute starting offset of each raw line in the original markdown.
|
|
202
|
+
// lineOffsets[i] = offset of first char of rawLines[i]. Line terminator '\n'
|
|
203
|
+
// contributes exactly 1 character between consecutive lines.
|
|
204
|
+
const lineOffsets = new Array(rawLines.length);
|
|
205
|
+
{
|
|
206
|
+
let cur = 0;
|
|
207
|
+
for (let k = 0; k < rawLines.length; k++) {
|
|
208
|
+
lineOffsets[k] = cur;
|
|
209
|
+
cur += rawLines[k].length + 1; // +1 for the '\n' that was split away
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const lineEndOffset = (k) => lineOffsets[k] + rawLines[k].length;
|
|
92
213
|
let i = 0;
|
|
93
214
|
while (i < rawLines.length) {
|
|
94
215
|
const line = rawLines[i];
|
|
@@ -101,12 +222,20 @@ export function parseMarkdown(markdown) {
|
|
|
101
222
|
// Heading
|
|
102
223
|
const headingMatch = trimmed.match(HEADING_RE);
|
|
103
224
|
if (headingMatch) {
|
|
104
|
-
const
|
|
225
|
+
const rawText = stripInlineFormatting(headingMatch[2]);
|
|
226
|
+
const srcStart = lineOffsets[i];
|
|
227
|
+
const srcEnd = lineEndOffset(i);
|
|
228
|
+
const mapping = buildBlockMapping(markdown, srcStart, srcEnd, [
|
|
229
|
+
{ text: rawText, bold: false, italic: false },
|
|
230
|
+
]);
|
|
105
231
|
blocks.push({
|
|
106
232
|
type: 'heading',
|
|
107
|
-
text,
|
|
108
|
-
spans: [{ text, bold: false }],
|
|
233
|
+
text: mapping.text,
|
|
234
|
+
spans: mapping.spans.length > 0 ? mapping.spans : [{ text: '', bold: false, italic: false }],
|
|
109
235
|
level: headingMatch[1].length,
|
|
236
|
+
sourceStart: srcStart,
|
|
237
|
+
sourceEnd: srcEnd,
|
|
238
|
+
sourceMap: mapping.sourceMap,
|
|
110
239
|
});
|
|
111
240
|
i++;
|
|
112
241
|
continue;
|
|
@@ -114,36 +243,54 @@ export function parseMarkdown(markdown) {
|
|
|
114
243
|
// Blockquote — collect consecutive > lines
|
|
115
244
|
if (trimmed.startsWith('>')) {
|
|
116
245
|
const quoteLines = [];
|
|
246
|
+
const startIdx = i;
|
|
247
|
+
let lastIdx = i;
|
|
117
248
|
while (i < rawLines.length) {
|
|
118
249
|
const ql = rawLines[i].trim();
|
|
119
250
|
if (!ql.startsWith('>'))
|
|
120
251
|
break;
|
|
121
252
|
quoteLines.push(ql.replace(/^>\s?/, ''));
|
|
253
|
+
lastIdx = i;
|
|
122
254
|
i++;
|
|
123
255
|
}
|
|
124
|
-
const
|
|
256
|
+
const rawSpans = parseInlineFormatting(quoteLines.join(' '));
|
|
257
|
+
const srcStart = lineOffsets[startIdx];
|
|
258
|
+
const srcEnd = lineEndOffset(lastIdx);
|
|
259
|
+
const mapping = buildBlockMapping(markdown, srcStart, srcEnd, rawSpans);
|
|
125
260
|
blocks.push({
|
|
126
261
|
type: 'blockquote',
|
|
127
|
-
text:
|
|
128
|
-
spans,
|
|
262
|
+
text: mapping.text,
|
|
263
|
+
spans: mapping.spans,
|
|
264
|
+
sourceStart: srcStart,
|
|
265
|
+
sourceEnd: srcEnd,
|
|
266
|
+
sourceMap: mapping.sourceMap,
|
|
129
267
|
});
|
|
130
268
|
continue;
|
|
131
269
|
}
|
|
132
270
|
// Paragraph — collect consecutive non-blank, non-special lines
|
|
133
271
|
const paraLines = [];
|
|
272
|
+
const startIdx = i;
|
|
273
|
+
let lastIdx = i;
|
|
134
274
|
while (i < rawLines.length) {
|
|
135
275
|
const pl = rawLines[i].trim();
|
|
136
276
|
if (pl === '' || pl.match(HEADING_RE) || pl.startsWith('>'))
|
|
137
277
|
break;
|
|
138
278
|
paraLines.push(pl);
|
|
279
|
+
lastIdx = i;
|
|
139
280
|
i++;
|
|
140
281
|
}
|
|
141
282
|
if (paraLines.length > 0) {
|
|
142
|
-
const
|
|
283
|
+
const rawSpans = parseInlineFormatting(paraLines.join(' '));
|
|
284
|
+
const srcStart = lineOffsets[startIdx];
|
|
285
|
+
const srcEnd = lineEndOffset(lastIdx);
|
|
286
|
+
const mapping = buildBlockMapping(markdown, srcStart, srcEnd, rawSpans);
|
|
143
287
|
blocks.push({
|
|
144
288
|
type: 'paragraph',
|
|
145
|
-
text:
|
|
146
|
-
spans,
|
|
289
|
+
text: mapping.text,
|
|
290
|
+
spans: mapping.spans,
|
|
291
|
+
sourceStart: srcStart,
|
|
292
|
+
sourceEnd: srcEnd,
|
|
293
|
+
sourceMap: mapping.sourceMap,
|
|
147
294
|
});
|
|
148
295
|
}
|
|
149
296
|
}
|
package/dist/parse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yCAAyC;AACzC,8EAA8E;
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAyC,MAAM,SAAS,CAAC;AAQpG,OAAO,EAQL,KAAK,WAAW,EAKjB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAyC,MAAM,SAAS,CAAC;AAQpG,OAAO,EAQL,KAAK,WAAW,EAKjB,MAAM,OAAO,CAAC;AA8Pf,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,aAAa,GACrB,WAAW,CAoUb"}
|
package/dist/pipeline.js
CHANGED
|
@@ -2,6 +2,8 @@ import { resolvePageConfig, resolveLayoutConfig, resolveBodyTextConfig, resolveH
|
|
|
2
2
|
import { dimensionToPx } from './units';
|
|
3
3
|
import { createBoundingBox, createVDTDocument, createVDTPage, createVDTColumn, createVDTBlock, } from './vdt';
|
|
4
4
|
import { parseMarkdown } from './parse';
|
|
5
|
+
import { computeHeadingNumbers } from './numbering';
|
|
6
|
+
import { extractFrontmatter } from './frontmatter';
|
|
5
7
|
import { buildFontString, measureBlock, measureRichBlock, initHyphenator } from './measure';
|
|
6
8
|
// ---------------------------------------------------------------------------
|
|
7
9
|
// Config resolution helpers
|
|
@@ -60,11 +62,14 @@ function resolveBodyStyle(resolved) {
|
|
|
60
62
|
const fontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, weight);
|
|
61
63
|
const boldWeight = resolved.bodyText.boldFontWeight.toString();
|
|
62
64
|
const boldFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, boldWeight);
|
|
65
|
+
const italicFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, weight, 'italic');
|
|
66
|
+
const boldItalicFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, boldWeight, 'italic');
|
|
63
67
|
const textAlign = resolved.bodyText.textAlign;
|
|
64
68
|
const hyphenate = resolved.bodyText.hyphenation.enabled && textAlign === 'justify';
|
|
65
69
|
const firstLineIndentPx = dimensionToPx(resolved.bodyText.firstLineIndent, dpi, fontSizePx);
|
|
66
70
|
const hangingIndent = resolved.bodyText.hangingIndent;
|
|
67
|
-
|
|
71
|
+
const marginBottomPx = resolved.bodyText.paragraphSpacing ? lineHeightPx : 0;
|
|
72
|
+
return { fontString, boldFontString, italicFontString, boldItalicFontString, fontSizePx, lineHeightPx, color: resolved.bodyText.color.hex, textAlign, hyphenate, marginTopPx: 0, marginBottomPx, firstLineIndentPx, hangingIndent };
|
|
68
73
|
}
|
|
69
74
|
function resolveHeadingStyle(level, resolved) {
|
|
70
75
|
const dpi = resolved.page.dpi;
|
|
@@ -79,11 +84,16 @@ function resolveHeadingStyle(level, resolved) {
|
|
|
79
84
|
lineHeightPx = dimensionToPx(lineHeightDim, dpi, fontSizePx);
|
|
80
85
|
}
|
|
81
86
|
const weight = headingConfig.fontWeight.toString();
|
|
82
|
-
const
|
|
87
|
+
const baseItalic = headingConfig.italic ? 'italic' : 'normal';
|
|
88
|
+
const flipItalic = headingConfig.italic ? 'normal' : 'italic';
|
|
89
|
+
const fontString = buildFontString(headingConfig.fontFamily, fontSizePx, weight, baseItalic);
|
|
90
|
+
const boldFontString = fontString;
|
|
91
|
+
const italicFontString = buildFontString(headingConfig.fontFamily, fontSizePx, weight, flipItalic);
|
|
92
|
+
const boldItalicFontString = italicFontString;
|
|
83
93
|
const textAlign = resolved.headings.textAlign;
|
|
84
94
|
const marginTopPx = dimensionToPx(headingConfig.marginTop, dpi, fontSizePx);
|
|
85
95
|
const marginBottomPx = dimensionToPx(headingConfig.marginBottom, dpi, fontSizePx);
|
|
86
|
-
return { fontString, fontSizePx, lineHeightPx, color: headingConfig.color.hex, textAlign, hyphenate: false, marginTopPx, marginBottomPx, firstLineIndentPx: 0, hangingIndent: false };
|
|
96
|
+
return { fontString, boldFontString, italicFontString, boldItalicFontString, fontSizePx, lineHeightPx, color: headingConfig.color.hex, textAlign, hyphenate: false, marginTopPx, marginBottomPx, firstLineIndentPx: 0, hangingIndent: false };
|
|
87
97
|
}
|
|
88
98
|
function resolveBlockquoteStyle(resolved) {
|
|
89
99
|
const dpi = resolved.page.dpi;
|
|
@@ -93,11 +103,14 @@ function resolveBlockquoteStyle(resolved) {
|
|
|
93
103
|
const fontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, weight, 'italic');
|
|
94
104
|
const boldWeight = resolved.bodyText.boldFontWeight.toString();
|
|
95
105
|
const boldFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, boldWeight, 'italic');
|
|
106
|
+
// Inside a blockquote (already italic), `*text*` flips back to upright.
|
|
107
|
+
const italicFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, weight, 'normal');
|
|
108
|
+
const boldItalicFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, boldWeight, 'normal');
|
|
96
109
|
const textAlign = resolved.bodyText.textAlign;
|
|
97
110
|
const hyphenate = resolved.bodyText.hyphenation.enabled && textAlign === 'justify';
|
|
98
111
|
const firstLineIndentPx = dimensionToPx(resolved.bodyText.firstLineIndent, dpi, fontSizePx);
|
|
99
112
|
const hangingIndent = resolved.bodyText.hangingIndent;
|
|
100
|
-
return { fontString, boldFontString, fontSizePx, lineHeightPx, color: '#666666', textAlign, hyphenate, marginTopPx: 0, marginBottomPx: 0, firstLineIndentPx, hangingIndent };
|
|
113
|
+
return { fontString, boldFontString, italicFontString, boldItalicFontString, fontSizePx, lineHeightPx, color: '#666666', textAlign, hyphenate, marginTopPx: 0, marginBottomPx: 0, firstLineIndentPx, hangingIndent };
|
|
101
114
|
}
|
|
102
115
|
// ---------------------------------------------------------------------------
|
|
103
116
|
// Line position reset (for split blocks)
|
|
@@ -188,8 +201,17 @@ export function buildDocument(content, config) {
|
|
|
188
201
|
// Create first page
|
|
189
202
|
const firstPage = createPageWithColumns(0, resolved, contentArea, pageWidthPx, pageHeightPx);
|
|
190
203
|
doc.pages.push(firstPage);
|
|
191
|
-
//
|
|
192
|
-
const
|
|
204
|
+
// Extract frontmatter, then parse the remaining markdown body
|
|
205
|
+
const { metadata: frontmatterMeta, content: markdownBody, contentOffset: bodyOffset } = extractFrontmatter(content.markdown);
|
|
206
|
+
doc.metadata = { ...(content.metadata ?? {}), ...frontmatterMeta };
|
|
207
|
+
const contentBlocks = parseMarkdown(markdownBody);
|
|
208
|
+
const headingTemplates = {};
|
|
209
|
+
for (const lvl of resolved.headings.levels) {
|
|
210
|
+
if (lvl.numberingTemplate && lvl.numberingTemplate.length > 0) {
|
|
211
|
+
headingTemplates[lvl.level] = lvl.numberingTemplate;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const headingPrefixes = computeHeadingNumbers(contentBlocks, headingTemplates);
|
|
193
215
|
// Resolve styles
|
|
194
216
|
const bodyStyle = resolveBodyStyle(resolved);
|
|
195
217
|
const blockquoteStyle = resolveBlockquoteStyle(resolved);
|
|
@@ -198,17 +220,29 @@ export function buildDocument(content, config) {
|
|
|
198
220
|
let blockIdCounter = 0;
|
|
199
221
|
let pendingSpacing = 0;
|
|
200
222
|
for (let blockIdx = 0; blockIdx < contentBlocks.length; blockIdx++) {
|
|
201
|
-
const
|
|
223
|
+
const rawBlock = contentBlocks[blockIdx];
|
|
202
224
|
const id = `block-${blockIdCounter++}`;
|
|
203
225
|
let style;
|
|
204
226
|
let vdtType;
|
|
205
227
|
let headingLevel;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
228
|
+
let numberPrefix;
|
|
229
|
+
let contentBlock = rawBlock;
|
|
230
|
+
switch (rawBlock.type) {
|
|
231
|
+
case 'heading': {
|
|
232
|
+
style = resolveHeadingStyle(rawBlock.level ?? 1, resolved);
|
|
209
233
|
vdtType = 'heading';
|
|
210
|
-
headingLevel =
|
|
234
|
+
headingLevel = rawBlock.level;
|
|
235
|
+
numberPrefix = headingPrefixes[blockIdx];
|
|
236
|
+
if (numberPrefix) {
|
|
237
|
+
const sep = `${numberPrefix} `;
|
|
238
|
+
const firstSpan = rawBlock.spans[0];
|
|
239
|
+
const newSpans = firstSpan
|
|
240
|
+
? [{ text: sep + firstSpan.text, bold: firstSpan.bold, italic: firstSpan.italic }, ...rawBlock.spans.slice(1)]
|
|
241
|
+
: [{ text: sep, bold: false, italic: false }];
|
|
242
|
+
contentBlock = { ...rawBlock, text: sep + rawBlock.text, spans: newSpans };
|
|
243
|
+
}
|
|
211
244
|
break;
|
|
245
|
+
}
|
|
212
246
|
case 'blockquote':
|
|
213
247
|
style = blockquoteStyle;
|
|
214
248
|
vdtType = 'blockquote';
|
|
@@ -220,18 +254,60 @@ export function buildDocument(content, config) {
|
|
|
220
254
|
}
|
|
221
255
|
// Measure text — use rich measurement for blocks with bold spans
|
|
222
256
|
const col = currentColumn(doc, cursor);
|
|
223
|
-
const hasRichSpans = contentBlock.spans.some((s) => s.bold);
|
|
257
|
+
const hasRichSpans = contentBlock.spans.some((s) => s.bold || s.italic);
|
|
224
258
|
const measureOptions = {
|
|
225
259
|
textAlign: style.textAlign,
|
|
226
260
|
hyphenate: style.hyphenate,
|
|
227
261
|
firstLineIndentPx: style.firstLineIndentPx,
|
|
228
262
|
hangingIndent: style.hangingIndent,
|
|
229
263
|
};
|
|
230
|
-
const measured = hasRichSpans && style.boldFontString
|
|
231
|
-
? measureRichBlock(contentBlock.spans, style.fontString, style.boldFontString, col.bbox.width, style.lineHeightPx, measureOptions)
|
|
264
|
+
const measured = hasRichSpans && style.boldFontString && style.italicFontString && style.boldItalicFontString
|
|
265
|
+
? measureRichBlock(contentBlock.spans, style.fontString, style.boldFontString, style.italicFontString, style.boldItalicFontString, col.bbox.width, style.lineHeightPx, measureOptions)
|
|
232
266
|
: measureBlock(contentBlock.text, style.fontString, col.bbox.width, style.lineHeightPx, measureOptions);
|
|
233
267
|
if (measured.lines.length === 0)
|
|
234
268
|
continue;
|
|
269
|
+
// Per-line source-range mapping using the block's plain→source map.
|
|
270
|
+
// Accounts for heading numbering prefix which prepends chars with no source.
|
|
271
|
+
const blockSrcStart = rawBlock.sourceStart + bodyOffset;
|
|
272
|
+
const blockSrcEnd = rawBlock.sourceEnd + bodyOffset;
|
|
273
|
+
const srcMap = rawBlock.sourceMap;
|
|
274
|
+
const prefixLen = contentBlock.text.length - rawBlock.text.length;
|
|
275
|
+
const plainToSrc = (p) => {
|
|
276
|
+
const idx = p - prefixLen;
|
|
277
|
+
if (idx <= 0)
|
|
278
|
+
return blockSrcStart;
|
|
279
|
+
if (idx >= srcMap.length)
|
|
280
|
+
return blockSrcEnd;
|
|
281
|
+
return srcMap[idx] + bodyOffset;
|
|
282
|
+
};
|
|
283
|
+
let cumPlain = 0;
|
|
284
|
+
const lastLineIdx = measured.lines.length - 1;
|
|
285
|
+
for (let li = 0; li < measured.lines.length; li++) {
|
|
286
|
+
const line = measured.lines[li];
|
|
287
|
+
// If segments are present, prefer their aggregate text length for a more
|
|
288
|
+
// accurate plain-char count (excludes trailing hyphen for hyphenated lines).
|
|
289
|
+
let lineLen;
|
|
290
|
+
if (line.segments && line.segments.length > 0) {
|
|
291
|
+
lineLen = line.segments.reduce((s, seg) => s + seg.text.length, 0);
|
|
292
|
+
if (line.hyphenated) {
|
|
293
|
+
const last = line.segments[line.segments.length - 1];
|
|
294
|
+
if (last.text.endsWith('-'))
|
|
295
|
+
lineLen -= 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
lineLen = line.text.length - (line.hyphenated ? 1 : 0);
|
|
300
|
+
}
|
|
301
|
+
line.plainStart = cumPlain;
|
|
302
|
+
line.plainEnd = cumPlain + lineLen;
|
|
303
|
+
// Advance past the separator space that was consumed to break the line
|
|
304
|
+
// (skip when hyphenated — break was at a soft hyphen — or on the last line).
|
|
305
|
+
const skipSeparator = !line.hyphenated && li !== lastLineIdx ? 1 : 0;
|
|
306
|
+
cumPlain = line.plainEnd + skipSeparator;
|
|
307
|
+
line.sourceStart = plainToSrc(line.plainStart);
|
|
308
|
+
line.sourceEnd = plainToSrc(line.plainEnd);
|
|
309
|
+
}
|
|
310
|
+
const absoluteSourceMap = srcMap.map((o) => o + bodyOffset);
|
|
235
311
|
// For headings, only snap to baseline grid if the next block is NOT a heading.
|
|
236
312
|
// Consecutive headings flow without grid snapping; the last heading in the
|
|
237
313
|
// group snaps so that the following body text realigns with the grid.
|
|
@@ -267,11 +343,24 @@ export function buildDocument(content, config) {
|
|
|
267
343
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
268
344
|
if (style.boldFontString)
|
|
269
345
|
blk.boldFontString = style.boldFontString;
|
|
270
|
-
if (
|
|
346
|
+
if (style.italicFontString)
|
|
347
|
+
blk.italicFontString = style.italicFontString;
|
|
348
|
+
if (style.boldItalicFontString)
|
|
349
|
+
blk.boldItalicFontString = style.boldItalicFontString;
|
|
350
|
+
if (partIndex === 0) {
|
|
271
351
|
blk.headingLevel = headingLevel;
|
|
352
|
+
if (numberPrefix)
|
|
353
|
+
blk.numberPrefix = numberPrefix;
|
|
354
|
+
}
|
|
272
355
|
blk.lines = resetLinePositions(remainingLines, style.lineHeightPx);
|
|
273
356
|
blk.dirty = false;
|
|
274
357
|
blk.snappedToGrid = shouldSnapToGrid && partIndex === 0;
|
|
358
|
+
if (remainingLines.length > 0) {
|
|
359
|
+
blk.sourceStart = remainingLines[0].sourceStart;
|
|
360
|
+
blk.sourceEnd = remainingLines[remainingLines.length - 1].sourceEnd;
|
|
361
|
+
}
|
|
362
|
+
blk.sourceMap = absoluteSourceMap;
|
|
363
|
+
blk.plainPrefixLen = prefixLen;
|
|
275
364
|
let h = totalRemainHeight;
|
|
276
365
|
if (shouldSnapToGrid && partIndex === 0) {
|
|
277
366
|
// Snap using the absolute position in the column so that the block
|
|
@@ -302,11 +391,24 @@ export function buildDocument(content, config) {
|
|
|
302
391
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
303
392
|
if (style.boldFontString)
|
|
304
393
|
blk.boldFontString = style.boldFontString;
|
|
305
|
-
if (
|
|
394
|
+
if (style.italicFontString)
|
|
395
|
+
blk.italicFontString = style.italicFontString;
|
|
396
|
+
if (style.boldItalicFontString)
|
|
397
|
+
blk.boldItalicFontString = style.boldItalicFontString;
|
|
398
|
+
if (partIndex === 0) {
|
|
306
399
|
blk.headingLevel = headingLevel;
|
|
400
|
+
if (numberPrefix)
|
|
401
|
+
blk.numberPrefix = numberPrefix;
|
|
402
|
+
}
|
|
307
403
|
blk.lines = resetLinePositions(splitLines, style.lineHeightPx);
|
|
308
404
|
blk.dirty = false;
|
|
309
405
|
blk.snappedToGrid = false;
|
|
406
|
+
if (splitLines.length > 0) {
|
|
407
|
+
blk.sourceStart = splitLines[0].sourceStart;
|
|
408
|
+
blk.sourceEnd = splitLines[splitLines.length - 1].sourceEnd;
|
|
409
|
+
}
|
|
410
|
+
blk.sourceMap = absoluteSourceMap;
|
|
411
|
+
blk.plainPrefixLen = prefixLen;
|
|
310
412
|
const splitHeight = linesPerAvailable * style.lineHeightPx;
|
|
311
413
|
placeBlockInColumn(blk, splitHeight, curCol, cursor);
|
|
312
414
|
doc.blocks.push(blk);
|
|
@@ -327,11 +429,21 @@ export function buildDocument(content, config) {
|
|
|
327
429
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
328
430
|
if (style.boldFontString)
|
|
329
431
|
blk.boldFontString = style.boldFontString;
|
|
432
|
+
if (style.italicFontString)
|
|
433
|
+
blk.italicFontString = style.italicFontString;
|
|
434
|
+
if (style.boldItalicFontString)
|
|
435
|
+
blk.boldItalicFontString = style.boldItalicFontString;
|
|
330
436
|
if (partIndex === 0)
|
|
331
437
|
blk.headingLevel = headingLevel;
|
|
332
438
|
blk.lines = resetLinePositions(remainingLines, style.lineHeightPx);
|
|
333
439
|
blk.dirty = false;
|
|
334
440
|
blk.snappedToGrid = false;
|
|
441
|
+
if (remainingLines.length > 0) {
|
|
442
|
+
blk.sourceStart = remainingLines[0].sourceStart;
|
|
443
|
+
blk.sourceEnd = remainingLines[remainingLines.length - 1].sourceEnd;
|
|
444
|
+
}
|
|
445
|
+
blk.sourceMap = absoluteSourceMap;
|
|
446
|
+
blk.plainPrefixLen = prefixLen;
|
|
335
447
|
placeBlockInColumn(blk, totalRemainHeight, curCol, cursor);
|
|
336
448
|
doc.blocks.push(blk);
|
|
337
449
|
pendingSpacing = style.marginBottomPx;
|