Skip to content

Commit

Permalink
Add template option to load text from file
Browse files Browse the repository at this point in the history
  • Loading branch information
pablerass committed Jan 19, 2024
1 parent b716b83 commit e7f8c65
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 16 deletions.
36 changes: 33 additions & 3 deletions cartuli/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,23 @@ def _load_image(image_file: str | Path) -> Image.Image:
return Image.open(image_file)


def _load_text(text_file: str | Path) -> str:
text_file = Path(text_file)

if text_file.suffix == '.md':
# TODO: Implement convert markdown to supported svg content
pass

return text_file.read_text()


# TODO: Implement load template parameters from yml or CSV file
class _TemplateParameters:
EXTENSION_MAPPINGS = {
tuple(Image.registered_extensions()): _load_image,
tuple(['.txt', '.html', '.md']): _load_text
}

def __init__(self, parameters: Iterable[dict[ParameterKey, ParameterValue]]):
self.__parameters = parameters

Expand All @@ -70,16 +86,26 @@ def _convert_dict_of_lists_to_list_of_dicts(dict_of_lists: dict) -> list:

return list_of_dicts

@classmethod
def _load_parameter_from_file(cls, parameter_file: str | Path) -> ParameterValue:
file_extension = Path(parameter_file).suffix

for extensions, function in cls.EXTENSION_MAPPINGS.items():
if file_extension in extensions:
return function(parameter_file)

raise ValueError(f"Unmanageable extension for '{parameter_file}'")

@classmethod
def from_dict(cls, definition: dict | str) -> _TemplateParameters:
if isinstance(definition, str):
return # TODO: Implement

parameter_values = {}
for parameter in definition.keys():
image_files = sorted(glob(definition[parameter]))
files = sorted(glob(definition[parameter]))
parameter_values |= {
parameter: [_load_image(image_file) for image_file in image_files]
parameter: [cls._load_parameter_from_file(file) for file in files]
}

return cls(cls._convert_dict_of_lists_to_list_of_dicts(parameter_values))
Expand All @@ -89,7 +115,11 @@ def create_images(self, template: Template, name_parameter: str = None) -> list[
for parameters in self.__parameters:
image = template.create_image(parameters)
if name_parameter:
image.filename = parameters[name_parameter].filename
if isinstance(parameters[name_parameter], Image.Image):
image.filename = parameters[name_parameter].filename
else:
# TUNE: This does not work as expected for filters as this does not contains the file name
image.filename = parameters[name_parameter]
images.append(image)

return images
Expand Down
33 changes: 26 additions & 7 deletions cartuli/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
ParameterValue = Image.Image | str


# TODO: Implement multiline and rich formatted text template values support

def _image_to_uri(image: Image.Image | str | Path, encoding: str = 'UTF-8') -> str:
if isinstance(image, str):
image = Path(image)
Expand All @@ -31,12 +33,18 @@ def _image_to_uri(image: Image.Image | str | Path, encoding: str = 'UTF-8') -> s
return f"data:image/png;base64,{base64.b64encode(image_buffer.getvalue()).decode(encoding)}"


def _get_innermost_tspan(text_element: etree._Element) -> etree._Element:
return next(text_element.iterchildren("*", reversed=True))


def _set_template_text(text_element: etree._Element, value: str):
text_element.getchildren()[0].text = value
# TODO: Find the last tspan to set the text at
_get_innermost_tspan(text_element).text = value


def _get_template_text(text_element: etree._Element) -> str:
return text_element.getchildren()[0].text
# TODO: Find the last tspan to set the text at
return _get_innermost_tspan(text_element).text


def _set_template_image(image_element: etree._Element, value: Image.Image, encoding: str = 'UTF-8'):
Expand All @@ -52,6 +60,7 @@ def _get_template_image(image_element: etree._Element, encoding: str = 'UTF-8')

# TUNE: Probably this should not be implemented here
def svg_file_to_image(svg_file: str | Path, dpi: int = DEFAULT_SVG_DPI) -> Image.Image:
# TUNE: CairoSVG does different things than Inkspace
if isinstance(svg_file, str):
svg_file = Path(svg_file)

Expand All @@ -66,6 +75,15 @@ def svg_content_to_image(svg_content: str, dpi: int = DEFAULT_SVG_DPI) -> Image.
return Image.open(io.BytesIO(image_data))


def _is_text_element(element: etree._Element) -> bool:
return (element.tag.endswith('text') or
element.tag.endswith('tspan'))


def _is_image_element(element: etree._Element) -> bool:
return (element.tag.endswith('image'))


class Template:
def __init__(self, template: TemplateContent | etree._Element, parameters: Iterable[ParameterKey],
dpi: int = DEFAULT_SVG_DPI):
Expand All @@ -87,7 +105,8 @@ def __init__(self, template: TemplateContent | etree._Element, parameters: Itera

if element is None:
raise ValueError(f"Parameter '{parameter}' not found in template")
if not (element.tag.endswith('image') or element.tag.endswith('text')):
# TODO: Add tspan also as possible tag value
if not (_is_image_element(element) or _is_text_element(element)):
raise ValueError(f"Parameter '{parameter}' element '{element.tag}' is unsupported")

self.__parameters = tuple(parameters)
Expand Down Expand Up @@ -125,13 +144,13 @@ def apply_parameters(self, parameters: dict[ParameterKey, ParameterValue]) -> Te

if element is None:
raise ValueError(f"Parameter '{parameter}' not found in template")
if element.tag.endswith('image'):
if _is_image_element(element):
if isinstance(value, Image.Image):
_set_template_image(element, value, encoding=self.__encoding)
else:
raise ValueError((f"Parameter '{parameter}' value '{value}' is invalid "
f"for '{element.tag}'"))
if element.tag.endswith('text'):
if _is_text_element(element):
if isinstance(value, str):
_set_template_text(element, value)
else:
Expand Down Expand Up @@ -160,9 +179,9 @@ def get_values(self, content: TemplateContent | etree._Element,
element = content_tree.find(f".//*[@id='{parameter}']")
if element is None:
raise ValueError(f"parameter '{parameter}' not found in content")
if element.tag.endswith('image'):
if _is_image_element(element):
value = _get_template_image(element)
if element.tag.endswith('text'):
if _is_text_element(element):
value = _get_template_text(element)

values |= {parameter: value}
Expand Down
12 changes: 6 additions & 6 deletions tests/test_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,7 @@ def test_definition(random_image_file):
assert definition.sheets['cards', ].print_margin == 3*mm


def test_filters(random_image_file):
# TODO: Implement filters testing
pass


def test_convert_dict_of_lists_to_list_of_dicts():
def test_template_parameters_convert_dict_of_lists_to_list_of_dicts():
assert _TemplateParameters._convert_dict_of_lists_to_list_of_dicts({
'a': [1, 2, 3, 4],
'b': [5, 6, 7, 8]
Expand All @@ -141,3 +136,8 @@ def test_convert_dict_of_lists_to_list_of_dicts():
{'a': 3, 'b': 7},
{'a': 4, 'b': 8},
]


# TODO: Test load template parameters from yml or CSV file
def test_template_parameters_load_parameter_from_file():
pass

0 comments on commit e7f8c65

Please sign in to comment.