quicklook-pptx-renderer 0.3.1 → 0.3.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/README.md CHANGED
@@ -165,6 +165,11 @@ for (const issue of result.issues) {
165
165
  // issue.fix.childCount = 5, issue.fix.containsPictures = false
166
166
  // Promote group children to slide-level drawables
167
167
  break;
168
+
169
+ case "strip-embedded-fonts":
170
+ // issue.fix.fonts = [{ name: "Montserrat", replacement: "Nunito Sans" }]
171
+ // Strip embedded font data and replace with cross-platform alternatives
172
+ break;
168
173
  }
169
174
  }
170
175
  ```
@@ -189,6 +194,7 @@ Every issue carries a typed `fix` object with machine-readable remediation data
189
194
  | `geometry-forces-pdf` | info | Non-rect geometry renders as PDF — opaque background may cover adjacent content | — |
190
195
  | `rotation-forces-pdf` | info | Rotated rect renders as PDF — enlarged bounding box may cover adjacent content | — |
191
196
  | `text-inscription-shift` | info | Text in non-rect shape uses geometry-inscribed bounds (text may shift) | `replace-geometry` (suggests `rect`) |
197
+ | `embedded-font` | warn | Embedded fonts ignored by QuickLook — text renders with system substitutes | `strip-embedded-fonts` (includes per-font replacement when metrics available) |
192
198
  | `vertical-text` | info | Vertical text uses CSS writing-mode | — |
193
199
 
194
200
  ---
package/dist/lint.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * re-detecting the problem.
10
10
  */
11
11
  export type Severity = "error" | "warn" | "info";
12
- export type RuleId = "unsupported-geometry" | "opaque-pdf-block" | "gradient-flattened" | "font-substitution" | "table-style-unresolved" | "group-as-pdf" | "chart-no-fallback" | "text-inscription-shift" | "rotation-forces-pdf" | "geometry-forces-pdf" | "vertical-text";
12
+ export type RuleId = "unsupported-geometry" | "opaque-pdf-block" | "gradient-flattened" | "font-substitution" | "table-style-unresolved" | "group-as-pdf" | "chart-no-fallback" | "text-inscription-shift" | "rotation-forces-pdf" | "geometry-forces-pdf" | "vertical-text" | "embedded-font";
13
13
  /** Machine-readable remediation data — one variant per fix strategy. */
14
14
  export type LintFix = {
15
15
  action: "replace-geometry";
@@ -41,6 +41,12 @@ export type LintFix = {
41
41
  action: "ungroup";
42
42
  childCount: number;
43
43
  containsPictures: boolean;
44
+ } | {
45
+ action: "strip-embedded-fonts";
46
+ fonts: Array<{
47
+ name: string;
48
+ replacement?: string;
49
+ }>;
44
50
  };
45
51
  export interface LintIssue {
46
52
  rule: RuleId;
package/dist/lint.js CHANGED
@@ -105,6 +105,40 @@ export async function lint(pptxBuffer) {
105
105
  const pkg = await PptxPackage.open(pptxBuffer);
106
106
  const pres = await readPresentation(pkg);
107
107
  const issues = [];
108
+ // Presentation-level: detect embedded fonts (QuickLook ignores them)
109
+ const presXml = await pkg.getPartXml("ppt/presentation.xml");
110
+ const presNode = presXml?.Presentation ?? presXml?.presentation ?? presXml ?? {};
111
+ const embFontLst = presNode.embeddedFontLst?.embeddedFont;
112
+ if (embFontLst) {
113
+ const entries = Array.isArray(embFontLst) ? embFontLst : [embFontLst];
114
+ const safeCandidates = Object.keys(FONT_METRICS).filter(f => !FONT_SUBSTITUTIONS[f]);
115
+ const fonts = [];
116
+ for (const entry of entries) {
117
+ const name = entry.font?.["@_typeface"];
118
+ if (!name)
119
+ continue;
120
+ let replacement;
121
+ if (FONT_METRICS[name]) {
122
+ const matches = findClosestFont(name, { candidates: safeCandidates, sameCategory: true, limit: 1 });
123
+ if (matches.length > 0)
124
+ replacement = matches[0].font;
125
+ }
126
+ fonts.push({ name, replacement });
127
+ }
128
+ if (fonts.length > 0) {
129
+ const details = fonts.map(f => f.replacement ? `${f.name} → ${f.replacement}` : f.name).join(", ");
130
+ issues.push({
131
+ rule: "embedded-font",
132
+ severity: "warn",
133
+ slide: 0,
134
+ message: `Embedded font${fonts.length > 1 ? "s" : ""}: ${details} — QuickLook ignores embedded fonts, text will render with system substitutes`,
135
+ suggestion: fonts.some(f => f.replacement)
136
+ ? `Strip embedded data and replace with cross-platform alternatives`
137
+ : `Remove embedded fonts to reduce file size; use cross-platform fonts instead`,
138
+ fix: { action: "strip-embedded-fonts", fonts },
139
+ });
140
+ }
141
+ }
108
142
  for (let i = 0; i < pres.slides.length; i++) {
109
143
  const slide = pres.slides[i];
110
144
  const slideNum = i + 1;
@@ -431,7 +465,7 @@ export function formatIssues(result) {
431
465
  for (const issue of result.issues) {
432
466
  if (issue.slide !== currentSlide) {
433
467
  currentSlide = issue.slide;
434
- lines.push(`\nSlide ${currentSlide}:`);
468
+ lines.push(currentSlide === 0 ? `\nPresentation:` : `\nSlide ${currentSlide}:`);
435
469
  }
436
470
  const icon = SEVERITY_ICON[issue.severity];
437
471
  const elem = issue.element ? ` (${issue.element})` : "";
@@ -8,7 +8,7 @@ const ARRAY_ELEMENTS = new Set([
8
8
  "AlternateContent",
9
9
  "p", "r", "br", "fld",
10
10
  "tr", "tc", "gridCol",
11
- "gs", "gd", "font",
11
+ "gs", "gd", "font", "embeddedFont",
12
12
  "effectStyle",
13
13
  "Relationship", "Default", "Override",
14
14
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quicklook-pptx-renderer",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Open-source PPTX rendering engine that replicates Apple's macOS QuickLook and iOS preview — pixel for pixel. Test how PowerPoint files look on Mac/iPhone without a Mac.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",