postext 0.1.7 → 0.1.8
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 +74 -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 +127 -16
- package/dist/pipeline.js.map +1 -1
- package/dist/types.d.ts +31 -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;AA6Pf,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,13 @@ 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
|
-
return { fontString, boldFontString, fontSizePx, lineHeightPx, color: resolved.bodyText.color.hex, textAlign, hyphenate, marginTopPx: 0, marginBottomPx: 0, firstLineIndentPx, hangingIndent };
|
|
71
|
+
return { fontString, boldFontString, italicFontString, boldItalicFontString, fontSizePx, lineHeightPx, color: resolved.bodyText.color.hex, textAlign, hyphenate, marginTopPx: 0, marginBottomPx: 0, firstLineIndentPx, hangingIndent };
|
|
68
72
|
}
|
|
69
73
|
function resolveHeadingStyle(level, resolved) {
|
|
70
74
|
const dpi = resolved.page.dpi;
|
|
@@ -79,11 +83,16 @@ function resolveHeadingStyle(level, resolved) {
|
|
|
79
83
|
lineHeightPx = dimensionToPx(lineHeightDim, dpi, fontSizePx);
|
|
80
84
|
}
|
|
81
85
|
const weight = headingConfig.fontWeight.toString();
|
|
82
|
-
const
|
|
86
|
+
const baseItalic = headingConfig.italic ? 'italic' : 'normal';
|
|
87
|
+
const flipItalic = headingConfig.italic ? 'normal' : 'italic';
|
|
88
|
+
const fontString = buildFontString(headingConfig.fontFamily, fontSizePx, weight, baseItalic);
|
|
89
|
+
const boldFontString = fontString;
|
|
90
|
+
const italicFontString = buildFontString(headingConfig.fontFamily, fontSizePx, weight, flipItalic);
|
|
91
|
+
const boldItalicFontString = italicFontString;
|
|
83
92
|
const textAlign = resolved.headings.textAlign;
|
|
84
93
|
const marginTopPx = dimensionToPx(headingConfig.marginTop, dpi, fontSizePx);
|
|
85
94
|
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 };
|
|
95
|
+
return { fontString, boldFontString, italicFontString, boldItalicFontString, fontSizePx, lineHeightPx, color: headingConfig.color.hex, textAlign, hyphenate: false, marginTopPx, marginBottomPx, firstLineIndentPx: 0, hangingIndent: false };
|
|
87
96
|
}
|
|
88
97
|
function resolveBlockquoteStyle(resolved) {
|
|
89
98
|
const dpi = resolved.page.dpi;
|
|
@@ -93,11 +102,14 @@ function resolveBlockquoteStyle(resolved) {
|
|
|
93
102
|
const fontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, weight, 'italic');
|
|
94
103
|
const boldWeight = resolved.bodyText.boldFontWeight.toString();
|
|
95
104
|
const boldFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, boldWeight, 'italic');
|
|
105
|
+
// Inside a blockquote (already italic), `*text*` flips back to upright.
|
|
106
|
+
const italicFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, weight, 'normal');
|
|
107
|
+
const boldItalicFontString = buildFontString(resolved.bodyText.fontFamily, fontSizePx, boldWeight, 'normal');
|
|
96
108
|
const textAlign = resolved.bodyText.textAlign;
|
|
97
109
|
const hyphenate = resolved.bodyText.hyphenation.enabled && textAlign === 'justify';
|
|
98
110
|
const firstLineIndentPx = dimensionToPx(resolved.bodyText.firstLineIndent, dpi, fontSizePx);
|
|
99
111
|
const hangingIndent = resolved.bodyText.hangingIndent;
|
|
100
|
-
return { fontString, boldFontString, fontSizePx, lineHeightPx, color: '#666666', textAlign, hyphenate, marginTopPx: 0, marginBottomPx: 0, firstLineIndentPx, hangingIndent };
|
|
112
|
+
return { fontString, boldFontString, italicFontString, boldItalicFontString, fontSizePx, lineHeightPx, color: '#666666', textAlign, hyphenate, marginTopPx: 0, marginBottomPx: 0, firstLineIndentPx, hangingIndent };
|
|
101
113
|
}
|
|
102
114
|
// ---------------------------------------------------------------------------
|
|
103
115
|
// Line position reset (for split blocks)
|
|
@@ -188,8 +200,17 @@ export function buildDocument(content, config) {
|
|
|
188
200
|
// Create first page
|
|
189
201
|
const firstPage = createPageWithColumns(0, resolved, contentArea, pageWidthPx, pageHeightPx);
|
|
190
202
|
doc.pages.push(firstPage);
|
|
191
|
-
//
|
|
192
|
-
const
|
|
203
|
+
// Extract frontmatter, then parse the remaining markdown body
|
|
204
|
+
const { metadata: frontmatterMeta, content: markdownBody, contentOffset: bodyOffset } = extractFrontmatter(content.markdown);
|
|
205
|
+
doc.metadata = { ...(content.metadata ?? {}), ...frontmatterMeta };
|
|
206
|
+
const contentBlocks = parseMarkdown(markdownBody);
|
|
207
|
+
const headingTemplates = {};
|
|
208
|
+
for (const lvl of resolved.headings.levels) {
|
|
209
|
+
if (lvl.numberingTemplate && lvl.numberingTemplate.length > 0) {
|
|
210
|
+
headingTemplates[lvl.level] = lvl.numberingTemplate;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const headingPrefixes = computeHeadingNumbers(contentBlocks, headingTemplates);
|
|
193
214
|
// Resolve styles
|
|
194
215
|
const bodyStyle = resolveBodyStyle(resolved);
|
|
195
216
|
const blockquoteStyle = resolveBlockquoteStyle(resolved);
|
|
@@ -198,17 +219,29 @@ export function buildDocument(content, config) {
|
|
|
198
219
|
let blockIdCounter = 0;
|
|
199
220
|
let pendingSpacing = 0;
|
|
200
221
|
for (let blockIdx = 0; blockIdx < contentBlocks.length; blockIdx++) {
|
|
201
|
-
const
|
|
222
|
+
const rawBlock = contentBlocks[blockIdx];
|
|
202
223
|
const id = `block-${blockIdCounter++}`;
|
|
203
224
|
let style;
|
|
204
225
|
let vdtType;
|
|
205
226
|
let headingLevel;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
227
|
+
let numberPrefix;
|
|
228
|
+
let contentBlock = rawBlock;
|
|
229
|
+
switch (rawBlock.type) {
|
|
230
|
+
case 'heading': {
|
|
231
|
+
style = resolveHeadingStyle(rawBlock.level ?? 1, resolved);
|
|
209
232
|
vdtType = 'heading';
|
|
210
|
-
headingLevel =
|
|
233
|
+
headingLevel = rawBlock.level;
|
|
234
|
+
numberPrefix = headingPrefixes[blockIdx];
|
|
235
|
+
if (numberPrefix) {
|
|
236
|
+
const sep = `${numberPrefix} `;
|
|
237
|
+
const firstSpan = rawBlock.spans[0];
|
|
238
|
+
const newSpans = firstSpan
|
|
239
|
+
? [{ text: sep + firstSpan.text, bold: firstSpan.bold, italic: firstSpan.italic }, ...rawBlock.spans.slice(1)]
|
|
240
|
+
: [{ text: sep, bold: false, italic: false }];
|
|
241
|
+
contentBlock = { ...rawBlock, text: sep + rawBlock.text, spans: newSpans };
|
|
242
|
+
}
|
|
211
243
|
break;
|
|
244
|
+
}
|
|
212
245
|
case 'blockquote':
|
|
213
246
|
style = blockquoteStyle;
|
|
214
247
|
vdtType = 'blockquote';
|
|
@@ -220,18 +253,60 @@ export function buildDocument(content, config) {
|
|
|
220
253
|
}
|
|
221
254
|
// Measure text — use rich measurement for blocks with bold spans
|
|
222
255
|
const col = currentColumn(doc, cursor);
|
|
223
|
-
const hasRichSpans = contentBlock.spans.some((s) => s.bold);
|
|
256
|
+
const hasRichSpans = contentBlock.spans.some((s) => s.bold || s.italic);
|
|
224
257
|
const measureOptions = {
|
|
225
258
|
textAlign: style.textAlign,
|
|
226
259
|
hyphenate: style.hyphenate,
|
|
227
260
|
firstLineIndentPx: style.firstLineIndentPx,
|
|
228
261
|
hangingIndent: style.hangingIndent,
|
|
229
262
|
};
|
|
230
|
-
const measured = hasRichSpans && style.boldFontString
|
|
231
|
-
? measureRichBlock(contentBlock.spans, style.fontString, style.boldFontString, col.bbox.width, style.lineHeightPx, measureOptions)
|
|
263
|
+
const measured = hasRichSpans && style.boldFontString && style.italicFontString && style.boldItalicFontString
|
|
264
|
+
? measureRichBlock(contentBlock.spans, style.fontString, style.boldFontString, style.italicFontString, style.boldItalicFontString, col.bbox.width, style.lineHeightPx, measureOptions)
|
|
232
265
|
: measureBlock(contentBlock.text, style.fontString, col.bbox.width, style.lineHeightPx, measureOptions);
|
|
233
266
|
if (measured.lines.length === 0)
|
|
234
267
|
continue;
|
|
268
|
+
// Per-line source-range mapping using the block's plain→source map.
|
|
269
|
+
// Accounts for heading numbering prefix which prepends chars with no source.
|
|
270
|
+
const blockSrcStart = rawBlock.sourceStart + bodyOffset;
|
|
271
|
+
const blockSrcEnd = rawBlock.sourceEnd + bodyOffset;
|
|
272
|
+
const srcMap = rawBlock.sourceMap;
|
|
273
|
+
const prefixLen = contentBlock.text.length - rawBlock.text.length;
|
|
274
|
+
const plainToSrc = (p) => {
|
|
275
|
+
const idx = p - prefixLen;
|
|
276
|
+
if (idx <= 0)
|
|
277
|
+
return blockSrcStart;
|
|
278
|
+
if (idx >= srcMap.length)
|
|
279
|
+
return blockSrcEnd;
|
|
280
|
+
return srcMap[idx] + bodyOffset;
|
|
281
|
+
};
|
|
282
|
+
let cumPlain = 0;
|
|
283
|
+
const lastLineIdx = measured.lines.length - 1;
|
|
284
|
+
for (let li = 0; li < measured.lines.length; li++) {
|
|
285
|
+
const line = measured.lines[li];
|
|
286
|
+
// If segments are present, prefer their aggregate text length for a more
|
|
287
|
+
// accurate plain-char count (excludes trailing hyphen for hyphenated lines).
|
|
288
|
+
let lineLen;
|
|
289
|
+
if (line.segments && line.segments.length > 0) {
|
|
290
|
+
lineLen = line.segments.reduce((s, seg) => s + seg.text.length, 0);
|
|
291
|
+
if (line.hyphenated) {
|
|
292
|
+
const last = line.segments[line.segments.length - 1];
|
|
293
|
+
if (last.text.endsWith('-'))
|
|
294
|
+
lineLen -= 1;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
lineLen = line.text.length - (line.hyphenated ? 1 : 0);
|
|
299
|
+
}
|
|
300
|
+
line.plainStart = cumPlain;
|
|
301
|
+
line.plainEnd = cumPlain + lineLen;
|
|
302
|
+
// Advance past the separator space that was consumed to break the line
|
|
303
|
+
// (skip when hyphenated — break was at a soft hyphen — or on the last line).
|
|
304
|
+
const skipSeparator = !line.hyphenated && li !== lastLineIdx ? 1 : 0;
|
|
305
|
+
cumPlain = line.plainEnd + skipSeparator;
|
|
306
|
+
line.sourceStart = plainToSrc(line.plainStart);
|
|
307
|
+
line.sourceEnd = plainToSrc(line.plainEnd);
|
|
308
|
+
}
|
|
309
|
+
const absoluteSourceMap = srcMap.map((o) => o + bodyOffset);
|
|
235
310
|
// For headings, only snap to baseline grid if the next block is NOT a heading.
|
|
236
311
|
// Consecutive headings flow without grid snapping; the last heading in the
|
|
237
312
|
// group snaps so that the following body text realigns with the grid.
|
|
@@ -267,11 +342,24 @@ export function buildDocument(content, config) {
|
|
|
267
342
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
268
343
|
if (style.boldFontString)
|
|
269
344
|
blk.boldFontString = style.boldFontString;
|
|
270
|
-
if (
|
|
345
|
+
if (style.italicFontString)
|
|
346
|
+
blk.italicFontString = style.italicFontString;
|
|
347
|
+
if (style.boldItalicFontString)
|
|
348
|
+
blk.boldItalicFontString = style.boldItalicFontString;
|
|
349
|
+
if (partIndex === 0) {
|
|
271
350
|
blk.headingLevel = headingLevel;
|
|
351
|
+
if (numberPrefix)
|
|
352
|
+
blk.numberPrefix = numberPrefix;
|
|
353
|
+
}
|
|
272
354
|
blk.lines = resetLinePositions(remainingLines, style.lineHeightPx);
|
|
273
355
|
blk.dirty = false;
|
|
274
356
|
blk.snappedToGrid = shouldSnapToGrid && partIndex === 0;
|
|
357
|
+
if (remainingLines.length > 0) {
|
|
358
|
+
blk.sourceStart = remainingLines[0].sourceStart;
|
|
359
|
+
blk.sourceEnd = remainingLines[remainingLines.length - 1].sourceEnd;
|
|
360
|
+
}
|
|
361
|
+
blk.sourceMap = absoluteSourceMap;
|
|
362
|
+
blk.plainPrefixLen = prefixLen;
|
|
275
363
|
let h = totalRemainHeight;
|
|
276
364
|
if (shouldSnapToGrid && partIndex === 0) {
|
|
277
365
|
// Snap using the absolute position in the column so that the block
|
|
@@ -302,11 +390,24 @@ export function buildDocument(content, config) {
|
|
|
302
390
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
303
391
|
if (style.boldFontString)
|
|
304
392
|
blk.boldFontString = style.boldFontString;
|
|
305
|
-
if (
|
|
393
|
+
if (style.italicFontString)
|
|
394
|
+
blk.italicFontString = style.italicFontString;
|
|
395
|
+
if (style.boldItalicFontString)
|
|
396
|
+
blk.boldItalicFontString = style.boldItalicFontString;
|
|
397
|
+
if (partIndex === 0) {
|
|
306
398
|
blk.headingLevel = headingLevel;
|
|
399
|
+
if (numberPrefix)
|
|
400
|
+
blk.numberPrefix = numberPrefix;
|
|
401
|
+
}
|
|
307
402
|
blk.lines = resetLinePositions(splitLines, style.lineHeightPx);
|
|
308
403
|
blk.dirty = false;
|
|
309
404
|
blk.snappedToGrid = false;
|
|
405
|
+
if (splitLines.length > 0) {
|
|
406
|
+
blk.sourceStart = splitLines[0].sourceStart;
|
|
407
|
+
blk.sourceEnd = splitLines[splitLines.length - 1].sourceEnd;
|
|
408
|
+
}
|
|
409
|
+
blk.sourceMap = absoluteSourceMap;
|
|
410
|
+
blk.plainPrefixLen = prefixLen;
|
|
310
411
|
const splitHeight = linesPerAvailable * style.lineHeightPx;
|
|
311
412
|
placeBlockInColumn(blk, splitHeight, curCol, cursor);
|
|
312
413
|
doc.blocks.push(blk);
|
|
@@ -327,11 +428,21 @@ export function buildDocument(content, config) {
|
|
|
327
428
|
const blk = createVDTBlock(partId, vdtType, style.fontString, style.color, style.textAlign);
|
|
328
429
|
if (style.boldFontString)
|
|
329
430
|
blk.boldFontString = style.boldFontString;
|
|
431
|
+
if (style.italicFontString)
|
|
432
|
+
blk.italicFontString = style.italicFontString;
|
|
433
|
+
if (style.boldItalicFontString)
|
|
434
|
+
blk.boldItalicFontString = style.boldItalicFontString;
|
|
330
435
|
if (partIndex === 0)
|
|
331
436
|
blk.headingLevel = headingLevel;
|
|
332
437
|
blk.lines = resetLinePositions(remainingLines, style.lineHeightPx);
|
|
333
438
|
blk.dirty = false;
|
|
334
439
|
blk.snappedToGrid = false;
|
|
440
|
+
if (remainingLines.length > 0) {
|
|
441
|
+
blk.sourceStart = remainingLines[0].sourceStart;
|
|
442
|
+
blk.sourceEnd = remainingLines[remainingLines.length - 1].sourceEnd;
|
|
443
|
+
}
|
|
444
|
+
blk.sourceMap = absoluteSourceMap;
|
|
445
|
+
blk.plainPrefixLen = prefixLen;
|
|
335
446
|
placeBlockInColumn(blk, totalRemainHeight, curCol, cursor);
|
|
336
447
|
doc.blocks.push(blk);
|
|
337
448
|
pendingSpacing = style.marginBottomPx;
|