PageRenderTime 32ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/python/helpers/py2only/docutils/writers/odf_odt/__init__.py

http://github.com/JetBrains/intellij-community
Python | 3295 lines | 3129 code | 101 blank | 65 comment | 77 complexity | 09875e6e0f24a7d51e852c94c8cb13e5 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, MPL-2.0-no-copyleft-exception, MIT, EPL-1.0, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. # $Id: __init__.py 7717 2013-08-21 22:01:21Z milde $
  2. # Author: Dave Kuhlman <dkuhlman@rexx.com>
  3. # Copyright: This module has been placed in the public domain.
  4. """
  5. Open Document Format (ODF) Writer.
  6. """
  7. VERSION = '1.0a'
  8. __docformat__ = 'reStructuredText'
  9. import sys
  10. import os
  11. import os.path
  12. import tempfile
  13. import zipfile
  14. from xml.dom import minidom
  15. import time
  16. import re
  17. import StringIO
  18. import copy
  19. import urllib2
  20. import docutils
  21. from docutils import frontend, nodes, utils, writers, languages
  22. from docutils.readers import standalone
  23. from docutils.transforms import references
  24. WhichElementTree = ''
  25. try:
  26. # 1. Try to use lxml.
  27. #from lxml import etree
  28. #WhichElementTree = 'lxml'
  29. raise ImportError('Ignoring lxml')
  30. except ImportError, e:
  31. try:
  32. # 2. Try to use ElementTree from the Python standard library.
  33. from xml.etree import ElementTree as etree
  34. WhichElementTree = 'elementtree'
  35. except ImportError, e:
  36. try:
  37. # 3. Try to use a version of ElementTree installed as a separate
  38. # product.
  39. from elementtree import ElementTree as etree
  40. WhichElementTree = 'elementtree'
  41. except ImportError, e:
  42. s1 = 'Must install either a version of Python containing ' \
  43. 'ElementTree (Python version >=2.5) or install ElementTree.'
  44. raise ImportError(s1)
  45. #
  46. # Import pygments and odtwriter pygments formatters if possible.
  47. try:
  48. import pygments
  49. import pygments.lexers
  50. from pygmentsformatter import OdtPygmentsProgFormatter, \
  51. OdtPygmentsLaTeXFormatter
  52. except ImportError, exp:
  53. pygments = None
  54. # check for the Python Imaging Library
  55. try:
  56. import PIL.Image
  57. except ImportError:
  58. try: # sometimes PIL modules are put in PYTHONPATH's root
  59. import Image
  60. class PIL(object): pass # dummy wrapper
  61. PIL.Image = Image
  62. except ImportError:
  63. PIL = None
  64. ## import warnings
  65. ## warnings.warn('importing IPShellEmbed', UserWarning)
  66. ## from IPython.Shell import IPShellEmbed
  67. ## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ',
  68. ## '-po', 'Out<\\#>: ', '-nosep']
  69. ## ipshell = IPShellEmbed(args,
  70. ## banner = 'Entering IPython. Press Ctrl-D to exit.',
  71. ## exit_msg = 'Leaving Interpreter, back to program.')
  72. #
  73. # ElementTree does not support getparent method (lxml does).
  74. # This wrapper class and the following support functions provide
  75. # that support for the ability to get the parent of an element.
  76. #
  77. if WhichElementTree == 'elementtree':
  78. import weakref
  79. _parents = weakref.WeakKeyDictionary()
  80. if isinstance(etree.Element, type):
  81. _ElementInterface = etree.Element
  82. else:
  83. _ElementInterface = etree._ElementInterface
  84. class _ElementInterfaceWrapper(_ElementInterface):
  85. def __init__(self, tag, attrib=None):
  86. _ElementInterface.__init__(self, tag, attrib)
  87. _parents[self] = None
  88. def setparent(self, parent):
  89. _parents[self] = parent
  90. def getparent(self):
  91. return _parents[self]
  92. #
  93. # Constants and globals
  94. SPACES_PATTERN = re.compile(r'( +)')
  95. TABS_PATTERN = re.compile(r'(\t+)')
  96. FILL_PAT1 = re.compile(r'^ +')
  97. FILL_PAT2 = re.compile(r' {2,}')
  98. TABLESTYLEPREFIX = 'rststyle-table-'
  99. TABLENAMEDEFAULT = '%s0' % TABLESTYLEPREFIX
  100. TABLEPROPERTYNAMES = ('border', 'border-top', 'border-left',
  101. 'border-right', 'border-bottom', )
  102. GENERATOR_DESC = 'Docutils.org/odf_odt'
  103. NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
  104. CONTENT_NAMESPACE_DICT = CNSD = {
  105. # 'office:version': '1.0',
  106. 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  107. 'dc': 'http://purl.org/dc/elements/1.1/',
  108. 'dom': 'http://www.w3.org/2001/xml-events',
  109. 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  110. 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  111. 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  112. 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  113. 'math': 'http://www.w3.org/1998/Math/MathML',
  114. 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  115. 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  116. 'office': NAME_SPACE_1,
  117. 'ooo': 'http://openoffice.org/2004/office',
  118. 'oooc': 'http://openoffice.org/2004/calc',
  119. 'ooow': 'http://openoffice.org/2004/writer',
  120. 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  121. 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  122. 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  123. 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  124. 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  125. 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  126. 'xforms': 'http://www.w3.org/2002/xforms',
  127. 'xlink': 'http://www.w3.org/1999/xlink',
  128. 'xsd': 'http://www.w3.org/2001/XMLSchema',
  129. 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
  130. }
  131. STYLES_NAMESPACE_DICT = SNSD = {
  132. # 'office:version': '1.0',
  133. 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  134. 'dc': 'http://purl.org/dc/elements/1.1/',
  135. 'dom': 'http://www.w3.org/2001/xml-events',
  136. 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  137. 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  138. 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  139. 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  140. 'math': 'http://www.w3.org/1998/Math/MathML',
  141. 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  142. 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  143. 'office': NAME_SPACE_1,
  144. 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  145. 'ooo': 'http://openoffice.org/2004/office',
  146. 'oooc': 'http://openoffice.org/2004/calc',
  147. 'ooow': 'http://openoffice.org/2004/writer',
  148. 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  149. 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  150. 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  151. 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  152. 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  153. 'xlink': 'http://www.w3.org/1999/xlink',
  154. }
  155. MANIFEST_NAMESPACE_DICT = MANNSD = {
  156. 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
  157. }
  158. META_NAMESPACE_DICT = METNSD = {
  159. # 'office:version': '1.0',
  160. 'dc': 'http://purl.org/dc/elements/1.1/',
  161. 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  162. 'office': NAME_SPACE_1,
  163. 'ooo': 'http://openoffice.org/2004/office',
  164. 'xlink': 'http://www.w3.org/1999/xlink',
  165. }
  166. #
  167. # Attribute dictionaries for use with ElementTree (not lxml), which
  168. # does not support use of nsmap parameter on Element() and SubElement().
  169. CONTENT_NAMESPACE_ATTRIB = {
  170. #'office:version': '1.0',
  171. 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  172. 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
  173. 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
  174. 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  175. 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  176. 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  177. 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  178. 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
  179. 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  180. 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  181. 'xmlns:office': NAME_SPACE_1,
  182. 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  183. 'xmlns:ooo': 'http://openoffice.org/2004/office',
  184. 'xmlns:oooc': 'http://openoffice.org/2004/calc',
  185. 'xmlns:ooow': 'http://openoffice.org/2004/writer',
  186. 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  187. 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  188. 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  189. 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  190. 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  191. 'xmlns:xforms': 'http://www.w3.org/2002/xforms',
  192. 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  193. 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
  194. 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
  195. }
  196. STYLES_NAMESPACE_ATTRIB = {
  197. #'office:version': '1.0',
  198. 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  199. 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
  200. 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
  201. 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  202. 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  203. 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  204. 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  205. 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
  206. 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  207. 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  208. 'xmlns:office': NAME_SPACE_1,
  209. 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  210. 'xmlns:ooo': 'http://openoffice.org/2004/office',
  211. 'xmlns:oooc': 'http://openoffice.org/2004/calc',
  212. 'xmlns:ooow': 'http://openoffice.org/2004/writer',
  213. 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  214. 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  215. 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  216. 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  217. 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  218. 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  219. }
  220. MANIFEST_NAMESPACE_ATTRIB = {
  221. 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
  222. }
  223. META_NAMESPACE_ATTRIB = {
  224. #'office:version': '1.0',
  225. 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
  226. 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  227. 'xmlns:office': NAME_SPACE_1,
  228. 'xmlns:ooo': 'http://openoffice.org/2004/office',
  229. 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  230. }
  231. #
  232. # Functions
  233. #
  234. #
  235. # ElementTree support functions.
  236. # In order to be able to get the parent of elements, must use these
  237. # instead of the functions with same name provided by ElementTree.
  238. #
  239. def Element(tag, attrib=None, nsmap=None, nsdict=CNSD):
  240. if attrib is None:
  241. attrib = {}
  242. tag, attrib = fix_ns(tag, attrib, nsdict)
  243. if WhichElementTree == 'lxml':
  244. el = etree.Element(tag, attrib, nsmap=nsmap)
  245. else:
  246. el = _ElementInterfaceWrapper(tag, attrib)
  247. return el
  248. def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD):
  249. if attrib is None:
  250. attrib = {}
  251. tag, attrib = fix_ns(tag, attrib, nsdict)
  252. if WhichElementTree == 'lxml':
  253. el = etree.SubElement(parent, tag, attrib, nsmap=nsmap)
  254. else:
  255. el = _ElementInterfaceWrapper(tag, attrib)
  256. parent.append(el)
  257. el.setparent(parent)
  258. return el
  259. def fix_ns(tag, attrib, nsdict):
  260. nstag = add_ns(tag, nsdict)
  261. nsattrib = {}
  262. for key, val in attrib.iteritems():
  263. nskey = add_ns(key, nsdict)
  264. nsattrib[nskey] = val
  265. return nstag, nsattrib
  266. def add_ns(tag, nsdict=CNSD):
  267. if WhichElementTree == 'lxml':
  268. nstag, name = tag.split(':')
  269. ns = nsdict.get(nstag)
  270. if ns is None:
  271. raise RuntimeError, 'Invalid namespace prefix: %s' % nstag
  272. tag = '{%s}%s' % (ns, name,)
  273. return tag
  274. def ToString(et):
  275. outstream = StringIO.StringIO()
  276. if sys.version_info >= (3, 2):
  277. et.write(outstream, encoding="unicode")
  278. else:
  279. et.write(outstream)
  280. s1 = outstream.getvalue()
  281. outstream.close()
  282. return s1
  283. def escape_cdata(text):
  284. text = text.replace("&", "&amp;")
  285. text = text.replace("<", "&lt;")
  286. text = text.replace(">", "&gt;")
  287. ascii = ''
  288. for char in text:
  289. if ord(char) >= ord("\x7f"):
  290. ascii += "&#x%X;" % ( ord(char), )
  291. else:
  292. ascii += char
  293. return ascii
  294. WORD_SPLIT_PAT1 = re.compile(r'\b(\w*)\b\W*')
  295. def split_words(line):
  296. # We need whitespace at the end of the string for our regexpr.
  297. line += ' '
  298. words = []
  299. pos1 = 0
  300. mo = WORD_SPLIT_PAT1.search(line, pos1)
  301. while mo is not None:
  302. word = mo.groups()[0]
  303. words.append(word)
  304. pos1 = mo.end()
  305. mo = WORD_SPLIT_PAT1.search(line, pos1)
  306. return words
  307. #
  308. # Classes
  309. #
  310. class TableStyle(object):
  311. def __init__(self, border=None, backgroundcolor=None):
  312. self.border = border
  313. self.backgroundcolor = backgroundcolor
  314. def get_border_(self):
  315. return self.border_
  316. def set_border_(self, border):
  317. self.border_ = border
  318. border = property(get_border_, set_border_)
  319. def get_backgroundcolor_(self):
  320. return self.backgroundcolor_
  321. def set_backgroundcolor_(self, backgroundcolor):
  322. self.backgroundcolor_ = backgroundcolor
  323. backgroundcolor = property(get_backgroundcolor_, set_backgroundcolor_)
  324. BUILTIN_DEFAULT_TABLE_STYLE = TableStyle(
  325. border = '0.0007in solid #000000')
  326. #
  327. # Information about the indentation level for lists nested inside
  328. # other contexts, e.g. dictionary lists.
  329. class ListLevel(object):
  330. def __init__(self, level, sibling_level=True, nested_level=True):
  331. self.level = level
  332. self.sibling_level = sibling_level
  333. self.nested_level = nested_level
  334. def set_sibling(self, sibling_level): self.sibling_level = sibling_level
  335. def get_sibling(self): return self.sibling_level
  336. def set_nested(self, nested_level): self.nested_level = nested_level
  337. def get_nested(self): return self.nested_level
  338. def set_level(self, level): self.level = level
  339. def get_level(self): return self.level
  340. class Writer(writers.Writer):
  341. MIME_TYPE = 'application/vnd.oasis.opendocument.text'
  342. EXTENSION = '.odt'
  343. supported = ('odt', )
  344. """Formats this writer supports."""
  345. default_stylesheet = 'styles' + EXTENSION
  346. default_stylesheet_path = utils.relative_path(
  347. os.path.join(os.getcwd(), 'dummy'),
  348. os.path.join(os.path.dirname(__file__), default_stylesheet))
  349. default_template = 'template.txt'
  350. default_template_path = utils.relative_path(
  351. os.path.join(os.getcwd(), 'dummy'),
  352. os.path.join(os.path.dirname(__file__), default_template))
  353. settings_spec = (
  354. 'ODF-Specific Options',
  355. None,
  356. (
  357. ('Specify a stylesheet. '
  358. 'Default: "%s"' % default_stylesheet_path,
  359. ['--stylesheet'],
  360. {
  361. 'default': default_stylesheet_path,
  362. 'dest': 'stylesheet'
  363. }),
  364. ('Specify a configuration/mapping file relative to the '
  365. 'current working '
  366. 'directory for additional ODF options. '
  367. 'In particular, this file may contain a section named '
  368. '"Formats" that maps default style names to '
  369. 'names to be used in the resulting output file allowing for '
  370. 'adhering to external standards. '
  371. 'For more info and the format of the configuration/mapping file, '
  372. 'see the odtwriter doc.',
  373. ['--odf-config-file'],
  374. {'metavar': '<file>'}),
  375. ('Obfuscate email addresses to confuse harvesters while still '
  376. 'keeping email links usable with standards-compliant browsers.',
  377. ['--cloak-email-addresses'],
  378. {'default': False,
  379. 'action': 'store_true',
  380. 'dest': 'cloak_email_addresses',
  381. 'validator': frontend.validate_boolean}),
  382. ('Do not obfuscate email addresses.',
  383. ['--no-cloak-email-addresses'],
  384. {'default': False,
  385. 'action': 'store_false',
  386. 'dest': 'cloak_email_addresses',
  387. 'validator': frontend.validate_boolean}),
  388. ('Specify the thickness of table borders in thousands of a cm. '
  389. 'Default is 35.',
  390. ['--table-border-thickness'],
  391. {'default': None,
  392. 'validator': frontend.validate_nonnegative_int}),
  393. ('Add syntax highlighting in literal code blocks.',
  394. ['--add-syntax-highlighting'],
  395. {'default': False,
  396. 'action': 'store_true',
  397. 'dest': 'add_syntax_highlighting',
  398. 'validator': frontend.validate_boolean}),
  399. ('Do not add syntax highlighting in literal code blocks. (default)',
  400. ['--no-syntax-highlighting'],
  401. {'default': False,
  402. 'action': 'store_false',
  403. 'dest': 'add_syntax_highlighting',
  404. 'validator': frontend.validate_boolean}),
  405. ('Create sections for headers. (default)',
  406. ['--create-sections'],
  407. {'default': True,
  408. 'action': 'store_true',
  409. 'dest': 'create_sections',
  410. 'validator': frontend.validate_boolean}),
  411. ('Do not create sections for headers.',
  412. ['--no-sections'],
  413. {'default': True,
  414. 'action': 'store_false',
  415. 'dest': 'create_sections',
  416. 'validator': frontend.validate_boolean}),
  417. ('Create links.',
  418. ['--create-links'],
  419. {'default': False,
  420. 'action': 'store_true',
  421. 'dest': 'create_links',
  422. 'validator': frontend.validate_boolean}),
  423. ('Do not create links. (default)',
  424. ['--no-links'],
  425. {'default': False,
  426. 'action': 'store_false',
  427. 'dest': 'create_links',
  428. 'validator': frontend.validate_boolean}),
  429. ('Generate endnotes at end of document, not footnotes '
  430. 'at bottom of page.',
  431. ['--endnotes-end-doc'],
  432. {'default': False,
  433. 'action': 'store_true',
  434. 'dest': 'endnotes_end_doc',
  435. 'validator': frontend.validate_boolean}),
  436. ('Generate footnotes at bottom of page, not endnotes '
  437. 'at end of document. (default)',
  438. ['--no-endnotes-end-doc'],
  439. {'default': False,
  440. 'action': 'store_false',
  441. 'dest': 'endnotes_end_doc',
  442. 'validator': frontend.validate_boolean}),
  443. ('Generate a bullet list table of contents, not '
  444. 'an ODF/oowriter table of contents.',
  445. ['--generate-list-toc'],
  446. {'default': True,
  447. 'action': 'store_false',
  448. 'dest': 'generate_oowriter_toc',
  449. 'validator': frontend.validate_boolean}),
  450. ('Generate an ODF/oowriter table of contents, not '
  451. 'a bullet list. (default)',
  452. ['--generate-oowriter-toc'],
  453. {'default': True,
  454. 'action': 'store_true',
  455. 'dest': 'generate_oowriter_toc',
  456. 'validator': frontend.validate_boolean}),
  457. ('Specify the contents of an custom header line. '
  458. 'See odf_odt writer documentation for details '
  459. 'about special field character sequences.',
  460. ['--custom-odt-header'],
  461. { 'default': '',
  462. 'dest': 'custom_header',
  463. }),
  464. ('Specify the contents of an custom footer line. '
  465. 'See odf_odt writer documentation for details '
  466. 'about special field character sequences.',
  467. ['--custom-odt-footer'],
  468. { 'default': '',
  469. 'dest': 'custom_footer',
  470. }),
  471. )
  472. )
  473. settings_defaults = {
  474. 'output_encoding_error_handler': 'xmlcharrefreplace',
  475. }
  476. relative_path_settings = (
  477. 'stylesheet_path',
  478. )
  479. config_section = 'odf_odt writer'
  480. config_section_dependencies = (
  481. 'writers',
  482. )
  483. def __init__(self):
  484. writers.Writer.__init__(self)
  485. self.translator_class = ODFTranslator
  486. def translate(self):
  487. self.settings = self.document.settings
  488. self.visitor = self.translator_class(self.document)
  489. self.visitor.retrieve_styles(self.EXTENSION)
  490. self.document.walkabout(self.visitor)
  491. self.visitor.add_doc_title()
  492. self.assemble_my_parts()
  493. self.output = self.parts['whole']
  494. def assemble_my_parts(self):
  495. """Assemble the `self.parts` dictionary. Extend in subclasses.
  496. """
  497. writers.Writer.assemble_parts(self)
  498. f = tempfile.NamedTemporaryFile()
  499. zfile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
  500. self.write_zip_str(zfile, 'mimetype', self.MIME_TYPE,
  501. compress_type=zipfile.ZIP_STORED)
  502. content = self.visitor.content_astext()
  503. self.write_zip_str(zfile, 'content.xml', content)
  504. s1 = self.create_manifest()
  505. self.write_zip_str(zfile, 'META-INF/manifest.xml', s1)
  506. s1 = self.create_meta()
  507. self.write_zip_str(zfile, 'meta.xml', s1)
  508. s1 = self.get_stylesheet()
  509. self.write_zip_str(zfile, 'styles.xml', s1)
  510. self.store_embedded_files(zfile)
  511. self.copy_from_stylesheet(zfile)
  512. zfile.close()
  513. f.seek(0)
  514. whole = f.read()
  515. f.close()
  516. self.parts['whole'] = whole
  517. self.parts['encoding'] = self.document.settings.output_encoding
  518. self.parts['version'] = docutils.__version__
  519. def write_zip_str(self, zfile, name, bytes, compress_type=zipfile.ZIP_DEFLATED):
  520. localtime = time.localtime(time.time())
  521. zinfo = zipfile.ZipInfo(name, localtime)
  522. # Add some standard UNIX file access permissions (-rw-r--r--).
  523. zinfo.external_attr = (0x81a4 & 0xFFFF) << 16L
  524. zinfo.compress_type = compress_type
  525. zfile.writestr(zinfo, bytes)
  526. def store_embedded_files(self, zfile):
  527. embedded_files = self.visitor.get_embedded_file_list()
  528. for source, destination in embedded_files:
  529. if source is None:
  530. continue
  531. try:
  532. # encode/decode
  533. destination1 = destination.decode('latin-1').encode('utf-8')
  534. zfile.write(source, destination1)
  535. except OSError, e:
  536. self.document.reporter.warning(
  537. "Can't open file %s." % (source, ))
  538. def get_settings(self):
  539. """
  540. modeled after get_stylesheet
  541. """
  542. stylespath = self.settings.stylesheet
  543. zfile = zipfile.ZipFile(stylespath, 'r')
  544. s1 = zfile.read('settings.xml')
  545. zfile.close()
  546. return s1
  547. def get_stylesheet(self):
  548. """Get the stylesheet from the visitor.
  549. Ask the visitor to setup the page.
  550. """
  551. s1 = self.visitor.setup_page()
  552. return s1
  553. def copy_from_stylesheet(self, outzipfile):
  554. """Copy images, settings, etc from the stylesheet doc into target doc.
  555. """
  556. stylespath = self.settings.stylesheet
  557. inzipfile = zipfile.ZipFile(stylespath, 'r')
  558. # Copy the styles.
  559. s1 = inzipfile.read('settings.xml')
  560. self.write_zip_str(outzipfile, 'settings.xml', s1)
  561. # Copy the images.
  562. namelist = inzipfile.namelist()
  563. for name in namelist:
  564. if name.startswith('Pictures/'):
  565. imageobj = inzipfile.read(name)
  566. outzipfile.writestr(name, imageobj)
  567. inzipfile.close()
  568. def assemble_parts(self):
  569. pass
  570. def create_manifest(self):
  571. if WhichElementTree == 'lxml':
  572. root = Element('manifest:manifest',
  573. nsmap=MANIFEST_NAMESPACE_DICT,
  574. nsdict=MANIFEST_NAMESPACE_DICT,
  575. )
  576. else:
  577. root = Element('manifest:manifest',
  578. attrib=MANIFEST_NAMESPACE_ATTRIB,
  579. nsdict=MANIFEST_NAMESPACE_DICT,
  580. )
  581. doc = etree.ElementTree(root)
  582. SubElement(root, 'manifest:file-entry', attrib={
  583. 'manifest:media-type': self.MIME_TYPE,
  584. 'manifest:full-path': '/',
  585. }, nsdict=MANNSD)
  586. SubElement(root, 'manifest:file-entry', attrib={
  587. 'manifest:media-type': 'text/xml',
  588. 'manifest:full-path': 'content.xml',
  589. }, nsdict=MANNSD)
  590. SubElement(root, 'manifest:file-entry', attrib={
  591. 'manifest:media-type': 'text/xml',
  592. 'manifest:full-path': 'styles.xml',
  593. }, nsdict=MANNSD)
  594. SubElement(root, 'manifest:file-entry', attrib={
  595. 'manifest:media-type': 'text/xml',
  596. 'manifest:full-path': 'settings.xml',
  597. }, nsdict=MANNSD)
  598. SubElement(root, 'manifest:file-entry', attrib={
  599. 'manifest:media-type': 'text/xml',
  600. 'manifest:full-path': 'meta.xml',
  601. }, nsdict=MANNSD)
  602. s1 = ToString(doc)
  603. doc = minidom.parseString(s1)
  604. s1 = doc.toprettyxml(' ')
  605. return s1
  606. def create_meta(self):
  607. if WhichElementTree == 'lxml':
  608. root = Element('office:document-meta',
  609. nsmap=META_NAMESPACE_DICT,
  610. nsdict=META_NAMESPACE_DICT,
  611. )
  612. else:
  613. root = Element('office:document-meta',
  614. attrib=META_NAMESPACE_ATTRIB,
  615. nsdict=META_NAMESPACE_DICT,
  616. )
  617. doc = etree.ElementTree(root)
  618. root = SubElement(root, 'office:meta', nsdict=METNSD)
  619. el1 = SubElement(root, 'meta:generator', nsdict=METNSD)
  620. el1.text = 'Docutils/rst2odf.py/%s' % (VERSION, )
  621. s1 = os.environ.get('USER', '')
  622. el1 = SubElement(root, 'meta:initial-creator', nsdict=METNSD)
  623. el1.text = s1
  624. s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime())
  625. el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD)
  626. el1.text = s2
  627. el1 = SubElement(root, 'dc:creator', nsdict=METNSD)
  628. el1.text = s1
  629. el1 = SubElement(root, 'dc:date', nsdict=METNSD)
  630. el1.text = s2
  631. el1 = SubElement(root, 'dc:language', nsdict=METNSD)
  632. el1.text = 'en-US'
  633. el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD)
  634. el1.text = '1'
  635. el1 = SubElement(root, 'meta:editing-duration', nsdict=METNSD)
  636. el1.text = 'PT00M01S'
  637. title = self.visitor.get_title()
  638. el1 = SubElement(root, 'dc:title', nsdict=METNSD)
  639. if title:
  640. el1.text = title
  641. else:
  642. el1.text = '[no title]'
  643. meta_dict = self.visitor.get_meta_dict()
  644. keywordstr = meta_dict.get('keywords')
  645. if keywordstr is not None:
  646. keywords = split_words(keywordstr)
  647. for keyword in keywords:
  648. el1 = SubElement(root, 'meta:keyword', nsdict=METNSD)
  649. el1.text = keyword
  650. description = meta_dict.get('description')
  651. if description is not None:
  652. el1 = SubElement(root, 'dc:description', nsdict=METNSD)
  653. el1.text = description
  654. s1 = ToString(doc)
  655. #doc = minidom.parseString(s1)
  656. #s1 = doc.toprettyxml(' ')
  657. return s1
  658. # class ODFTranslator(nodes.SparseNodeVisitor):
  659. class ODFTranslator(nodes.GenericNodeVisitor):
  660. used_styles = (
  661. 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
  662. 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
  663. 'bulletitem', 'bulletlist',
  664. 'caption', 'legend',
  665. 'centeredtextbody', 'codeblock', 'codeblock-indented',
  666. 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
  667. 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
  668. 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
  669. 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
  670. 'epigraph-enumitem', 'epigraph-enumlist', 'footer',
  671. 'footnote', 'citation',
  672. 'header', 'highlights', 'highlights-bulletitem',
  673. 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
  674. 'horizontalline', 'inlineliteral', 'quotation', 'rubric',
  675. 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
  676. 'title',
  677. 'subtitle',
  678. 'heading1',
  679. 'heading2',
  680. 'heading3',
  681. 'heading4',
  682. 'heading5',
  683. 'heading6',
  684. 'heading7',
  685. 'admon-attention-hdr',
  686. 'admon-attention-body',
  687. 'admon-caution-hdr',
  688. 'admon-caution-body',
  689. 'admon-danger-hdr',
  690. 'admon-danger-body',
  691. 'admon-error-hdr',
  692. 'admon-error-body',
  693. 'admon-generic-hdr',
  694. 'admon-generic-body',
  695. 'admon-hint-hdr',
  696. 'admon-hint-body',
  697. 'admon-important-hdr',
  698. 'admon-important-body',
  699. 'admon-note-hdr',
  700. 'admon-note-body',
  701. 'admon-tip-hdr',
  702. 'admon-tip-body',
  703. 'admon-warning-hdr',
  704. 'admon-warning-body',
  705. 'tableoption',
  706. 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
  707. 'Table%d.%c%d',
  708. 'lineblock1',
  709. 'lineblock2',
  710. 'lineblock3',
  711. 'lineblock4',
  712. 'lineblock5',
  713. 'lineblock6',
  714. 'image', 'figureframe',
  715. )
  716. def __init__(self, document):
  717. #nodes.SparseNodeVisitor.__init__(self, document)
  718. nodes.GenericNodeVisitor.__init__(self, document)
  719. self.settings = document.settings
  720. lcode = self.settings.language_code
  721. self.language = languages.get_language(lcode, document.reporter)
  722. self.format_map = { }
  723. if self.settings.odf_config_file:
  724. from ConfigParser import ConfigParser
  725. parser = ConfigParser()
  726. parser.read(self.settings.odf_config_file)
  727. for rststyle, format in parser.items("Formats"):
  728. if rststyle not in self.used_styles:
  729. self.document.reporter.warning(
  730. 'Style "%s" is not a style used by odtwriter.' % (
  731. rststyle, ))
  732. self.format_map[rststyle] = format.decode('utf-8')
  733. self.section_level = 0
  734. self.section_count = 0
  735. # Create ElementTree content and styles documents.
  736. if WhichElementTree == 'lxml':
  737. root = Element(
  738. 'office:document-content',
  739. nsmap=CONTENT_NAMESPACE_DICT,
  740. )
  741. else:
  742. root = Element(
  743. 'office:document-content',
  744. attrib=CONTENT_NAMESPACE_ATTRIB,
  745. )
  746. self.content_tree = etree.ElementTree(element=root)
  747. self.current_element = root
  748. SubElement(root, 'office:scripts')
  749. SubElement(root, 'office:font-face-decls')
  750. el = SubElement(root, 'office:automatic-styles')
  751. self.automatic_styles = el
  752. el = SubElement(root, 'office:body')
  753. el = self.generate_content_element(el)
  754. self.current_element = el
  755. self.body_text_element = el
  756. self.paragraph_style_stack = [self.rststyle('textbody'), ]
  757. self.list_style_stack = []
  758. self.table_count = 0
  759. self.column_count = ord('A') - 1
  760. self.trace_level = -1
  761. self.optiontablestyles_generated = False
  762. self.field_name = None
  763. self.field_element = None
  764. self.title = None
  765. self.image_count = 0
  766. self.image_style_count = 0
  767. self.image_dict = {}
  768. self.embedded_file_list = []
  769. self.syntaxhighlighting = 1
  770. self.syntaxhighlight_lexer = 'python'
  771. self.header_content = []
  772. self.footer_content = []
  773. self.in_header = False
  774. self.in_footer = False
  775. self.blockstyle = ''
  776. self.in_table_of_contents = False
  777. self.table_of_content_index_body = None
  778. self.list_level = 0
  779. self.def_list_level = 0
  780. self.footnote_ref_dict = {}
  781. self.footnote_list = []
  782. self.footnote_chars_idx = 0
  783. self.footnote_level = 0
  784. self.pending_ids = [ ]
  785. self.in_paragraph = False
  786. self.found_doc_title = False
  787. self.bumped_list_level_stack = []
  788. self.meta_dict = {}
  789. self.line_block_level = 0
  790. self.line_indent_level = 0
  791. self.citation_id = None
  792. self.style_index = 0 # use to form unique style names
  793. self.str_stylesheet = ''
  794. self.str_stylesheetcontent = ''
  795. self.dom_stylesheet = None
  796. self.table_styles = None
  797. self.in_citation = False
  798. def get_str_stylesheet(self):
  799. return self.str_stylesheet
  800. def retrieve_styles(self, extension):
  801. """Retrieve the stylesheet from either a .xml file or from
  802. a .odt (zip) file. Return the content as a string.
  803. """
  804. s2 = None
  805. stylespath = self.settings.stylesheet
  806. ext = os.path.splitext(stylespath)[1]
  807. if ext == '.xml':
  808. stylesfile = open(stylespath, 'r')
  809. s1 = stylesfile.read()
  810. stylesfile.close()
  811. elif ext == extension:
  812. zfile = zipfile.ZipFile(stylespath, 'r')
  813. s1 = zfile.read('styles.xml')
  814. s2 = zfile.read('content.xml')
  815. zfile.close()
  816. else:
  817. raise RuntimeError, 'stylesheet path (%s) must be %s or .xml file' %(stylespath, extension)
  818. self.str_stylesheet = s1
  819. self.str_stylesheetcontent = s2
  820. self.dom_stylesheet = etree.fromstring(self.str_stylesheet)
  821. self.dom_stylesheetcontent = etree.fromstring(self.str_stylesheetcontent)
  822. self.table_styles = self.extract_table_styles(s2)
  823. def extract_table_styles(self, styles_str):
  824. root = etree.fromstring(styles_str)
  825. table_styles = {}
  826. auto_styles = root.find(
  827. '{%s}automatic-styles' % (CNSD['office'], ))
  828. for stylenode in auto_styles:
  829. name = stylenode.get('{%s}name' % (CNSD['style'], ))
  830. tablename = name.split('.')[0]
  831. family = stylenode.get('{%s}family' % (CNSD['style'], ))
  832. if name.startswith(TABLESTYLEPREFIX):
  833. tablestyle = table_styles.get(tablename)
  834. if tablestyle is None:
  835. tablestyle = TableStyle()
  836. table_styles[tablename] = tablestyle
  837. if family == 'table':
  838. properties = stylenode.find(
  839. '{%s}table-properties' % (CNSD['style'], ))
  840. property = properties.get('{%s}%s' % (CNSD['fo'],
  841. 'background-color', ))
  842. if property is not None and property != 'none':
  843. tablestyle.backgroundcolor = property
  844. elif family == 'table-cell':
  845. properties = stylenode.find(
  846. '{%s}table-cell-properties' % (CNSD['style'], ))
  847. if properties is not None:
  848. border = self.get_property(properties)
  849. if border is not None:
  850. tablestyle.border = border
  851. return table_styles
  852. def get_property(self, stylenode):
  853. border = None
  854. for propertyname in TABLEPROPERTYNAMES:
  855. border = stylenode.get('{%s}%s' % (CNSD['fo'], propertyname, ))
  856. if border is not None and border != 'none':
  857. return border
  858. return border
  859. def add_doc_title(self):
  860. text = self.settings.title
  861. if text:
  862. self.title = text
  863. if not self.found_doc_title:
  864. el = Element('text:p', attrib = {
  865. 'text:style-name': self.rststyle('title'),
  866. })
  867. el.text = text
  868. self.body_text_element.insert(0, el)
  869. el = self.find_first_text_p(self.body_text_element)
  870. if el is not None:
  871. self.attach_page_style(el)
  872. def find_first_text_p(self, el):
  873. """Search the generated doc and return the first <text:p> element.
  874. """
  875. if (
  876. el.tag == 'text:p' or
  877. el.tag == 'text:h'
  878. ):
  879. return el
  880. elif el.getchildren():
  881. for child in el.getchildren():
  882. el1 = self.find_first_text_p(child)
  883. if el1 is not None:
  884. return el1
  885. return None
  886. else:
  887. return None
  888. def attach_page_style(self, el):
  889. """Attach the default page style.
  890. Create an automatic-style that refers to the current style
  891. of this element and that refers to the default page style.
  892. """
  893. current_style = el.get('text:style-name')
  894. style_name = 'P1003'
  895. el1 = SubElement(
  896. self.automatic_styles, 'style:style', attrib={
  897. 'style:name': style_name,
  898. 'style:master-page-name': "rststyle-pagedefault",
  899. 'style:family': "paragraph",
  900. }, nsdict=SNSD)
  901. if current_style:
  902. el1.set('style:parent-style-name', current_style)
  903. el.set('text:style-name', style_name)
  904. def rststyle(self, name, parameters=( )):
  905. """
  906. Returns the style name to use for the given style.
  907. If `parameters` is given `name` must contain a matching number of ``%`` and
  908. is used as a format expression with `parameters` as the value.
  909. """
  910. name1 = name % parameters
  911. stylename = self.format_map.get(name1, 'rststyle-%s' % name1)
  912. return stylename
  913. def generate_content_element(self, root):
  914. return SubElement(root, 'office:text')
  915. def setup_page(self):
  916. self.setup_paper(self.dom_stylesheet)
  917. if (len(self.header_content) > 0 or len(self.footer_content) > 0 or
  918. self.settings.custom_header or self.settings.custom_footer):
  919. self.add_header_footer(self.dom_stylesheet)
  920. new_content = etree.tostring(self.dom_stylesheet)
  921. return new_content
  922. def setup_paper(self, root_el):
  923. try:
  924. fin = os.popen("paperconf -s 2> /dev/null")
  925. w, h = map(float, fin.read().split())
  926. fin.close()
  927. except:
  928. w, h = 612, 792 # default to Letter
  929. def walk(el):
  930. if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \
  931. not el.attrib.has_key("{%s}page-width" % SNSD["fo"]):
  932. el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w
  933. el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h
  934. el.attrib["{%s}margin-left" % SNSD["fo"]] = \
  935. el.attrib["{%s}margin-right" % SNSD["fo"]] = \
  936. "%.3fpt" % (.1 * w)
  937. el.attrib["{%s}margin-top" % SNSD["fo"]] = \
  938. el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \
  939. "%.3fpt" % (.1 * h)
  940. else:
  941. for subel in el.getchildren(): walk(subel)
  942. walk(root_el)
  943. def add_header_footer(self, root_el):
  944. automatic_styles = root_el.find(
  945. '{%s}automatic-styles' % SNSD['office'])
  946. path = '{%s}master-styles' % (NAME_SPACE_1, )
  947. master_el = root_el.find(path)
  948. if master_el is None:
  949. return
  950. path = '{%s}master-page' % (SNSD['style'], )
  951. master_el_container = master_el.findall(path)
  952. master_el = None
  953. target_attrib = '{%s}name' % (SNSD['style'], )
  954. target_name = self.rststyle('pagedefault')
  955. for el in master_el_container:
  956. if el.get(target_attrib) == target_name:
  957. master_el = el
  958. break
  959. if master_el is None:
  960. return
  961. el1 = master_el
  962. if self.header_content or self.settings.custom_header:
  963. if WhichElementTree == 'lxml':
  964. el2 = SubElement(el1, 'style:header', nsdict=SNSD)
  965. else:
  966. el2 = SubElement(el1, 'style:header',
  967. attrib=STYLES_NAMESPACE_ATTRIB,
  968. nsdict=STYLES_NAMESPACE_DICT,
  969. )
  970. for el in self.header_content:
  971. attrkey = add_ns('text:style-name', nsdict=SNSD)
  972. el.attrib[attrkey] = self.rststyle('header')
  973. el2.append(el)
  974. if self.settings.custom_header:
  975. elcustom = self.create_custom_headfoot(el2,
  976. self.settings.custom_header, 'header', automatic_styles)
  977. if self.footer_content or self.settings.custom_footer:
  978. if WhichElementTree == 'lxml':
  979. el2 = SubElement(el1, 'style:footer', nsdict=SNSD)
  980. else:
  981. el2 = SubElement(el1, 'style:footer',
  982. attrib=STYLES_NAMESPACE_ATTRIB,
  983. nsdict=STYLES_NAMESPACE_DICT,
  984. )
  985. for el in self.footer_content:
  986. attrkey = add_ns('text:style-name', nsdict=SNSD)
  987. el.attrib[attrkey] = self.rststyle('footer')
  988. el2.append(el)
  989. if self.settings.custom_footer:
  990. elcustom = self.create_custom_headfoot(el2,
  991. self.settings.custom_footer, 'footer', automatic_styles)
  992. code_none, code_field, code_text = range(3)
  993. field_pat = re.compile(r'%(..?)%')
  994. def create_custom_headfoot(self, parent, text, style_name, automatic_styles):
  995. parent = SubElement(parent, 'text:p', attrib={
  996. 'text:style-name': self.rststyle(style_name),
  997. })
  998. current_element = None
  999. field_iter = self.split_field_specifiers_iter(text)
  1000. for item in field_iter:
  1001. if item[0] == ODFTranslator.code_field:
  1002. if item[1] not in ('p', 'P',
  1003. 't1', 't2', 't3', 't4',
  1004. 'd1', 'd2', 'd3', 'd4', 'd5',
  1005. 's', 't', 'a'):
  1006. msg = 'bad field spec: %%%s%%' % (item[1], )
  1007. raise RuntimeError, msg
  1008. el1 = self.make_field_element(parent,
  1009. item[1], style_name, automatic_styles)
  1010. if el1 is None:
  1011. msg = 'bad field spec: %%%s%%' % (item[1], )
  1012. raise RuntimeError, msg
  1013. else:
  1014. current_element = el1
  1015. else:
  1016. if current_element is None:
  1017. parent.text = item[1]
  1018. else:
  1019. current_element.tail = item[1]
  1020. def make_field_element(self, parent, text, style_name, automatic_styles):
  1021. if text == 'p':
  1022. el1 = SubElement(parent, 'text:page-number', attrib={
  1023. #'text:style-name': self.rststyle(style_name),
  1024. 'text:select-page': 'current',
  1025. })
  1026. elif text == 'P':
  1027. el1 = SubElement(parent, 'text:page-count', attrib={
  1028. #'text:style-name': self.rststyle(style_name),
  1029. })
  1030. elif text == 't1':
  1031. self.style_index += 1
  1032. el1 = SubElement(parent, 'text:time', attrib={
  1033. 'text:style-name': self.rststyle(style_name),
  1034. 'text:fixed': 'true',
  1035. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1036. })
  1037. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1038. 'style:name': 'rst-time-style-%d' % self.style_index,
  1039. 'xmlns:number': SNSD['number'],
  1040. 'xmlns:style': SNSD['style'],
  1041. })
  1042. el3 = SubElement(el2, 'number:hours', attrib={
  1043. 'number:style': 'long',
  1044. })
  1045. el3 = SubElement(el2, 'number:text')
  1046. el3.text = ':'
  1047. el3 = SubElement(el2, 'number:minutes', attrib={
  1048. 'number:style': 'long',
  1049. })
  1050. elif text == 't2':
  1051. self.style_index += 1
  1052. el1 = SubElement(parent, 'text:time', attrib={
  1053. 'text:style-name': self.rststyle(style_name),
  1054. 'text:fixed': 'true',
  1055. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1056. })
  1057. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1058. 'style:name': 'rst-time-style-%d' % self.style_index,
  1059. 'xmlns:number': SNSD['number'],
  1060. 'xmlns:style': SNSD['style'],
  1061. })
  1062. el3 = SubElement(el2, 'number:hours', attrib={
  1063. 'number:style': 'long',
  1064. })
  1065. el3 = SubElement(el2, 'number:text')
  1066. el3.text = ':'
  1067. el3 = SubElement(el2, 'number:minutes', attrib={
  1068. 'number:style': 'long',
  1069. })
  1070. el3 = SubElement(el2, 'number:text')
  1071. el3.text = ':'
  1072. el3 = SubElement(el2, 'number:seconds', attrib={
  1073. 'number:style': 'long',
  1074. })
  1075. elif text == 't3':
  1076. self.style_index += 1
  1077. el1 = SubElement(parent, 'text:time', attrib={
  1078. 'text:style-name': self.rststyle(style_name),
  1079. 'text:fixed': 'true',
  1080. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1081. })
  1082. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1083. 'style:name': 'rst-time-style-%d' % self.style_index,
  1084. 'xmlns:number': SNSD['number'],
  1085. 'xmlns:style': SNSD['style'],
  1086. })
  1087. el3 = SubElement(el2, 'number:hours', attrib={
  1088. 'number:style': 'long',
  1089. })
  1090. el3 = SubElement(el2, 'number:text')
  1091. el3.text = ':'
  1092. el3 = SubElement(el2, 'number:minutes', attrib={
  1093. 'number:style': 'long',
  1094. })
  1095. el3 = SubElement(el2, 'number:text')
  1096. el3.text = ' '
  1097. el3 = SubElement(el2, 'number:am-pm')
  1098. elif text == 't4':
  1099. self.style_index += 1
  1100. el1 = SubElement(parent, 'text:time', attrib={
  1101. 'text:style-name': self.rststyle(style_name),
  1102. 'text:fixed': 'true',
  1103. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1104. })
  1105. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1106. 'style:name': 'rst-time-style-%d' % self.style_index,
  1107. 'xmlns:number': SNSD['number'],
  1108. 'xmlns:style': SNSD['style'],
  1109. })
  1110. el3 = SubElement(el2, 'number:hours', attrib={
  1111. 'number:style': 'long',
  1112. })
  1113. el3 = SubElement(el2, 'number:text')
  1114. el3.text = ':'
  1115. el3 = SubElement(el2, 'number:minutes', attrib={
  1116. 'number:style': 'long',
  1117. })
  1118. el3 = SubElement(el2, 'number:text')
  1119. el3.text = ':'
  1120. el3 = SubElement(el2, 'number:seconds', attrib={
  1121. 'number:style': 'long',
  1122. })
  1123. el3 = SubElement(el2, 'number:text')
  1124. el3.text = ' '
  1125. el3 = SubElement(el2, 'number:am-pm')
  1126. elif text == 'd1':
  1127. self.style_index += 1
  1128. el1 = SubElement(parent, 'text:date', attrib={
  1129. 'text:style-name': self.rststyle(style_name),
  1130. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1131. })
  1132. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1133. 'style:name': 'rst-date-style-%d' % self.style_index,
  1134. 'number:automatic-order': 'true',
  1135. 'xmlns:number': SNSD['number'],
  1136. 'xmlns:style': SNSD['style'],
  1137. })
  1138. el3 = SubElement(el2, 'number:month', attrib={
  1139. 'number:style': 'long',
  1140. })
  1141. el3 = SubElement(el2, 'number:text')
  1142. el3.text = '/'
  1143. el3 = SubElement(el2, 'number:day', attrib={
  1144. 'number:style': 'long',
  1145. })
  1146. el3 = SubElement(el2, 'number:text')
  1147. el3.text = '/'
  1148. el3 = SubElement(el2, 'number:year')
  1149. elif text == 'd2':
  1150. self.style_index += 1
  1151. el1 = SubElement(parent, 'text:date', attrib={
  1152. 'text:style-name': self.rststyle(style_name),
  1153. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1154. })
  1155. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1156. 'style:name': 'rst-date-style-%d' % self.style_index,
  1157. 'number:automatic-order': 'true',
  1158. 'xmlns:number': SNSD['number'],
  1159. 'xmlns:style': SNSD['style'],
  1160. })
  1161. el3 = SubElement(el2, 'number:month', attrib={
  1162. 'number:style': 'long',
  1163. })
  1164. el3 = SubElement(el2, 'number:text')
  1165. el3.text = '/'
  1166. el3 = SubElement(el2, 'number:day', attrib={
  1167. 'number:style': 'long',
  1168. })
  1169. el3 = SubElement(el2, 'number:text')
  1170. el3.text = '/'
  1171. el3 = SubElement(el2, 'number:year', attrib={
  1172. 'number:style': 'long',
  1173. })
  1174. elif text == 'd3':
  1175. self.style_index += 1
  1176. el1 = SubElement(parent, 'text:date', attrib={
  1177. 'text:style-name': self.rststyle(style_name),
  1178. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1179. })
  1180. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1181. 'style:n…

Large files files are truncated, but you can click here to view the full file