reports: Add BaseODS.border_style() method.

This commit is contained in:
Brett Smith 2020-06-27 23:08:23 -04:00
parent 138928eebf
commit 15becebf5c
2 changed files with 78 additions and 3 deletions

View file

@ -18,6 +18,7 @@ import abc
import collections
import copy
import datetime
import enum
import itertools
import operator
import re
@ -518,6 +519,14 @@ class BaseSpreadsheet(Generic[RT, ST], metaclass=abc.ABCMeta):
self.end_spreadsheet()
class Border(enum.IntFlag):
TOP = 1
RIGHT = 2
BOTTOM = 4
LEFT = 8
# in CSS order, clockwise from top
class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
"""Abstract base class to help write OpenDocument spreadsheets
@ -548,7 +557,7 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
self.locale = babel.core.Locale.default('LC_MONETARY')
self.currency_fmt_key = 'accounting'
self._name_counter = itertools.count(1)
self._currency_style_cache: MutableMapping[str, odf.style.Style] = {}
self._style_cache: MutableMapping[str, odf.style.Style] = {}
self.document = odf.opendocument.OpenDocumentSpreadsheet()
self.init_settings()
self.init_styles()
@ -666,6 +675,32 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
### Styles
def border_style(self,
edges: int,
width: str='1px',
style: str='solid',
color: str='#000000',
) -> odf.style.Style:
flags = [edge for edge in Border if edges & edge]
if not flags:
raise ValueError(f"no valid edges in {edges!r}")
border_attr = f'{width} {style} {color}'
key = f'{",".join(f.name for f in flags)} {border_attr}'
try:
retval = self._style_cache[key]
except KeyError:
props = odf.style.TableCellProperties()
for flag in flags:
props.setAttribute(f'border{flag.name.lower()}', border_attr)
retval = odf.style.Style(
name=f'Border{next(self._name_counter)}',
family='table-cell',
)
retval.addElement(props)
self.document.styles.addElement(retval)
self._style_cache[key] = retval
return retval
def column_style(self, width: Union[float, str], **attrs: Any) -> odf.style.Style:
if not isinstance(width, str) or (width and not width[-1].isalpha()):
width = f'{width}in'
@ -785,7 +820,7 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
cache_parts.append(f'{key}={value}')
cache_key = '\0'.join(cache_parts)
try:
style = self._currency_style_cache[cache_key]
style = self._style_cache[cache_key]
except KeyError:
pos_style = self._build_currency_style(
root, locale, code, 0, positive_properties, volatile=True,
@ -803,7 +838,7 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
family='table-cell',
datastylename=curr_style,
)
self._currency_style_cache[cache_key] = style
self._style_cache[cache_key] = style
return style
def _merge_style_iter_names(

View file

@ -313,6 +313,46 @@ def test_ods_writer_style(ods_writer, attr_name, child_type, checked_attr):
child = get_child(actual, child_type)
assert child.getAttribute(checked_attr)
@pytest.mark.parametrize('edges,width,style,color', [
(core.Border.TOP,
'5px', 'solid', '#ff0000'),
(core.Border.RIGHT | core.Border.LEFT,
'2pt', 'dashed', '#00ff00'),
(core.Border.BOTTOM | core.Border.RIGHT | core.Border.LEFT,
'1em', 'dotted', '#0000ff'),
(core.Border.TOP | core.Border.BOTTOM | core.Border.RIGHT | core.Border.LEFT,
'1cm', 'thick', '#aaaaaa'),
])
def test_ods_writer_border_style(ods_writer, edges, width, style, color):
actual = ods_writer.border_style(edges, width, style, color)
props, = actual.childNodes
attr_s = f'{width} {style} {color}'
for edge_exp, edge_name in enumerate(['top', 'right', 'bottom', 'left']):
expected = attr_s if edges & (2 ** edge_exp) else None
assert props.getAttribute(f'border{edge_name}') == expected
def test_ods_writer_border_style_caches(ods_writer):
expected = ods_writer.border_style(core.Border.TOP)
width, style, color = expected.childNodes[0].getAttribute('bordertop').split()
actual = ods_writer.border_style(core.Border.TOP, width, style, color)
assert actual is expected
@pytest.mark.parametrize('argname,val1,val2', [
('edges', core.Border.TOP, core.Border.LEFT),
('edges', core.Border.TOP, core.Border.TOP | core.Border.BOTTOM),
('style', 'solid', 'dashed'),
('width', '1px', '1em'),
('width', '1px', '2px'),
('color', '#0000fe', '#0000ff'),
])
def test_ods_writer_border_no_caching(ods_writer, argname, val1, val2):
kwargs = {'edges': core.Border.TOP}
kwargs[argname] = val1
style1 = ods_writer.border_style(**kwargs)
kwargs[argname] = val2
style2 = ods_writer.border_style(**kwargs)
assert style1 is not style2
def test_ods_writer_merge_styles(ods_writer):
style = ods_writer.merge_styles(ods_writer.style_bold, ods_writer.style_dividerline)
actual = get_child(