@@ -376,3 +376,95 @@ fn test_shape_shadow_default_opacity() {
376376 shadow. opacity
377377 ) ;
378378}
379+
380+ // ── fillRef style fallback tests ─────────────────────────────────
381+
382+ #[ test]
383+ fn test_shape_fill_from_style_fill_ref ( ) {
384+ // Shape with no explicit fill, but <p:style><a:fillRef> referencing accent1.
385+ // accent1 = #4472C4 in standard theme.
386+ let shape_xml = r#"<p:sp><p:nvSpPr><p:cNvPr id="2" name="Rect"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="914400" cy="914400"/></a:xfrm><a:prstGeom prst="roundRect"><a:avLst/></a:prstGeom><a:ln><a:solidFill><a:srgbClr val="000000"/></a:solidFill></a:ln></p:spPr><p:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="1"><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:sp>"# . to_string ( ) ;
387+ let slide_xml = make_slide_xml ( & [ shape_xml] ) ;
388+
389+ let theme_xml = make_theme_xml ( & standard_theme_colors ( ) , "Calibri" , "Calibri" ) ;
390+ let data = build_test_pptx_with_theme ( SLIDE_CX , SLIDE_CY , & [ slide_xml] , & theme_xml) ;
391+ let parser = PptxParser ;
392+ let ( doc, _warnings) = parser. parse ( & data, & ConvertOptions :: default ( ) ) . unwrap ( ) ;
393+
394+ let page = first_fixed_page ( & doc) ;
395+ let shape = get_shape ( & page. elements [ 0 ] ) ;
396+ // accent1 = #4472C4
397+ assert_eq ! (
398+ shape. fill,
399+ Some ( Color :: new( 0x44 , 0x72 , 0xC4 ) ) ,
400+ "Shape should get fill from fillRef accent1"
401+ ) ;
402+ }
403+
404+ #[ test]
405+ fn test_shape_explicit_fill_overrides_fill_ref ( ) {
406+ // Shape with explicit solidFill AND <p:style><a:fillRef>.
407+ // Explicit fill should win.
408+ let shape_xml = r#"<p:sp><p:nvSpPr><p:cNvPr id="2" name="Rect"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="914400" cy="914400"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:solidFill><a:srgbClr val="FF0000"/></a:solidFill></p:spPr><p:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="1"><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:sp>"# . to_string ( ) ;
409+ let slide_xml = make_slide_xml ( & [ shape_xml] ) ;
410+
411+ let theme_xml = make_theme_xml ( & standard_theme_colors ( ) , "Calibri" , "Calibri" ) ;
412+ let data = build_test_pptx_with_theme ( SLIDE_CX , SLIDE_CY , & [ slide_xml] , & theme_xml) ;
413+ let parser = PptxParser ;
414+ let ( doc, _warnings) = parser. parse ( & data, & ConvertOptions :: default ( ) ) . unwrap ( ) ;
415+
416+ let page = first_fixed_page ( & doc) ;
417+ let shape = get_shape ( & page. elements [ 0 ] ) ;
418+ assert_eq ! (
419+ shape. fill,
420+ Some ( Color :: new( 255 , 0 , 0 ) ) ,
421+ "Explicit solidFill should override fillRef"
422+ ) ;
423+ }
424+
425+ #[ test]
426+ fn test_shape_no_fill_overrides_fill_ref ( ) {
427+ // Shape with explicit <a:noFill/> AND <p:style><a:fillRef>.
428+ // noFill should prevent style fallback.
429+ let shape_xml = r#"<p:sp><p:nvSpPr><p:cNvPr id="2" name="Rect"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="914400" cy="914400"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="1"><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:sp>"# . to_string ( ) ;
430+ let slide_xml = make_slide_xml ( & [ shape_xml] ) ;
431+
432+ let theme_xml = make_theme_xml ( & standard_theme_colors ( ) , "Calibri" , "Calibri" ) ;
433+ let data = build_test_pptx_with_theme ( SLIDE_CX , SLIDE_CY , & [ slide_xml] , & theme_xml) ;
434+ let parser = PptxParser ;
435+ let ( doc, _warnings) = parser. parse ( & data, & ConvertOptions :: default ( ) ) . unwrap ( ) ;
436+
437+ let page = first_fixed_page ( & doc) ;
438+ let shape = get_shape ( & page. elements [ 0 ] ) ;
439+ assert_eq ! (
440+ shape. fill, None ,
441+ "noFill should prevent style fillRef fallback"
442+ ) ;
443+ }
444+
445+ #[ test]
446+ fn test_textbox_fill_from_style_fill_ref ( ) {
447+ // TextBox with roundRect (non-rectangular shape) and text gets split into
448+ // two elements: Shape background (with fill) + transparent TextBox overlay.
449+ let shape_xml = r#"<p:sp><p:nvSpPr><p:cNvPr id="2" name="TextBox"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="914400" cy="914400"/></a:xfrm><a:prstGeom prst="roundRect"><a:avLst/></a:prstGeom><a:ln><a:solidFill><a:srgbClr val="000000"/></a:solidFill></a:ln></p:spPr><p:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="1"><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:p><a:r><a:rPr lang="en-US"/><a:t>Hello</a:t></a:r></a:p></p:txBody></p:sp>"# . to_string ( ) ;
450+ let slide_xml = make_slide_xml ( & [ shape_xml] ) ;
451+
452+ let theme_xml = make_theme_xml ( & standard_theme_colors ( ) , "Calibri" , "Calibri" ) ;
453+ let data = build_test_pptx_with_theme ( SLIDE_CX , SLIDE_CY , & [ slide_xml] , & theme_xml) ;
454+ let parser = PptxParser ;
455+ let ( doc, _warnings) = parser. parse ( & data, & ConvertOptions :: default ( ) ) . unwrap ( ) ;
456+
457+ let page = first_fixed_page ( & doc) ;
458+ // First element: Shape background with geometry and fill
459+ assert_eq ! ( page. elements. len( ) , 2 , "Expected Shape + TextBox pair" ) ;
460+ let shape = get_shape ( & page. elements [ 0 ] ) ;
461+ assert_eq ! (
462+ shape. fill,
463+ Some ( Color :: new( 0x44 , 0x72 , 0xC4 ) ) ,
464+ "Shape background should get fill from fillRef accent1"
465+ ) ;
466+ assert ! ( matches!( shape. kind, ShapeKind :: RoundedRectangle { .. } ) ) ;
467+ // Second element: Transparent text overlay
468+ let tb = text_box_data ( & page. elements [ 1 ] ) ;
469+ assert_eq ! ( tb. fill, None , "Text overlay should have no fill" ) ;
470+ }
0 commit comments