Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht

### Fixed

- Preserved explicit paragraph `defRPr` text colors when a shape also provides a theme
`fontRef` color, preventing light theme text from overriding authored paragraph colors.
- Keep near-fit, single-paragraph square-wrapped headings on one line with a conservative 2%
browser-metric correction while preserving deliberate multi-line text.
- Resolve macOS PowerPoint exports and macro hosts by exact full path, close only that presentation,
Expand Down
44 changes: 44 additions & 0 deletions test/unit/renderer/ShapeRenderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6034,6 +6034,50 @@ describe('ShapeRenderer', () => {
expect(hasWhiteText).toBe(true);
});

it('keeps paragraph defRPr color over shape fontRef color', () => {
const xml = `
<p:sp xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<p:nvSpPr>
<p:cNvPr id="102" name="Paragraph Color Override"/>
<p:cNvSpPr/>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm><a:off x="0" y="0"/><a:ext cx="2000000" cy="1000000"/></a:xfrm>
<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>
<a:solidFill><a:srgbClr val="EAF2F0"/></a:solidFill>
</p:spPr>
<p:style>
<a:lnRef idx="0"><a:schemeClr val="accent1"/></a:lnRef>
<a:fillRef idx="0"><a:schemeClr val="accent1"/></a:fillRef>
<a:effectRef idx="0"><a:schemeClr val="accent1"/></a:effectRef>
<a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef>
</p:style>
<p:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p>
<a:pPr>
<a:defRPr>
<a:solidFill><a:srgbClr val="243B53"/></a:solidFill>
</a:defRPr>
</a:pPr>
<a:r><a:rPr lang="en-US"/><a:t>Paragraph Color</a:t></a:r>
</a:p>
</p:txBody>
</p:sp>
`;

const el = renderShape(parseShapeNode(parseXml(xml)), createMockRenderContext());
const textSpan = Array.from(el.querySelectorAll('span')).find(
(span) => span.textContent === 'Paragraph Color',
);

expect(textSpan).toBeDefined();
expect(textSpan!.style.color).toBe('rgb(36, 59, 83)');
});

// ---------------------------------------------------------------------------
// Dynamic normAutofit scaling (lines ~1142-1162 in ShapeRenderer.ts)
// In jsdom, scrollHeight often equals clientHeight, so the scale branch may not
Expand Down
76 changes: 76 additions & 0 deletions test/unit/renderer/TextRenderer.branches.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,82 @@ describe('TextRenderer — branch coverage (uncovered paths)', () => {
expect(span!.style.color).toBe('rgb(255, 0, 0)');
});

it('prefers explicit paragraph defRPr color over fontRefColor', () => {
const body = makeTextBody({
paragraphs: [
{
properties: xmlNode(
'<pPr><defRPr><solidFill><srgbClr val="243B53"/></solidFill></defRPr></pPr>',
),
runs: [{ text: 'Paragraph color wins' }],
level: 0,
},
],
});
const ctx = createMockRenderContext();
const container = document.createElement('div');
renderTextBody(body, undefined, ctx, container, {
fontRefColor: '#FFFFFF',
});
const span = container.querySelector('span');
expect(span!.style.color).toBe('rgb(36, 59, 83)');
});

it('uses fontRefColor when paragraph defRPr has no color', () => {
const body = makeTextBody({
paragraphs: [
{
properties: xmlNode('<pPr><defRPr sz="1800"/></pPr>'),
runs: [{ text: 'Shape style color wins' }],
level: 0,
},
],
});
const ctx = createMockRenderContext();
const container = document.createElement('div');
renderTextBody(body, undefined, ctx, container, {
fontRefColor: '#FFFFFF',
});
const span = container.querySelector('span');
expect(span!.style.color).toBe('rgb(255, 255, 255)');
});

it('keeps fontRefColor precedence over inherited list style colors', () => {
const body = makeTextBody({
listStyle:
'<lstStyle><lvl1pPr><defRPr><solidFill><srgbClr val="243B53"/></solidFill></defRPr></lvl1pPr></lstStyle>',
paragraphs: [{ runs: [{ text: 'Shape style color wins' }], level: 0 }],
});
const ctx = createMockRenderContext();
const container = document.createElement('div');
renderTextBody(body, undefined, ctx, container, {
fontRefColor: '#FFFFFF',
});
const span = container.querySelector('span');
expect(span!.style.color).toBe('rgb(255, 255, 255)');
});

it('prefers explicit paragraph defRPr color over cellTextColor', () => {
const body = makeTextBody({
paragraphs: [
{
properties: xmlNode(
'<pPr><defRPr><solidFill><srgbClr val="243B53"/></solidFill></defRPr></pPr>',
),
runs: [{ text: 'Paragraph color wins' }],
level: 0,
},
],
});
const ctx = createMockRenderContext();
const container = document.createElement('div');
renderTextBody(body, undefined, ctx, container, {
cellTextColor: '#FF0000',
});
const span = container.querySelector('span');
expect(span!.style.color).toBe('rgb(36, 59, 83)');
});

it('uses cellTextColor as fallback when no other color set', () => {
const body = makeTextBody({
paragraphs: [
Expand Down