pptx-glimpse 0.11.0 → 0.11.2
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 +102 -23
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +102 -23
- package/package.json +29 -30
package/dist/index.cjs
CHANGED
|
@@ -1163,16 +1163,35 @@ var OpentypeTextMeasurer = class {
|
|
|
1163
1163
|
return measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa);
|
|
1164
1164
|
}
|
|
1165
1165
|
const fontSizePx = fontSizePt * PX_PER_PT2;
|
|
1166
|
+
const latinFontResolved = latinFont ?? fallbackFont;
|
|
1167
|
+
const eaFontResolved = eaFont ?? fallbackFont;
|
|
1168
|
+
const boldLatinFont = bold ? this.resolveBoldFont(fontFamily) : null;
|
|
1169
|
+
const latinGlyphCache = /* @__PURE__ */ new Map();
|
|
1170
|
+
const eaGlyphCache = eaFontResolved !== latinFontResolved ? /* @__PURE__ */ new Map() : latinGlyphCache;
|
|
1171
|
+
const boldGlyphCache = /* @__PURE__ */ new Map();
|
|
1166
1172
|
let totalWidth = 0;
|
|
1167
1173
|
for (const char of text) {
|
|
1168
1174
|
const codePoint = char.codePointAt(0);
|
|
1169
1175
|
const isEa = isCjkCodePoint(codePoint);
|
|
1170
|
-
const font = isEa ?
|
|
1176
|
+
const font = isEa ? eaFontResolved : latinFontResolved;
|
|
1177
|
+
const cache = isEa ? eaGlyphCache : latinGlyphCache;
|
|
1178
|
+
if (!cache.has(char)) {
|
|
1179
|
+
cache.set(char, font.stringToGlyphs(char)[0]);
|
|
1180
|
+
}
|
|
1171
1181
|
const scale = fontSizePx / font.unitsPerEm;
|
|
1172
|
-
const
|
|
1173
|
-
let charWidth = (
|
|
1182
|
+
const glyph = cache.get(char);
|
|
1183
|
+
let charWidth = (glyph?.advanceWidth ?? font.unitsPerEm * 0.6) * scale;
|
|
1174
1184
|
if (bold && !isEa) {
|
|
1175
|
-
|
|
1185
|
+
if (boldLatinFont) {
|
|
1186
|
+
if (!boldGlyphCache.has(char)) {
|
|
1187
|
+
boldGlyphCache.set(char, boldLatinFont.stringToGlyphs(char)[0]);
|
|
1188
|
+
}
|
|
1189
|
+
const boldGlyph = boldGlyphCache.get(char);
|
|
1190
|
+
const boldScale = fontSizePx / boldLatinFont.unitsPerEm;
|
|
1191
|
+
charWidth = (boldGlyph?.advanceWidth ?? boldLatinFont.unitsPerEm * 0.6) * boldScale;
|
|
1192
|
+
} else {
|
|
1193
|
+
charWidth *= BOLD_FACTOR2;
|
|
1194
|
+
}
|
|
1176
1195
|
}
|
|
1177
1196
|
totalWidth += charWidth;
|
|
1178
1197
|
}
|
|
@@ -1188,6 +1207,24 @@ var OpentypeTextMeasurer = class {
|
|
|
1188
1207
|
if (!font) return 1;
|
|
1189
1208
|
return font.ascender / font.unitsPerEm;
|
|
1190
1209
|
}
|
|
1210
|
+
resolveBoldFont(name) {
|
|
1211
|
+
if (!name) return null;
|
|
1212
|
+
const bases = [name];
|
|
1213
|
+
const mappedBase = getCurrentMappedFont(name);
|
|
1214
|
+
if (mappedBase && mappedBase !== name) bases.push(mappedBase);
|
|
1215
|
+
for (const base of bases) {
|
|
1216
|
+
for (const boldName of [`${base} Bold`, `${base}-Bold`]) {
|
|
1217
|
+
const direct = this.fonts.get(boldName);
|
|
1218
|
+
if (direct) return direct;
|
|
1219
|
+
const mapped = getCurrentMappedFont(boldName);
|
|
1220
|
+
if (mapped) {
|
|
1221
|
+
const mappedFont = this.fonts.get(mapped);
|
|
1222
|
+
if (mappedFont) return mappedFont;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
return null;
|
|
1227
|
+
}
|
|
1191
1228
|
resolveFont(name) {
|
|
1192
1229
|
if (!name) return null;
|
|
1193
1230
|
const direct = this.fonts.get(name);
|
|
@@ -1883,6 +1920,8 @@ var ColorResolver = class {
|
|
|
1883
1920
|
this.colorScheme = colorScheme;
|
|
1884
1921
|
this.colorMap = colorMap;
|
|
1885
1922
|
}
|
|
1923
|
+
colorScheme;
|
|
1924
|
+
colorMap;
|
|
1886
1925
|
resolve(colorNode) {
|
|
1887
1926
|
if (!colorNode) return null;
|
|
1888
1927
|
if (colorNode.srgbClr) {
|
|
@@ -1962,25 +2001,48 @@ var LazyMediaMap = class {
|
|
|
1962
2001
|
return data;
|
|
1963
2002
|
}
|
|
1964
2003
|
};
|
|
2004
|
+
var LazyXmlMap = class {
|
|
2005
|
+
rawInput;
|
|
2006
|
+
cache = /* @__PURE__ */ new Map();
|
|
2007
|
+
entryIndex;
|
|
2008
|
+
constructor(rawInput, xmlEntryNames) {
|
|
2009
|
+
this.rawInput = rawInput;
|
|
2010
|
+
this.entryIndex = xmlEntryNames;
|
|
2011
|
+
}
|
|
2012
|
+
has(path) {
|
|
2013
|
+
return this.entryIndex.has(path);
|
|
2014
|
+
}
|
|
2015
|
+
get(path) {
|
|
2016
|
+
if (!this.entryIndex.has(path)) return void 0;
|
|
2017
|
+
const cached = this.cache.get(path);
|
|
2018
|
+
if (cached !== void 0) return cached;
|
|
2019
|
+
const result = (0, import_fflate.unzipSync)(this.rawInput, {
|
|
2020
|
+
filter: (file) => file.name === path
|
|
2021
|
+
});
|
|
2022
|
+
const data = result[path];
|
|
2023
|
+
if (data) {
|
|
2024
|
+
const str = (0, import_fflate.strFromU8)(data);
|
|
2025
|
+
this.cache.set(path, str);
|
|
2026
|
+
return str;
|
|
2027
|
+
}
|
|
2028
|
+
return void 0;
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
1965
2031
|
function readPptx(input) {
|
|
1966
2032
|
const rawInput = new Uint8Array(input);
|
|
2033
|
+
const xmlEntryNames = /* @__PURE__ */ new Set();
|
|
1967
2034
|
const mediaEntryNames = /* @__PURE__ */ new Set();
|
|
1968
|
-
|
|
2035
|
+
(0, import_fflate.unzipSync)(rawInput, {
|
|
1969
2036
|
filter: (file) => {
|
|
1970
2037
|
if (file.name.startsWith("ppt/media/")) {
|
|
1971
2038
|
mediaEntryNames.add(file.name);
|
|
1972
|
-
|
|
2039
|
+
} else if (file.name.endsWith(".xml") || file.name.endsWith(".rels") || file.name === "[Content_Types].xml") {
|
|
2040
|
+
xmlEntryNames.add(file.name);
|
|
1973
2041
|
}
|
|
1974
|
-
return
|
|
2042
|
+
return false;
|
|
1975
2043
|
}
|
|
1976
2044
|
});
|
|
1977
|
-
const files =
|
|
1978
|
-
for (const [relativePath, data] of Object.entries(unzipped)) {
|
|
1979
|
-
if (relativePath.endsWith("/")) continue;
|
|
1980
|
-
if (relativePath.endsWith(".xml") || relativePath.endsWith(".rels") || relativePath === "[Content_Types].xml") {
|
|
1981
|
-
files.set(relativePath, (0, import_fflate.strFromU8)(data));
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
2045
|
+
const files = new LazyXmlMap(rawInput, xmlEntryNames);
|
|
1984
2046
|
const media = new LazyMediaMap(rawInput, mediaEntryNames);
|
|
1985
2047
|
return { files, media };
|
|
1986
2048
|
}
|
|
@@ -3176,6 +3238,10 @@ function createDefaultBorders() {
|
|
|
3176
3238
|
|
|
3177
3239
|
// src/parser/slide-parser.ts
|
|
3178
3240
|
var SHAPE_TAGS = /* @__PURE__ */ new Set(["sp", "pic", "cxnSp", "grpSp", "graphicFrame"]);
|
|
3241
|
+
var SMARTART_DIAGRAM_URIS = /* @__PURE__ */ new Set([
|
|
3242
|
+
"http://schemas.openxmlformats.org/drawingml/2006/diagram",
|
|
3243
|
+
"http://purl.oclc.org/ooxml/drawingml/diagram"
|
|
3244
|
+
]);
|
|
3179
3245
|
function navigateOrdered(ordered, path) {
|
|
3180
3246
|
let current = ordered;
|
|
3181
3247
|
for (const key of path) {
|
|
@@ -3723,7 +3789,7 @@ function parseGraphicFrame(gf, rels, slidePath, archive, colorResolver, fontSche
|
|
|
3723
3789
|
if (!tableData) return null;
|
|
3724
3790
|
return { type: "table", transform, table: tableData };
|
|
3725
3791
|
}
|
|
3726
|
-
if (graphicData["@_uri"]
|
|
3792
|
+
if (SMARTART_DIAGRAM_URIS.has(graphicData["@_uri"])) {
|
|
3727
3793
|
return parseSmartArt(
|
|
3728
3794
|
graphicData,
|
|
3729
3795
|
transform,
|
|
@@ -4156,7 +4222,7 @@ function extractMathText(node) {
|
|
|
4156
4222
|
if (key === "t") {
|
|
4157
4223
|
if (typeof value === "object" && value !== null) {
|
|
4158
4224
|
text += value["#text"] ?? "";
|
|
4159
|
-
} else if (value
|
|
4225
|
+
} else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4160
4226
|
text += String(value);
|
|
4161
4227
|
}
|
|
4162
4228
|
} else if (typeof value === "object" && value !== null) {
|
|
@@ -4175,7 +4241,7 @@ function extractTextContent(node) {
|
|
|
4175
4241
|
if (typeof text === "object") {
|
|
4176
4242
|
return text["#text"] ?? "";
|
|
4177
4243
|
}
|
|
4178
|
-
return text
|
|
4244
|
+
return typeof text === "string" || typeof text === "number" || typeof text === "boolean" ? String(text) : "";
|
|
4179
4245
|
}
|
|
4180
4246
|
function parseParagraph(p, colorResolver, rels, fontScheme, lstStyle, orderedPChildren) {
|
|
4181
4247
|
const pPr = p.pPr;
|
|
@@ -6864,7 +6930,7 @@ var presetGeometries = {
|
|
|
6864
6930
|
const shaftEnd = h - headLength;
|
|
6865
6931
|
return `<polygon points="${bodyLeft},0 ${bodyRight},0 ${bodyRight},${shaftEnd} ${w},${shaftEnd} ${w / 2},${h} 0,${shaftEnd} ${bodyLeft},${shaftEnd}"/>`;
|
|
6866
6932
|
},
|
|
6867
|
-
line: () => ""
|
|
6933
|
+
line: (w, h) => `<line x1="0" y1="0" x2="${w}" y2="${h}"/>`,
|
|
6868
6934
|
// =====================
|
|
6869
6935
|
// Connector shapes
|
|
6870
6936
|
// =====================
|
|
@@ -8475,10 +8541,23 @@ function computePathLineX(alignment, textStartX, effectiveTextWidth, width, marg
|
|
|
8475
8541
|
return textStartX;
|
|
8476
8542
|
}
|
|
8477
8543
|
}
|
|
8478
|
-
function measureLineWidth(segments, defaultFontSize, fontScale) {
|
|
8544
|
+
function measureLineWidth(segments, defaultFontSize, fontScale, fontResolver) {
|
|
8479
8545
|
let totalWidth = 0;
|
|
8546
|
+
const jpanFallback = fontResolver ? getJpanFallbackFont() : null;
|
|
8480
8547
|
for (const seg of segments) {
|
|
8481
8548
|
const fontSize = (seg.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
8549
|
+
if (fontResolver) {
|
|
8550
|
+
const fontSizePx = fontSize * PX_PER_PT3;
|
|
8551
|
+
const font = fontResolver.resolveFont(
|
|
8552
|
+
seg.properties.fontFamily,
|
|
8553
|
+
seg.properties.fontFamilyEa,
|
|
8554
|
+
jpanFallback
|
|
8555
|
+
);
|
|
8556
|
+
if (font) {
|
|
8557
|
+
totalWidth += font.getAdvanceWidth(seg.text, fontSizePx);
|
|
8558
|
+
continue;
|
|
8559
|
+
}
|
|
8560
|
+
}
|
|
8482
8561
|
totalWidth += getTextMeasurer().measureTextWidth(
|
|
8483
8562
|
seg.text,
|
|
8484
8563
|
fontSize,
|
|
@@ -8534,7 +8613,7 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
8534
8613
|
const processSegment = (segText, fontFamily, fontFamilyEa) => {
|
|
8535
8614
|
if (segText.length === 0) return;
|
|
8536
8615
|
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback);
|
|
8537
|
-
const segWidth = getTextMeasurer().measureTextWidth(
|
|
8616
|
+
const segWidth = font ? font.getAdvanceWidth(segText, fontSizePx) : getTextMeasurer().measureTextWidth(
|
|
8538
8617
|
segText,
|
|
8539
8618
|
fontSize,
|
|
8540
8619
|
props.bold,
|
|
@@ -8559,7 +8638,7 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
8559
8638
|
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback);
|
|
8560
8639
|
const fillAttrs = buildPathFillAttrs(props);
|
|
8561
8640
|
for (const char of segText) {
|
|
8562
|
-
const charWidth = getTextMeasurer().measureTextWidth(
|
|
8641
|
+
const charWidth = font ? font.getAdvanceWidth(char, fontSizePx) : getTextMeasurer().measureTextWidth(
|
|
8563
8642
|
char,
|
|
8564
8643
|
fontSize,
|
|
8565
8644
|
props.bold,
|
|
@@ -8731,7 +8810,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
8731
8810
|
if (!isFirstLine) {
|
|
8732
8811
|
currentY += lineNaturalHeightPt * PX_PER_PT3 * getLineSpacing(para, lnSpcReduction) + lineGapPx;
|
|
8733
8812
|
}
|
|
8734
|
-
const lineWidth = measureLineWidth(line.segments, defaultFontSize, fontScale);
|
|
8813
|
+
const lineWidth = measureLineWidth(line.segments, defaultFontSize, fontScale, fontResolver);
|
|
8735
8814
|
const lineStartX = computePathLineX(
|
|
8736
8815
|
para.properties.alignment,
|
|
8737
8816
|
textStartX,
|
|
@@ -8780,7 +8859,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
8780
8859
|
currentY += naturalHeightPt * PX_PER_PT3 * getLineSpacing(para, lnSpcReduction) + paragraphGapPx;
|
|
8781
8860
|
}
|
|
8782
8861
|
const runsAsSegments = para.runs.filter((r) => r.text.length > 0).map((r) => ({ text: r.text, properties: r.properties }));
|
|
8783
|
-
const lineWidth = measureLineWidth(runsAsSegments, defaultFontSize, fontScale);
|
|
8862
|
+
const lineWidth = measureLineWidth(runsAsSegments, defaultFontSize, fontScale, fontResolver);
|
|
8784
8863
|
const lineStartX = computePathLineX(
|
|
8785
8864
|
para.properties.alignment,
|
|
8786
8865
|
textStartX,
|
package/dist/index.d.cts
CHANGED
|
@@ -136,6 +136,7 @@ declare class OpentypeTextMeasurer implements TextMeasurer {
|
|
|
136
136
|
measureTextWidth(text: string, fontSizePt: number, bold: boolean, fontFamily?: string | null, fontFamilyEa?: string | null): number;
|
|
137
137
|
getLineHeightRatio(fontFamily?: string | null, fontFamilyEa?: string | null): number;
|
|
138
138
|
getAscenderRatio(fontFamily?: string | null, fontFamilyEa?: string | null): number;
|
|
139
|
+
private resolveBoldFont;
|
|
139
140
|
private resolveFont;
|
|
140
141
|
}
|
|
141
142
|
|
|
@@ -151,6 +152,7 @@ interface OpentypeFullFont {
|
|
|
151
152
|
ascender: number;
|
|
152
153
|
descender: number;
|
|
153
154
|
getPath(text: string, x: number, y: number, fontSize: number): OpentypePath;
|
|
155
|
+
getAdvanceWidth(text: string, fontSize: number): number;
|
|
154
156
|
}
|
|
155
157
|
interface TextPathFontResolver {
|
|
156
158
|
resolveFont(fontFamily: string | null | undefined, fontFamilyEa: string | null | undefined, jpanFallback?: string | null): OpentypeFullFont | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -136,6 +136,7 @@ declare class OpentypeTextMeasurer implements TextMeasurer {
|
|
|
136
136
|
measureTextWidth(text: string, fontSizePt: number, bold: boolean, fontFamily?: string | null, fontFamilyEa?: string | null): number;
|
|
137
137
|
getLineHeightRatio(fontFamily?: string | null, fontFamilyEa?: string | null): number;
|
|
138
138
|
getAscenderRatio(fontFamily?: string | null, fontFamilyEa?: string | null): number;
|
|
139
|
+
private resolveBoldFont;
|
|
139
140
|
private resolveFont;
|
|
140
141
|
}
|
|
141
142
|
|
|
@@ -151,6 +152,7 @@ interface OpentypeFullFont {
|
|
|
151
152
|
ascender: number;
|
|
152
153
|
descender: number;
|
|
153
154
|
getPath(text: string, x: number, y: number, fontSize: number): OpentypePath;
|
|
155
|
+
getAdvanceWidth(text: string, fontSize: number): number;
|
|
154
156
|
}
|
|
155
157
|
interface TextPathFontResolver {
|
|
156
158
|
resolveFont(fontFamily: string | null | undefined, fontFamilyEa: string | null | undefined, jpanFallback?: string | null): OpentypeFullFont | null;
|
package/dist/index.js
CHANGED
|
@@ -1126,16 +1126,35 @@ var OpentypeTextMeasurer = class {
|
|
|
1126
1126
|
return measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa);
|
|
1127
1127
|
}
|
|
1128
1128
|
const fontSizePx = fontSizePt * PX_PER_PT2;
|
|
1129
|
+
const latinFontResolved = latinFont ?? fallbackFont;
|
|
1130
|
+
const eaFontResolved = eaFont ?? fallbackFont;
|
|
1131
|
+
const boldLatinFont = bold ? this.resolveBoldFont(fontFamily) : null;
|
|
1132
|
+
const latinGlyphCache = /* @__PURE__ */ new Map();
|
|
1133
|
+
const eaGlyphCache = eaFontResolved !== latinFontResolved ? /* @__PURE__ */ new Map() : latinGlyphCache;
|
|
1134
|
+
const boldGlyphCache = /* @__PURE__ */ new Map();
|
|
1129
1135
|
let totalWidth = 0;
|
|
1130
1136
|
for (const char of text) {
|
|
1131
1137
|
const codePoint = char.codePointAt(0);
|
|
1132
1138
|
const isEa = isCjkCodePoint(codePoint);
|
|
1133
|
-
const font = isEa ?
|
|
1139
|
+
const font = isEa ? eaFontResolved : latinFontResolved;
|
|
1140
|
+
const cache = isEa ? eaGlyphCache : latinGlyphCache;
|
|
1141
|
+
if (!cache.has(char)) {
|
|
1142
|
+
cache.set(char, font.stringToGlyphs(char)[0]);
|
|
1143
|
+
}
|
|
1134
1144
|
const scale = fontSizePx / font.unitsPerEm;
|
|
1135
|
-
const
|
|
1136
|
-
let charWidth = (
|
|
1145
|
+
const glyph = cache.get(char);
|
|
1146
|
+
let charWidth = (glyph?.advanceWidth ?? font.unitsPerEm * 0.6) * scale;
|
|
1137
1147
|
if (bold && !isEa) {
|
|
1138
|
-
|
|
1148
|
+
if (boldLatinFont) {
|
|
1149
|
+
if (!boldGlyphCache.has(char)) {
|
|
1150
|
+
boldGlyphCache.set(char, boldLatinFont.stringToGlyphs(char)[0]);
|
|
1151
|
+
}
|
|
1152
|
+
const boldGlyph = boldGlyphCache.get(char);
|
|
1153
|
+
const boldScale = fontSizePx / boldLatinFont.unitsPerEm;
|
|
1154
|
+
charWidth = (boldGlyph?.advanceWidth ?? boldLatinFont.unitsPerEm * 0.6) * boldScale;
|
|
1155
|
+
} else {
|
|
1156
|
+
charWidth *= BOLD_FACTOR2;
|
|
1157
|
+
}
|
|
1139
1158
|
}
|
|
1140
1159
|
totalWidth += charWidth;
|
|
1141
1160
|
}
|
|
@@ -1151,6 +1170,24 @@ var OpentypeTextMeasurer = class {
|
|
|
1151
1170
|
if (!font) return 1;
|
|
1152
1171
|
return font.ascender / font.unitsPerEm;
|
|
1153
1172
|
}
|
|
1173
|
+
resolveBoldFont(name) {
|
|
1174
|
+
if (!name) return null;
|
|
1175
|
+
const bases = [name];
|
|
1176
|
+
const mappedBase = getCurrentMappedFont(name);
|
|
1177
|
+
if (mappedBase && mappedBase !== name) bases.push(mappedBase);
|
|
1178
|
+
for (const base of bases) {
|
|
1179
|
+
for (const boldName of [`${base} Bold`, `${base}-Bold`]) {
|
|
1180
|
+
const direct = this.fonts.get(boldName);
|
|
1181
|
+
if (direct) return direct;
|
|
1182
|
+
const mapped = getCurrentMappedFont(boldName);
|
|
1183
|
+
if (mapped) {
|
|
1184
|
+
const mappedFont = this.fonts.get(mapped);
|
|
1185
|
+
if (mappedFont) return mappedFont;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1154
1191
|
resolveFont(name) {
|
|
1155
1192
|
if (!name) return null;
|
|
1156
1193
|
const direct = this.fonts.get(name);
|
|
@@ -1845,6 +1882,8 @@ var ColorResolver = class {
|
|
|
1845
1882
|
this.colorScheme = colorScheme;
|
|
1846
1883
|
this.colorMap = colorMap;
|
|
1847
1884
|
}
|
|
1885
|
+
colorScheme;
|
|
1886
|
+
colorMap;
|
|
1848
1887
|
resolve(colorNode) {
|
|
1849
1888
|
if (!colorNode) return null;
|
|
1850
1889
|
if (colorNode.srgbClr) {
|
|
@@ -1924,25 +1963,48 @@ var LazyMediaMap = class {
|
|
|
1924
1963
|
return data;
|
|
1925
1964
|
}
|
|
1926
1965
|
};
|
|
1966
|
+
var LazyXmlMap = class {
|
|
1967
|
+
rawInput;
|
|
1968
|
+
cache = /* @__PURE__ */ new Map();
|
|
1969
|
+
entryIndex;
|
|
1970
|
+
constructor(rawInput, xmlEntryNames) {
|
|
1971
|
+
this.rawInput = rawInput;
|
|
1972
|
+
this.entryIndex = xmlEntryNames;
|
|
1973
|
+
}
|
|
1974
|
+
has(path) {
|
|
1975
|
+
return this.entryIndex.has(path);
|
|
1976
|
+
}
|
|
1977
|
+
get(path) {
|
|
1978
|
+
if (!this.entryIndex.has(path)) return void 0;
|
|
1979
|
+
const cached = this.cache.get(path);
|
|
1980
|
+
if (cached !== void 0) return cached;
|
|
1981
|
+
const result = unzipSync(this.rawInput, {
|
|
1982
|
+
filter: (file) => file.name === path
|
|
1983
|
+
});
|
|
1984
|
+
const data = result[path];
|
|
1985
|
+
if (data) {
|
|
1986
|
+
const str = strFromU8(data);
|
|
1987
|
+
this.cache.set(path, str);
|
|
1988
|
+
return str;
|
|
1989
|
+
}
|
|
1990
|
+
return void 0;
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1927
1993
|
function readPptx(input) {
|
|
1928
1994
|
const rawInput = new Uint8Array(input);
|
|
1995
|
+
const xmlEntryNames = /* @__PURE__ */ new Set();
|
|
1929
1996
|
const mediaEntryNames = /* @__PURE__ */ new Set();
|
|
1930
|
-
|
|
1997
|
+
unzipSync(rawInput, {
|
|
1931
1998
|
filter: (file) => {
|
|
1932
1999
|
if (file.name.startsWith("ppt/media/")) {
|
|
1933
2000
|
mediaEntryNames.add(file.name);
|
|
1934
|
-
|
|
2001
|
+
} else if (file.name.endsWith(".xml") || file.name.endsWith(".rels") || file.name === "[Content_Types].xml") {
|
|
2002
|
+
xmlEntryNames.add(file.name);
|
|
1935
2003
|
}
|
|
1936
|
-
return
|
|
2004
|
+
return false;
|
|
1937
2005
|
}
|
|
1938
2006
|
});
|
|
1939
|
-
const files =
|
|
1940
|
-
for (const [relativePath, data] of Object.entries(unzipped)) {
|
|
1941
|
-
if (relativePath.endsWith("/")) continue;
|
|
1942
|
-
if (relativePath.endsWith(".xml") || relativePath.endsWith(".rels") || relativePath === "[Content_Types].xml") {
|
|
1943
|
-
files.set(relativePath, strFromU8(data));
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
2007
|
+
const files = new LazyXmlMap(rawInput, xmlEntryNames);
|
|
1946
2008
|
const media = new LazyMediaMap(rawInput, mediaEntryNames);
|
|
1947
2009
|
return { files, media };
|
|
1948
2010
|
}
|
|
@@ -3138,6 +3200,10 @@ function createDefaultBorders() {
|
|
|
3138
3200
|
|
|
3139
3201
|
// src/parser/slide-parser.ts
|
|
3140
3202
|
var SHAPE_TAGS = /* @__PURE__ */ new Set(["sp", "pic", "cxnSp", "grpSp", "graphicFrame"]);
|
|
3203
|
+
var SMARTART_DIAGRAM_URIS = /* @__PURE__ */ new Set([
|
|
3204
|
+
"http://schemas.openxmlformats.org/drawingml/2006/diagram",
|
|
3205
|
+
"http://purl.oclc.org/ooxml/drawingml/diagram"
|
|
3206
|
+
]);
|
|
3141
3207
|
function navigateOrdered(ordered, path) {
|
|
3142
3208
|
let current = ordered;
|
|
3143
3209
|
for (const key of path) {
|
|
@@ -3685,7 +3751,7 @@ function parseGraphicFrame(gf, rels, slidePath, archive, colorResolver, fontSche
|
|
|
3685
3751
|
if (!tableData) return null;
|
|
3686
3752
|
return { type: "table", transform, table: tableData };
|
|
3687
3753
|
}
|
|
3688
|
-
if (graphicData["@_uri"]
|
|
3754
|
+
if (SMARTART_DIAGRAM_URIS.has(graphicData["@_uri"])) {
|
|
3689
3755
|
return parseSmartArt(
|
|
3690
3756
|
graphicData,
|
|
3691
3757
|
transform,
|
|
@@ -4118,7 +4184,7 @@ function extractMathText(node) {
|
|
|
4118
4184
|
if (key === "t") {
|
|
4119
4185
|
if (typeof value === "object" && value !== null) {
|
|
4120
4186
|
text += value["#text"] ?? "";
|
|
4121
|
-
} else if (value
|
|
4187
|
+
} else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4122
4188
|
text += String(value);
|
|
4123
4189
|
}
|
|
4124
4190
|
} else if (typeof value === "object" && value !== null) {
|
|
@@ -4137,7 +4203,7 @@ function extractTextContent(node) {
|
|
|
4137
4203
|
if (typeof text === "object") {
|
|
4138
4204
|
return text["#text"] ?? "";
|
|
4139
4205
|
}
|
|
4140
|
-
return text
|
|
4206
|
+
return typeof text === "string" || typeof text === "number" || typeof text === "boolean" ? String(text) : "";
|
|
4141
4207
|
}
|
|
4142
4208
|
function parseParagraph(p, colorResolver, rels, fontScheme, lstStyle, orderedPChildren) {
|
|
4143
4209
|
const pPr = p.pPr;
|
|
@@ -6826,7 +6892,7 @@ var presetGeometries = {
|
|
|
6826
6892
|
const shaftEnd = h - headLength;
|
|
6827
6893
|
return `<polygon points="${bodyLeft},0 ${bodyRight},0 ${bodyRight},${shaftEnd} ${w},${shaftEnd} ${w / 2},${h} 0,${shaftEnd} ${bodyLeft},${shaftEnd}"/>`;
|
|
6828
6894
|
},
|
|
6829
|
-
line: () => ""
|
|
6895
|
+
line: (w, h) => `<line x1="0" y1="0" x2="${w}" y2="${h}"/>`,
|
|
6830
6896
|
// =====================
|
|
6831
6897
|
// Connector shapes
|
|
6832
6898
|
// =====================
|
|
@@ -8437,10 +8503,23 @@ function computePathLineX(alignment, textStartX, effectiveTextWidth, width, marg
|
|
|
8437
8503
|
return textStartX;
|
|
8438
8504
|
}
|
|
8439
8505
|
}
|
|
8440
|
-
function measureLineWidth(segments, defaultFontSize, fontScale) {
|
|
8506
|
+
function measureLineWidth(segments, defaultFontSize, fontScale, fontResolver) {
|
|
8441
8507
|
let totalWidth = 0;
|
|
8508
|
+
const jpanFallback = fontResolver ? getJpanFallbackFont() : null;
|
|
8442
8509
|
for (const seg of segments) {
|
|
8443
8510
|
const fontSize = (seg.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
8511
|
+
if (fontResolver) {
|
|
8512
|
+
const fontSizePx = fontSize * PX_PER_PT3;
|
|
8513
|
+
const font = fontResolver.resolveFont(
|
|
8514
|
+
seg.properties.fontFamily,
|
|
8515
|
+
seg.properties.fontFamilyEa,
|
|
8516
|
+
jpanFallback
|
|
8517
|
+
);
|
|
8518
|
+
if (font) {
|
|
8519
|
+
totalWidth += font.getAdvanceWidth(seg.text, fontSizePx);
|
|
8520
|
+
continue;
|
|
8521
|
+
}
|
|
8522
|
+
}
|
|
8444
8523
|
totalWidth += getTextMeasurer().measureTextWidth(
|
|
8445
8524
|
seg.text,
|
|
8446
8525
|
fontSize,
|
|
@@ -8496,7 +8575,7 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
8496
8575
|
const processSegment = (segText, fontFamily, fontFamilyEa) => {
|
|
8497
8576
|
if (segText.length === 0) return;
|
|
8498
8577
|
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback);
|
|
8499
|
-
const segWidth = getTextMeasurer().measureTextWidth(
|
|
8578
|
+
const segWidth = font ? font.getAdvanceWidth(segText, fontSizePx) : getTextMeasurer().measureTextWidth(
|
|
8500
8579
|
segText,
|
|
8501
8580
|
fontSize,
|
|
8502
8581
|
props.bold,
|
|
@@ -8521,7 +8600,7 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
8521
8600
|
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback);
|
|
8522
8601
|
const fillAttrs = buildPathFillAttrs(props);
|
|
8523
8602
|
for (const char of segText) {
|
|
8524
|
-
const charWidth = getTextMeasurer().measureTextWidth(
|
|
8603
|
+
const charWidth = font ? font.getAdvanceWidth(char, fontSizePx) : getTextMeasurer().measureTextWidth(
|
|
8525
8604
|
char,
|
|
8526
8605
|
fontSize,
|
|
8527
8606
|
props.bold,
|
|
@@ -8693,7 +8772,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
8693
8772
|
if (!isFirstLine) {
|
|
8694
8773
|
currentY += lineNaturalHeightPt * PX_PER_PT3 * getLineSpacing(para, lnSpcReduction) + lineGapPx;
|
|
8695
8774
|
}
|
|
8696
|
-
const lineWidth = measureLineWidth(line.segments, defaultFontSize, fontScale);
|
|
8775
|
+
const lineWidth = measureLineWidth(line.segments, defaultFontSize, fontScale, fontResolver);
|
|
8697
8776
|
const lineStartX = computePathLineX(
|
|
8698
8777
|
para.properties.alignment,
|
|
8699
8778
|
textStartX,
|
|
@@ -8742,7 +8821,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
8742
8821
|
currentY += naturalHeightPt * PX_PER_PT3 * getLineSpacing(para, lnSpcReduction) + paragraphGapPx;
|
|
8743
8822
|
}
|
|
8744
8823
|
const runsAsSegments = para.runs.filter((r) => r.text.length > 0).map((r) => ({ text: r.text, properties: r.properties }));
|
|
8745
|
-
const lineWidth = measureLineWidth(runsAsSegments, defaultFontSize, fontScale);
|
|
8824
|
+
const lineWidth = measureLineWidth(runsAsSegments, defaultFontSize, fontScale, fontResolver);
|
|
8746
8825
|
const lineStartX = computePathLineX(
|
|
8747
8826
|
para.properties.alignment,
|
|
8748
8827
|
textStartX,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pptx-glimpse",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
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",
|
|
@@ -13,36 +13,9 @@
|
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
18
|
-
"bench": "vitest bench",
|
|
19
|
-
"lint": "eslint src/ vrt/ scripts/ bench/ e2e/",
|
|
20
|
-
"lint:fix": "eslint src/ vrt/ scripts/ bench/ e2e/ --fix",
|
|
21
|
-
"format": "prettier --write 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts' 'e2e/**/*.ts'",
|
|
22
|
-
"format:check": "prettier --check 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts' 'e2e/**/*.ts'",
|
|
23
|
-
"test": "vitest run",
|
|
24
|
-
"test:coverage": "vitest run --coverage",
|
|
25
|
-
"test:watch": "vitest",
|
|
26
|
-
"typecheck": "tsc --noEmit",
|
|
27
|
-
"knip": "knip",
|
|
28
|
-
"dev": "tsx scripts/dev-server.ts",
|
|
29
|
-
"render": "tsx scripts/test-render.ts",
|
|
30
|
-
"inspect": "tsx scripts/inspect-pptx.ts",
|
|
31
|
-
"vrt:snapshot:docker-build": "docker build -t pptx-glimpse-snapshot-vrt docker/snapshot-vrt",
|
|
32
|
-
"vrt:snapshot:fixtures": "tsx vrt/snapshot/create-fixtures.ts",
|
|
33
|
-
"vrt:snapshot:update": "docker run --rm -v \"$(pwd)\":/workspace -v pptx-glimpse-snapshot-vrt-nm:/workspace/node_modules pptx-glimpse-snapshot-vrt bash /workspace/vrt/snapshot/docker-run.sh bash -c \"npx tsx vrt/snapshot/create-fixtures.ts && npx tsx vrt/snapshot/update-snapshots.ts\"",
|
|
34
|
-
"vrt:lo:docker-build": "docker build -t pptx-glimpse-vrt docker/libreoffice-vrt",
|
|
35
|
-
"vrt:lo:fixtures": "docker run --rm -v \"$(pwd)\":/workspace pptx-glimpse-vrt python3 /workspace/vrt/libreoffice/create_fixtures.py",
|
|
36
|
-
"vrt:lo:update": "npm run vrt:lo:docker-build && npm run vrt:lo:fixtures && docker run --rm -v \"$(pwd)\":/workspace pptx-glimpse-vrt bash /workspace/vrt/libreoffice/update_snapshots.sh",
|
|
37
|
-
"test:package": "npm run build && bash scripts/test-package.sh",
|
|
38
|
-
"changeset": "changeset",
|
|
39
|
-
"demo:dev": "cd demo && npm run dev",
|
|
40
|
-
"demo:build": "npm run build && cd demo && npm install && npm run build"
|
|
41
|
-
},
|
|
42
16
|
"files": [
|
|
43
17
|
"dist"
|
|
44
18
|
],
|
|
45
|
-
"packageManager": "npm@10.9.6",
|
|
46
19
|
"engines": {
|
|
47
20
|
"node": ">=20"
|
|
48
21
|
},
|
|
@@ -67,7 +40,7 @@
|
|
|
67
40
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
68
41
|
"fast-xml-parser": "^5.7.3",
|
|
69
42
|
"fflate": "^0.8.2",
|
|
70
|
-
"opentype.js": "
|
|
43
|
+
"opentype.js": "1.3.4"
|
|
71
44
|
},
|
|
72
45
|
"devDependencies": {
|
|
73
46
|
"@changesets/cli": "^2.29.8",
|
|
@@ -88,5 +61,31 @@
|
|
|
88
61
|
"typescript-eslint": "^8.55.0",
|
|
89
62
|
"vitest": "^3.0.0",
|
|
90
63
|
"ws": "^8.19.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
67
|
+
"bench": "vitest bench",
|
|
68
|
+
"lint": "eslint src/ vrt/ scripts/ bench/ e2e/",
|
|
69
|
+
"lint:fix": "eslint src/ vrt/ scripts/ bench/ e2e/ --fix",
|
|
70
|
+
"format": "prettier --write 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts' 'e2e/**/*.ts'",
|
|
71
|
+
"format:check": "prettier --check 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts' 'e2e/**/*.ts'",
|
|
72
|
+
"test": "vitest run",
|
|
73
|
+
"test:coverage": "vitest run --coverage",
|
|
74
|
+
"test:watch": "vitest",
|
|
75
|
+
"typecheck": "tsc --noEmit",
|
|
76
|
+
"knip": "knip",
|
|
77
|
+
"dev": "tsx scripts/dev-server.ts",
|
|
78
|
+
"render": "tsx scripts/test-render.ts",
|
|
79
|
+
"inspect": "tsx scripts/inspect-pptx.ts",
|
|
80
|
+
"vrt:snapshot:docker-build": "docker build -t pptx-glimpse-snapshot-vrt docker/snapshot-vrt",
|
|
81
|
+
"vrt:snapshot:fixtures": "tsx vrt/snapshot/create-fixtures.ts",
|
|
82
|
+
"vrt:snapshot:update": "docker run --rm -v \"$(pwd)\":/workspace -v pptx-glimpse-snapshot-vrt-nm:/workspace/node_modules pptx-glimpse-snapshot-vrt bash /workspace/vrt/snapshot/docker-run.sh bash -c \"npx tsx vrt/snapshot/create-fixtures.ts && npx tsx vrt/snapshot/update-snapshots.ts\"",
|
|
83
|
+
"vrt:lo:docker-build": "docker build -t pptx-glimpse-vrt docker/libreoffice-vrt",
|
|
84
|
+
"vrt:lo:fixtures": "docker run --rm -v \"$(pwd)\":/workspace pptx-glimpse-vrt python3 /workspace/vrt/libreoffice/create_fixtures.py",
|
|
85
|
+
"vrt:lo:update": "pnpm run vrt:lo:docker-build && pnpm run vrt:lo:fixtures && docker run --rm -v \"$(pwd)\":/workspace pptx-glimpse-vrt bash /workspace/vrt/libreoffice/update_snapshots.sh",
|
|
86
|
+
"test:package": "pnpm run build && bash scripts/test-package.sh",
|
|
87
|
+
"changeset": "changeset",
|
|
88
|
+
"demo:dev": "cd demo && npm run dev",
|
|
89
|
+
"demo:build": "pnpm run build && cd demo && npm install && npm run build"
|
|
91
90
|
}
|
|
92
|
-
}
|
|
91
|
+
}
|