rapier-markdown-kit 1.2.19 → 1.2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/images/playback.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
export function createMediaPlayback(win, inspectGIF) {
|
|
3
|
+
const FRAMES = 3600, LOOP_MS = 3600000;
|
|
3
4
|
const active = new Map(), encode = bytes => bytes.toBase64();
|
|
4
5
|
const bytes = async url => {
|
|
5
6
|
const text = url.slice(url.indexOf(',') + 1), slice = 1 << 20;
|
|
@@ -14,7 +15,7 @@ export function createMediaPlayback(win, inspectGIF) {
|
|
|
14
15
|
function raster(url, canvas, changed = () => {}, dimensions = null) {
|
|
15
16
|
if (url.length > 24 * 1024 * 1024 || !/^data:image\/(gif|jxl);base64,/i.test(url) || (url.length - url.indexOf(',') - 1) % 4) throw new Error('Unsupported animation source');
|
|
16
17
|
const type = url.slice(5, url.indexOf(';')).toLowerCase();
|
|
17
|
-
let data = null, info = null, size = null, decoder = null, timer = 0, closed = false, paused = true, ticket = 0, next = 0, loops = 0, duration = 0, single = false, held = false;
|
|
18
|
+
let data = null, info = null, size = null, decoder = null, timer = 0, closed = false, paused = true, ticket = 0, next = 0, loops = 0, duration = 0, single = false, held = false, due = 0;
|
|
18
19
|
const state = {animated: true, status: 'loading', first: '', frames: 0};
|
|
19
20
|
const admitted = (async () => {
|
|
20
21
|
const buffer = await bytes(url); info = type === 'image/gif' ? inspectGIF(buffer) : null;
|
|
@@ -47,11 +48,12 @@ export function createMediaPlayback(win, inspectGIF) {
|
|
|
47
48
|
state.status = 'playback-budget'; paused = true; changed(state); return;
|
|
48
49
|
}
|
|
49
50
|
active.set(canvas, size.width * size.height);
|
|
51
|
+
next = 0; duration = 0; due = 0;
|
|
50
52
|
decoder = new win.ImageDecoder({data, type, preferAnimation: true});
|
|
51
53
|
const current = decoder; await settle(current.tracks.ready);
|
|
52
54
|
if (closed || paused || stamp !== ticket || decoder !== current) return;
|
|
53
55
|
const track = current.tracks.selectedTrack;
|
|
54
|
-
if (!track || track.frameCount < 1 || track.frameCount >
|
|
56
|
+
if (!track || track.frameCount < 1 || track.frameCount > FRAMES || info && track.frameCount !== info.frames) throw new Error('Animation frame budget or container mismatch');
|
|
55
57
|
state.frames = track.frameCount; state.animated = track.animated;
|
|
56
58
|
}
|
|
57
59
|
const current = decoder, decoding = current.decode({frameIndex: next, completeFramesOnly: true});
|
|
@@ -62,13 +64,13 @@ export function createMediaPlayback(win, inspectGIF) {
|
|
|
62
64
|
try {
|
|
63
65
|
if (closed || paused || stamp !== ticket || decoder !== current) return;
|
|
64
66
|
const w = frame.displayWidth, h = frame.displayHeight;
|
|
65
|
-
if (!result.complete || w !== size.width || h !== size.height
|
|
67
|
+
if (!result.complete || w !== size.width || h !== size.height) throw new Error('Decoded animation exceeds the image budget');
|
|
66
68
|
if (canvas.width !== w) canvas.width = w;
|
|
67
69
|
if (canvas.height !== h) canvas.height = h;
|
|
68
70
|
const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, w, h); ctx.drawImage(frame, 0, 0);
|
|
69
71
|
if (!state.first && next === 0) state.first = canvas.toDataURL('image/png');
|
|
70
72
|
const delay = type !== 'image/gif' ? Math.max(8, frame.duration / 1000) : frame.duration > 10000 ? Math.max(20, frame.duration / 1000) : 100;
|
|
71
|
-
duration += delay; if (duration >
|
|
73
|
+
duration += delay; if (duration > LOOP_MS) throw new Error('Animation exceeds the duration budget');
|
|
72
74
|
state.status = 'ready'; state.frame = next; changed(state);
|
|
73
75
|
if (single) {paused = true; closeDecoder(); return;}
|
|
74
76
|
next++;
|
|
@@ -76,7 +78,8 @@ export function createMediaPlayback(win, inspectGIF) {
|
|
|
76
78
|
next = 0; duration = 0; loops++;
|
|
77
79
|
if (!state.animated || Number.isFinite(current.tracks.selectedTrack.repetitionCount) && loops > current.tracks.selectedTrack.repetitionCount) {paused = true; closeDecoder(); return;}
|
|
78
80
|
}
|
|
79
|
-
|
|
81
|
+
const now = win.performance.now(); due = due && now - due < 1000 ? due + delay : now + delay;
|
|
82
|
+
timer = win.setTimeout(step, Math.max(0, due - now));
|
|
80
83
|
} finally {frame.close();}
|
|
81
84
|
} catch (error) {
|
|
82
85
|
if (closed || stamp !== ticket) return;
|
|
@@ -85,7 +88,7 @@ export function createMediaPlayback(win, inspectGIF) {
|
|
|
85
88
|
}
|
|
86
89
|
return {state, play() {if (!closed && paused && !['decode-failed', 'admission-failed'].includes(state.status)) {paused = false; void step();}},
|
|
87
90
|
async first() {if (!state.first && !closed) {single = true; next = 0; paused = false; try {await step();} finally {single = false; paused = true; closeDecoder(); if (held) {held = false; this.suspend();}}} return state.first;},
|
|
88
|
-
pause() {paused = true; ticket++; win.clearTimeout(timer); timer = 0;},
|
|
91
|
+
pause() {paused = true; ticket++; win.clearTimeout(timer); timer = 0; due = 0;},
|
|
89
92
|
suspend() {if (single) {held = true; return;} paused = true; closeDecoder(); canvas.width = canvas.height = 0; state.status = 'suspended'; changed(state);},
|
|
90
93
|
close() {closed = true; paused = true; closeDecoder(); canvas.width = canvas.height = 0;},
|
|
91
94
|
get paused() {return paused;}};
|
|
@@ -184,7 +184,8 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
184
184
|
[side, settings?.geometry ? settings.geometry[side] / 20 : (side === 'top' ? 12 : 14) * 72 / 25.4]));
|
|
185
185
|
const bodyWidth = Math.floor((width - margins.left - margins.right) * 4 / 3);
|
|
186
186
|
const bodyHeight = Math.floor((height - margins.top - margins.bottom) * 4 / 3);
|
|
187
|
-
if (bodyWidth < 1 || bodyHeight < 1
|
|
187
|
+
if (bodyWidth < 1 || bodyHeight < 1) refuse('pdf_page_geometry_invalid');
|
|
188
|
+
const tiled = bodyWidth > CAPTURE_EDGE || bodyHeight > CAPTURE_EDGE || bodyWidth * bodyHeight > CAPTURE_PIXELS;
|
|
188
189
|
const frame = document.createElement('iframe');
|
|
189
190
|
let vectors, backdrop = null, nativeGlyphs, filmSession;
|
|
190
191
|
let rejectAbort;
|
|
@@ -319,7 +320,21 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
319
320
|
if (imageBytes > maxBytes) refuse('export_too_large');
|
|
320
321
|
pages.push({rgb: packed});
|
|
321
322
|
};
|
|
322
|
-
|
|
323
|
+
const tileWidth = Math.min(bodyWidth, CAPTURE_EDGE), tileHeight = Math.min(bodyHeight, CAPTURE_EDGE, Math.floor(CAPTURE_PIXELS / tileWidth));
|
|
324
|
+
for (let index = 0; tiled && index < count; index++) {
|
|
325
|
+
const tiles = [];
|
|
326
|
+
for (let y = 0; y < bodyHeight; y += tileHeight) for (let x = 0; x < bodyWidth; x += tileWidth) {
|
|
327
|
+
check();
|
|
328
|
+
const width = Math.min(tileWidth, bodyWidth - x), height = Math.min(tileHeight, bodyHeight - y);
|
|
329
|
+
const shot = await step(filmSession.raster({x: index * bodyWidth + x, y, width, height}));
|
|
330
|
+
if (shot?.width !== width || shot?.height !== height || !(shot.rgb instanceof Uint8Array)) refuse('pdf_render_unavailable');
|
|
331
|
+
await pack(shot.rgb);
|
|
332
|
+
shot.rgb = null;
|
|
333
|
+
tiles.push({...pages.pop(), x, y, width, height});
|
|
334
|
+
}
|
|
335
|
+
pages.push({tiles});
|
|
336
|
+
}
|
|
337
|
+
for (let first = 0; !tiled && first < count;) {
|
|
323
338
|
check();
|
|
324
339
|
const span = Math.min(share, count - first);
|
|
325
340
|
try {
|
|
@@ -698,9 +713,10 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
698
713
|
const marked = [];
|
|
699
714
|
parentTree.push(marked);
|
|
700
715
|
const held = pages[index], flate = !!held?.rgb;
|
|
701
|
-
const
|
|
716
|
+
const tiles = held?.tiles || [{...(flate ? held : {jpeg: held}), x: 0, y: 0, width: pixelWidth, height: pixelHeight}];
|
|
717
|
+
const pictures = tiles.map((tile, at) => '/Im0_' + at + ' ' + stream('/Type /XObject /Subtype /Image /Width ' + tile.width + ' /Height ' + tile.height +
|
|
718
|
+
' /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /' + (tile.rgb ? 'FlateDecode' : 'DCTDecode'), tile.rgb || tile.jpeg) + ' 0 R'), states = [];
|
|
702
719
|
pages[index] = null;
|
|
703
|
-
const pictures = ['/Im0 ' + picture + ' 0 R'], states = [];
|
|
704
720
|
const emit = (drawing, left, top, clipWidth, clipHeight) => {
|
|
705
721
|
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`;
|
|
706
722
|
out += [drawing.clip.x, drawing.clip.y, drawing.clip.width, drawing.clip.height].map(decimal).join(' ') + ' re W n\n';
|
|
@@ -728,7 +744,8 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
728
744
|
};
|
|
729
745
|
let commands = '';
|
|
730
746
|
for (const drawing of backdrop || []) commands += emit(drawing, 0, height, Math.round(width * 4 / 3), Math.round(height * 4 / 3));
|
|
731
|
-
commands += '/Artifact BMC\
|
|
747
|
+
commands += '/Artifact BMC\n' + tiles.map((tile, at) => 'q\n' + [decimal(tile.width * .75), 0, 0, decimal(tile.height * .75), decimal(margins.left + tile.x * .75),
|
|
748
|
+
decimal(height - margins.top - (tile.y + tile.height) * .75)].join(' ') + ' cm\n/Im0_' + at + ' Do\nQ\n').join('') + 'EMC\n';
|
|
732
749
|
for (const drawing of vectorPages[index] || []) commands += emit(drawing, margins.left, height - margins.top, pixelWidth, pixelHeight);
|
|
733
750
|
const textAlphas = new Map();
|
|
734
751
|
for (const run of textPages[index]) {
|
|
@@ -119,7 +119,16 @@ const PAPERS = Object.freeze({
|
|
|
119
119
|
a4: Object.freeze({name: 'a4', width: 11906, height: 16838, css: 'A4'}),
|
|
120
120
|
a5: Object.freeze({name: 'a5', width: 8391, height: 11906, css: 'A5'}),
|
|
121
121
|
legal: Object.freeze({name: 'legal', width: 12240, height: 20160, css: 'legal'}),
|
|
122
|
+
tabloid: Object.freeze({name: 'tabloid', width: 15840, height: 24480, css: '11in 17in'}),
|
|
123
|
+
a3: Object.freeze({name: 'a3', width: 16838, height: 23811, css: 'A3'}),
|
|
122
124
|
});
|
|
125
|
+
function paperOf(value) {
|
|
126
|
+
const name = String(value || '').toLowerCase(), named = Object.hasOwn(PAPERS, name) ? PAPERS[name] : null;
|
|
127
|
+
if (named) return named;
|
|
128
|
+
const pixels = /^(\d{3,5})x(\d{3,5})px$/.exec(name), width = Number(pixels?.[1]), height = Number(pixels?.[2]);
|
|
129
|
+
return pixels && width <= 10000 && height <= 10000 ? Object.freeze({name, width: width * 15, height: height * 15, css: width + 'px ' + height + 'px'}) : null;
|
|
130
|
+
}
|
|
131
|
+
export const PAGE_SIZES = Object.freeze(['letter', 'legal', 'tabloid', 'a3', 'a4', 'a5', '1080x1080px', '1080x1350px', '1920x1080px', '1200x630px']);
|
|
123
132
|
const EMPTY = Object.freeze({
|
|
124
133
|
title: null, subtitle: null, lang: null, fontsize: null, mainfont: null, sansfont: null, monofont: null, defaultfont: null, linestretch: null,
|
|
125
134
|
papersize: null, geometry: null, pagestyle: null, spacing: null,
|
|
@@ -334,7 +343,7 @@ export function readDocumentSettings(markdown) {
|
|
|
334
343
|
const monofont = documentFont(take('monofont'));
|
|
335
344
|
const defaultfont = documentFont(take('defaultfont'));
|
|
336
345
|
const linestretch = leading(take('linestretch'));
|
|
337
|
-
const papersize =
|
|
346
|
+
const papersize = paperOf(take('papersize'));
|
|
338
347
|
const geometry = take('geometry') != null ? marginOf(take('geometry')) : null;
|
|
339
348
|
const page = String(take('pagestyle') || '').toLowerCase();
|
|
340
349
|
return Object.freeze({
|
|
@@ -359,14 +368,14 @@ export function updateDocumentSettings(markdown, patch) {
|
|
|
359
368
|
text: (source[field.start - 1] === ':' ? ' ' : '') + text + (source[field.end] === '#' ? ' ' : '')});
|
|
360
369
|
for (const [key, supplied] of Object.entries(patch || {})) {
|
|
361
370
|
const font = ['mainfont', 'sansfont', 'monofont', 'defaultfont'].includes(key);
|
|
362
|
-
if (!font && !['fontsize', 'linestretch', 'spacing', 'geometry'].includes(key)) throw new TypeError('Unknown document setting: ' + key);
|
|
371
|
+
if (!font && !['fontsize', 'linestretch', 'spacing', 'geometry', 'papersize'].includes(key)) throw new TypeError('Unknown document setting: ' + key);
|
|
363
372
|
const matches = fields.filter(item => item.key === key);
|
|
364
373
|
if (matches.length > 1) throw new RangeError('Duplicate document setting: ' + key);
|
|
365
374
|
const field = matches[0];
|
|
366
|
-
const value = supplied === null ? null : font ? documentFont(supplied) : key === 'fontsize' ? fontSize(supplied) : key === 'spacing' ? normalizeDocumentSpacing(supplied) : key === 'geometry' ? marginOf(supplied) : leading(supplied);
|
|
375
|
+
const value = supplied === null ? null : font ? documentFont(supplied) : key === 'fontsize' ? fontSize(supplied) : key === 'spacing' ? normalizeDocumentSpacing(supplied) : key === 'geometry' ? marginOf(supplied) : key === 'papersize' ? paperOf(supplied) : leading(supplied);
|
|
367
376
|
if (supplied !== null && !value) throw new TypeError('Invalid document setting: ' + key);
|
|
368
|
-
const encoded = value === null ? '' : font ? JSON.stringify(value.family) : key === 'fontsize' ? value.step : key === 'spacing' ? JSON.stringify(value) : key === 'geometry' ? unquote(supplied).trim() : String(value.factor);
|
|
369
|
-
if (value !== null && before[key] && (font ? before[key].family === value.family : key === 'fontsize' ? same(before.fontsize.unit, value.unit) : key === 'spacing' ? JSON.stringify(before.spacing) === encoded : key === 'geometry' ? ['top', 'right', 'bottom', 'left'].every(side => before.geometry[side] === value[side]) : before.linestretch.factor === value.factor)) continue;
|
|
377
|
+
const encoded = value === null ? '' : font ? JSON.stringify(value.family) : key === 'fontsize' ? value.step : key === 'spacing' ? JSON.stringify(value) : key === 'geometry' ? unquote(supplied).trim() : key === 'papersize' ? value.name : String(value.factor);
|
|
378
|
+
if (value !== null && before[key] && (font ? before[key].family === value.family : key === 'fontsize' ? same(before.fontsize.unit, value.unit) : key === 'spacing' ? JSON.stringify(before.spacing) === encoded : key === 'geometry' ? ['top', 'right', 'bottom', 'left'].every(side => before.geometry[side] === value[side]) : key === 'papersize' ? before.papersize.name === value.name : before.linestretch.factor === value.factor)) continue;
|
|
370
379
|
if (key === 'fontsize' && value !== null && fontSize(encoded)?.unit !== value.unit) refuse(key);
|
|
371
380
|
if (!field) { if (value !== null) append.push(key + ': ' + encoded + eol); continue; }
|
|
372
381
|
if (field.opaque || field.invalid || field.multiline || field.block) refuse(key);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-markdown-kit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.20",
|
|
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",
|