Skip to content

Commit 4cf6299

Browse files
committed
Add table cell background color
1 parent 98c6aa6 commit 4cf6299

File tree

9 files changed

+115
-4
lines changed

9 files changed

+115
-4
lines changed

pydocx/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
# https://en.wikipedia.org/wiki/Twip
2626
TWIPS_PER_POINT = 20
2727

28+
# According to this: http://alienryderflex.com/hsp.html
29+
BRIGHTNESS_DARK_COLOR = 50
30+
31+
COLOR_FOR_DARK_BACKGROUND = 'FFFFFF'
32+
2833
# TODO These alignment values are for traditional conformance. Strict
2934
# conformance uses different values
3035
JUSTIFY_CENTER = 'center'

pydocx/export/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ def get_run_styles_to_apply(self, run):
359359
(properties.hidden, self.export_run_property_hidden),
360360
(properties.vertical_align, self.export_run_property_vertical_align),
361361
(properties.color, self.export_run_property_color),
362+
(not properties.color, self.export_run_property_parent_background_color),
362363
]
363364
for actual_value, handler in property_rules:
364365
if actual_value:
@@ -407,6 +408,9 @@ def export_run_property_vertical_align(self, run, results):
407408
def export_run_property_color(self, run, results):
408409
return results
409410

411+
def export_run_property_parent_background_color(self, run, results):
412+
return results
413+
410414
def export_hyperlink(self, hyperlink):
411415
return self.yield_nested(hyperlink.children, self.export_node)
412416

pydocx/export/html.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@
1717
POINTS_PER_EM,
1818
PYDOCX_STYLES,
1919
TWIPS_PER_POINT,
20-
EMUS_PER_PIXEL
20+
EMUS_PER_PIXEL,
21+
BRIGHTNESS_DARK_COLOR,
22+
COLOR_FOR_DARK_BACKGROUND
2123
)
2224
from pydocx.export.base import PyDocXExporter
2325
from pydocx.export.numbering_span import NumberingItem
2426
from pydocx.openxml import wordprocessing
27+
from pydocx.openxml.wordprocessing.paragraph import Paragraph
28+
from pydocx.openxml.wordprocessing.table_cell import TableCell
29+
from pydocx.util import color
2530
from pydocx.util.uri import uri_is_external
2631
from pydocx.util.xml import (
2732
convert_dictionary_to_html_attributes,
@@ -572,6 +577,29 @@ def export_run_property_color(self, run, results):
572577
tag = HtmlTag('span', **attrs)
573578
return self.export_run_property(tag, run, results)
574579

580+
def export_run_property_parent_background_color(self, run, results):
581+
background_color = None
582+
583+
if isinstance(run.parent, (Paragraph,)):
584+
paragraph = run.parent
585+
if isinstance(paragraph.parent, (TableCell,)) and paragraph.parent.properties:
586+
table_cell_prop = paragraph.parent.properties
587+
background_color = table_cell_prop.background_color
588+
589+
if background_color:
590+
brightness = color.brightness(background_color)
591+
# We need to change the text color if background color is dark
592+
# and text run does not have custom color.
593+
# Office Word does this automatically.
594+
if brightness < BRIGHTNESS_DARK_COLOR:
595+
attrs = {
596+
'style': 'color: #%s' % COLOR_FOR_DARK_BACKGROUND
597+
}
598+
tag = HtmlTag('span', **attrs)
599+
results = self.export_run_property(tag, run, results)
600+
601+
return results
602+
575603
def export_text(self, text):
576604
results = super(PyDocXHTMLExporter, self).export_text(text)
577605
for result in results:
@@ -668,6 +696,12 @@ def export_table_cell(self, table_cell):
668696
attrs['colspan'] = colspan
669697
if rowspan > 1:
670698
attrs['rowspan'] = rowspan
699+
700+
if table_cell.properties:
701+
background_color = table_cell.properties.background_color
702+
if background_color:
703+
attrs['style'] = 'background-color: #%s' % background_color
704+
671705
tag = HtmlTag('td', **attrs)
672706

673707
numbering_spans = self.yield_numbering_spans(table_cell.children)

pydocx/openxml/wordprocessing/table_cell_properties.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class TableCellProperties(XmlModel):
1313

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

16+
background_fill = XmlChild(name='shd', attrname='fill')
17+
1618
vertical_merge = XmlChild(name='vMerge', type=lambda el: dict(el.attrib)) # noqa
1719

1820
def should_close_previous_vertical_merge(self):
@@ -25,3 +27,13 @@ def should_close_previous_vertical_merge(self):
2527
if merge != 'continue':
2628
return True
2729
return False
30+
31+
@property
32+
def background_color(self):
33+
background_fill = getattr(self, 'background_fill', None)
34+
35+
# There is no need to set white background color
36+
if background_fill not in ('auto', 'FFFFFF'):
37+
return background_fill
38+
39+
return None

pydocx/test/document_builder.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,14 @@ def li(self, text, ilvl, numId, bold=False):
206206
)
207207

