scanny / python-pptx

Create Open XML PowerPoint documents in Python
MIT License
2.36k stars 511 forks source link

shape.shadow.inherit attribute not working #446

Open 6lick opened 5 years ago

6lick commented 5 years ago

I am trying to make shapes without shadows. I followed the documentation here: [https://python-pptx.readthedocs.io/en/latest/dev/analysis/shp-shadow.html#protocol] Shape.shadow.inherit = False but shapes are still being created with a shadow. I am using the default power point presentation template provided by pptx. If anyone has any solutions please let me know. If your solution is a new .potx file please share your custom potx file! Thanks

scanny commented 5 years ago

Hmm, that's odd. I'd be interested to see your code. Can you post the minimum code to reproduce? I'd expect it would be something like:

from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])
shape = slide.shapes.add_shape(
    MSO_SHAPE.RECTANGLE, 0, 0, Inches(1), Inches(1)
)
shape.shadow.inherit = False
prs.save('test.pptx')

For me, that produces a shape without a shadow. If I leave out the shape.shadow.inherit line I get a shape with a shadow.

6lick commented 5 years ago

@scanny the code you posted reproduces error for me- I just ran it and got a blue rectangle with a shadow.

scanny commented 5 years ago

What application and version are you using to look at the presentation?

6lick commented 5 years ago

I am running Linux so using LibreOffice Impress Version: 5.1.6.2

6lick commented 5 years ago
    title_only_slide_layout = prs.slide_layouts[6]
    slide = prs.slides.add_slide(title_only_slide_layout)
    shapes = slide.shapes
    title_only_slide_layout = prs.slide_layouts[6]
    slide3 = prs.slides.add_slide(title_only_slide_layout)
    shapes = slide3.shapes
    for y in range(1, 4):
        left = Inches(.5)
        for x in range(1,8):
            #init shapes
            shape = shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height)
            #set box shadow
            shadow = shape.shadow
            shadow.inherit = False
            print(shadow.inherit)

Here is the relevant chunk of code. Friend had same issue running the function on Windows opening powerpoint in Microsoft Office powerpoint not sure what version

scanny commented 5 years ago

What's the prs = ... line?

scanny commented 5 years ago

So placing possible rendering differences aside for a moment, a higher-strength version of this de-shadowizing is to set the effect style of the shape. This isn't supported in the API yet, but something like this should get it done:

effectRef = shape._element.xpath('.//p:style/a:effectRef')[0]
effectRef.set('idx', '0')

The XML you're manipulating looks like this:

<p:sp>
  <!-- a couple other child elements of shape -->
  <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>
  <!-- one or two other child elements of shape -->
</p:sp>

This p:style child maps the default formatting of the shape to style items defined in the theme. The snippet above chooses the so-called "Subtle" effect from the theme, which is no-shadow in this case. 1, 2, and 3 are subtle, moderate, and intense, in theme effect terms. Setting it to zero appears to be "none of the above".

Even higher strength than this is to change the underlying theme. The only way I know to do that is by editing the XML. You'd be looking for something around a:theme/a:themeElements/a:fmtScheme/a:effectStyleLst in the XML hierarchy.

By the way, it just occurred to me, there's an important difference between gradient fill and a shadow. If you've been calling the default gradient fill a "shadow" then we're barking up the wrong tree here :)