Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: set table cell background color #52

Closed
castaway opened this issue Oct 24, 2013 · 9 comments
Closed

feature: set table cell background color #52

castaway opened this issue Oct 24, 2013 · 9 comments
Milestone

Comments

@castaway
Copy link

Hi..

I'm trying to add the ability in my code to set the background colour of the cell. This should be done similar to the font colours, by adding a solidFill etc, so I wrote:

def set_cell_background_color(cell, rgbColor):
   print "cell %r" % cell
   print "color %s" % rgbColor
   tc = cell._Cell__tc
   solidFill = _SubElement(tc, 'a:solidFill')
   srgbClr = _SubElement(solidFill, 'a:srgbClr')
   srgbClr.set('val', rgbColor)

Which I guessed as being correct after looking in shapes.py at _Cell, and seeing where it put the marL etc attributes. This ends up creating an empty tcPr element, followed by the solidFill, which Powerpoint doesn't like.

It seems that _Cell does, somehow/somewhere, create an empty tcPr element, but I can't see how to add my solidFill to it. If I change my code to:

def set_cell_background_color(cell, rgbColor):
   print "cell %r" % cell
   print "color %s" % rgbColor
   tc = cell._Cell__tc
   solidFill = _SubElement(tc, 'a:solidFill')
   srgbClr = _SubElement(solidFill, 'a:srgbClr')
   srgbClr.set('val', rgbColor)

Then I get TWO tcPr elements, which isn't allowed.

Any ideas?

(Issue completely rewritten as I was being a nitwit..)

From the relaxng spec:
a_CT_TableCell =

default value: 1

attribute rowSpan { xsd:int }?,

default value: 1

attribute gridSpan { xsd:int }?,

default value: false

attribute hMerge { xsd:boolean }?,

default value: false

attribute vMerge { xsd:boolean }?,
attribute id { xsd:string }?,
element txBody { a_CT_TextBody }?,
element tcPr { a_CT_TableCellProperties }?,
element extLst { a_CT_OfficeArtExtensionList }?

a_CT_TableCellProperties =

default value: 91440

attribute marL { a_ST_Coordinate32 }?,

default value: 91440

attribute marR { a_ST_Coordinate32 }?,

default value: 45720

attribute marT { a_ST_Coordinate32 }?,

default value: 45720

attribute marB { a_ST_Coordinate32 }?,

default value: horz

attribute vert { a_ST_TextVerticalType }?,

default value: t

attribute anchor { a_ST_TextAnchoringType }?,

default value: false

attribute anchorCtr { xsd:boolean }?,

default value: clip

attribute horzOverflow { a_ST_TextHorzOverflowType }?,
element lnL { a_CT_LineProperties }?,
element lnR { a_CT_LineProperties }?,
element lnT { a_CT_LineProperties }?,
element lnB { a_CT_LineProperties }?,
element lnTlToBr { a_CT_LineProperties }?,
element lnBlToTr { a_CT_LineProperties }?,
element cell3D { a_CT_Cell3D }?,
a_EG_FillProperties?,
element headers { a_CT_Headers }?,
element extLst { a_CT_OfficeArtExtensionList }?

@castaway
Copy link
Author

Aha, poked around for a while in oxml.py, and figured out where the tcPr appears from.. so now I have:

   def set_cell_background_color(cell, rgbColor):
       print "cell %r" % cell
       print "color %s" % rgbColor
       tc = cell._Cell__tc
       tcPr = None
       if not hasattr(tc, 'tcPr'):
           tcPr = _Element('tcPr')
           idx = 1 if hasattr(self, 'txBody') else 0
           self.insert(idx, tcPr)
       else:
           tcPr = tc.tcPr

       solidFill = _SubElement(tcPr, 'a:solidFill')
       srgbClr = _SubElement(solidFill, 'a:srgbClr')
       srgbClr.set('val', rgbColor)

@scanny
Copy link
Owner

scanny commented Oct 24, 2013

@castaway As the first step, I would recommend installing opc-diag and doing a diff between a before and after version of the change you want to implement. Might look something like this:

$ opc diff before-cell-color.pptx after-cell-color.pptx
--- before-cell-color/ppt/slides/slide1.xml

+++ after-cell-color/ppt/slides/slide1.xml

@@ -72,7 +78,11 @@

                       <a:endParaRPr lang="en-US"/>
                     </a:p>
                   </a:txBody>
-                  <a:tcPr/>
+                  <a:tcPr>
+                    <a:solidFill>
+                      <a:schemeClr val="accent6"/>
+                    </a:solidFill>
+                  </a:tcPr>
                 </a:tc>
                 <a:tc>
                   <a:txBody>

You seem to have gotten most of the way here on this one, but just a good way to start because it narrows down the change you need to make.

@scanny
Copy link
Owner

scanny commented Oct 24, 2013

@castaway A couple of things to look at:

  • There are a couple different ways to specify a color. Looks like you chose the RGB color method. That's a good baseline method, but has the limitation that it's essentially hard-coded. The diff above shows me having used the 'palette' to pick a color. That adds a level of indirection, the symbolic name of the color, and will change with the palette. I'm sure theoretically that's more flexible, but I can't say I've had much call to do theme changes on existing presentations, which is where that might be handy.
  • The XML schema specifies particular sequences for elements in most cases, so the code to add the new element might be a little brittle. If you're always creating from scratch you probably won't run into any trouble. However if you're modifying an existing presentation you might encounter a "won't load without repair step" error. Just something to keep in mind. The code to properly sequence the new element (<a:tcPr> in this case) would go into oxml.py in an lxml custom element class something like CT_TableCell and perhaps named .add_tcPr().
  • Also, in the same vein, if <a:tcPr> were already there and already had something in it you'd need to clear it etc. But all that is probably only relevant if you're thinking of submitting a patch. If this bit works for you as is you should be fine :)

Btw, your code paste didn't indent properly. I believe it takes a four-character indentation to get it to do that. I fixed it. The other way is with three back-ticks and the name of the language of the code. If you go into edit mode on your comment you should see it. Otherwise it's in the GitHub Flavored Markdown help link here that appears while you're editing a comment.

@scanny
Copy link
Owner

scanny commented Oct 24, 2013

Now all that said, I'm not sure if what you have so far is working or not. If it's not working yet, if you can get it down to a diff between the XML your function is generating and the XML you want, I can help you with the lxml calls you need to get there.

@castaway
Copy link
Author

It does seem to be working, using the response I commented above, thanks. Ta also for the suggestion of opc-diag, I'll have to look into that one.

These are currently all new files, so hopefully I won't get repair issues. Presumably python-pptx only does verification checking on the XML parts it actually supports?

@scanny
Copy link
Owner

scanny commented Oct 30, 2013

So far I've avoided including validation of the XML against a schema. I've considered it briefly a couple times, but have concluded it would result in more false positives than anything else. The once or twice I've run validation by hand it's choked on file contents that PowerPoint loads fine. So far I've found that if new code does something that prevents the file from loading it seems to get caught long before shipping; so I'm not convinced yet it would result in higher quality code. I definitely don't see the point of adding it as a step in production; it would always be either a problem in the template the user provided or in the library code, not in the way they used the library.

There are apparently some undocumented leniencies MS PowerPoint has built in and the performance standard I've adopted is that python-pptx should load anything that PowerPoint will. So I try to restrict attention to the particular place where an operation is making changes and take the rest on faith. That's seemed to work out well so far.

That said, when making a change, I don't make any assumptions in the production code about what's already present or not in the XML. If the schema says it could be there I account for it. That makes adding small features a big job sometimes, but such is the price of robustness :)

It might be an interesting feature to add to opc-diag however. I could see it coming in handy for debugging.

@scanny
Copy link
Owner

scanny commented Oct 30, 2013

I've changed the title of this issue and am leaving it open as a feature request for making cell background color settable.

@scanny
Copy link
Owner

scanny commented Dec 8, 2013

This feature was added with the latest commit on develop: c5e0fba

Usage is the same as for shape, substituting cell where shape appears in this sample code

@scanny scanny closed this as completed Dec 8, 2013
@scanny
Copy link
Owner

scanny commented Dec 19, 2013

Here's a specific snippet for a table cell:

from pptx.dml.color import RGBColor

# cell is a table cell
# set fill type to solid color first
cell.fill.solid()

# set foreground (fill) color to a specific RGB color
cell.fill.fore_color.rgb = RGBColor(0xFB, 0x8F, 0x00)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants