reports: Add BaseODS.border_style() method.
This commit is contained in:
parent
138928eebf
commit
15becebf5c
2 changed files with 78 additions and 3 deletions
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue