Shadow

Shadow is inherited, and a shape often appears with a shadow without explicitly applying a shadow format.

The only difference in a shape with shadow turned off is the presence of an empty <a:effectLst/> child in its <p:spPr> element. Inherited shadow is turned off when p:spPr/a:effectLst is present with no a:outerShdw child element. Other effect child elements may be present.

A shadow is one type of “effect”. The others are glow/soft-edges and reflection.

Shadow may be of type outer (perhaps most common), inner, or perspective.

Scope

  • Shape.shadow is a ShadowFormat object (all shape types)
  • shape.shadow.inherit = True removes any explicitly applied effects (including glow/soft-edge and reflection, not just shadow). This restores the “inherit” state for effects and makes the shape sensitive to changes in theme.
  • shape.shadow.inherit = False overrides any default (theme) effects. Any inherited shadow, glow, and/or reflection will no longer appear. This operation adds an empty effects element, which causes no effects to be applied, regardless of theme settings.

Out-of-scope

Minimum for specifying a basic shadow

  • ShadowFormat.visible - applies a reasonable standard shadow override.
  • ShadowFormat.shadow_type (inner, outer, perspective)
  • ShadowFormat.alignment (shadow anchor, automatic based on angle)
  • ShadowFormat.angle (0-degrees is to the right, increasing CCW)
  • ShadowFormat.blur_radius (generally a few points, maybe 3-5)
  • ShadowFormat.color (theme or RGB color)
  • ColorFormat.transparency (needed for proper shadow rendering)
  • ShadowFormat.distance (generally a couple points, maybe 1-3)
  • shape.shadow.style (MSO_SHADOW_STYLE) indicates whether shadow is inner, outer, or perspective (or None).

Nice to have for finer tuning

  • ShadowFormat.rotate_with_shape (boolean)
  • ShadowFormat.scale (shadow bigger/smaller than shape, default 100%)
  • Retaining non-shadow effects when turning off shadow. This requires the ability to “clone” the currently enabled defaults. Effects are not inherited separately; making explicit the currently active default is how PowerPoint works around the “all-or-nothing” inheritance behavior.
  • Clone effective shadow, glow, and/or reflection.

Protocol

The .shadow property on a shape always returns the ShadowFormat object for that shape, regardless of whether its shadow is explicit or inherited:

>>> shape = prs.slides[0].shapes[0]
>>> shadow = shape.shadow
>>> shadow
<pptx.dml.effect.ShadowFormat object at 0x108080490>

The .shadow property is idempotent, meaning the same ShadowFormat object (verified by comparing ID) is returned on every call, for the lifetime of the shape object:

>>> shape.shadow
<pptx.dml.effect.ShadowFormat object at 0x108080490>
>>> shape.shadow
<pptx.dml.effect.ShadowFormat object at 0x108080490>

The ShadowFormat.inherit property indicates whether the shape inherits its shadow effect or overrides it with an explicitly defined setting. The default setting for a new shape is True:

>>> shadow.inherit
True

Assigning False breaks the inheritance link by explicitly defining a “no-shadow” setting for the shape. This causes the shape to appear without a shadow, regardless of the applied theme:

>>> shadow.inherit = False
>>> shadow.inherit
False

Note that this has the side-effect of disabling inheritance of all effects for that shape.

PowerPoint behaviors

  • All 5 shape-types can display a shadow, but graphics-frame objects like chart and table use a different mechanism than the other shapes. Those won’t be supported initially.
    • AutoShape
    • Connector
    • Picture
    • Group Shape (parent is p:grpSpPr rather than p:spPr)
    • Graphics Frame (UI allows, but uses a different mechanism)
  • Adding shadow to a group shape adds that shadow to each component shape in the group.
  • There is a “new-shape format” concept. This format determines what a new shape looks like, but does not change the appearance of shapes already in place. It’s basically a template imprinted on new shapes when they are added.

Theme Effects are a thing here. They are Subtle, Moderate, and Intense.

There are 40 built-in theme effects. Each of these have …

  • Setting visible off (Format Shape > Shadow > Clear Shadow checkbox) for a customized shadow removes all customized settings and they are not recoverable by setting the shadow visible again (clicking the shadow checkbox).

MS API

ShadowFormat object

  • ShadowFormat.Visible

Specimen XML

Shape inheriting shadow. Note the absence of p:spPr/a:effectLst, causing all effects to be inherited:

<p:sp>
  <p:nvSpPr>
    <p:cNvPr id="4" name="Rounded Rectangle 3"/>
    <p:cNvSpPr/>
    <p:nvPr/>
  </p:nvSpPr>
  <p:spPr>
    <a:xfrm>
      <a:off x="4114800" y="2971800"/>
      <a:ext cx="914400" cy="914400"/>
    </a:xfrm>
    <a:prstGeom prst="roundRect">
      <a:avLst/>
    </a:prstGeom>
  </p:spPr>
  <p:style>
    <a:lnRef idx="1">
      <a:schemeClr val="accent1"/>
    </a:lnRef>
    <a:fillRef idx="3">
      <a:schemeClr val="accent1"/>
    </a:fillRef>
    <a:effectRef idx="2">
      <a:schemeClr val="accent1"/>
    </a:effectRef>
    <a:fontRef idx="minor">
      <a:schemeClr val="lt1"/>
    </a:fontRef>
  </p:style>
  <p:txBody>
    <a:bodyPr rtlCol="0" anchor="ctr"/>
    <a:lstStyle/>
    <a:p>
      <a:pPr algn="ctr"/>
      <a:endParaRPr lang="en-US"/>
    </a:p>
  </p:txBody>
</p:sp>

Shape with inherited shadow turned off:

<p:sp>
  <p:nvSpPr>
    <p:cNvPr id="4" name="Rounded Rectangle 3"/>
    <p:cNvSpPr/>
    <p:nvPr/>
  </p:nvSpPr>
  <p:spPr>
    <a:xfrm>
      <a:off x="4114800" y="2971800"/>
      <a:ext cx="914400" cy="914400"/>
    </a:xfrm>
    <a:prstGeom prst="roundRect">
      <a:avLst/>
    </a:prstGeom>
    <a:effectLst/>
  </p:spPr>
  <p:style>
    <a:lnRef idx="1">
      <a:schemeClr val="accent1"/>
    </a:lnRef>
    <a:fillRef idx="3">
      <a:schemeClr val="accent1"/>
    </a:fillRef>
    <a:effectRef idx="2">
      <a:schemeClr val="accent1"/>
    </a:effectRef>
    <a:fontRef idx="minor">
      <a:schemeClr val="lt1"/>
    </a:fontRef>
  </p:style>
  <p:txBody>
    <a:bodyPr rtlCol="0" anchor="ctr"/>
    <a:lstStyle/>
    <a:p>
      <a:pPr algn="ctr"/>
      <a:endParaRPr lang="en-US"/>
    </a:p>
  </p:txBody>
</p:sp>

XML Semantics

Effect inheritance is “all-or-nothing”

  • If p:spPr/a:effectLst is present, all desired effects must be specified explicitly as its children; a missing child, such as a:outerShdw, will cause that effect to be turned off. PowerPoint automatically adds those populated with inherited values when one of the effects is customized, necessitating that addition of an a:effectLst element.

  • Theme sub-tree a:theme/a:objectDefaults/a:spDef/a:style/a:effectRef/idx=2 specifies that new objects will get the second effect in a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst. That effect looks like this:

    <a:effectStyle>
      <a:effectLst>
        <a:outerShdw blurRad="40000" dist="23000" dir="5400000"
            rotWithShape="0">
          <a:srgbClr val="000000">
            <a:alpha val="35000"/>
          </a:srgbClr>
        </a:outerShdw>
      </a:effectLst>
    </a:effectStyle>
    

Schema excerpt