208208
@classmethod
209-
def table_cell(self, paragraph, merge=False, merge_continue=False):
209+
def table_cell(self, paragraph, merge=False, merge_continue=False, fill_color='auto'):
210210
template = env.get_template(templates['tc'])
211211
return template_render(
212212
template,
213213
paragraph=paragraph,
214214
merge=merge,
215215
merge_continue=merge_continue,
216+
fill_color=fill_color
216217
)
217218

218219
@classmethod

pydocx/util/color.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# coding: utf-8
2+
from __future__ import (
3+
absolute_import,
4+
print_function,
5+
unicode_literals,
6+
)
7+
8+
import math
9+
10+
11+
def hex_to_rgb(value):
12+
"""Return (red, green, blue) for the color given as #rrggbb."""
13+
14+
value = value.lstrip('#')
15+
lv = len(value)
16+
return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
17+
18+
19+
def brightness(hex_color):
20+
"""A trick to determine the brightness of the color.
21+
More info about this here: http://alienryderflex.com/hsp.html
22+
"""
23+
24+
r, g, b = hex_to_rgb(hex_color)
25+
26+
return math.sqrt(math.pow(r, 2) * .241 + math.pow(g, 2) * .691 + math.pow(b, 2) * .068)

tests/export/test_xml.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,35 @@ def get_xml(self):
296296
return xml
297297

298298

299+
class TableWithCellBackgroundColor(TranslationTestCase):
300+
expected_output = '''
301+
<table border="1">
302+
<tr>
303+
<td style="background-color: #FF00FF">AAA</td>
304+
<td>BBB</td>
305+
</tr>
306+
<tr>
307+
<td>CCC</td>
308+
<td style="background-color: #000000">
309+
<span style="color: #FFFFFF">DDD</span>
310+
</td>
311+
</tr>
312+
</table>
313+
'''
314+
315+
def get_xml(self):
316+
cell1 = DXB.table_cell(paragraph=DXB.p_tag('AAA'), fill_color='FF00FF')
317+
cell2 = DXB.table_cell(paragraph=DXB.p_tag('BBB'), fill_color='FFFFFF')
318+
cell3 = DXB.table_cell(paragraph=DXB.p_tag('CCC'), fill_color='auto')
319+
# for dark color we get white text color
320+
cell4 = DXB.table_cell(paragraph=DXB.p_tag('DDD'), fill_color='000000')
321+
rows = [DXB.table_row([cell1, cell2]), DXB.table_row([cell3, cell4])]
322+
table = DXB.table(rows)
323+
body = table
324+
xml = DXB.xml(body)
325+
return xml
326+
327+
299328
class SimpleListTestCase(TranslationTestCase):
300329
expected_output = '''
301330
<ol class="pydocx-list-style-type-lowerLetter">

tests/fixtures/styled_color.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<p><span style="color:#31849B">BBB</span></p>
33
<table border="1">
44
<tr>
5-
<td>CCC</td>
5+
<td style="background-color: #5F497A">CCC</td>
66
</tr>
77
</table>
88
<p>DDD</p>

tests/templates/tc.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<w:left w:color="000000" w:space="0" w:sz="2" w:val="single"/>
1515
<w:bottom w:color="000000" w:space="0" w:sz="2" w:val="single"/>
1616
</w:tcBorders>
17-
<w:shd w:fill="auto" w:val="clear"/>
17+
<w:shd w:fill="{{ fill_color }}" w:val="clear"/>
1818
<w:tcMar>
1919
<w:top w:type="dxa" w:w="55"/>
2020
<w:left w:type="dxa" w:w="55"/>

0 commit comments

Comments
 (0)