Skip to content

Commit

Permalink
Add table cell background color
Browse files Browse the repository at this point in the history
  • Loading branch information
botzill committed Feb 18, 2017
1 parent 98c6aa6 commit 4cf6299
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 4 deletions.
5 changes: 5 additions & 0 deletions pydocx/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
# https://en.wikipedia.org/wiki/Twip
TWIPS_PER_POINT = 20

# According to this: http://alienryderflex.com/hsp.html
BRIGHTNESS_DARK_COLOR = 50

COLOR_FOR_DARK_BACKGROUND = 'FFFFFF'

# TODO These alignment values are for traditional conformance. Strict
# conformance uses different values
JUSTIFY_CENTER = 'center'
Expand Down
4 changes: 4 additions & 0 deletions pydocx/export/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ def get_run_styles_to_apply(self, run):
(properties.hidden, self.export_run_property_hidden),
(properties.vertical_align, self.export_run_property_vertical_align),
(properties.color, self.export_run_property_color),
(not properties.color, self.export_run_property_parent_background_color),
]
for actual_value, handler in property_rules:
if actual_value:
Expand Down Expand Up @@ -407,6 +408,9 @@ def export_run_property_vertical_align(self, run, results):
def export_run_property_color(self, run, results):
return results

def export_run_property_parent_background_color(self, run, results):
return results

def export_hyperlink(self, hyperlink):
return self.yield_nested(hyperlink.children, self.export_node)

Expand Down
36 changes: 35 additions & 1 deletion pydocx/export/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
POINTS_PER_EM,
PYDOCX_STYLES,
TWIPS_PER_POINT,
EMUS_PER_PIXEL
EMUS_PER_PIXEL,
BRIGHTNESS_DARK_COLOR,
COLOR_FOR_DARK_BACKGROUND
)
from pydocx.export.base import PyDocXExporter
from pydocx.export.numbering_span import NumberingItem
from pydocx.openxml import wordprocessing
from pydocx.openxml.wordprocessing.paragraph import Paragraph
from pydocx.openxml.wordprocessing.table_cell import TableCell
from pydocx.util import color
from pydocx.util.uri import uri_is_external
from pydocx.util.xml import (
convert_dictionary_to_html_attributes,
Expand Down Expand Up @@ -572,6 +577,29 @@ def export_run_property_color(self, run, results):
tag = HtmlTag('span', **attrs)
return self.export_run_property(tag, run, results)

def export_run_property_parent_background_color(self, run, results):
background_color = None

if isinstance(run.parent, (Paragraph,)):
paragraph = run.parent
if isinstance(paragraph.parent, (TableCell,)) and paragraph.parent.properties:
table_cell_prop = paragraph.parent.properties
background_color = table_cell_prop.background_color

if background_color:
brightness = color.brightness(background_color)
# We need to change the text color if background color is dark
# and text run does not have custom color.
# Office Word does this automatically.
if brightness < BRIGHTNESS_DARK_COLOR:
attrs = {
'style': 'color: #%s' % COLOR_FOR_DARK_BACKGROUND
}
tag = HtmlTag('span', **attrs)
results = self.export_run_property(tag, run, results)

return results

def export_text(self, text):
results = super(PyDocXHTMLExporter, self).export_text(text)
for result in results:
Expand Down Expand Up @@ -668,6 +696,12 @@ def export_table_cell(self, table_cell):
attrs['colspan'] = colspan
if rowspan > 1:
attrs['rowspan'] = rowspan

if table_cell.properties:
background_color = table_cell.properties.background_color
if background_color:
attrs['style'] = 'background-color: #%s' % background_color

tag = HtmlTag('td', **attrs)

numbering_spans = self.yield_numbering_spans(table_cell.children)
Expand Down
12 changes: 12 additions & 0 deletions pydocx/openxml/wordprocessing/table_cell_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class TableCellProperties(XmlModel):

grid_span = XmlChild(name='gridSpan', attrname='val')

background_fill = XmlChild(name='shd', attrname='fill')

vertical_merge = XmlChild(name='vMerge', type=lambda el: dict(el.attrib)) # noqa

def should_close_previous_vertical_merge(self):
Expand All @@ -25,3 +27,13 @@ def should_close_previous_vertical_merge(self):
if merge != 'continue':
return True
return False

@property
def background_color(self):
background_fill = getattr(self, 'background_fill', None)

# There is no need to set white background color
if background_fill not in ('auto', 'FFFFFF'):
return background_fill

return None
3 changes: 2 additions & 1 deletion pydocx/test/document_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,14 @@ def li(self, text, ilvl, numId, bold=False):
)

