PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/documentor/libraries/docutils-0.9.1-py3.2/docutils/writers/odf_odt/__init__.py

https://github.com/tictactatic/Superdesk
Python | 3266 lines | 3100 code | 101 blank | 65 comment | 76 complexity | e5cb5316ba91e8388738a17d68aacd31 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, GPL-2.0

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

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

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