rapier-markdown-kit 1.2.9 → 1.2.10
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.
|
@@ -171,7 +171,7 @@ function _rapierPrintWillPageCss() {
|
|
|
171
171
|
|
|
172
172
|
const CAPTURE_EDGE = 4096, CAPTURE_PIXELS = 4 * 1024 * 1024;
|
|
173
173
|
|
|
174
|
-
async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = false, filename, title, settings}, {capture, signal, current, maxBytes = 8 * 1024 * 1024, film, ink} = {}) {
|
|
174
|
+
async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = false, filename, title, settings, templatePage = null}, {capture, signal, current, maxBytes = 8 * 1024 * 1024, film, ink} = {}) {
|
|
175
175
|
const refuse = code => { throw Object.assign(new Error(code), {code}); };
|
|
176
176
|
const check = () => {
|
|
177
177
|
if (signal?.aborted) refuse('cancelled');
|
|
@@ -187,7 +187,7 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
187
187
|
const bodyHeight = Math.floor((height - margins.top - margins.bottom) * 4 / 3);
|
|
188
188
|
if (bodyWidth < 1 || bodyHeight < 1 || bodyWidth * bodyHeight > 4 * 1024 * 1024) refuse('pdf_page_geometry_invalid');
|
|
189
189
|
const frame = document.createElement('iframe');
|
|
190
|
-
let vectors, nativeGlyphs, filmSession;
|
|
190
|
+
let vectors, backdrop = null, nativeGlyphs, filmSession;
|
|
191
191
|
let rejectAbort;
|
|
192
192
|
const aborted = new Promise((_, reject) => { rejectAbort = reject; });
|
|
193
193
|
aborted.catch(() => {});
|
|
@@ -233,6 +233,7 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
233
233
|
strip.append(root); surface.append(strip); into.body.append(surface);
|
|
234
234
|
const main = root.querySelector('main');
|
|
235
235
|
if (!main) refuse('pdf_render_unavailable');
|
|
236
|
+
if (templatePage) for (const image of main.querySelectorAll('img')) if (image.getAttribute('src') === templatePage.url) (image.closest('p,figure') || image).remove();
|
|
236
237
|
main.setAttribute('data-md-theme', 'light');
|
|
237
238
|
for (const details of main.querySelectorAll('details')) details.open = true;
|
|
238
239
|
main.getBoundingClientRect();
|
|
@@ -255,7 +256,7 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
255
256
|
}
|
|
256
257
|
for (const [name, value] of Object.entries({width: bodyWidth + 'px', height: bodyHeight + 'px',
|
|
257
258
|
'max-width': 'none', 'min-height': '0', padding: '0', margin: '0', overflow: 'visible',
|
|
258
|
-
'column-width': bodyWidth + 'px', 'column-gap': '0', 'column-count': 'auto', 'column-fill': 'auto'}))
|
|
259
|
+
'column-width': bodyWidth + 'px', 'column-gap': '0', 'column-count': 'auto', 'column-fill': 'auto', background: 'none'}))
|
|
259
260
|
main.style.setProperty(name, value, 'important');
|
|
260
261
|
globalThis.RapierMath?.fit(main);
|
|
261
262
|
applyPrintCapitals(main);
|
|
@@ -334,11 +335,22 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
334
335
|
first += span;
|
|
335
336
|
}
|
|
336
337
|
check();
|
|
337
|
-
|
|
338
|
+
if (templatePage) {
|
|
339
|
+
const sheetWidth = Math.round(width * 4 / 3), sheetHeight = Math.round(height * 4 / 3);
|
|
340
|
+
const holder = into.createElement('div'), picture = into.createElement('img');
|
|
341
|
+
holder.style.cssText = `position:fixed;left:0;top:0;width:${sheetWidth}px;height:${sheetHeight}px;pointer-events:none`;
|
|
342
|
+
picture.style.cssText = 'display:block;width:100%;height:100%'; picture.src = templatePage.url;
|
|
343
|
+
holder.append(picture); into.body.append(holder);
|
|
344
|
+
await step(picture.decode());
|
|
345
|
+
backdrop = await collectPdfVectors(holder, {width: sheetWidth, height: sheetHeight, count: 1, capture, signal, current, check, maxBytes,
|
|
346
|
+
sanitizeSvg: globalThis.RapierImageAssets?.sanitizeSvgText});
|
|
347
|
+
if (!backdrop.pages[0].length) refuse('pdf_template_border_unavailable');
|
|
348
|
+
}
|
|
349
|
+
const bytes = await writeRasterPdf({pages, textPages, linkPages, vectorPages: vectors.pages, canonical, bom, filename, title, font, width, height, margins, backdrop: backdrop?.pages[0] || null,
|
|
338
350
|
pixelWidth: bodyWidth, pixelHeight: bodyHeight, pageNumbers: settings?.pagestyle === 'plain', maxBytes, check});
|
|
339
351
|
check();
|
|
340
352
|
return {bytes, pages: count};
|
|
341
|
-
} finally { filmSession?.close(); nativeGlyphs?.restore(); vectors?.restore(); signal?.removeEventListener('abort', abort); frame.remove(); }
|
|
353
|
+
} finally { filmSession?.close(); nativeGlyphs?.restore(); vectors?.restore(); backdrop?.restore(); signal?.removeEventListener('abort', abort); frame.remove(); }
|
|
342
354
|
}
|
|
343
355
|
|
|
344
356
|
async function _rapierPrintTextPages(main, pageWidth, count, check, origin = main.getBoundingClientRect(), nativeNodes = null) {
|
|
@@ -481,7 +493,7 @@ function _rapierPrintSlots(text, rtl) {
|
|
|
481
493
|
return {slots, count};
|
|
482
494
|
}
|
|
483
495
|
|
|
484
|
-
async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [], canonical, bom = false, filename, title, font, width, height, margins, pixelWidth, pixelHeight, pageNumbers, maxBytes, check}) {
|
|
496
|
+
async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [], backdrop = null, canonical, bom = false, filename, title, font, width, height, margins, pixelWidth, pixelHeight, pageNumbers, maxBytes, check}) {
|
|
485
497
|
const encoder = new TextEncoder(), encode = value => encoder.encode(value);
|
|
486
498
|
const fail = code => { throw Object.assign(new Error(code), {code}); };
|
|
487
499
|
const concat = parts => {
|
|
@@ -565,11 +577,10 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
565
577
|
const held = pages[index], flate = !!held?.rgb;
|
|
566
578
|
const picture = stream('/Type /XObject /Subtype /Image /Width ' + pixelWidth + ' /Height ' + pixelHeight + ' /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /' + (flate ? 'FlateDecode' : 'DCTDecode'), flate ? held.rgb : held);
|
|
567
579
|
pages[index] = null;
|
|
568
|
-
let commands = '/Artifact BMC\nq\n' + [decimal(pixelWidth * .75), 0, 0, decimal(pixelHeight * .75), decimal(margins.left), decimal(height - margins.top - pixelHeight * .75)].join(' ') + ' cm\n/Im0 Do\nQ\nEMC\n';
|
|
569
580
|
const pictures = ['/Im0 ' + picture + ' 0 R'], states = [];
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
581
|
+
const emit = (drawing, left, top, clipWidth, clipHeight) => {
|
|
582
|
+
let out = `q\n.75 0 0 -.75 ${decimal(left)} ${decimal(top)} cm\n0 0 ${clipWidth} ${clipHeight} re W n\n1 0 0 1 ${decimal(drawing.x)} 0 cm\n`;
|
|
583
|
+
out += [drawing.clip.x, drawing.clip.y, drawing.clip.width, drawing.clip.height].map(decimal).join(' ') + ' re W n\n';
|
|
573
584
|
for (const piece of drawing.sequence) {
|
|
574
585
|
if (piece.image) {
|
|
575
586
|
let shared = sharedImages.get(piece.image);
|
|
@@ -583,15 +594,19 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
583
594
|
sharedImages.set(piece.image, shared);
|
|
584
595
|
}
|
|
585
596
|
if (!pictures.includes(shared.resource)) pictures.push(shared.resource);
|
|
586
|
-
|
|
597
|
+
out += `q\n${[piece.width, 0, 0, -piece.height, piece.x, piece.y + piece.height].map(decimal).join(' ')} cm\n${shared.name} Do\nQ\n`;
|
|
587
598
|
} else {
|
|
588
599
|
const state = '/Gs' + states.length;
|
|
589
600
|
states.push(state + ' ' + add('<< /Type /ExtGState /ca ' + decimal(piece.fillAlpha) + ' /CA ' + decimal(piece.strokeAlpha) + ' >>') + ' 0 R');
|
|
590
|
-
|
|
601
|
+
out += 'q\n' + state + ' gs\n' + piece.commands + 'Q\n';
|
|
591
602
|
}
|
|
592
603
|
}
|
|
593
|
-
|
|
594
|
-
}
|
|
604
|
+
return out + 'Q\n';
|
|
605
|
+
};
|
|
606
|
+
let commands = '';
|
|
607
|
+
for (const drawing of backdrop || []) commands += emit(drawing, 0, height, Math.round(width * 4 / 3), Math.round(height * 4 / 3));
|
|
608
|
+
commands += '/Artifact BMC\nq\n' + [decimal(pixelWidth * .75), 0, 0, decimal(pixelHeight * .75), decimal(margins.left), decimal(height - margins.top - pixelHeight * .75)].join(' ') + ' cm\n/Im0 Do\nQ\nEMC\n';
|
|
609
|
+
for (const drawing of vectorPages[index] || []) commands += emit(drawing, margins.left, height - margins.top, pixelWidth, pixelHeight);
|
|
595
610
|
const textAlphas = new Map();
|
|
596
611
|
for (const run of textPages[index]) {
|
|
597
612
|
if (!run.text) continue;
|
package/dist/kit/render.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {documentLanguage, documentSettingsFonts} from '../spec/document-settings
|
|
|
8
8
|
import {createLineMeasurer, linesHeight} from '../layout/model.mjs';
|
|
9
9
|
import {markPrintCapitals, createPrintCapitals, followingPrintProse} from './print-capitals.mjs';
|
|
10
10
|
import {freezePublishedInk} from './render-ink.mjs';
|
|
11
|
+
import {isTemplatePage, isPageBorderLayout} from '../spec/template-page.mjs';
|
|
11
12
|
export function positionNativeDiagram(drawing) {
|
|
12
13
|
const surface=drawing?.closest?.('.diagram-block,.diagram-live-preview');
|
|
13
14
|
if(!surface?.clientWidth)return;
|
|
@@ -109,6 +110,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
109
110
|
const styledRoot = work ? await _rapierProjectStyledRootAsync(context.semanticRoot, styledOptions)
|
|
110
111
|
: _rapierProjectStyledRoot(context.semanticRoot, styledOptions);
|
|
111
112
|
styledRoot.setAttribute('lang', language);
|
|
113
|
+
const templatePage = _rapierTemplatePage(context.canonical, settings, styledRoot);
|
|
112
114
|
if (completePage && typeof _rapierFinalizeExportCode === 'function') await _rapierFinalizeExportCode(styledRoot);
|
|
113
115
|
if (!opts.print) _rapierAnnotateExportBoxPolygons(styledRoot);
|
|
114
116
|
if (completePage || opts.print) {
|
|
@@ -121,7 +123,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
121
123
|
}
|
|
122
124
|
if (typeof opts.afterRoot === 'function') await opts.afterRoot(styledRoot);
|
|
123
125
|
markPrintCapitals(styledRoot);
|
|
124
|
-
const theme = opts.print
|
|
126
|
+
const theme = opts.print || templatePage
|
|
125
127
|
? 'light'
|
|
126
128
|
: _rapierArtifactPreference('theme', ['system', 'light', 'dark']);
|
|
127
129
|
const highlights = _rapierArtifactPreference('highlights', ['accent', 'standard']);
|
|
@@ -147,7 +149,8 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
147
149
|
+ (opts.extraCss ? '\n\n' + opts.extraCss : '')
|
|
148
150
|
+ (willFont ? '\n\n@font-face{font-family:' + willFont.family + ';src:url(' + await _rapierBlobDataUrl(new Blob([willFont.bytes], { type: 'font/ttf' })) + ') format("truetype")}' : '')
|
|
149
151
|
+ (settingsCss ? '\n' + settingsCss : '')
|
|
150
|
-
+ (typographyPrintCss ? '\n' + typographyPrintCss : '')
|
|
152
|
+
+ (typographyPrintCss ? '\n' + typographyPrintCss : '')
|
|
153
|
+
+ (templatePage ? '\n' + templatePage.css : '');
|
|
151
154
|
let factoryFontCss = '';
|
|
152
155
|
if (!opts.print) {
|
|
153
156
|
if (fontIndex) await requireDocumentFonts(context.canonical, {parser: md, index: fontIndex, root: styledRoot, settings, css, fontCss, validateFont: runtime.validateFont});
|
|
@@ -180,7 +183,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
180
183
|
+ '<meta name="generator" content="Rapier ' + escapeRapierHtmlText(version) + '">\n'
|
|
181
184
|
+ '<title>' + escapeRapierHtmlText(pageTitle) + '</title>\n'
|
|
182
185
|
+ '<style>' + css + '</style>\n</head>\n'
|
|
183
|
-
+ '<body class="' + bodyClass + '"
|
|
186
|
+
+ '<body class="' + bodyClass + '">' + (templatePage ? '<div class="rapier-template-border" aria-hidden="true"></div>' : '') + '<main class="' + docClass + '" data-md-theme="' + theme + '">'
|
|
184
187
|
+ bodyHtml + '</main>' + carrier + layoutScript + inkScript + htmlScript + '</body>\n</html>';
|
|
185
188
|
|
|
186
189
|
return {
|
|
@@ -191,6 +194,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
191
194
|
fontCss,
|
|
192
195
|
fontRequests: fontCss ? fontRequests(styledRoot, {settings, css, fontCss}) : [],
|
|
193
196
|
settings,
|
|
197
|
+
templatePage: templatePage && {url: templatePage.url, width: templatePage.width, height: templatePage.height, margin: templatePage.margin},
|
|
194
198
|
docClass,
|
|
195
199
|
language,
|
|
196
200
|
metadata,
|
|
@@ -198,6 +202,27 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
198
202
|
};
|
|
199
203
|
}
|
|
200
204
|
|
|
205
|
+
function _rapierTemplatePage(canonical, settings, root) {
|
|
206
|
+
if (!isTemplatePage(canonical) || !settings?.papersize) return null;
|
|
207
|
+
let url = '';
|
|
208
|
+
for (const paragraph of root.querySelectorAll(':scope > p[data-md-layout]')) {
|
|
209
|
+
const image = paragraph.querySelector('img');
|
|
210
|
+
const layout = globalThis.RapierMarkdownLayout.parseLayoutAttribute(paragraph.getAttribute('data-md-layout'));
|
|
211
|
+
const source = image?.getAttribute('src') || '';
|
|
212
|
+
if (isPageBorderLayout(layout) && /^data:image\/svg\+xml;base64,[A-Za-z0-9+/=]+$/.test(source)) { url = source; paragraph.remove(); break; }
|
|
213
|
+
}
|
|
214
|
+
if (!url) return null;
|
|
215
|
+
const width = settings.papersize.width / 15, height = settings.papersize.height / 15, margin = (settings.geometry?.twips ?? 1440) / 15;
|
|
216
|
+
const ground = 'background:var(--rapier-template-border) 0 0/100% 100% no-repeat';
|
|
217
|
+
const css = ':root{--rapier-template-border:url("' + url + '")}\n'
|
|
218
|
+
+ '@media screen{.rapier-template-border{display:none}body{background:#e8e8e8}.rapier-page{box-sizing:border-box;width:min(100%,' + width + 'px);min-height:' + height + 'px;margin:0 auto;padding:' + margin
|
|
219
|
+
+ 'px;background:var(--rapier-template-border) 0 0/100% ' + height + 'px repeat-y,#fff;color:#111}}\n'
|
|
220
|
+
+ '@media print{@page{size:' + width + 'px ' + height + 'px;margin:0}html,body{margin:0;-webkit-print-color-adjust:exact;print-color-adjust:exact}'
|
|
221
|
+
+ '.rapier-page{box-sizing:border-box;width:auto;max-width:none;margin:0;padding:' + margin + 'px!important;position:relative!important;z-index:1;box-decoration-break:clone;-webkit-box-decoration-break:clone}'
|
|
222
|
+
+ '.rapier-template-border{display:block;position:fixed;left:0;top:0;width:' + width + 'px;height:' + height + 'px;z-index:0;' + ground + '}}';
|
|
223
|
+
return {url, width, height, margin, css};
|
|
224
|
+
}
|
|
225
|
+
|
|
201
226
|
function _rapierPagePreviewMeta(root, fallbackTitle) {
|
|
202
227
|
const words = node => String(node?.textContent || '').replace(/\s+/g, ' ').trim();
|
|
203
228
|
const title = words(root.querySelector('h1,h2,h3,h4,h5,h6')) || String(fallbackTitle || '');
|
|
@@ -72,7 +72,7 @@ export function documentSpacingCss(settings, scope = '.md-render', defaults = fa
|
|
|
72
72
|
css += selector + '{margin-block:' + length(row.before) + ' ' + length(row.after) + ';line-height:' + row.line + '}';
|
|
73
73
|
if (settings?.word && /^h[1-6]$/.test(type)) css += selector + '{font-size:' + length(documentHeadingSize(settings, Number(type[1]))) + '}';
|
|
74
74
|
}
|
|
75
|
-
css += scope + ' :is(li,td,th,blockquote)>:last-child{margin-bottom:0}' +
|
|
75
|
+
css += scope + ' :is(li,td,th,blockquote)>:last-child:not(input){margin-bottom:0}' +
|
|
76
76
|
scope + ' li:last-child{margin-bottom:0}' + scope + ' li>:is(ul,ol){margin-bottom:0}' +
|
|
77
77
|
scope + ' .table-scroll-wrap>table{margin-block:0}' +
|
|
78
78
|
scope + ' pre{font-size:calc(var(--md-text-body) * .7386)}' +
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
export function isTemplatePage(source) {
|
|
3
|
+
const match = /^(?:)?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(String(source).slice(0, 65536));
|
|
4
|
+
return !!match && /^template:[ \t]*page[ \t]*(?:#.*)?$/m.test(match[1]);
|
|
5
|
+
}
|
|
6
|
+
export const isPageBorderLayout = layout => layout?.wrap === 'behind' && layout.lock === 'on';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-markdown-kit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.10",
|
|
4
4
|
"description": "Read, render and write Self-contained Markdown: one .md file that carries its pictures, drawings and layout, and that any Markdown app can read. HTML renderer, stylesheet, text layout, Will/1 agent permissions, and an edit history with authorship and offline merge. MIT, no dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|