<xsd:complexType name="CT_Shape">  <!-- p:sp element -->
  <xsd:sequence>
    <xsd:element name="nvSpPr" type="CT_ShapeNonVisual"/>
    <xsd:element name="spPr"   type="a:CT_ShapeProperties"/>
    <xsd:element name="style"  type="a:CT_ShapeStyle"        minOccurs="0"/>
    <xsd:element name="txBody" type="a:CT_TextBody"          minOccurs="0"/>
    <xsd:element name="extLst" type="CT_ExtensionListModify" minOccurs="0"/>
  </xsd:sequence>
  <xsd:attribute name="useBgFill" type="xsd:boolean" default="false"/>
</xsd:complexType>

<xsd:complexType name="CT_ShapeProperties">  <!--denormalized-->
  <xsd:sequence>
    <xsd:element name="xfrm"              type="CT_Transform2D"            minOccurs="0"/>
    <xsd:group   ref ="EG_Geometry"                                        minOccurs="0"/>
    <xsd:group   ref ="EG_FillProperties"                                  minOccurs="0"/>
    <xsd:element name="ln"                type="CT_LineProperties"         minOccurs="0"/>
    <xsd:choice minOccurs="0"/>  <!--EG_EffectProperties-->
      <xsd:element name="effectLst"       type="CT_EffectList"/>
      <xsd:element name="effectDag"       type="CT_EffectContainer"/>
    </xsd:choice>
    <xsd:element name="scene3d"           type="CT_Scene3D"                minOccurs="0"/>
    <xsd:element name="sp3d"              type="CT_Shape3D"                minOccurs="0"/>
    <xsd:element name="extLst"            type="CT_OfficeArtExtensionList" minOccurs="0"/>
  </xsd:sequence>
  <xsd:attribute name="bwMode" type="ST_BlackWhiteMode"/>
</xsd:complexType>

<xsd:complexType name="CT_EffectList">
  <xsd:sequence>
    <xsd:element name="blur"        type="CT_BlurEffect"         minOccurs="0"/>
    <xsd:element name="fillOverlay" type="CT_FillOverlayEffect"  minOccurs="0"/>
    <xsd:element name="glow"        type="CT_GlowEffect"         minOccurs="0"/>
    <xsd:element name="innerShdw"   type="CT_InnerShadowEffect"  minOccurs="0"/>
    <xsd:element name="outerShdw"   type="CT_OuterShadowEffect"  minOccurs="0"/>
    <xsd:element name="prstShdw"    type="CT_PresetShadowEffect" minOccurs="0"/>
    <xsd:element name="reflection"  type="CT_ReflectionEffect"   minOccurs="0"/>
    <xsd:element name="softEdge"    type="CT_SoftEdgesEffect"    minOccurs="0"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="CT_OuterShadowEffect">
  <xsd:sequence>
    <xsd:group ref="EG_ColorChoice" minOccurs="1" maxOccurs="1"/>
  </xsd:sequence>
  <xsd:attribute name="blurRad"      type="ST_PositiveCoordinate" default="0"/>
  <xsd:attribute name="dist"         type="ST_PositiveCoordinate" default="0"/>
  <xsd:attribute name="dir"          type="ST_PositiveFixedAngle" default="0"/>
  <xsd:attribute name="sx"           type="ST_Percentage"         default="100%"/>
  <xsd:attribute name="sy"           type="ST_Percentage"         default="100%"/>
  <xsd:attribute name="kx"           type="ST_FixedAngle"         default="0"/>
  <xsd:attribute name="ky"           type="ST_FixedAngle"         default="0"/>
  <xsd:attribute name="algn"         type="ST_RectAlignment"      default="b"/>
  <xsd:attribute name="rotWithShape" type="xsd:boolean"           default="true"/>
</xsd:complexType>

<xsd:simpleType name="ST_RectAlignment">
  <xsd:restriction base="xsd:token">
    <xsd:enumeration value="tl"/>
    <xsd:enumeration value="t"/>
    <xsd:enumeration value="tr"/>
    <xsd:enumeration value="l"/>
    <xsd:enumeration value="ctr"/>
    <xsd:enumeration value="r"/>
    <xsd:enumeration value="bl"/>
    <xsd:enumeration value="b"/>
    <xsd:enumeration value="br"/>
  </xsd:restriction>
</xsd:simpleType>