rtf.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. __author__ = 'marcos.medeiros'
  2. class RtfTemplate(object):
  3. """
  4. A file object that when read fills an RTF template with context data.
  5. The template variables must come between brackets "[]", instead of braces "{}",
  6. and will be interpreted with the builtin format rules (that means you can reference
  7. fields and elements, apply formatting rules (with comma ":") to the returned values, etc).
  8. Use two open brackets to represent an open bracket. Close brackets without an equivalent
  9. open bracket are repeated verbatim. Thus, to represent the text "[]", the original document
  10. must contain the text "[[]".
  11. Brackets were chosen because they aren't parsed by the RTF format, and thus can be inserted
  12. directly from WYSIWYG editors.
  13. """
  14. def __init__(self, file, context):
  15. """
  16. Open the template for reading, replacing the variables with the values from the
  17. given context.
  18. :param file: Template that will be read.
  19. :param context: Values that will be inserted at the place of the variables.
  20. """
  21. self.name = file
  22. self.template = open(file, 'r')
  23. self.encoding = self.template.encoding
  24. self.context = context
  25. self.pattern = ''
  26. self.brackets = 0
  27. self.partial = ''
  28. self.softspace = 0
  29. def format(self, template):
  30. """
  31. Formats the template with this object's context.
  32. Notice that a template may contain only part of a variable,
  33. what will make its value appear only at a later call of format.
  34. :param template: Original template text.
  35. :return: The formatted text.
  36. """
  37. ret = ''
  38. for c in template:
  39. if self.brackets == 0 and c != '[':
  40. ret += c
  41. else:
  42. if c == '[':
  43. if self.brackets != 0:
  44. self.pattern += c
  45. self.brackets += 1
  46. if self.brackets == 2 and self.pattern == '[':
  47. ret += '['
  48. self.brackets = 0
  49. self.pattern = ''
  50. elif c == ']':
  51. self.brackets -= 1
  52. if self.brackets == 0:
  53. ret += ('{'+self.pattern+'}').format(**self.context)
  54. self.pattern = ''
  55. else:
  56. self.pattern += c
  57. else:
  58. self.pattern += c
  59. return ret
  60. def _next_with(self, func):
  61. buf = func()
  62. f = self.format(buf)
  63. if buf and not f:
  64. return self._next_with(func)
  65. return f
  66. def next(self):
  67. return self._next_with(self.template.next)
  68. def __next__(self):
  69. return self._next_with(self.template.__next__)
  70. def _read_by(self, func, size):
  71. actual_size = size
  72. if self.partial:
  73. actual_size = size - len(self.partial)
  74. if actual_size < 0 < size:
  75. f = self.partial[0:size]
  76. self.partial = self.partial[size:]
  77. return f
  78. buf = func(actual_size)
  79. f = self.partial + self.format(buf)
  80. if buf and not f:
  81. return self.read(size)
  82. if 0 < size < len(f):
  83. self.partial = f[size:]
  84. return f[0:size]
  85. self.partial = ''
  86. return f
  87. def read(self, size=-1):
  88. return self._read_by(self.template.read, size)
  89. def readline(self, size):
  90. return self._read_by(self.template.readline, size)
  91. def close(self):
  92. self.template.close()
  93. def isatty(self):
  94. return self.template.isatty()
  95. def __iter__(self):
  96. return self
  97. @property
  98. def closed(self):
  99. return self.template.closed
  100. @property
  101. def newlines(self):
  102. return self.template.newlines
  103. @classmethod
  104. def format_into(cls, template, context, file):
  105. """
  106. Formats the template, writing the resulting data into the given file.
  107. :param template: Name of the template file.
  108. :param context: Context where variables will be evaluated.
  109. :param file: Name of the file that will be written with the formatted data.
  110. """
  111. t = RtfTemplate(template, context)
  112. f = open(file, 'w')
  113. for l in t:
  114. f.write(l)
  115. f.close()
  116. t.close()