__author__ = 'marcos.medeiros' class RtfTemplate(object): """ A file object that when read fills an RTF template with context data. The template variables must come between brackets "[]", instead of braces "{}", and will be interpreted with the builtin format rules (that means you can reference fields and elements, apply formatting rules (with comma ":") to the returned values, etc). Use two open brackets to represent an open bracket. Close brackets without an equivalent open bracket are repeated verbatim. Thus, to represent the text "[]", the original document must contain the text "[[]". Brackets were chosen because they aren't parsed by the RTF format, and thus can be inserted directly from WYSIWYG editors. """ def __init__(self, file, context): """ Open the template for reading, replacing the variables with the values from the given context. :param file: Template that will be read. :param context: Values that will be inserted at the place of the variables. """ self.name = file self.template = open(file, 'r') self.encoding = self.template.encoding self.context = context self.pattern = '' self.brackets = 0 self.partial = '' self.softspace = 0 def format(self, template): """ Formats the template with this object's context. Notice that a template may contain only part of a variable, what will make its value appear only at a later call of format. :param template: Original template text. :return: The formatted text. """ ret = '' for c in template: if self.brackets == 0 and c != '[': ret += c else: if c == '[': if self.brackets != 0: self.pattern += c self.brackets += 1 if self.brackets == 2 and self.pattern == '[': ret += '[' self.brackets = 0 self.pattern = '' elif c == ']': self.brackets -= 1 if self.brackets == 0: ret += ('{'+self.pattern+'}').format(**self.context) self.pattern = '' else: self.pattern += c else: self.pattern += c return ret def _next_with(self, func): buf = func() f = self.format(buf) if buf and not f: return self._next_with(func) return f def next(self): return self._next_with(self.template.next) def __next__(self): return self._next_with(self.template.__next__) def _read_by(self, func, size): actual_size = size if self.partial: actual_size = size - len(self.partial) if actual_size < 0 < size: f = self.partial[0:size] self.partial = self.partial[size:] return f buf = func(actual_size) f = self.partial + self.format(buf) if buf and not f: return self.read(size) if 0 < size < len(f): self.partial = f[size:] return f[0:size] self.partial = '' return f def read(self, size=-1): return self._read_by(self.template.read, size) def readline(self, size): return self._read_by(self.template.readline, size) def close(self): self.template.close() def isatty(self): return self.template.isatty() def __iter__(self): return self @property def closed(self): return self.template.closed @property def newlines(self): return self.template.newlines @classmethod def format_into(cls, template, context, file): """ Formats the template, writing the resulting data into the given file. :param template: Name of the template file. :param context: Context where variables will be evaluated. :param file: Name of the file that will be written with the formatted data. """ t = RtfTemplate(template, context) f = open(file, 'w') for l in t: f.write(l) f.close() t.close()