pptx-glimpse 1.0.0 → 1.0.1
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/index.cjs +77 -14
- package/dist/index.js +77 -14
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,9 @@ __export(index_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
|
+
// src/converter.ts
|
|
39
|
+
var import_node_fs2 = require("fs");
|
|
40
|
+
|
|
38
41
|
// src/font/font-mapping.ts
|
|
39
42
|
var DEFAULT_FONT_MAPPING = {
|
|
40
43
|
// ラテン文字フォント
|
|
@@ -1801,6 +1804,10 @@ async function svgToPng(svgString, options) {
|
|
|
1801
1804
|
} else if (options?.height) {
|
|
1802
1805
|
resvgOptions.fitTo = { mode: "height", value: options.height };
|
|
1803
1806
|
}
|
|
1807
|
+
const fontBuffers = options?.fontBuffers;
|
|
1808
|
+
if (fontBuffers && fontBuffers.length > 0) {
|
|
1809
|
+
resvgOptions.font = { fontBuffers };
|
|
1810
|
+
}
|
|
1804
1811
|
const resvg = new import_resvg_wasm.Resvg(svgString, resvgOptions);
|
|
1805
1812
|
const rendered = resvg.render();
|
|
1806
1813
|
return {
|
|
@@ -2760,7 +2767,7 @@ function parseChartTitle(titleNode) {
|
|
|
2760
2767
|
function parseLegend(legendNode) {
|
|
2761
2768
|
if (!legendNode) return null;
|
|
2762
2769
|
const legendPos = legendNode.legendPos;
|
|
2763
|
-
const pos = legendPos?.["@_val"] ?? "
|
|
2770
|
+
const pos = legendPos?.["@_val"] ?? "r";
|
|
2764
2771
|
return { position: pos };
|
|
2765
2772
|
}
|
|
2766
2773
|
|
|
@@ -5417,6 +5424,7 @@ var DEFAULT_SERIES_COLORS = [
|
|
|
5417
5424
|
{ hex: "#5B9BD5", alpha: 1 },
|
|
5418
5425
|
{ hex: "#70AD47", alpha: 1 }
|
|
5419
5426
|
];
|
|
5427
|
+
var LEGEND_SIDE_WIDTH = 100;
|
|
5420
5428
|
function renderChart(element) {
|
|
5421
5429
|
const { transform, chart } = element;
|
|
5422
5430
|
const w = emuToPixels(transform.extentWidth);
|
|
@@ -5435,6 +5443,8 @@ function renderChart(element) {
|
|
|
5435
5443
|
if (chart.legend) {
|
|
5436
5444
|
if (chart.legend.position === "b") margin.bottom = 50;
|
|
5437
5445
|
else if (chart.legend.position === "t") margin.top += 20;
|
|
5446
|
+
else if (chart.legend.position === "r") margin.right = LEGEND_SIDE_WIDTH;
|
|
5447
|
+
else if (chart.legend.position === "l") margin.left += LEGEND_SIDE_WIDTH;
|
|
5438
5448
|
}
|
|
5439
5449
|
const plotX = margin.left;
|
|
5440
5450
|
const plotY = margin.top;
|
|
@@ -6189,18 +6199,34 @@ function renderLegend(chart, chartW, chartH, position) {
|
|
|
6189
6199
|
color: s.color
|
|
6190
6200
|
}));
|
|
6191
6201
|
if (entries2.length === 0) return "";
|
|
6192
|
-
const
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6202
|
+
const ENTRY_HEIGHT = 20;
|
|
6203
|
+
if (position === "r" || position === "l") {
|
|
6204
|
+
const totalH = entries2.length * ENTRY_HEIGHT;
|
|
6205
|
+
const startY = Math.max((chartH - totalH) / 2, 5);
|
|
6206
|
+
const legendX = position === "r" ? chartW - LEGEND_SIDE_WIDTH + 5 : 5;
|
|
6207
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
6208
|
+
const ey = startY + i * ENTRY_HEIGHT;
|
|
6209
|
+
parts.push(
|
|
6210
|
+
`<rect x="${round(legendX)}" y="${round(ey)}" width="12" height="12" ${fillAttr(entries2[i].color)}/>`
|
|
6211
|
+
);
|
|
6212
|
+
parts.push(
|
|
6213
|
+
`<text x="${round(legendX + 16)}" y="${round(ey + 10)}" font-size="10" fill="#595959">${escapeXml(entries2[i].label)}</text>`
|
|
6214
|
+
);
|
|
6215
|
+
}
|
|
6216
|
+
} else {
|
|
6217
|
+
const entryWidth = 80;
|
|
6218
|
+
const totalWidth = entries2.length * entryWidth;
|
|
6219
|
+
const startX = Math.max((chartW - totalWidth) / 2, 5);
|
|
6220
|
+
const legendY = position === "t" ? 25 : chartH - 15;
|
|
6221
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
6222
|
+
const ex = startX + i * entryWidth;
|
|
6223
|
+
parts.push(
|
|
6224
|
+
`<rect x="${round(ex)}" y="${round(legendY - 6)}" width="12" height="12" ${fillAttr(entries2[i].color)}/>`
|
|
6225
|
+
);
|
|
6226
|
+
parts.push(
|
|
6227
|
+
`<text x="${round(ex + 16)}" y="${round(legendY + 4)}" font-size="10" fill="#595959">${escapeXml(entries2[i].label)}</text>`
|
|
6228
|
+
);
|
|
6229
|
+
}
|
|
6204
6230
|
}
|
|
6205
6231
|
return parts.join("");
|
|
6206
6232
|
}
|
|
@@ -9234,13 +9260,50 @@ async function convertPptxToSvg(input, options) {
|
|
|
9234
9260
|
resetScriptFonts();
|
|
9235
9261
|
}
|
|
9236
9262
|
}
|
|
9263
|
+
var cachedFontBuffers = null;
|
|
9264
|
+
var cachedFontBuffersKey = null;
|
|
9265
|
+
var MAX_TOTAL_FONT_BUFFER_BYTES = 100 * 1024 * 1024;
|
|
9266
|
+
function loadFontBuffers(fontDirs, skipSystemFonts) {
|
|
9267
|
+
const key = `${(fontDirs ?? []).join("\0")}
|
|
9268
|
+
${skipSystemFonts ?? false}`;
|
|
9269
|
+
if (cachedFontBuffers !== null && cachedFontBuffersKey === key) {
|
|
9270
|
+
return cachedFontBuffers;
|
|
9271
|
+
}
|
|
9272
|
+
const allPaths = collectFontFilePaths(fontDirs, skipSystemFonts);
|
|
9273
|
+
const ttfOtfPaths = allPaths.filter((p) => {
|
|
9274
|
+
const lower = p.toLowerCase();
|
|
9275
|
+
return lower.endsWith(".ttf") || lower.endsWith(".otf");
|
|
9276
|
+
});
|
|
9277
|
+
const pathsWithSize = [];
|
|
9278
|
+
for (const p of ttfOtfPaths) {
|
|
9279
|
+
try {
|
|
9280
|
+
pathsWithSize.push({ path: p, size: (0, import_node_fs2.statSync)(p).size });
|
|
9281
|
+
} catch {
|
|
9282
|
+
}
|
|
9283
|
+
}
|
|
9284
|
+
pathsWithSize.sort((a, b) => a.size - b.size);
|
|
9285
|
+
const buffers = [];
|
|
9286
|
+
let totalSize = 0;
|
|
9287
|
+
for (const { path, size } of pathsWithSize) {
|
|
9288
|
+
if (totalSize + size > MAX_TOTAL_FONT_BUFFER_BYTES) break;
|
|
9289
|
+
try {
|
|
9290
|
+
buffers.push(new Uint8Array((0, import_node_fs2.readFileSync)(path)));
|
|
9291
|
+
totalSize += size;
|
|
9292
|
+
} catch {
|
|
9293
|
+
}
|
|
9294
|
+
}
|
|
9295
|
+
cachedFontBuffers = buffers;
|
|
9296
|
+
cachedFontBuffersKey = key;
|
|
9297
|
+
return buffers;
|
|
9298
|
+
}
|
|
9237
9299
|
async function convertPptxToPng(input, options) {
|
|
9238
9300
|
const svgResults = await convertPptxToSvg(input, options);
|
|
9239
9301
|
const width = options?.width ?? DEFAULT_OUTPUT_WIDTH;
|
|
9240
9302
|
const height = options?.height;
|
|
9303
|
+
const fontBuffers = loadFontBuffers(options?.fontDirs, options?.skipSystemFonts);
|
|
9241
9304
|
const results = [];
|
|
9242
9305
|
for (const { slideNumber, svg } of svgResults) {
|
|
9243
|
-
const pngResult = await svgToPng(svg, { width, height });
|
|
9306
|
+
const pngResult = await svgToPng(svg, { width, height, fontBuffers });
|
|
9244
9307
|
results.push({
|
|
9245
9308
|
slideNumber,
|
|
9246
9309
|
png: pngResult.png,
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/converter.ts
|
|
2
|
+
import { readFileSync, statSync } from "fs";
|
|
3
|
+
|
|
1
4
|
// src/font/font-mapping.ts
|
|
2
5
|
var DEFAULT_FONT_MAPPING = {
|
|
3
6
|
// ラテン文字フォント
|
|
@@ -1763,6 +1766,10 @@ async function svgToPng(svgString, options) {
|
|
|
1763
1766
|
} else if (options?.height) {
|
|
1764
1767
|
resvgOptions.fitTo = { mode: "height", value: options.height };
|
|
1765
1768
|
}
|
|
1769
|
+
const fontBuffers = options?.fontBuffers;
|
|
1770
|
+
if (fontBuffers && fontBuffers.length > 0) {
|
|
1771
|
+
resvgOptions.font = { fontBuffers };
|
|
1772
|
+
}
|
|
1766
1773
|
const resvg = new Resvg(svgString, resvgOptions);
|
|
1767
1774
|
const rendered = resvg.render();
|
|
1768
1775
|
return {
|
|
@@ -2722,7 +2729,7 @@ function parseChartTitle(titleNode) {
|
|
|
2722
2729
|
function parseLegend(legendNode) {
|
|
2723
2730
|
if (!legendNode) return null;
|
|
2724
2731
|
const legendPos = legendNode.legendPos;
|
|
2725
|
-
const pos = legendPos?.["@_val"] ?? "
|
|
2732
|
+
const pos = legendPos?.["@_val"] ?? "r";
|
|
2726
2733
|
return { position: pos };
|
|
2727
2734
|
}
|
|
2728
2735
|
|
|
@@ -5379,6 +5386,7 @@ var DEFAULT_SERIES_COLORS = [
|
|
|
5379
5386
|
{ hex: "#5B9BD5", alpha: 1 },
|
|
5380
5387
|
{ hex: "#70AD47", alpha: 1 }
|
|
5381
5388
|
];
|
|
5389
|
+
var LEGEND_SIDE_WIDTH = 100;
|
|
5382
5390
|
function renderChart(element) {
|
|
5383
5391
|
const { transform, chart } = element;
|
|
5384
5392
|
const w = emuToPixels(transform.extentWidth);
|
|
@@ -5397,6 +5405,8 @@ function renderChart(element) {
|
|
|
5397
5405
|
if (chart.legend) {
|
|
5398
5406
|
if (chart.legend.position === "b") margin.bottom = 50;
|
|
5399
5407
|
else if (chart.legend.position === "t") margin.top += 20;
|
|
5408
|
+
else if (chart.legend.position === "r") margin.right = LEGEND_SIDE_WIDTH;
|
|
5409
|
+
else if (chart.legend.position === "l") margin.left += LEGEND_SIDE_WIDTH;
|
|
5400
5410
|
}
|
|
5401
5411
|
const plotX = margin.left;
|
|
5402
5412
|
const plotY = margin.top;
|
|
@@ -6151,18 +6161,34 @@ function renderLegend(chart, chartW, chartH, position) {
|
|
|
6151
6161
|
color: s.color
|
|
6152
6162
|
}));
|
|
6153
6163
|
if (entries2.length === 0) return "";
|
|
6154
|
-
const
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6164
|
+
const ENTRY_HEIGHT = 20;
|
|
6165
|
+
if (position === "r" || position === "l") {
|
|
6166
|
+
const totalH = entries2.length * ENTRY_HEIGHT;
|
|
6167
|
+
const startY = Math.max((chartH - totalH) / 2, 5);
|
|
6168
|
+
const legendX = position === "r" ? chartW - LEGEND_SIDE_WIDTH + 5 : 5;
|
|
6169
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
6170
|
+
const ey = startY + i * ENTRY_HEIGHT;
|
|
6171
|
+
parts.push(
|
|
6172
|
+
`<rect x="${round(legendX)}" y="${round(ey)}" width="12" height="12" ${fillAttr(entries2[i].color)}/>`
|
|
6173
|
+
);
|
|
6174
|
+
parts.push(
|
|
6175
|
+
`<text x="${round(legendX + 16)}" y="${round(ey + 10)}" font-size="10" fill="#595959">${escapeXml(entries2[i].label)}</text>`
|
|
6176
|
+
);
|
|
6177
|
+
}
|
|
6178
|
+
} else {
|
|
6179
|
+
const entryWidth = 80;
|
|
6180
|
+
const totalWidth = entries2.length * entryWidth;
|
|
6181
|
+
const startX = Math.max((chartW - totalWidth) / 2, 5);
|
|
6182
|
+
const legendY = position === "t" ? 25 : chartH - 15;
|
|
6183
|
+
for (let i = 0; i < entries2.length; i++) {
|
|
6184
|
+
const ex = startX + i * entryWidth;
|
|
6185
|
+
parts.push(
|
|
6186
|
+
`<rect x="${round(ex)}" y="${round(legendY - 6)}" width="12" height="12" ${fillAttr(entries2[i].color)}/>`
|
|
6187
|
+
);
|
|
6188
|
+
parts.push(
|
|
6189
|
+
`<text x="${round(ex + 16)}" y="${round(legendY + 4)}" font-size="10" fill="#595959">${escapeXml(entries2[i].label)}</text>`
|
|
6190
|
+
);
|
|
6191
|
+
}
|
|
6166
6192
|
}
|
|
6167
6193
|
return parts.join("");
|
|
6168
6194
|
}
|
|
@@ -9196,13 +9222,50 @@ async function convertPptxToSvg(input, options) {
|
|
|
9196
9222
|
resetScriptFonts();
|
|
9197
9223
|
}
|
|
9198
9224
|
}
|
|
9225
|
+
var cachedFontBuffers = null;
|
|
9226
|
+
var cachedFontBuffersKey = null;
|
|
9227
|
+
var MAX_TOTAL_FONT_BUFFER_BYTES = 100 * 1024 * 1024;
|
|
9228
|
+
function loadFontBuffers(fontDirs, skipSystemFonts) {
|
|
9229
|
+
const key = `${(fontDirs ?? []).join("\0")}
|
|
9230
|
+
${skipSystemFonts ?? false}`;
|
|
9231
|
+
if (cachedFontBuffers !== null && cachedFontBuffersKey === key) {
|
|
9232
|
+
return cachedFontBuffers;
|
|
9233
|
+
}
|
|
9234
|
+
const allPaths = collectFontFilePaths(fontDirs, skipSystemFonts);
|
|
9235
|
+
const ttfOtfPaths = allPaths.filter((p) => {
|
|
9236
|
+
const lower = p.toLowerCase();
|
|
9237
|
+
return lower.endsWith(".ttf") || lower.endsWith(".otf");
|
|
9238
|
+
});
|
|
9239
|
+
const pathsWithSize = [];
|
|
9240
|
+
for (const p of ttfOtfPaths) {
|
|
9241
|
+
try {
|
|
9242
|
+
pathsWithSize.push({ path: p, size: statSync(p).size });
|
|
9243
|
+
} catch {
|
|
9244
|
+
}
|
|
9245
|
+
}
|
|
9246
|
+
pathsWithSize.sort((a, b) => a.size - b.size);
|
|
9247
|
+
const buffers = [];
|
|
9248
|
+
let totalSize = 0;
|
|
9249
|
+
for (const { path, size } of pathsWithSize) {
|
|
9250
|
+
if (totalSize + size > MAX_TOTAL_FONT_BUFFER_BYTES) break;
|
|
9251
|
+
try {
|
|
9252
|
+
buffers.push(new Uint8Array(readFileSync(path)));
|
|
9253
|
+
totalSize += size;
|
|
9254
|
+
} catch {
|
|
9255
|
+
}
|
|
9256
|
+
}
|
|
9257
|
+
cachedFontBuffers = buffers;
|
|
9258
|
+
cachedFontBuffersKey = key;
|
|
9259
|
+
return buffers;
|
|
9260
|
+
}
|
|
9199
9261
|
async function convertPptxToPng(input, options) {
|
|
9200
9262
|
const svgResults = await convertPptxToSvg(input, options);
|
|
9201
9263
|
const width = options?.width ?? DEFAULT_OUTPUT_WIDTH;
|
|
9202
9264
|
const height = options?.height;
|
|
9265
|
+
const fontBuffers = loadFontBuffers(options?.fontDirs, options?.skipSystemFonts);
|
|
9203
9266
|
const results = [];
|
|
9204
9267
|
for (const { slideNumber, svg } of svgResults) {
|
|
9205
|
-
const pngResult = await svgToPng(svg, { width, height });
|
|
9268
|
+
const pngResult = await svgToPng(svg, { width, height, fontBuffers });
|
|
9206
9269
|
results.push({
|
|
9207
9270
|
slideNumber,
|
|
9208
9271
|
png: pngResult.png,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pptx-glimpse",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A lightweight JavaScript library for rendering PowerPoint (.pptx) files as SVG or PNG in Node.js. No LibreOffice required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@changesets/cli": "^2.29.8",
|
|
47
47
|
"@types/node": "^25.2.2",
|
|
48
48
|
"@types/ws": "^8.18.1",
|
|
49
|
-
"@vitest/coverage-v8": "
|
|
49
|
+
"@vitest/coverage-v8": "4.1.0",
|
|
50
50
|
"eslint": "^9.0.0",
|
|
51
51
|
"eslint-config-prettier": "^10.0.0",
|
|
52
52
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"tsx": "^4.21.0",
|
|
60
60
|
"typescript": "^5.9.3",
|
|
61
61
|
"typescript-eslint": "^8.55.0",
|
|
62
|
-
"vitest": "^
|
|
62
|
+
"vitest": "^4.1.0",
|
|
63
63
|
"ws": "^8.19.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|