rapier-markdown-kit 1.2.10 → 1.2.12
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/agent/vendor/hyphen-en-us/SOURCE.json +3 -3
- package/dist/agent/vendor/hyphen-en-us/patterns.js +4 -3
- package/dist/kit/conformance/run.mjs +1 -0
- package/dist/kit/export-fonts.mjs +7 -0
- package/dist/kit/ledger/authorship.mjs +10 -19
- package/dist/kit/ledger/format.mjs +1 -1
- package/dist/kit/ledger/history.mjs +12 -3
- package/dist/kit/ledger/inverse-source.mjs +9 -126
- package/dist/kit/ledger/journal-records.mjs +2 -17
- package/dist/kit/ledger/source.mjs +323 -0
- package/dist/kit/pdf-glyphs.mjs +388 -96
- package/dist/kit/pdf-vectors.mjs +7 -6
- package/dist/kit/render-markdown.mjs +9 -6
- package/dist/kit/render-print.mjs +193 -51
- package/dist/kit/render-sanitize.mjs +7 -7
- package/dist/kit/render.mjs +16 -1
- package/dist/layout/preparation.mjs +1 -1
- package/dist/spec/md-assets.mjs +99 -8
- package/package.json +1 -1
- package/style.css +34 -8
|
@@ -121,9 +121,10 @@ function initMarkdownIt() {
|
|
|
121
121
|
const alt = suffix && semanticAlt.endsWith(suffix) ? semanticAlt.slice(0, -suffix.length) : semanticAlt;
|
|
122
122
|
const layout = token.meta?.mdLayout?.imageOnly ? token.meta.mdLayout.marker : '';
|
|
123
123
|
const asset = globalThis.RapierImageAssets.dataImage(src), reference = token.meta?.mdImage?.reference;
|
|
124
|
+
const originalRelative = globalThis.RapierPlatform?.files.relativeCandidate?.(src) || globalThis.RapierSiblingPictures?.has(src);
|
|
124
125
|
if (!env.rapierPage && typeof globalThis.RapierEmbeddedImages?.imageHtml === 'function' &&
|
|
125
|
-
(_rapierEmbedAssetSource?.(src) || asset && (reference || asset.codec === 'image/jxl' || asset.codec === 'image/gif' || asset.codec === 'image/svg+xml'))) return globalThis.RapierEmbeddedImages.imageHtml(
|
|
126
|
-
reference, alt, title, size, layout, rawAlt, token.meta?.mdImage?.source, src, env.rapierImagePresentation !== false);
|
|
126
|
+
(originalRelative || _rapierEmbedAssetSource?.(src) || asset && (reference || asset.codec === 'image/jxl' || asset.codec === 'image/gif' || asset.codec === 'image/svg+xml'))) return globalThis.RapierEmbeddedImages.imageHtml(
|
|
127
|
+
originalRelative ? null : reference, alt, title, size, layout, rawAlt, token.meta?.mdImage?.source, src, env.rapierImagePresentation !== false);
|
|
127
128
|
const drawingAlt = typeof rawAlt === 'string' && alt === '' && asset?.codec === 'image/svg+xml' ? globalThis.RapierImageAssets.drawingImageAlt(src) : null;
|
|
128
129
|
const shownAlt = drawingAlt ?? alt;
|
|
129
130
|
let loadable = false;
|
|
@@ -901,14 +902,14 @@ function* _rapierDecorateJumpListsSteps(root) {
|
|
|
901
902
|
if (isWayfinder) {
|
|
902
903
|
list.classList.add('rapier-jump-list');
|
|
903
904
|
const label = list.previousElementSibling;
|
|
904
|
-
if (label && /^jump\s+to:?$/i.test(String(label.textContent || '').trim())) {
|
|
905
|
+
if (label && /^(?:jump\s+to|contents|table\s+of\s+contents):?$/i.test(String(label.textContent || '').trim())) {
|
|
905
906
|
label.classList.add('rapier-jump-label');
|
|
906
907
|
}
|
|
907
908
|
}
|
|
908
909
|
}
|
|
909
910
|
|
|
910
911
|
const first = root.firstElementChild, only = first && !first.nextElementSibling ? first : null;
|
|
911
|
-
if (only && only.tagName === 'P' && /^jump\s+to:?$/i.test(String(only.textContent || '').trim())) {
|
|
912
|
+
if (only && only.tagName === 'P' && /^(?:jump\s+to|contents|table\s+of\s+contents):?$/i.test(String(only.textContent || '').trim())) {
|
|
912
913
|
only.classList.add('rapier-jump-label-candidate');
|
|
913
914
|
}
|
|
914
915
|
}
|
|
@@ -1176,7 +1177,7 @@ function* _rapierProjectPortableNodesSteps(semanticRoot, options) {
|
|
|
1176
1177
|
math.replaceWith(code);
|
|
1177
1178
|
}
|
|
1178
1179
|
|
|
1179
|
-
yield* _rapierFlattenPortableDetailsSteps(root);
|
|
1180
|
+
if (!keepLayoutData) yield* _rapierFlattenPortableDetailsSteps(root);
|
|
1180
1181
|
|
|
1181
1182
|
for (const section of root.querySelectorAll('section.footnotes')) { yield;
|
|
1182
1183
|
for (const node of section.querySelectorAll('.footnotes-sep')) { yield;node.remove();}
|
|
@@ -1227,8 +1228,10 @@ function* _rapierProjectPortableNodesSteps(semanticRoot, options) {
|
|
|
1227
1228
|
|
|
1228
1229
|
const typed = globalThis.RapierMarkdownSpec.typography, typography = typed.nodeStyle(element);
|
|
1229
1230
|
|
|
1231
|
+
if (keepLayoutData && element.tagName === 'MARK' && !element.style.backgroundColor && !element.hasAttribute('data-rapier-highlight'))
|
|
1232
|
+
element.setAttribute('data-rapier-highlight', 'default');
|
|
1230
1233
|
const highlightHex = element.tagName === 'MARK'
|
|
1231
|
-
? (RAPIER_PORTABLE_HIGHLIGHT_HEX[String(element.getAttribute('data-rapier-highlight') || '').toLowerCase()] || RAPIER_PORTABLE_HIGHLIGHT_HEX.yellow)
|
|
1234
|
+
? (element.style.backgroundColor || RAPIER_PORTABLE_HIGHLIGHT_HEX[String(element.getAttribute('data-rapier-highlight') || '').toLowerCase()] || RAPIER_PORTABLE_HIGHLIGHT_HEX.yellow)
|
|
1232
1235
|
: null;
|
|
1233
1236
|
|
|
1234
1237
|
const isTableCaption = element.tagName === 'P' && element.classList.contains('rapier-table-caption');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {collectPdfVectors} from './pdf-vectors.mjs';
|
|
3
3
|
import {collectPdfGlyphs} from './pdf-glyphs.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {pdfFontSources, waitForFonts} from './export-fonts.mjs';
|
|
5
5
|
import {apply as applyTypography} from '../layout/typography.mjs';
|
|
6
6
|
import {documentLanguage} from '../spec/document-settings.mjs';
|
|
7
7
|
import {applyPrintCapitals} from './print-capitals.mjs';
|
|
@@ -264,17 +264,28 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
264
264
|
ink?.(main);
|
|
265
265
|
const count = Math.max(1, Math.ceil((main.scrollWidth - 1) / bodyWidth));
|
|
266
266
|
if (!Number.isSafeInteger(count)) refuse('pdf_page_geometry_invalid');
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
267
|
+
const customFonts = pdfFontSources(fontCss), customFamilies = new Set(customFonts.map(face => face.family.toLowerCase()));
|
|
268
|
+
const glyphOptions = {width: bodyWidth, check,
|
|
269
|
+
prepareFont: globalThis.RapierExportFonts?.preparePdfFont && ((bytes, text, style, face) =>
|
|
270
|
+
globalThis.RapierExportFonts.preparePdfFont(bytes, text, style, face, {subset: globalThis.RapierFontSubsetPlugin?.subset})),
|
|
271
|
+
systemFonts: globalThis.RapierExportFonts?.systemPdfFonts,
|
|
272
|
+
maskFont: globalThis.RapierExportFonts?.maskPdfFont,
|
|
273
|
+
faces: [...customFonts, ...(globalThis.RapierDocxFonts?.DOCX_FONTS || []).filter(face => !customFamilies.has(face.family.toLowerCase())),
|
|
274
|
+
...pdfFontSources(css).filter(face => !customFamilies.has(face.family.toLowerCase()))]};
|
|
275
|
+
nativeGlyphs = await collectPdfGlyphs(main, glyphOptions);
|
|
276
|
+
const textPages = await _rapierPrintTextPages(main, bodyWidth, count, check, undefined, nativeGlyphs.nodes, nativeGlyphs.markers);
|
|
271
277
|
const linkPages = _rapierPrintLinkPages(main, bodyWidth, bodyHeight, count);
|
|
272
278
|
vectors = await collectPdfVectors(main, {width: bodyWidth, height: bodyHeight, count, capture, signal, current, check, maxBytes,
|
|
273
|
-
sanitizeSvg: globalThis.RapierImageAssets?.sanitizeSvgText
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
279
|
+
sanitizeSvg: globalThis.RapierImageAssets?.sanitizeSvgText,
|
|
280
|
+
collectText: root => collectPdfGlyphs(root, {...glyphOptions, origin: main.getBoundingClientRect()})});
|
|
281
|
+
for (const {root, original, text} of vectors.textRoots) {
|
|
282
|
+
const held = await _rapierPrintTextPages(root, bodyWidth, count, check, main.getBoundingClientRect(), text?.nodes);
|
|
283
|
+
nativeGlyphs.painted.push(...(text?.painted || []));
|
|
284
|
+
for (const [page, runs] of held.entries()) {
|
|
285
|
+
const at = textPages[page].findIndex(run => run.imageRoot === original);
|
|
286
|
+
if (at >= 0) textPages[page].splice(at, 1, ...runs); else textPages[page].push(...runs);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
278
289
|
const text = textPages.flatMap(page => page.map(run => run.text));
|
|
279
290
|
const glyphs = new Set();
|
|
280
291
|
for (const line of text) for (const character of line) {
|
|
@@ -349,11 +360,11 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
349
360
|
const bytes = await writeRasterPdf({pages, textPages, linkPages, vectorPages: vectors.pages, canonical, bom, filename, title, font, width, height, margins, backdrop: backdrop?.pages[0] || null,
|
|
350
361
|
pixelWidth: bodyWidth, pixelHeight: bodyHeight, pageNumbers: settings?.pagestyle === 'plain', maxBytes, check});
|
|
351
362
|
check();
|
|
352
|
-
return {bytes, pages: count};
|
|
353
|
-
} finally { filmSession?.close();
|
|
363
|
+
return {bytes, pages: count, painted: nativeGlyphs.painted};
|
|
364
|
+
} finally { filmSession?.close(); backdrop?.restore(); vectors?.restore(); nativeGlyphs?.restore(); signal?.removeEventListener('abort', abort); frame.remove(); }
|
|
354
365
|
}
|
|
355
366
|
|
|
356
|
-
async function _rapierPrintTextPages(main, pageWidth, count, check, origin = main.getBoundingClientRect(), nativeNodes = null) {
|
|
367
|
+
async function _rapierPrintTextPages(main, pageWidth, count, check, origin = main.getBoundingClientRect(), nativeNodes = null, markerNodes = null) {
|
|
357
368
|
const into = main.ownerDocument;
|
|
358
369
|
const pages = Array.from({length: count}, () => []), walker = into.createTreeWalker(main, 5);
|
|
359
370
|
const markerMetrics = _rapierPrintWillMetrics(Array.from(main.querySelectorAll('.rapier-will-carrier'), node => node.textContent));
|
|
@@ -363,6 +374,12 @@ async function _rapierPrintTextPages(main, pageWidth, count, check, origin = mai
|
|
|
363
374
|
for (let node; (node = walker.nextNode());) {
|
|
364
375
|
if (++nodes % 128 === 0) { await new Promise(resolve => setTimeout(resolve, 0)); check(); }
|
|
365
376
|
const element = node.nodeType === 1 ? node : node.parentElement;
|
|
377
|
+
if (node.nodeType === 1 && node.localName === 'img') {
|
|
378
|
+
const rect = node.getBoundingClientRect(), page = Math.max(0, Math.min(count - 1, Math.floor((rect.left - origin.left + .01) / pageWidth)));
|
|
379
|
+
pages[page].push({imageRoot: node, text: '', parts: []});
|
|
380
|
+
}
|
|
381
|
+
const marker = markerNodes?.get(node);
|
|
382
|
+
if (marker && marker.page < count) pages[marker.page].push({...marker, block: node, carrier: null, marker: true});
|
|
366
383
|
const formula = node.nodeType === 1 && node.matches('.math-rendered[data-math-src]') && node.querySelector('svg');
|
|
367
384
|
if (node.nodeType === 1 && !formula || node.nodeType === 3 && element.closest('.math-rendered[data-math-src]')) continue;
|
|
368
385
|
let value;
|
|
@@ -377,10 +394,12 @@ async function _rapierPrintTextPages(main, pageWidth, count, check, origin = mai
|
|
|
377
394
|
const native = nativeNodes?.get(node);
|
|
378
395
|
let characters = null;
|
|
379
396
|
const pageOf = x => Math.max(0, Math.min(count - 1, Math.floor((x - origin.left + .01) / pageWidth)));
|
|
380
|
-
const visit = (start, end) => {
|
|
397
|
+
const visit = (start, end, measured = null) => {
|
|
381
398
|
if (++operations % 128 === 0) check();
|
|
382
399
|
if (!formula) { range.setStart(node, start); range.setEnd(node, end); }
|
|
383
|
-
const rects =
|
|
400
|
+
const rects = measured ? measured.map(part => ({left: origin.left + part.page * pageWidth + part.x, right: origin.left + part.page * pageWidth + part.x + part.width,
|
|
401
|
+
top: origin.top + part.y, bottom: origin.top + part.y + part.height, width: part.width, height: part.height})) :
|
|
402
|
+
Array.from(formula ? [formula.getBoundingClientRect()] : range.getClientRects()).filter(rect => rect.height > 0 && (rect.width > 0 || carrier));
|
|
384
403
|
if (!rects.length) { if (carrier) markerLost(); return; }
|
|
385
404
|
const first = rects[0], page = pageOf(first.left);
|
|
386
405
|
const oneLine = rects.every(rect => pageOf(rect.left) === page && pageOf(rect.width ? rect.right - .02 : rect.left) === page && Math.abs(rect.top - first.top) < .5);
|
|
@@ -390,30 +409,50 @@ async function _rapierPrintTextPages(main, pageWidth, count, check, origin = mai
|
|
|
390
409
|
if (cluster) middle = cluster.start > start ? cluster.start : cluster.end;
|
|
391
410
|
if (!oneLine && middle > start && middle < end) { visit(start, middle); visit(middle, end); return; }
|
|
392
411
|
let text = value.slice(start, end);
|
|
393
|
-
if (!formula) text = preserve ? text.replace(/\r\n?|\n/g, '') : text.replace(/[ \t\r\n\f]+/g, ' ');
|
|
394
|
-
if (!text) return;
|
|
412
|
+
if (!formula) text = (preserve ? text.replace(/\r\n?|\n/g, '') : text.replace(/[ \t\r\n\f]+/g, ' ')).replace(/\u00ad/g, '');
|
|
413
|
+
if (!text && !native?.some(item => item.start >= start && item.end <= end && item.soft)) return;
|
|
395
414
|
const x = Math.min(...rects.map(rect => rect.left)) - origin.left - page * pageWidth;
|
|
396
415
|
const y = Math.min(...rects.map(rect => rect.top)) - origin.top;
|
|
397
416
|
let right = Math.max(...rects.map(rect => rect.right)) - origin.left - page * pageWidth;
|
|
398
417
|
if (carrier && right === x) right += Array.from(text).length * markerMetrics.advance * markerMetrics.size / 1000;
|
|
399
418
|
const bottom = Math.max(...rects.map(rect => rect.bottom)) - origin.top;
|
|
400
|
-
|
|
401
|
-
|
|
419
|
+
let parts = native ? native.filter(item => item.start >= start && item.end <= end && item.text).map(item => ({...item, rtl: style.direction === 'rtl'}))
|
|
420
|
+
: [{text, x, y, width: right - x, height: bottom - y, rtl: style.direction === 'rtl'}];
|
|
421
|
+
if (native) {
|
|
422
|
+
const joined = [];
|
|
423
|
+
for (const part of parts) {
|
|
424
|
+
const last = joined.at(-1);
|
|
425
|
+
if (last && !last.font && !part.font) {
|
|
426
|
+
const right = Math.max(last.x + last.width, part.x + part.width);
|
|
427
|
+
last.x = Math.min(last.x, part.x); last.width = right - last.x;
|
|
428
|
+
last.text += part.text; last.end = part.end;
|
|
429
|
+
} else joined.push(part);
|
|
430
|
+
}
|
|
431
|
+
parts = joined;
|
|
432
|
+
}
|
|
402
433
|
const last = pages[page].at(-1);
|
|
403
|
-
if (last && !formula && !last.math && last.block === block && last.carrier === carrier && Math.abs(last.y - y) < Math.max(2, Math.min(last.height, bottom - y) * .2)) {
|
|
434
|
+
if (last && !formula && !last.math && last.block === block && last.carrier === carrier && (last.marker || Math.abs(last.y - y) < Math.max(2, Math.min(last.height, bottom - y) * .2))) {
|
|
404
435
|
if (!preserve && last.text.endsWith(' ') && text.startsWith(' ') && parts.length) {
|
|
405
436
|
const first = parts[0];
|
|
406
437
|
if (first.text === ' ') parts.shift();
|
|
407
438
|
else {const advance = first.width / Array.from(first.text).length; first.text = first.text.slice(1); first.x += advance; first.width -= advance;}
|
|
408
439
|
}
|
|
409
440
|
last.text = preserve ? last.text + text : (last.text + text).replace(/[ \t\r\n\f]+/g, ' ');
|
|
410
|
-
last.parts.push(...parts);
|
|
441
|
+
last.parts.push(...parts); last.marker = false;
|
|
411
442
|
last.width = Math.max(last.x + last.width, right) - Math.min(last.x, x);
|
|
412
443
|
last.height = Math.max(last.y + last.height, bottom) - Math.min(last.y, y);
|
|
413
444
|
last.x = Math.min(last.x, x); last.y = Math.min(last.y, y);
|
|
414
445
|
} else pages[page].push({text, x, y, width: right - x, height: bottom - y, block, carrier, math: !!formula, parts});
|
|
415
446
|
};
|
|
416
|
-
|
|
447
|
+
if (native) {
|
|
448
|
+
const lines = [];
|
|
449
|
+
for (const part of native) {
|
|
450
|
+
const last = lines.at(-1);
|
|
451
|
+
if (last && last[0].page === part.page && Math.abs(last[0].y - part.y) < .5) last.push(part);
|
|
452
|
+
else lines.push([part]);
|
|
453
|
+
}
|
|
454
|
+
for (const line of lines) visit(line[0].start, line.at(-1).end, line);
|
|
455
|
+
} else visit(0, value.length);
|
|
417
456
|
}
|
|
418
457
|
check();
|
|
419
458
|
const pageHeight = origin.height;
|
|
@@ -518,44 +557,129 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
518
557
|
const source = encoder.encode((bom ? '\uFEFF' : '') + canonical);
|
|
519
558
|
const embedded = stream('/Type /EmbeddedFile /Subtype /text#2Fplain /Params <</Size ' + source.length + '>> /Filter /FlateDecode', await compressed(source));
|
|
520
559
|
const file = add('<< /Type /Filespec /F ' + literal(String(filename).replace(/[^\x20-\x7e]/g, '_')) + ' /UF <feff' + hex(filename) + '> /AFRelationship /Source /EF << /F ' + embedded + ' 0 R /UF ' + embedded + ' 0 R >> >>');
|
|
560
|
+
for (const page of textPages) for (const run of page) {
|
|
561
|
+
let previous = null;
|
|
562
|
+
for (const part of run.parts || []) {
|
|
563
|
+
part.unicode = part.text.normalize('NFC'); part.actual = part.text.replace(/\u00ad/g, '');
|
|
564
|
+
if (part.font && /^ +$/.test(part.text) && previous) {
|
|
565
|
+
if (previous.font && /[\p{Script=Hebrew}]/u.test(previous.text) && previous.font.glyph(32)) {
|
|
566
|
+
part.font = previous.font; part.glyph = previous.font.glyph(32); previous = part;
|
|
567
|
+
} else {
|
|
568
|
+
if (previous.font) previous.unicode += part.text; else previous.text += part.text;
|
|
569
|
+
previous.actual += part.actual;
|
|
570
|
+
const right = Math.max(previous.x + previous.width, part.x + part.width);
|
|
571
|
+
if (!previous.font) previous.x = Math.min(previous.x, part.x);
|
|
572
|
+
previous.width = right - previous.x; part.unicode = '';
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else if (part.text !== '\u00ad') previous = part;
|
|
576
|
+
}
|
|
577
|
+
if (previous && !run.carrier && !run.parts?.at(-1)?.soft && previous === run.parts?.findLast(part => part.text.trim() && part.text !== '\u00ad')) {
|
|
578
|
+
if (previous.font) {
|
|
579
|
+
let ending = previous;
|
|
580
|
+
const tail = [];
|
|
581
|
+
for (let at = run.parts.indexOf(previous); at >= 0; at--) {
|
|
582
|
+
const part = run.parts[at];
|
|
583
|
+
if (part.font && !part.unicode) continue;
|
|
584
|
+
if (part.font !== previous.font) break;
|
|
585
|
+
tail.push(part);
|
|
586
|
+
}
|
|
587
|
+
if (tail.some(part => /\p{Script=Hebrew}/u.test(part.text))) ending = tail.reduce((a, b) => a.x > b.x ? a : b);
|
|
588
|
+
ending.unicode += '\n';
|
|
589
|
+
} else previous.copyEnd = '\n';
|
|
590
|
+
}
|
|
591
|
+
}
|
|
521
592
|
const codes = new Map(font.codes.map((code, index) => [code, index + 1]));
|
|
522
593
|
const rows = font.codes.map((code, index) => '<' + (index + 1).toString(16).padStart(4, '0') + '> <' + hex(String.fromCodePoint(code)) + '>');
|
|
594
|
+
const fallbackCharacters = (part, carrier) => {
|
|
595
|
+
const characters = Array.from(part.text);
|
|
596
|
+
if (!carrier && !/[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}]/u.test([...part.text.trimEnd()].at(-1) || '')) {
|
|
597
|
+
let suffix = part.copyEnd || '';
|
|
598
|
+
while (characters.length > 1 && characters.at(-1) === ' ') suffix = characters.pop() + suffix;
|
|
599
|
+
if (suffix && characters.length) characters[characters.length - 1] += suffix;
|
|
600
|
+
}
|
|
601
|
+
return characters;
|
|
602
|
+
};
|
|
603
|
+
const fallbackIds = font.codes.map((_, index) => index + 1);
|
|
604
|
+
for (const page of textPages) for (const run of page) for (const part of run.parts || [run]) if (!part.font)
|
|
605
|
+
for (const character of fallbackCharacters(part, run.carrier)) if ([...character].length > 1 && !codes.has(character)) {
|
|
606
|
+
const code = rows.length + 1; if (code > 65535) fail('pdf_text_glyph_limit');
|
|
607
|
+
codes.set(character, code); fallbackIds.push(codes.get(character.codePointAt(0)));
|
|
608
|
+
rows.push('<' + code.toString(16).padStart(4, '0') + '> <' + hex(character) + '>');
|
|
609
|
+
}
|
|
523
610
|
let cmap = '/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /RapierText def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <ffff>\nendcodespacerange\n';
|
|
524
611
|
for (let at = 0; at < rows.length; at += 100) cmap += Math.min(100, rows.length - at) + ' beginbfchar\n' + rows.slice(at, at + 100).join('\n') + '\nendbfchar\n';
|
|
525
612
|
cmap += 'endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend';
|
|
526
613
|
const mapping = stream('/Filter /FlateDecode', await compressed(encode(cmap)));
|
|
527
614
|
const fontFile = stream('/Length1 ' + font.bytes.length + ' /Filter /FlateDecode', await compressed(font.bytes));
|
|
528
615
|
const descriptor = add('<< /Type /FontDescriptor /FontName /RapierText /Flags 4 /FontBBox [' + font.bounds.join(' ') + '] /ItalicAngle 0 /Ascent 500 /Descent -500 /CapHeight 500 /StemV 80 /FontFile2 ' + fontFile + ' 0 R >>');
|
|
529
|
-
const
|
|
616
|
+
const fallbackMap = new Uint8Array((fallbackIds.length + 1) * 2);
|
|
617
|
+
fallbackIds.forEach((id, index) => {fallbackMap[(index + 1) * 2] = id >> 8; fallbackMap[(index + 1) * 2 + 1] = id & 255;});
|
|
618
|
+
const fallbackGlyphs = stream('/Filter /FlateDecode', await compressed(fallbackMap));
|
|
619
|
+
const descendant = add('<< /Type /Font /Subtype /CIDFontType2 /BaseFont /RapierText /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor ' + descriptor + ' 0 R /DW 500 /CIDToGIDMap ' + fallbackGlyphs + ' 0 R >>');
|
|
530
620
|
const type = add('<< /Type /Font /Subtype /Type0 /BaseFont /RapierText /Encoding /Identity-H /DescendantFonts [' + descendant + ' 0 R] /ToUnicode ' + mapping + ' 0 R >>');
|
|
531
621
|
const carrierDescriptor = add('<< /Type /FontDescriptor /FontName /RapierWillCarrier /Flags 4 /FontBBox [' + font.bounds.join(' ') + '] /ItalicAngle 0 /Ascent 500 /Descent -500 /CapHeight 500 /StemV 80 /FontFile2 ' + fontFile + ' 0 R >>');
|
|
532
622
|
const carrierDescendant = add('<< /Type /Font /Subtype /CIDFontType2 /BaseFont /RapierWillCarrier /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor ' + carrierDescriptor + ' 0 R /DW 500 /CIDToGIDMap /Identity >>');
|
|
533
623
|
const carrierType = add('<< /Type /Font /Subtype /Type0 /BaseFont /RapierWillCarrier /Encoding /Identity-H /DescendantFonts [' + carrierDescendant + ' 0 R] /ToUnicode ' + mapping + ' 0 R >>');
|
|
534
624
|
const numberFont = pageNumbers ? add('<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>') : null;
|
|
535
625
|
const kids = pages.map(() => { objects.push(null); return objects.length - 1; }), decimal = value => Number(value.toFixed(4));
|
|
536
|
-
const nativeFonts = new Map();
|
|
537
|
-
const glyphKey = part => part.glyph + ':' + part.
|
|
538
|
-
for (const page of textPages) for (const run of page) for (const part of run.parts || []) if (part.font) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (!
|
|
626
|
+
const nativeFonts = new Map(), nativeEntries = [], fontFiles = new Map();
|
|
627
|
+
const glyphKey = part => part.glyph + ':' + part.unicode + ':' + decimal(part.width / part.size / (part.scaleX || 1));
|
|
628
|
+
for (const page of textPages) for (const run of page) for (const part of run.parts || []) if (part.font && part.unicode) {
|
|
629
|
+
const key = glyphKey(part), cff = part.font.outline === 'cff';
|
|
630
|
+
const cid = cff ? part.font.cids?.[part.glyph] ?? part.glyph : null;
|
|
631
|
+
let variants = nativeFonts.get(part.font);
|
|
632
|
+
if (!variants) {variants = []; nativeFonts.set(part.font, variants);}
|
|
633
|
+
let entry = variants.find(entry => !cff || !entry.rows.some(row => row.code === cid && glyphKey(row) !== key));
|
|
634
|
+
if (!entry) {
|
|
635
|
+
entry = {font: part.font, name: '/Fn' + nativeEntries.length, codes: new Map(), rows: []};
|
|
636
|
+
variants.push(entry); nativeEntries.push(entry);
|
|
637
|
+
}
|
|
638
|
+
if (!entry.codes.has(key)) {
|
|
639
|
+
const code = cff ? cid : entry.rows.length + 1;
|
|
640
|
+
if (code > 65535) fail('pdf_text_glyph_limit');
|
|
641
|
+
entry.codes.set(key, code); entry.rows.push({...part, code});
|
|
642
|
+
}
|
|
643
|
+
part.pdfFont = entry;
|
|
543
644
|
}
|
|
544
|
-
const nativeResources = [];
|
|
545
|
-
for (const entry of
|
|
645
|
+
const nativeResources = [], fontNames = new Map();
|
|
646
|
+
for (const entry of nativeEntries) {
|
|
546
647
|
check();
|
|
547
|
-
const face = entry.font,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
let
|
|
555
|
-
for (let at = 0; at < entry.rows.length; at += 100) {const rows = entry.rows.slice(at, at + 100); unicode += rows.length + ' beginbfchar\n' + rows.map(row => '<' + row.code.toString(16).padStart(4, '0') + '> <' + hex(row.text) + '>').join('\n') + '\nendbfchar\n';}
|
|
648
|
+
const face = entry.font, scale = 1000 / face.units;
|
|
649
|
+
if (!fontNames.has(face)) {
|
|
650
|
+
const prefix = face.asset.subset ? fontNames.size.toString(26).padStart(6, '0').replace(/[0-9a-p]/g, c => String.fromCharCode(65 + parseInt(c, 26))) + '+' : '';
|
|
651
|
+
fontNames.set(face, prefix + (face.asset.postscriptName || 'Rapier' + entry.name.slice(1) + '-' + face.asset.family).replace(/[^a-z0-9-]/gi, ''));
|
|
652
|
+
}
|
|
653
|
+
const name = fontNames.get(face);
|
|
654
|
+
let unicode = '/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /' + name + 'Unicode def\n/CMapType 2 def\n1 begincodespacerange\n' + (face.type3 ? '<00> <ff>' : '<0000> <ffff>') + '\nendcodespacerange\n';
|
|
655
|
+
for (let at = 0; at < entry.rows.length; at += 100) {const rows = entry.rows.slice(at, at + 100); unicode += rows.length + ' beginbfchar\n' + rows.map(row => '<' + row.code.toString(16).padStart(face.type3 ? 2 : 4, '0') + '> <' + hex(row.unicode) + '>').join('\n') + '\nendbfchar\n';}
|
|
556
656
|
unicode += 'endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend';
|
|
557
657
|
const mapping = stream('/Filter /FlateDecode', await compressed(encode(unicode)));
|
|
558
|
-
|
|
658
|
+
if (face.type3) {
|
|
659
|
+
if (entry.rows.length > 255) fail('pdf_text_glyph_limit');
|
|
660
|
+
const widths = entry.rows.map(row => decimal(row.width / row.size / (row.scaleX || 1) * 1000));
|
|
661
|
+
const paths = [];
|
|
662
|
+
for (let index = 0; index < entry.rows.length; index++) {
|
|
663
|
+
const row = entry.rows[index], drawing = widths[index] + ' 0 d0\n' + face.commands;
|
|
664
|
+
paths[index] = '/g' + row.code + ' ' + stream('/Filter /FlateDecode', await compressed(encode(drawing))) + ' 0 R';
|
|
665
|
+
}
|
|
666
|
+
const type = add('<< /Type /Font /Subtype /Type3 /Name /' + name + ' /FontBBox [' + face.bbox.join(' ') + '] /FontMatrix [.001 0 0 .001 0 0] /CharProcs << ' + paths.join(' ') + ' >> /Encoding << /Type /Encoding /Differences [1 ' + entry.rows.map(row => '/g' + row.code).join(' ') + '] >> /FirstChar 1 /LastChar ' + entry.rows.length + ' /Widths [' + widths.join(' ') + '] /Resources << >> /ToUnicode ' + mapping + ' 0 R >>');
|
|
667
|
+
nativeResources.push(entry.name + ' ' + type + ' 0 R'); continue;
|
|
668
|
+
}
|
|
669
|
+
let descriptor = fontFiles.get(face);
|
|
670
|
+
if (!descriptor) {
|
|
671
|
+
const file = stream((face.outline === 'cff' ? '/Subtype /OpenType ' : '/Length1 ' + face.bytes.length) + ' /Filter /FlateDecode', await compressed(face.bytes));
|
|
672
|
+
descriptor = add('<< /Type /FontDescriptor /FontName /' + name + ' /Flags ' + (face.italic ? 96 : 32) + ' /FontBBox [' + face.bbox.map(value => decimal(value * scale)).join(' ') + '] /ItalicAngle ' + decimal(face.italicAngle || 0) + ' /Ascent ' + decimal(face.ascent * scale)
|
|
673
|
+
+ ' /Descent ' + decimal(face.descent * scale) + ' /CapHeight ' + decimal(face.capHeight * scale) + ' /StemV 80 /' + (face.outline === 'cff' ? 'FontFile3' : 'FontFile2') + ' ' + file + ' 0 R >>');
|
|
674
|
+
fontFiles.set(face, descriptor);
|
|
675
|
+
}
|
|
676
|
+
let glyphMap = '';
|
|
677
|
+
if (face.outline !== 'cff') {
|
|
678
|
+
const ids = new Uint8Array((entry.rows.length + 1) * 2);
|
|
679
|
+
for (const row of entry.rows) {ids[row.code * 2] = row.glyph >> 8; ids[row.code * 2 + 1] = row.glyph & 255;}
|
|
680
|
+
glyphMap = ' /CIDToGIDMap ' + stream('/Filter /FlateDecode', await compressed(ids)) + ' 0 R';
|
|
681
|
+
}
|
|
682
|
+
const descendant = add('<< /Type /Font /Subtype /' + (face.outline === 'cff' ? 'CIDFontType0' : 'CIDFontType2') + ' /BaseFont /' + name + ' /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor ' + descriptor + ' 0 R' + glyphMap + ' /W [' + entry.rows.map(row => row.code + ' [' + decimal(row.width / row.size / (row.scaleX || 1) * 1000) + ']').join(' ') + '] >>');
|
|
559
683
|
const type = add('<< /Type /Font /Subtype /Type0 /BaseFont /' + name + ' /Encoding /Identity-H /DescendantFonts [' + descendant + ' 0 R] /ToUnicode ' + mapping + ' 0 R >>');
|
|
560
684
|
nativeResources.push(entry.name + ' ' + type + ' 0 R');
|
|
561
685
|
}
|
|
@@ -609,28 +733,46 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
609
733
|
for (const drawing of vectorPages[index] || []) commands += emit(drawing, margins.left, height - margins.top, pixelWidth, pixelHeight);
|
|
610
734
|
const textAlphas = new Map();
|
|
611
735
|
for (const run of textPages[index]) {
|
|
612
|
-
if (!run.text) continue;
|
|
736
|
+
if (!run.text && !run.parts?.some(part => part.font)) continue;
|
|
613
737
|
const element = structOf(run.tags || []);
|
|
614
738
|
if (element) {element.kids.push({page: index, mcid: marked.length}); commands += `/${element.role} << /MCID ${marked.length} >> BDC\n`; marked.push(element);}
|
|
615
|
-
commands +=
|
|
739
|
+
commands += 'q\n';
|
|
740
|
+
const groups = [];
|
|
616
741
|
for (const part of run.parts || [run]) {
|
|
742
|
+
if (part.font && !part.unicode) continue;
|
|
743
|
+
const last = groups.at(-1), previous = last?.at(-1);
|
|
744
|
+
if (previous && previous.font === part.font && previous.skew === part.skew && previous.size === part.size && previous.scaleX === part.scaleX) last.push(part);
|
|
745
|
+
else groups.push([part]);
|
|
746
|
+
}
|
|
747
|
+
for (const group of groups) {
|
|
748
|
+
const actual = group.map(part => part.actual ?? part.text).join('');
|
|
749
|
+
let text = `/Span << /ActualText <feff${hex(actual)}> >> BDC\nBT\n100 Tz\n`;
|
|
750
|
+
const shown = group.some(part => part.font && /\p{Script=Hebrew}/u.test(part.text)) ? [...group].sort((a, b) => a.x - b.x) : group;
|
|
751
|
+
for (const part of shown) {
|
|
617
752
|
if (part.font) {
|
|
618
|
-
const entry =
|
|
753
|
+
const entry = part.pdfFont, value = entry.codes.get(glyphKey(part)).toString(16).padStart(part.font.type3 ? 2 : 4, '0'), alpha = decimal(part.rgba[3]);
|
|
619
754
|
let state = textAlphas.get(alpha);
|
|
620
755
|
if (!state) {state = '/Gs' + states.length; states.push(state + ' ' + add('<< /Type /ExtGState /ca ' + alpha + ' >>') + ' 0 R'); textAlphas.set(alpha, state);}
|
|
621
|
-
|
|
756
|
+
text += `${state} gs\n${part.rgba.slice(0, 3).map(decimal).join(' ')} rg\n${entry.name} ${decimal(part.size * .75)} Tf\n${decimal((part.scaleX || 1) * 100)} Tz\n0 Tr\n1 0 ${decimal((part.skew || 0) * (part.scaleX || 1))} 1 ${decimal(margins.left + part.x * .75)} ${decimal(height - margins.top - part.baseline * .75)} Tm\n<${value}> Tj\n`;
|
|
622
757
|
} else {
|
|
623
|
-
const characters =
|
|
758
|
+
const characters = fallbackCharacters(part, run.carrier); if (!characters.length) continue;
|
|
624
759
|
const {slots, count} = _rapierPrintSlots(part.text, part.rtl);
|
|
625
|
-
const advance = part.width * .75 / count
|
|
760
|
+
const advance = part.width * .75 / count;
|
|
626
761
|
const baseline = decimal(height - margins.top - (part.y + part.height * .8) * .75);
|
|
627
762
|
for (let letter = 0; letter < characters.length; letter++) {
|
|
628
|
-
const
|
|
629
|
-
|
|
763
|
+
const size = Math.max(.1, advance * 2 * [...characters[letter].replace(/\n/g, '')].length), scaleY = Math.max(.1, part.height * .75) / size;
|
|
764
|
+
const value = (codes.get(characters[letter]) || codes.get(characters[letter].codePointAt(0))).toString(16).padStart(4, '0');
|
|
765
|
+
text += `${run.carrier ? '/Fc' : '/F1'} ${decimal(size)} Tf\n3 Tr\n1 0 0 ${decimal(scaleY)} ${decimal(margins.left + part.x * .75 + slots[letter] * advance)} ${baseline} Tm\n<${value}> Tj\n`;
|
|
630
766
|
}
|
|
631
767
|
}
|
|
632
768
|
}
|
|
633
|
-
|
|
769
|
+
text += 'ET\nEMC\n';
|
|
770
|
+
const name = '/Tx' + pictures.length;
|
|
771
|
+
const object = stream('/Type /XObject /Subtype /Form /BBox [0 0 ' + decimal(width) + ' ' + decimal(height) + '] /Resources << /Font << /F1 ' + type + ' 0 R /Fc ' + carrierType + ' 0 R ' + nativeResources.join(' ') + ' >> /ExtGState << ' + states.join(' ') + ' >> >> /Filter /FlateDecode', await compressed(encode(text)));
|
|
772
|
+
pictures.push(name + ' ' + object + ' 0 R');
|
|
773
|
+
commands += name + ' Do\n';
|
|
774
|
+
}
|
|
775
|
+
commands += 'Q\n' + (element ? 'EMC\n' : '');
|
|
634
776
|
}
|
|
635
777
|
if (numberFont) commands += `BT\n/F2 9 Tf\n0 Tr\n1 0 0 1 ${decimal(width / 2 - String(index + 1).length * 2.5)} ${decimal(margins.bottom / 2)} Tm\n(${index + 1}) Tj\nET\n`;
|
|
636
778
|
const content = stream('/Filter /FlateDecode', await compressed(encode(commands)));
|
|
@@ -3,13 +3,13 @@ import {typography} from '../spec/md-marks.mjs';
|
|
|
3
3
|
const RAPIER_SANITIZE_FORBID_TAGS = Object.freeze(['form', 'style', 'dialog', 'template', 'iframe']);
|
|
4
4
|
const RAPIER_SANITIZE_FORBID_ATTR = Object.freeze(['action', 'formaction', 'form', 'for', 'command', 'commandfor', 'popover', 'popovertarget', 'popovertargetaction']);
|
|
5
5
|
const RAPIER_SANITIZE_PROFILES = Object.freeze({
|
|
6
|
-
htmlTags: Object.freeze(
|
|
7
|
-
svgTags: Object.freeze(
|
|
8
|
-
mathTags: Object.freeze(
|
|
9
|
-
htmlAttributes: Object.freeze(
|
|
10
|
-
svgAttributes: Object.freeze(
|
|
11
|
-
mathAttributes: Object.freeze(
|
|
12
|
-
xmlAttributes: Object.freeze(
|
|
6
|
+
htmlTags: Object.freeze('a abbr acronym address area article aside audio b bdi bdo big blink blockquote body br button canvas caption center cite code col colgroup content data datalist dd decorator del details dfn dialog dir div dl dt element em fieldset figcaption figure font footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i img input ins kbd label legend li main map mark marquee menu menuitem meter nav nobr ol optgroup option output p picture pre progress q rp rt ruby s samp search section select shadow slot small source spacer span strike strong style sub summary sup table tbody td template textarea tfoot th thead time tr track tt u ul var video wbr'.split(' ')),
|
|
7
|
+
svgTags: Object.freeze('svg a altglyph altglyphdef altglyphitem animatecolor animatemotion animatetransform circle clippath defs desc ellipse enterkeyhint exportparts filter font g glyph glyphref hkern image inputmode line lineargradient marker mask metadata mpath part path pattern polygon polyline radialgradient rect stop style switch symbol text textpath title tref tspan view vkern'.split(' ')),
|
|
8
|
+
mathTags: Object.freeze('math menclose merror mfenced mfrac mglyph mi mlabeledtr mmultiscripts mn mo mover mpadded mphantom mroot mrow ms mspace msqrt mstyle msub msup msubsup mtable mtd mtext mtr munder munderover mprescripts'.split(' ')),
|
|
9
|
+
htmlAttributes: Object.freeze('accept action align alt autocapitalize autocomplete autopictureinpicture autoplay background bgcolor border capture cellpadding cellspacing checked cite class clear color cols colspan command commandfor controls controlslist coords crossorigin datetime decoding default dir disabled disablepictureinpicture disableremoteplayback download draggable enctype enterkeyhint exportparts face for headers height hidden high href hreflang id inert inputmode integrity ismap kind label lang list loading loop low max maxlength media method min minlength multiple muted name nonce noshade novalidate nowrap open optimum part pattern placeholder playsinline popover popovertarget popovertargetaction poster preload pubdate radiogroup readonly rel required rev reversed role rows rowspan spellcheck scope selected shape size sizes slot span srclang start src srcset step style summary tabindex title translate type usemap valign value width wrap xmlns'.split(' ')),
|
|
10
|
+
svgAttributes: Object.freeze('accent-height accumulate additive alignment-baseline amplitude ascent attributename attributetype azimuth basefrequency baseline-shift begin bias by class clip clippathunits clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cx cy d dx dy diffuseconstant direction display divisor dominant-baseline dur edgemode elevation end exponent fill fill-opacity fill-rule filter filterunits flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight fx fy g1 g2 glyph-name glyphref gradientunits gradienttransform height href id image-rendering in in2 intercept k k1 k2 k3 k4 kerning keypoints keysplines keytimes lang lengthadjust letter-spacing kernelmatrix kernelunitlength lighting-color local marker-end marker-mid marker-start markerheight markerunits markerwidth maskcontentunits maskunits max mask mask-type media method mode min name numoctaves offset operator opacity order orient orientation origin overflow paint-order path pathlength patterncontentunits patterntransform patternunits pointer-events points preservealpha preserveaspectratio primitiveunits r rx ry radius refx refy repeatcount repeatdur restart result rotate scale seed shape-rendering slope specularconstant specularexponent spreadmethod startoffset stddeviation stitchtiles stop-color stop-opacity stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke stroke-width style surfacescale systemlanguage tabindex tablevalues targetx targety transform transform-origin text-anchor text-decoration text-orientation text-rendering textlength type u1 u2 unicode values vector-effect viewbox visibility version vert-adv-y vert-origin-x vert-origin-y width word-spacing wrap writing-mode xchannelselector ychannelselector x x1 x2 xmlns y y1 y2 z zoomandpan'.split(' ')),
|
|
11
|
+
mathAttributes: Object.freeze('accent accentunder align bevelled close columnalign columnlines columnspacing columnspan denomalign depth dir display displaystyle encoding fence frame height href id largeop length linethickness lquote lspace mathbackground mathcolor mathsize mathvariant maxsize minsize movablelimits notation numalign open rowalign rowlines rowspacing rowspan rspace rquote scriptlevel scriptminsize scriptsizemultiplier selection separator separators stretchy subscriptshift supscriptshift symmetric voffset width xmlns'.split(' ')),
|
|
12
|
+
xmlAttributes: Object.freeze('xlink:href xml:id xlink:title xml:space xmlns:xlink'.split(' ')),
|
|
13
13
|
});
|
|
14
14
|
const RAPIER_SANITIZE_DROP_CONTENTS = Object.freeze('annotation-xml.audio.colgroup.desc.foreignobject.head.iframe.math.mi.mn.mo.ms.mtext.noembed.noframes.noscript.plaintext.script.selectedcontent.style.svg.template.thead.title.video.xmp'.split('.'));
|
|
15
15
|
function _rapierSanitizeProfile(...kinds) {
|
package/dist/kit/render.mjs
CHANGED
|
@@ -330,7 +330,8 @@ function _rapierDocxRenderStyles(root) {
|
|
|
330
330
|
}
|
|
331
331
|
const coloured = codes.filter(code => code.querySelector('span'));
|
|
332
332
|
const quotes = Array.from(root.querySelectorAll('blockquote'));
|
|
333
|
-
|
|
333
|
+
const diagrams = Array.from(root.querySelectorAll('svg.pele'));
|
|
334
|
+
if (!coloured.length && !quotes.length && !diagrams.length) return;
|
|
334
335
|
const frame = document.createElement('iframe');
|
|
335
336
|
frame.setAttribute('aria-hidden', 'true');
|
|
336
337
|
frame.style.cssText = 'position:fixed;left:-100000px;top:0;width:1px;height:1px;border:0;pointer-events:none';
|
|
@@ -358,6 +359,20 @@ function _rapierDocxRenderStyles(root) {
|
|
|
358
359
|
}
|
|
359
360
|
return colours.get(colour);
|
|
360
361
|
};
|
|
362
|
+
for (const diagram of diagrams) {
|
|
363
|
+
const host = into.createElement('div'); host.className = 'diagram-block';
|
|
364
|
+
const probe = into.importNode(diagram, true); host.appendChild(probe); surface.appendChild(host);
|
|
365
|
+
const originals = [diagram, ...diagram.querySelectorAll('*')];
|
|
366
|
+
[probe, ...probe.querySelectorAll('*')].forEach((element, index) => {
|
|
367
|
+
const computed = into.defaultView.getComputedStyle(element), target = originals[index];
|
|
368
|
+
for (const property of ['fill', 'stroke', 'color', 'stop-color', 'stroke-width', 'stroke-dasharray',
|
|
369
|
+
'font-family', 'font-size', 'font-weight', 'font-style', 'rx', 'ry']) {
|
|
370
|
+
const value = computed.getPropertyValue(property);
|
|
371
|
+
if (value && !/url\(/i.test(value)) target.style.setProperty(property, value);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
host.remove();
|
|
375
|
+
}
|
|
361
376
|
for (const code of coloured) {
|
|
362
377
|
const pre = into.createElement('pre'), probe = into.importNode(code, true);
|
|
363
378
|
pre.appendChild(probe); surface.appendChild(pre);
|
|
@@ -95,7 +95,7 @@ export function supportsRichStyle(style, contentLanguage = '') {
|
|
|
95
95
|
!oneOf('fontStretch', ['normal', '100%']) || !oneOf('fontSizeAdjust', ['none']) ||
|
|
96
96
|
!oneOf('textRendering', ['auto']) || !oneOf('verticalAlign', ['baseline']) ||
|
|
97
97
|
!oneOf('whiteSpace', ['normal', 'pre-wrap', 'nowrap']) || !oneOf('wordBreak', ['normal', 'keep-all']) ||
|
|
98
|
-
!oneOf('hyphens', ['manual']) || !oneOf('lineBreak', ['auto']) || !oneOf('hangingPunctuation', ['none']) ||
|
|
98
|
+
!oneOf('hyphens', ['manual']) || !oneOf('lineBreak', ['auto', 'after-white-space']) || !oneOf('hangingPunctuation', ['none']) ||
|
|
99
99
|
!oneOf('tabSize', ['8']) || !oneOf('textWrapStyle', ['auto', 'pretty'])) return false;
|
|
100
100
|
for (const name of ['fontFeatureSettings', 'fontVariationSettings', 'fontVariant', 'fontVariantLigatures',
|
|
101
101
|
'fontVariantCaps', 'fontVariantNumeric', 'fontVariantEastAsian', 'fontVariantPosition', 'fontVariantAlternates']) {
|