From bbbdbc07c324dc94f5783f2731ff99b6b05b347c Mon Sep 17 00:00:00 2001
From: Sidhaarth Krishnan <161455529+RealSid08@users.noreply.github.com>
Date: Wed, 1 Apr 2026 19:55:41 +1100
Subject: [PATCH 1/2] Improve theme style ref fidelity
---
scripts/generate-visual-fixtures.mjs | 113 ++++++++++-
scripts/visual-cases.mjs | 11 +
src/parser.ts | 290 ++++++++++++++++++++++++---
test/helpers.ts | 8 +-
test/index.test.ts | 247 +++++++++++++++++++++++
5 files changed, 633 insertions(+), 36 deletions(-)
diff --git a/scripts/generate-visual-fixtures.mjs b/scripts/generate-visual-fixtures.mjs
index 7c719c2..343f9a5 100644
--- a/scripts/generate-visual-fixtures.mjs
+++ b/scripts/generate-visual-fixtures.mjs
@@ -14,8 +14,18 @@ await Promise.all([
writeVisualFixture("bullets-numbering", createBulletsNumberingSlide()),
writeVisualFixture("text-insets-anchor", createTextInsetsAnchorSlide()),
writeVisualFixture("paragraph-spacing", createParagraphSpacingSlide()),
- writeVisualFixture("theme-colors", createThemeColorsSlide()),
- writeVisualFixture("theme-luminance", createThemeLuminanceSlide()),
+ writeVisualFixture("theme-colors", createThemeColorsSlide(), {
+ relsXml: createThemeFixtureRelationships(),
+ }),
+ writeVisualFixture("theme-luminance", createThemeLuminanceSlide(), {
+ relsXml: createThemeFixtureRelationships(),
+ }),
+ writeVisualFixture("theme-style-refs", createThemeStyleRefsSlide(), {
+ relsXml: createThemeFixtureRelationships(),
+ files: {
+ "ppt/theme/theme1.xml": createThemeStyleRefsTheme(),
+ },
+ }),
writeVisualFixture("group-transform", createGroupTransformSlide()),
writeVisualFixture("nested-groups", createNestedGroupsSlide()),
writeVisualFixture("table-layout", createTableLayoutSlide()),
@@ -333,6 +343,98 @@ function createThemeLuminanceSlide() {
`);
}
+function createThemeStyleRefsSlide() {
+ return slideXml(`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Theme Style Refs
+
+
+
+ Fill, line, and font defaults come from p:style.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Secondary Box
+
+
+
+ `);
+}
+
+function createThemeStyleRefsTheme() {
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+}
+
function createGroupTransformSlide() {
return slideXml(`
@@ -701,6 +803,13 @@ function createEmptyRelationships() {
`;
}
+function createThemeFixtureRelationships() {
+ return `
+
+
+`;
+}
+
function hexToRgb(hex) {
const clean = hex.replace("#", "");
return [
diff --git a/scripts/visual-cases.mjs b/scripts/visual-cases.mjs
index 861900d..ed84e9b 100644
--- a/scripts/visual-cases.mjs
+++ b/scripts/visual-cases.mjs
@@ -91,6 +91,17 @@ export const visualCases = [
},
enforce: true,
},
+ {
+ id: "theme-style-refs",
+ sourceFile: ".tmp/visual-fixtures/theme-style-refs.pptx",
+ browserFixturePath: "/.tmp/visual-fixtures/theme-style-refs.pptx",
+ slides: [1],
+ thresholds: {
+ maxDiffRatio: 0.35,
+ meanDiffRatio: 0.2,
+ },
+ enforce: true,
+ },
{
id: "group-transform",
sourceFile: ".tmp/visual-fixtures/group-transform.pptx",
diff --git a/src/parser.ts b/src/parser.ts
index d07c603..fda077d 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -1,4 +1,5 @@
import JSZip from "jszip";
+import type { GradientFill } from "./types";
import type {
PptxSlide,
PptxParseResult,
@@ -37,7 +38,7 @@ export async function parsePptx(arrayBuffer: ArrayBuffer | Uint8Array): Promise<
const imageMap = await extractImages(zip);
// Load theme colors for resolving schemeClr references
- const themeColors = await loadThemeColors(zip);
+ const theme = await loadThemeData(zip);
// Parse each slide
const slides: PptxSlide[] = [];
@@ -58,7 +59,7 @@ export async function parsePptx(arrayBuffer: ArrayBuffer | Uint8Array): Promise<
relationships,
imageMap,
zip,
- themeColors,
+ theme,
);
slides.push(slide);
}
@@ -85,8 +86,26 @@ function assertParseApisAvailable(): void {
/**
* Load theme colors from theme1.xml
*/
-async function loadThemeColors(zip: JSZip): Promise