@classmethod
def table_cell(self, paragraph, merge=False, merge_continue=False):
def table_cell(self, paragraph, merge=False, merge_continue=False, fill_color='auto'):
template = env.get_template(templates['tc'])
return template_render(
template,
paragraph=paragraph,
merge=merge,
merge_continue=merge_continue,
fill_color=fill_color
)

@classmethod
Expand Down
26 changes: 26 additions & 0 deletions pydocx/util/color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# coding: utf-8
from __future__ import (
absolute_import,
print_function,
unicode_literals,
)

import math


def hex_to_rgb(value):
"""Return (red, green, blue) for the color given as #rrggbb."""

value = value.lstrip('#')
lv = len(value)
return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))


def brightness(hex_color):
"""A trick to determine the brightness of the color.
More info about this here: http://alienryderflex.com/hsp.html
"""

r, g, b = hex_to_rgb(hex_color)

return math.sqrt(math.pow(r, 2) * .241 + math.pow(g, 2) * .691 + math.pow(b, 2) * .068)
29 changes: 29 additions & 0 deletions tests/export/test_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,35 @@ def get_xml(self):
return xml


class TableWithCellBackgroundColor(TranslationTestCase):
expected_output = '''
<table border="1">
<tr>
<td style="background-color: #FF00FF">AAA</td>
<td>BBB</td>
</tr>
<tr>
<td>CCC</td>
<td style="background-color: #000000">
<span style="color: #FFFFFF">DDD</span>
</td>
</tr>
</table>
'''

def get_xml(self):
cell1 = DXB.table_cell(paragraph=DXB.p_tag('AAA'), fill_color='FF00FF')
cell2 = DXB.table_cell(paragraph=DXB.p_tag('BBB'), fill_color='FFFFFF')
cell3 = DXB.table_cell(paragraph=DXB.p_tag('CCC'), fill_color='auto')
# for dark color we get white text color
cell4 = DXB.table_cell(paragraph=DXB.p_tag('DDD'), fill_color='000000')
rows = [DXB.table_row([cell1, cell2]), DXB.table_row([cell3, cell4])]
table = DXB.table(rows)
body = table
xml = DXB.xml(body)
return xml


class SimpleListTestCase(TranslationTestCase):
expected_output = '''
<ol class="pydocx-list-style-type-lowerLetter">
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/styled_color.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<p><span style="color:#31849B">BBB</span></p>
<table border="1">
<tr>
<td>CCC</td>
<td style="background-color: #5F497A">CCC</td>
</tr>
</table>
<p>DDD</p>
2 changes: 1 addition & 1 deletion tests/templates/tc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<w:left w:color="000000" w:space="0" w:sz="2" w:val="single"/>
<w:bottom w:color="000000" w:space="0" w:sz="2" w:val="single"/>
</w:tcBorders>
<w:shd w:fill="auto" w:val="clear"/>
<w:shd w:fill="{{ fill_color }}" w:val="clear"/>
<w:tcMar>
<w:top w:type="dxa" w:w="55"/>
<w:left w:type="dxa" w:w="55"/>
Expand Down

0 comments on commit 4cf6299

Please sign in to comment.