pptx-browser 4.1.3 → 4.1.5
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/package.json +1 -1
- package/src/pdf.js +6 -0
- package/src/svg.js +18 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pptx-browser",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.5",
|
|
4
4
|
"description": "Render, edit, and export PowerPoint (PPTX) slides. Canvas rendering, SVG export, PDF export, PPTX writer/template engine, slide show, text extraction, animations. Zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/pdf.js
CHANGED
|
@@ -62,6 +62,12 @@ class PdfBuf {
|
|
|
62
62
|
|
|
63
63
|
/** Extract raw JPEG bytes from a canvas element. Returns Uint8Array. */
|
|
64
64
|
async function canvasToJpeg(canvas, quality = 0.92) {
|
|
65
|
+
// OffscreenCanvas uses convertToBlob(); HTMLCanvasElement uses toBlob()
|
|
66
|
+
if (typeof canvas.convertToBlob === 'function') {
|
|
67
|
+
const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality });
|
|
68
|
+
const buf = await blob.arrayBuffer();
|
|
69
|
+
return new Uint8Array(buf);
|
|
70
|
+
}
|
|
65
71
|
return new Promise((resolve, reject) => {
|
|
66
72
|
canvas.toBlob(blob => {
|
|
67
73
|
if (!blob) { reject(new Error('canvas.toBlob failed')); return; }
|
package/src/svg.js
CHANGED
|
@@ -459,15 +459,15 @@ function toRoman(n) {
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
// ── Shape → SVG ───────────────────────────────────────────────────────────────
|
|
462
|
-
async function shapesToSvg(spTreeEl, rels,
|
|
462
|
+
async function shapesToSvg(spTreeEl, rels, files, themeColors, themeData, defs) {
|
|
463
463
|
if (!spTreeEl) return '';
|
|
464
464
|
let out = '';
|
|
465
465
|
for (const child of spTreeEl.children) {
|
|
466
466
|
const ln = child.localName;
|
|
467
467
|
if (ln === 'sp') out += await shapeToSvg(child, themeColors, themeData, defs);
|
|
468
|
-
else if (ln === 'pic') out += await pictureToSvg(child, rels,
|
|
468
|
+
else if (ln === 'pic') out += await pictureToSvg(child, rels, files, themeColors, defs);
|
|
469
469
|
else if (ln === 'cxnSp') out += await connectorToSvg(child, themeColors, defs);
|
|
470
|
-
else if (ln === 'grpSp') out += await groupToSvg(child, rels,
|
|
470
|
+
else if (ln === 'grpSp') out += await groupToSvg(child, rels, files, themeColors, themeData, defs);
|
|
471
471
|
else if (ln === 'graphicFrame') out += await graphicFrameToSvg(child, themeColors, defs);
|
|
472
472
|
}
|
|
473
473
|
return out;
|
|
@@ -543,7 +543,7 @@ async function shapeToSvg(spEl, themeColors, themeData, defs) {
|
|
|
543
543
|
return `<g>${pathSvg}${textSvg}</g>`;
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
-
async function pictureToSvg(picEl, rels,
|
|
546
|
+
async function pictureToSvg(picEl, rels, files, themeColors, defs) {
|
|
547
547
|
const spPr = g1(picEl, 'spPr');
|
|
548
548
|
const xfrm = g1(spPr, 'xfrm');
|
|
549
549
|
const b = xfrmBounds(xfrm);
|
|
@@ -553,7 +553,7 @@ async function pictureToSvg(picEl, rels, imageCache, themeColors, defs) {
|
|
|
553
553
|
const blip = blipFill ? g1(blipFill, 'blip') : null;
|
|
554
554
|
const rId = blip ? (blip.getAttribute('r:embed') || blip.getAttribute('embed')) : null;
|
|
555
555
|
const rel = rId ? rels[rId] : null;
|
|
556
|
-
const imgData = rel ?
|
|
556
|
+
const imgData = rel ? files[rel.fullPath] : null;
|
|
557
557
|
|
|
558
558
|
const transform = xfrmAttrs(xfrm);
|
|
559
559
|
const effectLst = g1(spPr, 'effectLst');
|
|
@@ -567,7 +567,8 @@ async function pictureToSvg(picEl, rels, imageCache, themeColors, defs) {
|
|
|
567
567
|
const ext = rel.fullPath.split('.').pop().toLowerCase();
|
|
568
568
|
const mime = ext === 'png' ? 'image/png' : ext === 'gif' ? 'image/gif'
|
|
569
569
|
: ext === 'svg' ? 'image/svg+xml' : 'image/jpeg';
|
|
570
|
-
const
|
|
570
|
+
const raw = imgData instanceof Uint8Array ? imgData : new Uint8Array(imgData);
|
|
571
|
+
const b64 = btoa(Array.from(raw, b => String.fromCharCode(b)).join(''));
|
|
571
572
|
|
|
572
573
|
// Clip to shape bounds
|
|
573
574
|
const clipId = uid('pic');
|
|
@@ -587,12 +588,12 @@ async function connectorToSvg(cxnSpEl, themeColors, defs) {
|
|
|
587
588
|
return `<line x1="${px(b.x)}" y1="${px(b.y)}" x2="${px(b.x+b.w)}" y2="${px(b.y+b.h)}" fill="none"${stroke}${transform}/>`;
|
|
588
589
|
}
|
|
589
590
|
|
|
590
|
-
async function groupToSvg(grpSpEl, rels,
|
|
591
|
+
async function groupToSvg(grpSpEl, rels, files, themeColors, themeData, defs) {
|
|
591
592
|
const spPr = g1(grpSpEl, 'grpSpPr');
|
|
592
593
|
const xfrm = g1(spPr, 'xfrm');
|
|
593
594
|
const b = xfrmBounds(xfrm);
|
|
594
595
|
const transform = xfrm ? xfrmAttrs(xfrm) : '';
|
|
595
|
-
const children = await shapesToSvg(grpSpEl, rels,
|
|
596
|
+
const children = await shapesToSvg(grpSpEl, rels, files, themeColors, themeData, defs);
|
|
596
597
|
return `<g${transform}>${children}</g>`;
|
|
597
598
|
}
|
|
598
599
|
|
|
@@ -606,7 +607,7 @@ async function graphicFrameToSvg(graphicFrame, themeColors, defs) {
|
|
|
606
607
|
}
|
|
607
608
|
|
|
608
609
|
// ── Background → SVG ──────────────────────────────────────────────────────────
|
|
609
|
-
async function backgroundToSvg(slideDoc, masterDoc, layoutDoc,
|
|
610
|
+
async function backgroundToSvg(slideDoc, masterDoc, layoutDoc, files, masterRels, themeColors, slideW, slideH, defs) {
|
|
610
611
|
const getbg = (doc) => {
|
|
611
612
|
const cSld = g1(doc, 'cSld');
|
|
612
613
|
const bg = cSld ? g1(cSld, 'bg') : null;
|
|
@@ -626,11 +627,12 @@ async function backgroundToSvg(slideDoc, masterDoc, layoutDoc, imageCache, maste
|
|
|
626
627
|
const blip = g1(fillEl, 'blip');
|
|
627
628
|
const rId = blip ? (blip.getAttribute('r:embed') || blip.getAttribute('embed')) : null;
|
|
628
629
|
const rel = rId && masterRels ? masterRels[rId] : null;
|
|
629
|
-
const imgData = rel ?
|
|
630
|
+
const imgData = rel ? files[rel.fullPath] : null;
|
|
630
631
|
if (imgData) {
|
|
631
632
|
const ext = rel.fullPath.split('.').pop().toLowerCase();
|
|
632
633
|
const mime = ext === 'png' ? 'image/png' : 'image/jpeg';
|
|
633
|
-
const
|
|
634
|
+
const raw = imgData instanceof Uint8Array ? imgData : new Uint8Array(imgData);
|
|
635
|
+
const b64 = btoa(Array.from(raw, b => String.fromCharCode(b)).join(''));
|
|
634
636
|
return `<image width="${px(slideW)}" height="${px(slideH)}" href="data:${mime};base64,${b64}" preserveAspectRatio="xMidYMid slice"/>`;
|
|
635
637
|
}
|
|
636
638
|
}
|
|
@@ -658,7 +660,7 @@ async function backgroundToSvg(slideDoc, masterDoc, layoutDoc, imageCache, maste
|
|
|
658
660
|
*/
|
|
659
661
|
export async function renderSlideToSvg(slideIndex, renderer) {
|
|
660
662
|
const { _files: files, slidePaths, slideSize, themeColors, themeData,
|
|
661
|
-
masterDoc, masterRels
|
|
663
|
+
masterDoc, masterRels } = renderer;
|
|
662
664
|
|
|
663
665
|
if (slideIndex < 0 || slideIndex >= slidePaths.length) throw new Error('Slide index out of range');
|
|
664
666
|
|
|
@@ -675,12 +677,6 @@ export async function renderSlideToSvg(slideIndex, renderer) {
|
|
|
675
677
|
? parseXml(new TextDecoder().decode(files[layoutRel.fullPath])) : null;
|
|
676
678
|
const layoutRels = layoutRel ? await getRels(files, layoutRel.fullPath) : {};
|
|
677
679
|
|
|
678
|
-
// Image cache
|
|
679
|
-
const { loadImages } = await import('./render.js');
|
|
680
|
-
const slideImages = await loadImages(files, slideRels);
|
|
681
|
-
const layoutImages = layoutRel ? await loadImages(files, layoutRels) : {};
|
|
682
|
-
const allImages = { ...masterImages, ...layoutImages, ...slideImages };
|
|
683
|
-
|
|
684
680
|
// Slide dimensions in px (96 dpi)
|
|
685
681
|
const W = slideSize.cx / 914400 * 96;
|
|
686
682
|
const H = slideSize.cy / 914400 * 96;
|
|
@@ -688,7 +684,7 @@ export async function renderSlideToSvg(slideIndex, renderer) {
|
|
|
688
684
|
const defs = [];
|
|
689
685
|
|
|
690
686
|
// Background
|
|
691
|
-
const bgSvg = await backgroundToSvg(slideDoc, masterDoc, layoutDoc,
|
|
687
|
+
const bgSvg = await backgroundToSvg(slideDoc, masterDoc, layoutDoc, files,
|
|
692
688
|
masterRels, themeColors, W, H, defs);
|
|
693
689
|
|
|
694
690
|
// Master / layout decorative shapes
|
|
@@ -697,11 +693,11 @@ export async function renderSlideToSvg(slideIndex, renderer) {
|
|
|
697
693
|
const slideTree = g1(g1(slideDoc, 'cSld'), 'spTree');
|
|
698
694
|
|
|
699
695
|
const masterSvg = masterTree
|
|
700
|
-
? await shapesToSvg(masterTree, masterRels,
|
|
696
|
+
? await shapesToSvg(masterTree, masterRels, files, themeColors, themeData, defs) : '';
|
|
701
697
|
const layoutSvg = layoutTree
|
|
702
|
-
? await shapesToSvg(layoutTree, layoutRels,
|
|
698
|
+
? await shapesToSvg(layoutTree, layoutRels, files, themeColors, themeData, defs) : '';
|
|
703
699
|
const slideSvg = slideTree
|
|
704
|
-
? await shapesToSvg(slideTree, slideRels,
|
|
700
|
+
? await shapesToSvg(slideTree, slideRels, files, themeColors, themeData, defs) : '';
|
|
705
701
|
|
|
706
702
|
const defsBlock = defs.length ? `<defs>${defs.join('\n')}</defs>` : '';
|
|
707
703
|
|