#!/usr/bin/env python # -*- coding: utf-8 -*- from secretary import Renderer, parseString from xml.parsers.expat import ExpatError, ErrorString from datetime import datetime import locale from PIL import Image import re from django.conf import settings RE_UNITS = re.compile("([.0-9]+)([a-z]+)") def parse_value_unit(value): m = RE_UNITS.match(value) if not m: return None, None value, unit = m.groups() value = float(value) return value, unit def replace_line_breaks(value): return (value or "").replace('\r\n', '\n') def capfirst_filter(value): return value[0].upper() + value[1:] if value else value def lowerfirst_filter(value): return value[0].lower() + value[1:] if value else value RE_CAP = re.compile(r"[^-' ]+") SEP = ("un", "une", "le", "la", "les", "lez", "d", "l", "de", "des", "du") def capitalize_filter(value): if not value: return "" value = value.lower() res = "" for m in RE_CAP.finditer(value): start = m.start() if start: res += value[start - 1] v = m.group() if v not in SEP: v = v[0].upper() + v[1:] res += v return res def human_date_filter(value): try: value = datetime.strptime(value, "%Y-%m-%d") except ValueError: return "" language_code = settings.LANGUAGE_CODE.split('-') language_code = language_code[0] + "_" + language_code[1].upper() for language_suffix in (".utf8", ""): try: locale.setlocale(locale.LC_TIME, language_code + language_suffix) break except locale.Error: pass return value.strftime(settings.DATE_FORMAT) def splitpart(value, index, char=','): if not value or not index: return "" splited = value.split(char) if len(splited) > index: return splited[index] return "" class IshtarSecretaryRenderer(Renderer): def __init__(self, *args, **kwargs): super(IshtarSecretaryRenderer, self).__init__(*args, **kwargs) self.media_callback = self.ishtar_media_loader self.media_path = settings.MEDIA_ROOT self.environment.filters['human_date'] = human_date_filter self.environment.filters['capfirst'] = capfirst_filter self.environment.filters['lowerfirst'] = lowerfirst_filter self.environment.filters['capitalize'] = capitalize_filter self.environment.filters['replace_line_breaks'] = replace_line_breaks self.environment.filters['splitpart'] = splitpart def ishtar_media_loader(self, media, *args, **kwargs): res = self.fs_loader(media, *args, **kwargs) if not res or not res[0]: return image_file, mime = res if "width" in kwargs: kwargs['frame_attrs']['svg:width'] = kwargs["width"] if "height" in kwargs: kwargs['frame_attrs']['svg:height'] = kwargs["height"] if "keep_ratio" in args: image = Image.open(image_file.name) width, width_unit = parse_value_unit( kwargs['frame_attrs']['svg:width']) height, height_unit = parse_value_unit( kwargs['frame_attrs']['svg:height']) if "height" not in kwargs and width: new_height = width * image.height / image.width kwargs['frame_attrs']['svg:height'] = "{}{}".format( new_height, width_unit ) if "width" not in kwargs and height: new_width = height * image.width / image.height kwargs['frame_attrs']['svg:width'] = "{}{}".format( new_width, height_unit ) return image_file, mime def _render_xml(self, xml_document, **kwargs): # Prepare the xml object to be processed by jinja2 self.log.debug('Rendering XML object') template_string = "" try: self.template_images = dict() self._prepare_document_tags(xml_document) xml_source = xml_document.toxml() xml_source = xml_source.encode('ascii', 'xmlcharrefreplace') jinja_template = self.environment.from_string( self._unescape_entities(xml_source.decode('utf-8')) ) result = jinja_template.render(**kwargs) final_xml = parseString(result.encode('ascii', 'xmlcharrefreplace')) if self.template_images: self.replace_images(final_xml) return final_xml except ExpatError as e: if not 'result' in locals(): result = xml_source ### changes try: near = result.split('\n')[e.lineno -1][e.offset-200:e.offset+200] except IndexError: near = "..." print(result) ### endchanges raise ExpatError('ExpatError "%s" at line %d, column %d\nNear of: "[...]%s[...]"' % \ (ErrorString(e.code), e.lineno, e.offset, near)) except: self.log.error('Error rendering template:\n%s', xml_document.toprettyxml(), exc_info=True) self.log.error('Unescaped template was:\n{0}'.format(template_string)) raise finally: self.log.debug('Rendering xml object finished')