python-openxml / python-docx

Create and modify Word documents with Python
MIT License
4.6k stars 1.13k forks source link

feature: Cell.shading #146

Open shapiromatron opened 9 years ago

shapiromatron commented 9 years ago

I'd like to be able to add cell-shading to a cell in a table.

I have some time and would be willing to submit a pull-request with this feature implemented, but I was curious if there's any code that's already been written elsewhere in the package that I can use as a reference for how to implement. Would adding bold or italics in spans be a good reference?

scanny commented 9 years ago

Best place to start is probably inspecting the XML Word produces for cell shading. opc-diag is a handy tool for that sort of thing. There are a couple ways it might go, basically the simple way and the more powerful way, but we probably want to start with modeling the mechanism Word uses by default. That way we establish both the read and write capability at the same time.

Once that's established I can guide better. Offhand though I expect python-pptx will be a better model. The architecture is virtually identical and the two file formats share a lot of the same XML structure for things like this.

You might want to have a look here at the AutoShape.fill property: https://github.com/scanny/python-pptx/blob/master/pptx/shapes/autoshape.py#L304

which constructs a FillFormat object: https://github.com/scanny/python-pptx/blob/master/pptx/dml/fill.py#L20

which via an intermediary or two ends up in the oxml layer here to manipulate the XML: https://github.com/scanny/python-pptx/blob/master/pptx/oxml/shapes/shared.py#L265

This general method is the "more complex but more powerful" method, so we'll need to see what Word does and then have a think on what would be the best initial approach.

If you can isolate an XML specimen or two that shows a shaded cell (with minimum context) and paste it into a reply post that will get us started.

shapiromatron commented 9 years ago

This just came up on the google-groups thread; attaching it as it accomplishes what I'd like to be able to do: https://groups.google.com/forum/#!topic/python-docx/-c3OrRHA3qo

from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml

# Set a cell background (shading) color to RGB D9D9D9. 
shading_elm = parse_xml(r'<w:shd {} w:fill="D9D9D9"/>'.format(nsdecls('w')))
cell._tc.get_or_add_tcPr().append(shading_elm)
proxyblue commented 9 years ago

I would like to +1 this feature. However, that code snippet is perfect for my needs at the moment. Thanks @scanny and @shapiromatron

mculv commented 9 years ago

@scanny and @shapiromatron, forgive me for being a n00b, but can you please elaborate how I'd use the attached sample code to format table cells in Docx?

Many thanks in advance

shapiromatron commented 9 years ago

See my cell rendering code here for a project I was working on: https://github.com/shapiromatron/docxUtils/blob/master/docxUtils/tables.py#L172-L178

import docx
from docx.oxml.shared import OxmlElement, qn

cell = tbl.cell(0, 0)
tcPr = cell._tc.get_or_add_tcPr()
tcVAlign = OxmlElement('w:shd')
tcVAlign.set(qn('w:fill'), shade)
tcPr.append(tcVAlign)
wich commented 8 years ago

I have this implemented in a limited fashion (fill only) in a local branch, but since it has binary overlap with pull request #301 I'll wait with making a new pull request for it until #301 has been integrated.

fwalloe commented 5 years ago

Any update on this feature?

shapiromatron commented 5 years ago

Updated my demo for a fully working one; it's pretty easy to add:

from docx import Document
from docx.oxml.shared import OxmlElement, qn

def shade_cells(cells, shade):
    for cell in cells:
        tcPr = cell._tc.get_or_add_tcPr()
        tcVAlign = OxmlElement("w:shd")
        tcVAlign.set(qn("w:fill"), shade)
        tcPr.append(tcVAlign)

document = Document()
table = document.add_table(rows=3, cols=3)
shade_cells([table.cell(0, 0), table.cell(1, 1), table.cell(2, 2)], "#99badd")  # unc
shade_cells([table.cell(0, 1), table.cell(1, 2)], "#CC0000")  # nc state
shade_cells([table.cell(1, 0), table.cell(2, 1)], "#001A57")  # duke
document.save("demo.docx")
pruppert commented 5 years ago

@shapiromatron Can this be used to fill existing cells in an existing docx? I can’t seem to bend your code to that end.

shapiromatron commented 5 years ago

@pruppert not sure but I don't see why not, if you can select a cell of an existing table? I haven't tested.

rinkef commented 5 years ago

@shapiromatron I think this is very useful! Can there be made a standard implementation of this in the code?

Sibiriak commented 5 years ago

Updated my demo for a fully working one; it's pretty easy to add:


from docx import Document
from docx.oxml.shared import OxmlElement, qn

How to read background color hex of existing cell by this method?