rapier-markdown-kit 1.1.102 → 1.2.3
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/LICENSE +5 -2
- package/README.md +245 -54
- package/dist/agent/vendor/hyphen-en-us/LICENSE +14 -0
- package/dist/agent/vendor/hyphen-en-us/SOURCE.json +33 -0
- package/dist/agent/vendor/hyphen-en-us/hyph-en-us.hyp.txt +14 -0
- package/dist/agent/vendor/hyphen-en-us/hyph-en-us.pat.txt +4938 -0
- package/dist/agent/vendor/hyphen-en-us/patterns.js +25 -0
- package/dist/agent/vendor/pretext/LICENSE +208 -0
- package/dist/agent/vendor/pretext/NOTICE +26 -13
- package/dist/agent/vendor/pretext/SOURCE.json +213 -45
- package/dist/agent/vendor/pretext/analysis.js +675 -1224
- package/dist/agent/vendor/pretext/entry-geometry.js +183 -0
- package/dist/agent/vendor/pretext/gecko-line-breaks.js +688 -0
- package/dist/agent/vendor/pretext/generated/engine-break-data.js +57 -0
- package/dist/agent/vendor/pretext/generated/webkit-generic-families.js +80 -0
- package/dist/agent/vendor/pretext/graphemes.js +98 -0
- package/dist/agent/vendor/pretext/han-kerning.js +253 -0
- package/dist/agent/vendor/pretext/layout.js +352 -290
- package/dist/agent/vendor/pretext/line-break.js +1154 -588
- package/dist/agent/vendor/pretext/line-breaks.js +892 -0
- package/dist/agent/vendor/pretext/line-text.js +81 -39
- package/dist/agent/vendor/pretext/measurement.js +1105 -176
- package/dist/agent/vendor/pretext/prepare.js +807 -0
- package/dist/agent/vendor/pretext/rich-inline.js +1242 -273
- package/dist/agent/will.mjs +2 -0
- package/dist/images/gif.mjs +47 -0
- package/dist/images/playback.mjs +101 -0
- package/dist/kit/assets.mjs +5 -1
- package/dist/kit/conformance/03-wrap-around-silhouette.md +1 -1
- package/dist/kit/conformance/04-wrap-box.md +1 -1
- package/dist/kit/conformance/05-behind.md +1 -1
- package/dist/kit/conformance/06-front.md +1 -1
- package/dist/kit/conformance/07-rotate-raster.md +1 -1
- package/dist/kit/conformance/08-drawing-rotation.md +1 -1
- package/dist/kit/conformance/09-drawing-ring-interior.expected.json +18 -18
- package/dist/kit/conformance/09-drawing-ring-interior.md +1 -1
- package/dist/kit/conformance/10-both-sides.md +1 -1
- package/dist/kit/conformance/11-neighbour-skips-image.md +1 -1
- package/dist/kit/conformance/{12-heading-barrier.expected.json → 12-rule-barrier.expected.json} +1 -1
- package/dist/kit/conformance/{12-heading-barrier.md → 12-rule-barrier.md} +2 -2
- package/dist/kit/conformance/README.md +19 -28
- package/dist/kit/conformance/run.mjs +13 -11
- package/dist/kit/export-fonts.mjs +26 -11
- package/dist/kit/index.mjs +1 -0
- package/dist/kit/ledger/carried.mjs +17 -10
- package/dist/kit/ledger/data.mjs +73 -13
- package/dist/kit/ledger/format.mjs +176 -54
- package/dist/kit/ledger/hash.mjs +91 -18
- package/dist/kit/ledger/history.mjs +156 -116
- package/dist/kit/ledger/inverse-source.mjs +158 -19
- package/dist/kit/ledger/journal-records.mjs +292 -67
- package/dist/kit/ledger/merge.mjs +4 -3
- package/dist/kit/model.mjs +1 -1
- package/dist/kit/pdf-glyphs.mjs +4 -4
- package/dist/kit/pdf-vectors.mjs +4 -1
- package/dist/kit/rapier-ledger.mjs +2 -2
- package/dist/kit/render-ink.mjs +1 -1
- package/dist/kit/render-markdown.mjs +400 -148
- package/dist/kit/render-print.mjs +131 -55
- package/dist/kit/render-sanitize.mjs +30 -7
- package/dist/kit/render-work.mjs +99 -20
- package/dist/kit/render.mjs +133 -140
- package/dist/kit/typography.mjs +2 -0
- package/dist/layout/hyphenation.mjs +127 -0
- package/dist/layout/line-plan.mjs +88 -96
- package/dist/layout/model.mjs +71 -88
- package/dist/layout/paragraph-fit.mjs +191 -0
- package/dist/layout/preparation.mjs +373 -0
- package/dist/layout/prepared-paragraph.mjs +223 -0
- package/dist/layout/typography.mjs +345 -111
- package/dist/packages/rapier-diff/index.mjs +359 -224
- package/dist/spec/document-fonts.mjs +14 -5
- package/dist/spec/document-settings.mjs +11 -164
- package/dist/spec/html-blocks.mjs +618 -79
- package/dist/spec/md-assets.mjs +178 -36
- package/dist/spec/md-layout.mjs +13 -2
- package/dist/spec/md-marks.mjs +220 -127
- package/dist/spec/md-source.mjs +157 -0
- package/package.json +2 -1
- package/style.css +15 -50
- package/dist/agent/vendor/pretext/bidi.js +0 -151
- package/dist/agent/vendor/pretext/generated/bidi-data.js +0 -3831
package/dist/kit/render.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
import {htmlImageRecord,
|
|
2
|
+
import {htmlImageRecord, htmlRuntimeSource} from '../spec/html-blocks.mjs';
|
|
3
3
|
import {finish, finishAsync, cloneTree, cleanTree, serializeTree, encodeUtf8Steps, pause} from './render-work.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {fontFamilyCss} from '../spec/md-marks.mjs';
|
|
5
5
|
import {readFontAssets, documentFontsCss} from '../spec/document-fonts.mjs';
|
|
6
6
|
import {fontRequests, requireDocumentFonts} from './export-fonts.mjs';
|
|
7
7
|
import {documentLanguage, documentSettingsFonts} from '../spec/document-settings.mjs';
|
|
8
|
-
import {createTypography} from '../layout/typography.mjs';
|
|
9
8
|
import {createLineMeasurer, linesHeight} from '../layout/model.mjs';
|
|
10
9
|
import {markPrintCapitals, createPrintCapitals, followingPrintProse} from './print-capitals.mjs';
|
|
11
10
|
import {freezePublishedInk} from './render-ink.mjs';
|
|
@@ -69,7 +68,7 @@ export function positionNativeDiagram(drawing) {
|
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
function createRenderer(runtime) {
|
|
72
|
-
const {
|
|
71
|
+
const {RapierLedgerCarried, RapierPageReturnAddress, RapierTextCodec, _rapierArtifactHighlight, _rapierArtifactPreference, _rapierArtifactStyles, _rapierBlobDataUrl, _rapierBuildInterchangeContext, _rapierDocumentNameIsAdmissible, _rapierLedgerParts, _rapierDrawReadSVGRecipe, _rapierDrawShapeProfileFor, _rapierExportFontCss, _rapierFillDiagram, _rapierFinalizeExportCode, _rapierLanguageClass, _rapierLineStartOffsets, _rapierMarkdownEnvironment, _rapierPortableHtml, _rapierPrepareInterchangeContext, _rapierProjectPortableRoot, _rapierProviders, _rapierSourceCharEscaped, _rapierSourceLineSpan, crypto, document, escapeRapierHtmlText, globalThis, sanitizeRapierHtml} = runtime;
|
|
73
72
|
let md = runtime.md;
|
|
74
73
|
async function render(source, options = {}) {
|
|
75
74
|
if (typeof source !== 'string') throw new TypeError('Document source must be a string');
|
|
@@ -158,14 +157,16 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
158
157
|
styledRoot.setAttribute('lang', language);
|
|
159
158
|
if (completePage && typeof _rapierFinalizeExportCode === 'function') await _rapierFinalizeExportCode(styledRoot);
|
|
160
159
|
// _rapierProjectStyledRoot already returns the sanitized projection.
|
|
161
|
-
//
|
|
162
|
-
// this annotation -- skip it there rather than pay the cost for numbers nothing will read.
|
|
160
|
+
// Picture-shape annotations have no consumer in a printed artifact.
|
|
163
161
|
if (!opts.print) _rapierAnnotateExportBoxPolygons(styledRoot);
|
|
162
|
+
// Paper keeps each picture's wrap through the same static float layout a page uses.
|
|
163
|
+
if (completePage || opts.print) {
|
|
164
|
+
if (work) await finishAsync(_rapierSharedPageLayoutSteps(styledRoot), work);
|
|
165
|
+
else _rapierSharedPageLayout(styledRoot);
|
|
166
|
+
}
|
|
164
167
|
if (completePage && !opts.print) {
|
|
165
|
-
if (work)
|
|
166
|
-
|
|
167
|
-
await finishAsync(_rapierShareCapLongCodeBlocksSteps(styledRoot), work);
|
|
168
|
-
} else { _rapierSharedPageLayout(styledRoot); _rapierShareCapLongCodeBlocks(styledRoot); }
|
|
168
|
+
if (work) await finishAsync(_rapierShareCapLongCodeBlocksSteps(styledRoot), work);
|
|
169
|
+
else _rapierShareCapLongCodeBlocks(styledRoot);
|
|
169
170
|
}
|
|
170
171
|
if (typeof opts.afterRoot === 'function') await opts.afterRoot(styledRoot);
|
|
171
172
|
markPrintCapitals(styledRoot);
|
|
@@ -173,6 +174,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
173
174
|
? 'light'
|
|
174
175
|
: _rapierArtifactPreference('theme', ['system', 'light', 'dark']);
|
|
175
176
|
const highlights = _rapierArtifactPreference('highlights', ['accent', 'standard']);
|
|
177
|
+
const invertImageAlpha = _rapierArtifactPreference('invertImageAlpha', ['true', 'false']) !== 'false';
|
|
176
178
|
const versionMeta = document.querySelector('meta[name="rapier-version"]');
|
|
177
179
|
const version = versionMeta && versionMeta.content ? versionMeta.content.trim() : '0.0.0';
|
|
178
180
|
const bodyClass = theme === 'light' ? 'light' : '';
|
|
@@ -185,8 +187,6 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
185
187
|
// rules of its own -- Share, for the no-script float it alone writes -- appends them.
|
|
186
188
|
// Print retains its existing factory resources. HTML adds only the optional small Sans subset below.
|
|
187
189
|
const includeFonts = !!opts.print;
|
|
188
|
-
const zenFontCss = opts.print && styledRoot.querySelector('svg[aria-roledescription="zenuml"]')
|
|
189
|
-
? (_rapierProviders?.mermaid?.exportFontCss?.() || '') : '';
|
|
190
190
|
const willFont = opts.print && opts.willFont ? opts.willFont : null;
|
|
191
191
|
const settingsCss = _rapierDocumentSettingsCss(settings);
|
|
192
192
|
const refined = styledRoot.querySelector('p[data-md-hyphens="auto"],p[data-md-lines],p[data-md-caps="small"],p[data-md-hanging="on"],img[data-rapier-print-lines]');
|
|
@@ -225,12 +225,11 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
225
225
|
// The reflow script is written first, because whether it exists decides the policy. A page that carries no script says so: script-src 'none' refuses every
|
|
226
226
|
// script outright. Declaring a nonce a page has no use for is a widening, small but real -- anything that could inject markup into the file could read the
|
|
227
227
|
// nonce out of it and be admitted.
|
|
228
|
-
const layoutScript =
|
|
228
|
+
const layoutScript = opts.scripts !== false ? _rapierArtifactLayoutScript(styledRoot, nonce, {pictures:!opts.print}) : '';
|
|
229
229
|
const inkScript = !opts.print && opts.scripts !== false ? _rapierArtifactInkScript(styledRoot, nonce) : '';
|
|
230
|
-
const
|
|
231
|
-
const htmlScript = opts.scripts !== false && styledRoot.querySelector('[data-rapier-html]') ? '<script nonce="' + nonce + '">(' + mountHtmlBlocks.toString().replace(/<\/script/gi, '<\\/script') + ')(document);</script>' : '';
|
|
230
|
+
const htmlScript = !opts.print && opts.scripts !== false && styledRoot.querySelector('[data-rapier-html],[data-rapier-animation]') ? '<script nonce="' + nonce + '">' + htmlRuntimeSource().replace(/<\/script/gi, '<\\/script') + '</script>' : '';
|
|
232
231
|
const page = '<!DOCTYPE html>\n'
|
|
233
|
-
+ '<html lang="' + escapeRapierHtmlText(language) + '" data-rapier-theme="' + theme + '" data-highlights="' + highlights + '">\n'
|
|
232
|
+
+ '<html lang="' + escapeRapierHtmlText(language) + '" data-rapier-theme="' + theme + '" data-highlights="' + highlights + '" data-invert-image-alpha="' + (invertImageAlpha ? 'on' : 'off') + '">\n'
|
|
234
233
|
+ '<head>\n<meta charset="UTF-8">\n' + (carrier ? RapierPageReturnAddress.PAGE_SEED + '\n' : '')
|
|
235
234
|
+ '<meta name="viewport" content="width=device-width, initial-scale=1">\n'
|
|
236
235
|
// Chrome 154's responsively sized frames: a host frame styled `frame-sizing: content-height` sizes
|
|
@@ -238,14 +237,14 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
238
237
|
// someone's page shows whole, with no inner scroll. Any origin may read the height: the page is the
|
|
239
238
|
// document, published by the person who exported it.
|
|
240
239
|
+ '<meta name="responsive-embedded-sizing" content="allow-origins=*">\n'
|
|
241
|
-
+ '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || inkScript ||
|
|
240
|
+
+ '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || inkScript || htmlScript ? nonce : '', includeFonts || !!fontCss || !!factoryFontCss) + '">\n'
|
|
242
241
|
+ '<meta name="referrer" content="no-referrer">\n'
|
|
243
242
|
+ '<link rel="icon" href="data:,">\n'
|
|
244
243
|
+ '<meta name="generator" content="Rapier ' + escapeRapierHtmlText(version) + '">\n'
|
|
245
244
|
+ '<title>' + escapeRapierHtmlText(pageTitle) + '</title>\n'
|
|
246
245
|
+ '<style>' + css + '</style>\n</head>\n'
|
|
247
|
-
+ '<body class="' + bodyClass + '"><main class="' + docClass + '" data-md-theme="' + theme + '"
|
|
248
|
-
+ bodyHtml + '</main>' + carrier + layoutScript + inkScript +
|
|
246
|
+
+ '<body class="' + bodyClass + '"><main class="' + docClass + '" data-md-theme="' + theme + '">'
|
|
247
|
+
+ bodyHtml + '</main>' + carrier + layoutScript + inkScript + htmlScript + '</body>\n</html>';
|
|
249
248
|
|
|
250
249
|
return {
|
|
251
250
|
html: page,
|
|
@@ -302,49 +301,6 @@ function* _rapierProjectStyledRootSteps(semanticRoot, options) {
|
|
|
302
301
|
if (lang) code.classList.add('language-' + String(lang).replace(/[^a-z0-9_-]/gi, ''));
|
|
303
302
|
}
|
|
304
303
|
} else {
|
|
305
|
-
if (root.hasAttribute('data-md-space')) {
|
|
306
|
-
// One raw HTML token may have several siblings. Its authored heights stay inside one line-sized box.
|
|
307
|
-
for (let element = root.firstElementChild; element;) {
|
|
308
|
-
const group = element.getAttribute('data-md-html-block');
|
|
309
|
-
if (group === null) { element = element.nextElementSibling; continue; }
|
|
310
|
-
let first = element;
|
|
311
|
-
// Sanitizing a container or repairing a table can leave its text before the first surviving tag.
|
|
312
|
-
while (first.previousSibling && first.previousSibling.nodeType !== 1) { first = first.previousSibling; yield; }
|
|
313
|
-
const wrap = document.createElement('div');
|
|
314
|
-
wrap.className = 'md-space-block';
|
|
315
|
-
root.insertBefore(wrap, first);
|
|
316
|
-
let node = first;
|
|
317
|
-
while (node && (node.nodeType !== 1 || node.getAttribute('data-md-html-block') === group)) {
|
|
318
|
-
const next = node.nextSibling;
|
|
319
|
-
wrap.appendChild(node);
|
|
320
|
-
node = next;
|
|
321
|
-
yield;
|
|
322
|
-
}
|
|
323
|
-
element = wrap.nextElementSibling;
|
|
324
|
-
}
|
|
325
|
-
// A removed raw container can leave only text. Reserve that surviving content in this projection.
|
|
326
|
-
for (let first = root.firstChild; first;) {
|
|
327
|
-
if (first.nodeType === 1) { first = first.nextSibling; continue; }
|
|
328
|
-
let end = first, hasText = false;
|
|
329
|
-
while (end && end.nodeType !== 1) {
|
|
330
|
-
if (end.nodeType === 3 && end.nodeValue.trim()) hasText = true;
|
|
331
|
-
end = end.nextSibling;
|
|
332
|
-
yield;
|
|
333
|
-
}
|
|
334
|
-
if (hasText) {
|
|
335
|
-
const wrap = document.createElement('div');
|
|
336
|
-
wrap.className = 'md-space-block';
|
|
337
|
-
root.insertBefore(wrap, first);
|
|
338
|
-
for (let node = first; node !== end;) {
|
|
339
|
-
const next = node.nextSibling;
|
|
340
|
-
wrap.appendChild(node);
|
|
341
|
-
node = next;
|
|
342
|
-
yield;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
first = end;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
304
|
for (const code of root.querySelectorAll('pre > code')) { yield;
|
|
349
305
|
if (code.parentElement?.classList.contains('diagram-source') &&
|
|
350
306
|
code.closest('.diagram-block')?.getAttribute('data-diagram-state') === 'ready') continue;
|
|
@@ -466,11 +422,15 @@ function _rapierPaintCode(el, html) {
|
|
|
466
422
|
|
|
467
423
|
function _rapierPrefixPortableAnchors(root, baseName) { return finish(_rapierPrefixPortableAnchorsSteps(root, baseName)); }
|
|
468
424
|
|
|
469
|
-
|
|
425
|
+
// The id an anchor carries on a portable page: the document's name, then the anchor with what an id cannot hold made a hyphen.
|
|
426
|
+
function _rapierPortableAnchorId(baseName, id) {
|
|
470
427
|
const slug = String(baseName || 'document').toLowerCase()
|
|
471
428
|
.replace(/[^a-z0-9]+/g, '-')
|
|
472
429
|
.replace(/^-+|-+$/g, '') || 'document';
|
|
473
|
-
|
|
430
|
+
return 'rapier-' + slug + '-' + (String(id).replace(/[^a-z0-9_-]+/gi, '-').replace(/^-+|-+$/g, '') || 'anchor');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function* _rapierPrefixPortableAnchorsSteps(root, baseName) {
|
|
474
434
|
const ids = new Map(), svgIds = new Map();
|
|
475
435
|
const used = new Set();
|
|
476
436
|
const scopeOf = element => {
|
|
@@ -487,11 +447,10 @@ function* _rapierPrefixPortableAnchorsSteps(root, baseName) {
|
|
|
487
447
|
for (const element of root.querySelectorAll('[id]')) { yield;
|
|
488
448
|
const oldId = element.id;
|
|
489
449
|
if (!oldId) continue;
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
let nextId = prefix + base;
|
|
450
|
+
const first = _rapierPortableAnchorId(baseName, oldId);
|
|
451
|
+
let nextId = first;
|
|
493
452
|
let suffix = 2;
|
|
494
|
-
while (used.has(nextId)) nextId =
|
|
453
|
+
while (used.has(nextId)) nextId = first + '-' + suffix++;
|
|
495
454
|
used.add(nextId);
|
|
496
455
|
// A document link resolves the first matching anchor, as it did before renaming.
|
|
497
456
|
// SVG references resolve within their own drawing, never the last repeated drawing.
|
|
@@ -694,13 +653,14 @@ function _rapierSharedResolve(text, ids, images, definitions) {
|
|
|
694
653
|
let broken = false;
|
|
695
654
|
const src = value => {
|
|
696
655
|
const found = images.get(value);
|
|
697
|
-
if (typeof found !== 'string' || !/^data:image\/(?:png|jpeg|webp|svg\+xml|jxl);base64,[A-Za-z0-9+/]+={0,2}$/i.test(found)) { broken = true; return null; }
|
|
656
|
+
if (typeof found !== 'string' || !/^data:image\/(?:png|jpeg|webp|gif|svg\+xml|jxl);base64,[A-Za-z0-9+/]+={0,2}$/i.test(found)) { broken = true; return null; }
|
|
698
657
|
return found;
|
|
699
658
|
};
|
|
700
659
|
// Authored # characters were encoded. A raw id after ]( can only be a structural
|
|
701
660
|
// substitution; matching its destination lets nested alt brackets survive unchanged.
|
|
702
661
|
const inline = new RegExp('(\\]\\([ \\t\\r\\n]*(?:<)?)#' + id + '(?=>?[ \\t\\r\\n]*\\)|>?[ \\t\\r\\n]+["\'(])', 'g');
|
|
703
|
-
|
|
662
|
+
// A definition may sit in a quote or a list item: its line starts with that container's markers.
|
|
663
|
+
const definition = new RegExp('(^[ \\t>]*\\[((?:\\\\.|[^\\]\\\\])+)\\]:[ \\t]*(?:(?:\\r\\n|\\r|\\n)[ \\t>]*)?(?:<)?)#' + id + '(?=>?[ \\t]*$|>?[ \\t]+["\'(])', 'gm');
|
|
704
664
|
const swapInline = (match, lead, value) => { const found = src(value); return found == null ? match : lead + found; };
|
|
705
665
|
const swapDefinition = (match, lead, label, value) => {
|
|
706
666
|
if (!definitions.has(md.utils.normalizeReference(_rapierSharedDecode(label)))) return match;
|
|
@@ -878,7 +838,7 @@ function* _rapierSharedPageLayoutSteps(root) {
|
|
|
878
838
|
// `layout.y` already owns `shape-outside` for its own inset reservation (below); the two are not
|
|
879
839
|
// combinable (a float has exactly one `shape-outside` value), so a `y`-offset `around` picture
|
|
880
840
|
// keeps that inset instead of silhouette wrapping -- a documented, narrow degradation.
|
|
881
|
-
if (layout.wrap === 'around' && !layout.rotate && !layout.y) {
|
|
841
|
+
if (layout.wrap === 'around' && !layout.rotate && !layout.y && !image.dataset.rapierContour?.endsWith('-box')) {
|
|
882
842
|
const src = image.getAttribute('src');
|
|
883
843
|
if (src) {
|
|
884
844
|
paragraph.style.setProperty('shape-outside', 'url("' + src + '")');
|
|
@@ -906,16 +866,12 @@ function* _rapierSharedPageLayoutSteps(root) {
|
|
|
906
866
|
for (const {image, paragraph, layout} of positionedAssignments) {
|
|
907
867
|
yield;
|
|
908
868
|
paragraph.style.cssText = 'position:relative;height:0;margin:0;padding:0;line-height:0';
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
image.style.left = 'calc(' + x + '% - ' + (naturalWidth / 2) + 'px)';
|
|
916
|
-
} else {
|
|
917
|
-
image.style.left = x + '%';
|
|
918
|
-
}
|
|
869
|
+
// Without x the picture starts at the left edge, as everywhere else (layout/model.mjs imageBox).
|
|
870
|
+
const x = layout.x, naturalWidth = Number(image.getAttribute('width')) || 0;
|
|
871
|
+
if (layout.width != null) image.style.width = layout.width + '%';
|
|
872
|
+
else if (naturalWidth > 0) image.style.width = naturalWidth + 'px';
|
|
873
|
+
image.style.left = x == null ? '0' : layout.width != null ? (x - layout.width / 2) + '%'
|
|
874
|
+
: naturalWidth > 0 ? 'calc(' + x + '% - ' + (naturalWidth / 2) + 'px)' : x + '%';
|
|
919
875
|
image.style.position = 'absolute';
|
|
920
876
|
image.style.top = (layout.y || 0) + 'em';
|
|
921
877
|
image.style.margin = '0';
|
|
@@ -1381,17 +1337,10 @@ function _rapierImageOpenRunsList(tokens) {
|
|
|
1381
1337
|
const stack = [];
|
|
1382
1338
|
for (const token of block.children) {
|
|
1383
1339
|
if (token.type === 'image') { list.push(stack.slice()); continue; }
|
|
1384
|
-
if (token.
|
|
1385
|
-
|
|
1386
|
-
stack.push({open: _rapierFormatColorOpen(hex), close: RAPIER_COLOR_CLOSE});
|
|
1387
|
-
continue;
|
|
1340
|
+
if (token.meta?.typography && token.nesting === 1) {
|
|
1341
|
+
stack.push({open: token.meta.typography.open, close: token.meta.typography.close}); continue;
|
|
1388
1342
|
}
|
|
1389
|
-
if (token.
|
|
1390
|
-
if (token.type === 'rapier_font_open') {
|
|
1391
|
-
stack.push({open: token.meta?.mdFont?.source || formatFontOpen(token.attrGet('data-md-font')), close: FONT_CLOSE});
|
|
1392
|
-
continue;
|
|
1393
|
-
}
|
|
1394
|
-
if (token.type === 'rapier_font_close') { stack.pop(); continue; }
|
|
1343
|
+
if (token.meta?.typography && token.nesting === -1) { stack.pop(); continue; }
|
|
1395
1344
|
if (token.type === 'strong_open' || token.type === 'em_open' || token.type === 's_open') {
|
|
1396
1345
|
stack.push({open: token.markup, close: token.markup});
|
|
1397
1346
|
continue;
|
|
@@ -1407,10 +1356,11 @@ function _rapierArtifactFactoryModules(dependencies, group) {
|
|
|
1407
1356
|
return paths.map(path => 'modules[' + JSON.stringify(path) + '] = (' + Function.prototype.toString.call(dependencies.factories[path]) + ')();').join('\n');
|
|
1408
1357
|
}
|
|
1409
1358
|
|
|
1410
|
-
function _rapierProjectArtifactLayout(root, metadata, geometry,
|
|
1411
|
-
if (!root || !metadata || !geometry || !
|
|
1359
|
+
function _rapierProjectArtifactLayout(root, metadata, geometry, preparation) {
|
|
1360
|
+
if (!root || !metadata || !geometry || !preparation) return null;
|
|
1412
1361
|
const doc = root.ownerDocument, view = doc.defaultView;
|
|
1413
1362
|
const styles = new Map(), originals = new Map(), spacers = [], floats = [], steps = [], profiles = new WeakMap();
|
|
1363
|
+
const prepared = preparation.createPreparationScope(doc);
|
|
1414
1364
|
const css = node => view.getComputedStyle(node);
|
|
1415
1365
|
const box = node => node.getBoundingClientRect();
|
|
1416
1366
|
const pixels = value => (Number.isFinite(value) ? Math.round(value * 100) / 100 : 0) + 'px';
|
|
@@ -1437,48 +1387,72 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1437
1387
|
steps.length = 0;
|
|
1438
1388
|
}
|
|
1439
1389
|
|
|
1440
|
-
//
|
|
1390
|
+
// One prepared rich paragraph retains original item text. Unsupported shaping keeps native flow.
|
|
1441
1391
|
function prepare(paragraph) {
|
|
1392
|
+
const refuse = () => { prepared.forget(paragraph); return null; };
|
|
1442
1393
|
if (!/^(P|H[1-6])$/.test(paragraph.tagName) || !paragraph.textContent.trim() || paragraph.textContent.length > 8192 ||
|
|
1443
|
-
paragraph.
|
|
1394
|
+
[...paragraph.querySelectorAll('[id]')].some(element => /^[\s\u00ad\u200b]*$/u.test(element.textContent)) ||
|
|
1395
|
+
paragraph.querySelector('img,svg,math,br,input,button,iframe,canvas,.math-rendered')) return refuse();
|
|
1444
1396
|
const computed = css(paragraph);
|
|
1445
|
-
|
|
1446
|
-
|
|
1397
|
+
const language = paragraph.closest('[lang]')?.getAttribute('lang') || '';
|
|
1398
|
+
for (let parent = paragraph; parent; parent = parent.parentElement) {
|
|
1399
|
+
const inherited = css(parent);
|
|
1400
|
+
if (parent.style.textWrapStyle === 'pretty' || inherited.transform !== 'none' ||
|
|
1401
|
+
!['1', 'normal'].includes(inherited.zoom)) return refuse();
|
|
1402
|
+
}
|
|
1403
|
+
if (computed.textAlign === 'justify' || computed.textWrapStyle === 'balance' ||
|
|
1404
|
+
!preparation.supportsRichStyle(computed, language) || computed.whiteSpace !== 'normal' ||
|
|
1405
|
+
computed.transform !== 'none' ||
|
|
1406
|
+
['::before', '::after'].some(pseudo => !['none', 'normal'].includes(view.getComputedStyle(paragraph, pseudo).content)) ||
|
|
1447
1407
|
// Lines start at the padding box's top-left: left, right or top inset is refused.
|
|
1448
1408
|
['paddingLeft', 'paddingRight', 'paddingTop', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth']
|
|
1449
|
-
.some(key => parseFloat(computed[key]) > 0)) return
|
|
1409
|
+
.some(key => parseFloat(computed[key]) > 0)) return refuse();
|
|
1450
1410
|
const runs = [], items = [], walker = doc.createTreeWalker(paragraph, 4);
|
|
1451
1411
|
for (let node; (node = walker.nextNode());) {
|
|
1452
|
-
if (runs.length === 192) return
|
|
1412
|
+
if (runs.length === 192) return refuse();
|
|
1453
1413
|
const parents = [];
|
|
1454
1414
|
for (let parent = node.parentElement; parent !== paragraph; parent = parent.parentElement) {
|
|
1455
|
-
if (!parent || !/^(A|SPAN|EM|I|STRONG|B|DEL|S|MARK|CODE|U|INS|ABBR|SUP|SUB)$/.test(parent.tagName)
|
|
1415
|
+
if (!parent || !/^(A|SPAN|EM|I|STRONG|B|DEL|S|MARK|CODE|KBD|SAMP|U|INS|ABBR|SUP|SUB)$/.test(parent.tagName) ||
|
|
1416
|
+
parent.hidden || parent.getAttribute('aria-hidden') === 'true' ||
|
|
1417
|
+
parent.style.textWrapStyle === 'pretty' ||
|
|
1418
|
+
parent.hasAttribute('lang') && parent.getAttribute('lang').toLowerCase() !== (language || '').toLowerCase()) return refuse();
|
|
1419
|
+
const parentStyle = css(parent);
|
|
1420
|
+
if (!preparation.supportsRichStyle(parentStyle, language) || parentStyle.display !== 'inline' ||
|
|
1421
|
+
parentStyle.cssFloat !== 'none' || !['static', 'relative'].includes(parentStyle.position) ||
|
|
1422
|
+
parentStyle.transform !== 'none' || !['1', 'normal'].includes(parentStyle.zoom) ||
|
|
1423
|
+
['::before', '::after'].some(pseudo => !['none', 'normal'].includes(view.getComputedStyle(parent, pseudo).content))) return refuse();
|
|
1424
|
+
if (!/^(CODE|KBD|SAMP)$/.test(parent.tagName) &&
|
|
1425
|
+
['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth', 'marginLeft', 'marginRight']
|
|
1426
|
+
.some(key => Math.abs(parseFloat(parentStyle[key]) || 0) > .01)) return refuse();
|
|
1456
1427
|
parents.unshift(parent);
|
|
1457
1428
|
}
|
|
1458
1429
|
const computedRun = css(node.parentElement);
|
|
1459
|
-
if (
|
|
1430
|
+
if (!preparation.supportsRichStyle(computedRun, language) ||
|
|
1431
|
+
computedRun.whiteSpace !== computed.whiteSpace && computedRun.whiteSpace !== 'nowrap') return refuse();
|
|
1460
1432
|
const font = `${computedRun.fontStyle} ${computedRun.fontWeight} ${computedRun.fontSize} ${computedRun.fontFamily}`;
|
|
1461
1433
|
const letterSpacing = parseFloat(computedRun.letterSpacing) || 0;
|
|
1462
|
-
const
|
|
1463
|
-
if (
|
|
1464
|
-
// A chip's box is repeated by each piece of it, and it breaks as the browser breaks inline code, as the editor's projection does.
|
|
1465
|
-
const boxed = parents.some(parent => parent.tagName === 'CODE');
|
|
1434
|
+
const boxed = parents.some(parent => /^(CODE|KBD|SAMP)$/.test(parent.tagName));
|
|
1435
|
+
if (boxed && (!/^(CODE|KBD|SAMP)$/.test(node.parentElement.tagName) || node.parentElement.childNodes.length !== 1)) return refuse();
|
|
1466
1436
|
const extraWidth = boxed ? ['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth', 'marginLeft', 'marginRight']
|
|
1467
1437
|
.reduce((width, key) => width + (parseFloat(computedRun[key]) || 0), 0) : 0;
|
|
1468
|
-
runs.push({parents,
|
|
1469
|
-
|
|
1438
|
+
runs.push({parents, raw: node.data, font, letterSpacing,
|
|
1439
|
+
directions: parents.map(parent => parent.getAttribute('dir') === 'auto' ? css(parent).direction : null)});
|
|
1440
|
+
items.push({text: node.data, font, letterSpacing,
|
|
1441
|
+
break: computedRun.whiteSpace === 'nowrap' || parents.some(parent => parent.hasAttribute('data-rapier-source')) ? 'never' : 'normal', extraWidth});
|
|
1470
1442
|
}
|
|
1471
|
-
if (!runs.length) return
|
|
1443
|
+
if (!runs.length) return refuse();
|
|
1444
|
+
const options = {whiteSpace: computed.whiteSpace, wordBreak: computed.wordBreak, language};
|
|
1445
|
+
const flow = prepared.prepare(paragraph, items, options);
|
|
1446
|
+
if (!flow) return refuse();
|
|
1472
1447
|
const fontSize = parseFloat(computed.fontSize) || 16;
|
|
1473
|
-
return {paragraph, runs, flow:
|
|
1448
|
+
return {paragraph, runs, flow, whiteSpace: computed.whiteSpace, direction: computed.direction,
|
|
1474
1449
|
align: computed.textAlign, fontSize, lineHeight: parseFloat(computed.lineHeight) || fontSize * 1.65,
|
|
1475
|
-
balance:
|
|
1450
|
+
balance: 0};
|
|
1476
1451
|
}
|
|
1477
1452
|
|
|
1478
1453
|
// An id survives on the first fragment only.
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
const run = record.runs[fragment.itemIndex], mapped = geometry.mapFragment(run?.prepared, fragment);
|
|
1454
|
+
function fragmentNode(record, fragment, idsKept) {
|
|
1455
|
+
const run = record.runs[fragment.itemIndex], mapped = geometry.mapFragment(run, fragment, {whiteSpace: record.whiteSpace});
|
|
1482
1456
|
if (!mapped) return null;
|
|
1483
1457
|
// A line ends on the white space it broke at. A chip, a link or a highlight must not paint that space, so it stands after the shell.
|
|
1484
1458
|
const kept = run.parents.length ? mapped.text.replace(/[ \t\n\r\f]+$/, '') : mapped.text;
|
|
@@ -1497,10 +1471,12 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1497
1471
|
}
|
|
1498
1472
|
|
|
1499
1473
|
function project(record, width, top, obstacles) {
|
|
1474
|
+
if (!prepared.current(record.flow)) return false;
|
|
1500
1475
|
const plan = geometry.flowLines(record.flow, width, top, obstacles, record.lineHeight,
|
|
1501
1476
|
metadata.wrapColumnFloor(record.fontSize), record.direction, record.balance, record.align);
|
|
1502
1477
|
if (!plan) return false;
|
|
1503
1478
|
const output = doc.createDocumentFragment();
|
|
1479
|
+
const idsKept = new Set();
|
|
1504
1480
|
let previous = null;
|
|
1505
1481
|
for (const line of plan.lines) {
|
|
1506
1482
|
const element = doc.createElement('span');
|
|
@@ -1508,17 +1484,21 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1508
1484
|
element.style.textAlign = ['left', 'center', 'right', 'start', 'end'].includes(record.align)
|
|
1509
1485
|
? record.align : record.direction === 'rtl' ? 'right' : 'left';
|
|
1510
1486
|
for (const fragment of line.fragments) {
|
|
1511
|
-
const child = fragmentNode(record, fragment);
|
|
1487
|
+
const child = fragmentNode(record, fragment, idsKept);
|
|
1512
1488
|
if (!child) return false;
|
|
1513
1489
|
if (hasGap(record, previous, fragment)) {
|
|
1514
1490
|
const gap = doc.createElement('span');
|
|
1491
|
+
const gapRun = record.runs[fragment.gapItemIndex];
|
|
1492
|
+
if (!gapRun && fragment.gapBefore !== 0) return false;
|
|
1515
1493
|
gap.style.cssText = 'display:inline-block;width:' + pixels(fragment.gapBefore);
|
|
1494
|
+
if (gapRun) { gap.style.font = gapRun.font; gap.style.letterSpacing = pixels(gapRun.letterSpacing); }
|
|
1516
1495
|
gap.textContent = ' '; element.append(gap);
|
|
1517
1496
|
}
|
|
1518
1497
|
element.append(child); previous = fragment;
|
|
1519
1498
|
}
|
|
1520
1499
|
output.append(element);
|
|
1521
1500
|
}
|
|
1501
|
+
if (!prepared.current(record.flow)) return false;
|
|
1522
1502
|
originals.set(record.paragraph, [...record.paragraph.childNodes]);
|
|
1523
1503
|
record.paragraph.replaceChildren(output);
|
|
1524
1504
|
style(record.paragraph, {position: 'relative', height: pixels(plan.height), 'min-height': '0'});
|
|
@@ -1529,10 +1509,11 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1529
1509
|
if (!previous) return false;
|
|
1530
1510
|
let found = false;
|
|
1531
1511
|
for (let index = previous.itemIndex; index <= current.itemIndex; index++) {
|
|
1532
|
-
const run = record.runs[index]
|
|
1533
|
-
|
|
1534
|
-
const
|
|
1535
|
-
|
|
1512
|
+
const run = record.runs[index];
|
|
1513
|
+
if (!run) return false;
|
|
1514
|
+
const start = index === previous.itemIndex ? previous.sourceEnd : 0;
|
|
1515
|
+
const end = index === current.itemIndex ? current.sourceStart : run.raw.length;
|
|
1516
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start || end > run.raw.length) return false;
|
|
1536
1517
|
const gap = run.raw.slice(start, end);
|
|
1537
1518
|
if (gap && !/^[ \t\n\r\f]+$/.test(gap)) return false;
|
|
1538
1519
|
found ||= !!gap;
|
|
@@ -1543,6 +1524,7 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1543
1524
|
// wrap=around reads data-rapier-occupancy, else alpha. Cache key: src, dimensions, layout.wrap, rotate angle.
|
|
1544
1525
|
// A turned raster's profile is recomputed here as pictureProfile does, never shipped.
|
|
1545
1526
|
function profile(image, layout) {
|
|
1527
|
+
if (layout.wrap === 'around' && image.dataset?.rapierContour?.endsWith('-box')) return null;
|
|
1546
1528
|
const src = image.currentSrc || image.getAttribute('src') || '', rotateRad = (layout.rotate || 0) * Math.PI / 180;
|
|
1547
1529
|
const cached = profiles.get(image);
|
|
1548
1530
|
if (cached?.src === src && cached.width === image.naturalWidth && cached.height === image.naturalHeight &&
|
|
@@ -1756,8 +1738,7 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1756
1738
|
if (image.hasAttribute('data-rapier-html')) {
|
|
1757
1739
|
const attrs = JSON.parse(decodeURIComponent(image.getAttribute('data-rapier-html'))).attrs;
|
|
1758
1740
|
const line = parseFloat(computed.lineHeight) || (parseFloat(computed.fontSize) || 16) * 1.6;
|
|
1759
|
-
unrotated.height =
|
|
1760
|
-
? Number(image.dataset.rapierNaturalHeight) || box(image).height : Number(attrs.height) * line;
|
|
1741
|
+
unrotated.height = metadata.objectHeight(unrotated.width, Number(image.dataset.rapierNaturalHeight) || box(image).height, attrs, line);
|
|
1761
1742
|
}
|
|
1762
1743
|
// rasterReserved (layout/model.mjs).
|
|
1763
1744
|
const rasterRad = (layout.rotate || 0) * Math.PI / 180;
|
|
@@ -1823,6 +1804,7 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1823
1804
|
|
|
1824
1805
|
function beforePrint() { printing = true; restore(); }
|
|
1825
1806
|
function afterPrint() { printing = false; schedule(); }
|
|
1807
|
+
function fontChanged() { prepared.invalidateFonts(); schedule(); }
|
|
1826
1808
|
|
|
1827
1809
|
const observer = typeof view.ResizeObserver === 'function' ? new view.ResizeObserver(() => {
|
|
1828
1810
|
const width = root.clientWidth;
|
|
@@ -1837,13 +1819,13 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1837
1819
|
view.addEventListener('pageshow', schedule);
|
|
1838
1820
|
view.addEventListener('beforeprint', beforePrint);
|
|
1839
1821
|
view.addEventListener('afterprint', afterPrint);
|
|
1840
|
-
doc.fonts?.addEventListener?.(
|
|
1822
|
+
for (const event of ['loading', 'loadingdone', 'loadingerror']) doc.fonts?.addEventListener?.(event, fontChanged);
|
|
1841
1823
|
Promise.resolve(doc.fonts?.ready).then(schedule, schedule);
|
|
1842
1824
|
schedule();
|
|
1843
|
-
return Object.freeze({reflow, destroy() {
|
|
1825
|
+
return Object.freeze({reflow, destroy({documentChanged = false} = {}) {
|
|
1844
1826
|
stopped = true;
|
|
1845
1827
|
if (scheduled) view.cancelAnimationFrame(scheduled);
|
|
1846
|
-
observer?.disconnect(); restore();
|
|
1828
|
+
observer?.disconnect(); restore(); prepared.retire({documentChanged});
|
|
1847
1829
|
root.removeEventListener('load', schedule, true);
|
|
1848
1830
|
root.removeEventListener('error', schedule, true);
|
|
1849
1831
|
root.removeEventListener('rapier-html-resize', schedule);
|
|
@@ -1852,23 +1834,28 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1852
1834
|
view.removeEventListener('pageshow', schedule);
|
|
1853
1835
|
view.removeEventListener('beforeprint', beforePrint);
|
|
1854
1836
|
view.removeEventListener('afterprint', afterPrint);
|
|
1855
|
-
doc.fonts?.removeEventListener?.(
|
|
1837
|
+
for (const event of ['loading', 'loadingdone', 'loadingerror']) doc.fonts?.removeEventListener?.(event, fontChanged);
|
|
1856
1838
|
}});
|
|
1857
1839
|
}
|
|
1858
1840
|
|
|
1859
|
-
function _rapierArtifactLayoutScript(root, nonce = '') {
|
|
1841
|
+
function _rapierArtifactLayoutScript(root, nonce = '', {pictures = true} = {}) {
|
|
1860
1842
|
const dependencies = globalThis.RapierArtifactLayoutDependencies;
|
|
1861
1843
|
// Every wrap value earns the reflow script, and an inline turned raster (keyed off data-rapier-image-layout).
|
|
1862
1844
|
const layoutOf = element => globalThis.RapierMarkdownLayout.parseLayoutAttribute(element.getAttribute('data-md-layout'));
|
|
1863
1845
|
const pictureLayoutOf = image => globalThis.RapierMarkdownLayout.parseLayoutAttribute(image.getAttribute('data-rapier-image-layout'));
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1846
|
+
const wrapping = pictures && ([...root.querySelectorAll('p[data-md-layout]')].some(element => ['around', 'box', 'behind', 'front'].includes(layoutOf(element)?.wrap)) ||
|
|
1847
|
+
[...root.querySelectorAll('img[data-rapier-image-layout]')].some(image => pictureLayoutOf(image)?.rotate));
|
|
1848
|
+
const typography = _rapierArtifactTypographyScript(root);
|
|
1849
|
+
if (!wrapping && !typography) return '';
|
|
1850
|
+
if (!dependencies?.factories || typography && !dependencies.factories['layout/typography.mjs'])
|
|
1851
|
+
throw new Error('Offline typography requires the complete retained MIT layout factories');
|
|
1867
1852
|
const modules = _rapierArtifactFactoryModules(dependencies, 'layout');
|
|
1868
|
-
const script = '/* Rapier export layout: SPDX-License-Identifier:
|
|
1869
|
-
'/* Pretext
|
|
1870
|
-
'(() => {\nconst modules =
|
|
1871
|
-
'
|
|
1853
|
+
const script = '/* Rapier export layout: SPDX-License-Identifier: MIT */\n' +
|
|
1854
|
+
'/* Pretext bac1774c0f8b7a92e44b9da43b6d5443d55d0d9b (Unreleased)\n' + dependencies.license + '\n*/\n' +
|
|
1855
|
+
'(() => {\nconst modules = Object.create(null);\n' + modules +
|
|
1856
|
+
(wrapping ? '\n(' + _rapierProjectArtifactLayout.toString() +
|
|
1857
|
+
')(document.querySelector("main.rapier-page"), modules["spec/md-layout.mjs"], modules["layout/model.mjs"], modules["layout/preparation.mjs"]);\n' : '') +
|
|
1858
|
+
typography + '\n})();';
|
|
1872
1859
|
|
|
1873
1860
|
return '<script' + (nonce ? ' nonce="' + nonce + '"' : '') + '>\n' + script.replace(/<\/script/gi, '<\\/script') + '\n</script>';
|
|
1874
1861
|
}
|
|
@@ -1883,23 +1870,29 @@ function _rapierArtifactInkScript(root, nonce = '') {
|
|
|
1883
1870
|
return '<script' + (nonce ? ' nonce="' + nonce + '"' : '') + '>\n' + script.replace(/<\/script/gi, '<\\/script') + '\n</script>';
|
|
1884
1871
|
}
|
|
1885
1872
|
|
|
1886
|
-
function _rapierArtifactTypographyScript(root
|
|
1873
|
+
function _rapierArtifactTypographyScript(root) {
|
|
1887
1874
|
const pictures = root.querySelector('img[data-rapier-print-lines]');
|
|
1888
1875
|
if (!pictures && !root.querySelector('p[data-md-hyphens="auto"],p[data-md-lines],p[data-md-caps="small"],p[data-md-hanging="on"]')) return '';
|
|
1889
|
-
|
|
1876
|
+
return '(() => {\nconst typography = modules["layout/typography.mjs"].createTypography();\n' +
|
|
1890
1877
|
(pictures ? 'const capitals = (' + createPrintCapitals.toString() + ')((' + createLineMeasurer.toString() + ')(),(' + linesHeight.toString() + '),(' + followingPrintProse.toString() + '));\n' : '') +
|
|
1891
|
-
'const root = document.querySelector("main.rapier-page"); let frame = 0, width = -1, printing =
|
|
1892
|
-
|
|
1878
|
+
'const root = document.querySelector("main.rapier-page"); const printMedia = matchMedia("print"); let frame = 0, width = -1, ready = null, printing = printMedia.matches;\n' +
|
|
1879
|
+
// beforeprint can still expose the screen viewport, not the selected paper's content width.
|
|
1880
|
+
// Only an authored paper width supplies a fixed target; automatic paper keeps native wrapping.
|
|
1881
|
+
'const apply = () => { cancelAnimationFrame(frame); frame = 0; ' + (pictures ? 'if (printing) capitals.apply(root); else capitals.clear(root); ' : '') + 'if (printing && !(parseFloat(getComputedStyle(root).getPropertyValue("--rapier-print-width")) > 0)) { typography.clear(root); ready?.(); ready = null; return; } if (typography.apply(root, {all:printing}).pending) schedule(); else { ready?.(); ready = null; } };\n' +
|
|
1893
1882
|
'const schedule = () => { if (!frame) frame = requestAnimationFrame(apply); };\n' +
|
|
1894
|
-
'globalThis.__RAPIER_EXPORT_READY__ = Promise.all([document.fonts?.ready, ...Array.from(root.querySelectorAll("img"), image => image.decode?.().catch(() => {}))]).then(() => new Promise(resolve =>
|
|
1883
|
+
'globalThis.__RAPIER_EXPORT_READY__ = Promise.all([document.fonts?.ready, ...Array.from(root.querySelectorAll("img"), image => image.decode?.().catch(() => {}))]).then(() => new Promise(resolve => { ready = resolve; schedule(); }));\n' +
|
|
1884
|
+
'printMedia.addEventListener("change", () => { printing = printMedia.matches; schedule(); });\n' +
|
|
1895
1885
|
'addEventListener("resize", schedule); addEventListener("beforeprint", () => { printing = true; apply(); });\n' +
|
|
1896
|
-
'addEventListener("
|
|
1886
|
+
'addEventListener("scroll", schedule, {passive:true, capture:true});\n' +
|
|
1887
|
+
'for (const event of ["copy","cut"]) addEventListener(event, () => { if (typography.clearSelection(root)) schedule(); }, true);\n' +
|
|
1888
|
+
'addEventListener("afterprint", () => { printing = printMedia.matches; ' + (pictures ? 'capitals.clear(root); ' : '') + 'schedule(); });\n' +
|
|
1897
1889
|
'root.addEventListener("load", schedule, true);\n' +
|
|
1890
|
+
'for (const event of ["loading","loadingdone","loadingerror"]) document.fonts?.addEventListener(event, () => { typography.invalidateFonts(document); schedule(); });\n' +
|
|
1891
|
+
'addEventListener("pagehide", () => { cancelAnimationFrame(frame); frame = 0; typography.retire(root); }); addEventListener("pageshow", schedule);\n' +
|
|
1898
1892
|
'if (typeof ResizeObserver === "function") new ResizeObserver(() => { const next = root.getBoundingClientRect().width; if (next !== width) { width = next; schedule(); } }).observe(root);\n' +
|
|
1899
1893
|
'})();';
|
|
1900
|
-
return '<script' + (nonce ? ' nonce="' + nonce + '"' : '') + '>\n' + script.replace(/<\/script/gi, '<\\/script') + '\n</script>';
|
|
1901
1894
|
}
|
|
1902
1895
|
|
|
1903
|
-
return {_rapierDataImageDestinationsAsync, _rapierSharedSourceFormsAsync, _rapierPrefixPortableAnchorsSteps, _rapierProjectStyledRootAsync, _rapierBuildArtifact, _rapierBuildPublishedPage, _rapierProjectStyledRoot, _rapierDocxRenderStyles, _rapierPaintCode, _rapierPrefixPortableAnchors, _rapierDocumentSettingsOf, _rapierDocumentSettingsCss, _rapierExportedPageCsp, _rapierArtifactNonce, _rapierAnnotateExportBoxPolygons, _rapierSharedSourceHash, _rapierSharedSourceForms, _rapierSharedSourceCarrier, _rapierSharedResolve, _rapierReadSharedDocument, _rapierSharedPageFallbackCss, _rapierShareWrapKind, _rapierSharePictureParagraph, _rapierSharedPageLayout, _rapierShareCapLongCodeBlocks, _rapierRequireOfflinePageImages, _rapierCssValueUrls, _rapierBuildSharedPage, _rapierImageSources, _rapierImageDestinations, _rapierScanMarkdownImages, _rapierParseMarkdownImageDestination, _rapierImageOpenRunsList, _rapierArtifactFactoryModules, _rapierProjectArtifactLayout, _rapierArtifactLayoutScript, _rapierArtifactInkScript , render};
|
|
1896
|
+
return {_rapierDataImageDestinationsAsync, _rapierSharedSourceFormsAsync, _rapierPrefixPortableAnchorsSteps, _rapierPortableAnchorId, _rapierProjectStyledRootAsync, _rapierBuildArtifact, _rapierBuildPublishedPage, _rapierProjectStyledRoot, _rapierDocxRenderStyles, _rapierPaintCode, _rapierPrefixPortableAnchors, _rapierDocumentSettingsOf, _rapierDocumentSettingsCss, _rapierExportedPageCsp, _rapierArtifactNonce, _rapierAnnotateExportBoxPolygons, _rapierSharedSourceHash, _rapierSharedSourceForms, _rapierSharedSourceCarrier, _rapierSharedResolve, _rapierReadSharedDocument, _rapierSharedPageFallbackCss, _rapierShareWrapKind, _rapierSharePictureParagraph, _rapierSharedPageLayout, _rapierShareCapLongCodeBlocks, _rapierRequireOfflinePageImages, _rapierCssValueUrls, _rapierBuildSharedPage, _rapierImageSources, _rapierImageDestinations, _rapierScanMarkdownImages, _rapierParseMarkdownImageDestination, _rapierImageOpenRunsList, _rapierArtifactFactoryModules, _rapierProjectArtifactLayout, _rapierArtifactLayoutScript, _rapierArtifactInkScript , render};
|
|
1904
1897
|
}
|
|
1905
1898
|
export {createRenderer};
|