PageRenderTime 40ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

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

http://github.com/JetBrains/intellij-community
Python | 3297 lines | 3131 code | 101 blank | 65 comment | 77 complexity | 93563875264b030ebfef20b0d0a830de 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
  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 copy
  10. import io
  11. import os
  12. import os.path
  13. import re
  14. import sys
  15. import tempfile
  16. import time
  17. import zipfile
  18. from xml.dom import minidom
  19. import urllib.error
  20. import urllib.parse
  21. import urllib.request
  22. import docutils
  23. from docutils import frontend, nodes, utils, writers, languages
  24. from docutils.readers import standalone
  25. from docutils.transforms import references
  26. WhichElementTree = ''
  27. try:
  28. # 1. Try to use lxml.
  29. #from lxml import etree
  30. #WhichElementTree = 'lxml'
  31. raise ImportError('Ignoring lxml')
  32. except ImportError as e:
  33. try:
  34. # 2. Try to use ElementTree from the Python standard library.
  35. from xml.etree import ElementTree as etree
  36. WhichElementTree = 'elementtree'
  37. except ImportError as e:
  38. try:
  39. # 3. Try to use a version of ElementTree installed as a separate
  40. # product.
  41. from elementtree import ElementTree as etree
  42. WhichElementTree = 'elementtree'
  43. except ImportError as e:
  44. s1 = 'Must install either a version of Python containing ' \
  45. 'ElementTree (Python version >=2.5) or install ElementTree.'
  46. raise ImportError(s1)
  47. #
  48. # Import pygments and odtwriter pygments formatters if possible.
  49. try:
  50. import pygments
  51. import pygments.lexers
  52. from .pygmentsformatter import OdtPygmentsProgFormatter, \
  53. OdtPygmentsLaTeXFormatter
  54. except ImportError as exp:
  55. pygments = None
  56. # check for the Python Imaging Library
  57. try:
  58. import PIL.Image
  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. import weakref
  81. _parents = weakref.WeakKeyDictionary()
  82. if isinstance(etree.Element, type):
  83. _ElementInterface = etree.Element
  84. else:
  85. _ElementInterface = etree._ElementInterface
  86. class _ElementInterfaceWrapper(_ElementInterface):
  87. def __init__(self, tag, attrib=None):
  88. _ElementInterface.__init__(self, tag, attrib)
  89. _parents[self] = None
  90. def setparent(self, parent):
  91. _parents[self] = parent
  92. def getparent(self):
  93. return _parents[self]
  94. #
  95. # Constants and globals
  96. SPACES_PATTERN = re.compile(r'( +)')
  97. TABS_PATTERN = re.compile(r'(\t+)')
  98. FILL_PAT1 = re.compile(r'^ +')
  99. FILL_PAT2 = re.compile(r' {2,}')
  100. TABLESTYLEPREFIX = 'rststyle-table-'
  101. TABLENAMEDEFAULT = '%s0' % TABLESTYLEPREFIX
  102. TABLEPROPERTYNAMES = ('border', 'border-top', 'border-left',
  103. 'border-right', 'border-bottom', )
  104. GENERATOR_DESC = 'Docutils.org/odf_odt'
  105. NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
  106. CONTENT_NAMESPACE_DICT = CNSD = {
  107. # 'office:version': '1.0',
  108. 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  109. 'dc': 'http://purl.org/dc/elements/1.1/',
  110. 'dom': 'http://www.w3.org/2001/xml-events',
  111. 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  112. 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  113. 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  114. 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  115. 'math': 'http://www.w3.org/1998/Math/MathML',
  116. 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  117. 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  118. 'office': NAME_SPACE_1,
  119. 'ooo': 'http://openoffice.org/2004/office',
  120. 'oooc': 'http://openoffice.org/2004/calc',
  121. 'ooow': 'http://openoffice.org/2004/writer',
  122. 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  123. 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  124. 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  125. 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  126. 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  127. 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  128. 'xforms': 'http://www.w3.org/2002/xforms',
  129. 'xlink': 'http://www.w3.org/1999/xlink',
  130. 'xsd': 'http://www.w3.org/2001/XMLSchema',
  131. 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
  132. }
  133. STYLES_NAMESPACE_DICT = SNSD = {
  134. # 'office:version': '1.0',
  135. 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  136. 'dc': 'http://purl.org/dc/elements/1.1/',
  137. 'dom': 'http://www.w3.org/2001/xml-events',
  138. 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  139. 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  140. 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  141. 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  142. 'math': 'http://www.w3.org/1998/Math/MathML',
  143. 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  144. 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  145. 'office': NAME_SPACE_1,
  146. 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  147. 'ooo': 'http://openoffice.org/2004/office',
  148. 'oooc': 'http://openoffice.org/2004/calc',
  149. 'ooow': 'http://openoffice.org/2004/writer',
  150. 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  151. 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  152. 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  153. 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  154. 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  155. 'xlink': 'http://www.w3.org/1999/xlink',
  156. }
  157. MANIFEST_NAMESPACE_DICT = MANNSD = {
  158. 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
  159. }
  160. META_NAMESPACE_DICT = METNSD = {
  161. # 'office:version': '1.0',
  162. 'dc': 'http://purl.org/dc/elements/1.1/',
  163. 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  164. 'office': NAME_SPACE_1,
  165. 'ooo': 'http://openoffice.org/2004/office',
  166. 'xlink': 'http://www.w3.org/1999/xlink',
  167. }
  168. #
  169. # Attribute dictionaries for use with ElementTree (not lxml), which
  170. # does not support use of nsmap parameter on Element() and SubElement().
  171. CONTENT_NAMESPACE_ATTRIB = {
  172. #'office:version': '1.0',
  173. 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  174. 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
  175. 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
  176. 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  177. 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  178. 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  179. 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  180. 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
  181. 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  182. 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  183. 'xmlns:office': NAME_SPACE_1,
  184. 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  185. 'xmlns:ooo': 'http://openoffice.org/2004/office',
  186. 'xmlns:oooc': 'http://openoffice.org/2004/calc',
  187. 'xmlns:ooow': 'http://openoffice.org/2004/writer',
  188. 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  189. 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  190. 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  191. 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  192. 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  193. 'xmlns:xforms': 'http://www.w3.org/2002/xforms',
  194. 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  195. 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
  196. 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
  197. }
  198. STYLES_NAMESPACE_ATTRIB = {
  199. #'office:version': '1.0',
  200. 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
  201. 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
  202. 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
  203. 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
  204. 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
  205. 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
  206. 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
  207. 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
  208. 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  209. 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
  210. 'xmlns:office': NAME_SPACE_1,
  211. 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
  212. 'xmlns:ooo': 'http://openoffice.org/2004/office',
  213. 'xmlns:oooc': 'http://openoffice.org/2004/calc',
  214. 'xmlns:ooow': 'http://openoffice.org/2004/writer',
  215. 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
  216. 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
  217. 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
  218. 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
  219. 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
  220. 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  221. }
  222. MANIFEST_NAMESPACE_ATTRIB = {
  223. 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
  224. }
  225. META_NAMESPACE_ATTRIB = {
  226. #'office:version': '1.0',
  227. 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
  228. 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
  229. 'xmlns:office': NAME_SPACE_1,
  230. 'xmlns:ooo': 'http://openoffice.org/2004/office',
  231. 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  232. }
  233. #
  234. # Functions
  235. #
  236. #
  237. # ElementTree support functions.
  238. # In order to be able to get the parent of elements, must use these
  239. # instead of the functions with same name provided by ElementTree.
  240. #
  241. def Element(tag, attrib=None, nsmap=None, nsdict=CNSD):
  242. if attrib is None:
  243. attrib = {}
  244. tag, attrib = fix_ns(tag, attrib, nsdict)
  245. if WhichElementTree == 'lxml':
  246. el = etree.Element(tag, attrib, nsmap=nsmap)
  247. else:
  248. el = _ElementInterfaceWrapper(tag, attrib)
  249. return el
  250. def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD):
  251. if attrib is None:
  252. attrib = {}
  253. tag, attrib = fix_ns(tag, attrib, nsdict)
  254. if WhichElementTree == 'lxml':
  255. el = etree.SubElement(parent, tag, attrib, nsmap=nsmap)
  256. else:
  257. el = _ElementInterfaceWrapper(tag, attrib)
  258. parent.append(el)
  259. el.setparent(parent)
  260. return el
  261. def fix_ns(tag, attrib, nsdict):
  262. nstag = add_ns(tag, nsdict)
  263. nsattrib = {}
  264. for key, val in attrib.items():
  265. nskey = add_ns(key, nsdict)
  266. nsattrib[nskey] = val
  267. return nstag, nsattrib
  268. def add_ns(tag, nsdict=CNSD):
  269. if WhichElementTree == 'lxml':
  270. nstag, name = tag.split(':')
  271. ns = nsdict.get(nstag)
  272. if ns is None:
  273. raise RuntimeError('Invalid namespace prefix: %s' % nstag)
  274. tag = '{%s}%s' % (ns, name,)
  275. return tag
  276. def ToString(et):
  277. outstream = io.StringIO()
  278. if sys.version_info >= (3, 2):
  279. et.write(outstream, encoding="unicode")
  280. else:
  281. et.write(outstream)
  282. s1 = outstream.getvalue()
  283. outstream.close()
  284. return s1
  285. def escape_cdata(text):
  286. text = text.replace("&", "&amp;")
  287. text = text.replace("<", "&lt;")
  288. text = text.replace(">", "&gt;")
  289. ascii = ''
  290. for char in text:
  291. if ord(char) >= ord("\x7f"):
  292. ascii += "&#x%X;" % ( ord(char), )
  293. else:
  294. ascii += char
  295. return ascii
  296. WORD_SPLIT_PAT1 = re.compile(r'\b(\w*)\b\W*')
  297. def split_words(line):
  298. # We need whitespace at the end of the string for our regexpr.
  299. line += ' '
  300. words = []
  301. pos1 = 0
  302. mo = WORD_SPLIT_PAT1.search(line, pos1)
  303. while mo is not None:
  304. word = mo.groups()[0]
  305. words.append(word)
  306. pos1 = mo.end()
  307. mo = WORD_SPLIT_PAT1.search(line, pos1)
  308. return words
  309. #
  310. # Classes
  311. #
  312. class TableStyle(object):
  313. def __init__(self, border=None, backgroundcolor=None):
  314. self.border = border
  315. self.backgroundcolor = backgroundcolor
  316. def get_border_(self):
  317. return self.border_
  318. def set_border_(self, border):
  319. self.border_ = border
  320. border = property(get_border_, set_border_)
  321. def get_backgroundcolor_(self):
  322. return self.backgroundcolor_
  323. def set_backgroundcolor_(self, backgroundcolor):
  324. self.backgroundcolor_ = backgroundcolor
  325. backgroundcolor = property(get_backgroundcolor_, set_backgroundcolor_)
  326. BUILTIN_DEFAULT_TABLE_STYLE = TableStyle(
  327. border = '0.0007in solid #000000')
  328. #
  329. # Information about the indentation level for lists nested inside
  330. # other contexts, e.g. dictionary lists.
  331. class ListLevel(object):
  332. def __init__(self, level, sibling_level=True, nested_level=True):
  333. self.level = level
  334. self.sibling_level = sibling_level
  335. self.nested_level = nested_level
  336. def set_sibling(self, sibling_level): self.sibling_level = sibling_level
  337. def get_sibling(self): return self.sibling_level
  338. def set_nested(self, nested_level): self.nested_level = nested_level
  339. def get_nested(self): return self.nested_level
  340. def set_level(self, level): self.level = level
  341. def get_level(self): return self.level
  342. class Writer(writers.Writer):
  343. MIME_TYPE = 'application/vnd.oasis.opendocument.text'
  344. EXTENSION = '.odt'
  345. supported = ('odt', )
  346. """Formats this writer supports."""
  347. default_stylesheet = 'styles' + EXTENSION
  348. default_stylesheet_path = utils.relative_path(
  349. os.path.join(os.getcwd(), 'dummy'),
  350. os.path.join(os.path.dirname(__file__), default_stylesheet))
  351. default_template = 'template.txt'
  352. default_template_path = utils.relative_path(
  353. os.path.join(os.getcwd(), 'dummy'),
  354. os.path.join(os.path.dirname(__file__), default_template))
  355. settings_spec = (
  356. 'ODF-Specific Options',
  357. None,
  358. (
  359. ('Specify a stylesheet. '
  360. 'Default: "%s"' % default_stylesheet_path,
  361. ['--stylesheet'],
  362. {
  363. 'default': default_stylesheet_path,
  364. 'dest': 'stylesheet'
  365. }),
  366. ('Specify a configuration/mapping file relative to the '
  367. 'current working '
  368. 'directory for additional ODF options. '
  369. 'In particular, this file may contain a section named '
  370. '"Formats" that maps default style names to '
  371. 'names to be used in the resulting output file allowing for '
  372. 'adhering to external standards. '
  373. 'For more info and the format of the configuration/mapping file, '
  374. 'see the odtwriter doc.',
  375. ['--odf-config-file'],
  376. {'metavar': '<file>'}),
  377. ('Obfuscate email addresses to confuse harvesters while still '
  378. 'keeping email links usable with standards-compliant browsers.',
  379. ['--cloak-email-addresses'],
  380. {'default': False,
  381. 'action': 'store_true',
  382. 'dest': 'cloak_email_addresses',
  383. 'validator': frontend.validate_boolean}),
  384. ('Do not obfuscate email addresses.',
  385. ['--no-cloak-email-addresses'],
  386. {'default': False,
  387. 'action': 'store_false',
  388. 'dest': 'cloak_email_addresses',
  389. 'validator': frontend.validate_boolean}),
  390. ('Specify the thickness of table borders in thousands of a cm. '
  391. 'Default is 35.',
  392. ['--table-border-thickness'],
  393. {'default': None,
  394. 'validator': frontend.validate_nonnegative_int}),
  395. ('Add syntax highlighting in literal code blocks.',
  396. ['--add-syntax-highlighting'],
  397. {'default': False,
  398. 'action': 'store_true',
  399. 'dest': 'add_syntax_highlighting',
  400. 'validator': frontend.validate_boolean}),
  401. ('Do not add syntax highlighting in literal code blocks. (default)',
  402. ['--no-syntax-highlighting'],
  403. {'default': False,
  404. 'action': 'store_false',
  405. 'dest': 'add_syntax_highlighting',
  406. 'validator': frontend.validate_boolean}),
  407. ('Create sections for headers. (default)',
  408. ['--create-sections'],
  409. {'default': True,
  410. 'action': 'store_true',
  411. 'dest': 'create_sections',
  412. 'validator': frontend.validate_boolean}),
  413. ('Do not create sections for headers.',
  414. ['--no-sections'],
  415. {'default': True,
  416. 'action': 'store_false',
  417. 'dest': 'create_sections',
  418. 'validator': frontend.validate_boolean}),
  419. ('Create links.',
  420. ['--create-links'],
  421. {'default': False,
  422. 'action': 'store_true',
  423. 'dest': 'create_links',
  424. 'validator': frontend.validate_boolean}),
  425. ('Do not create links. (default)',
  426. ['--no-links'],
  427. {'default': False,
  428. 'action': 'store_false',
  429. 'dest': 'create_links',
  430. 'validator': frontend.validate_boolean}),
  431. ('Generate endnotes at end of document, not footnotes '
  432. 'at bottom of page.',
  433. ['--endnotes-end-doc'],
  434. {'default': False,
  435. 'action': 'store_true',
  436. 'dest': 'endnotes_end_doc',
  437. 'validator': frontend.validate_boolean}),
  438. ('Generate footnotes at bottom of page, not endnotes '
  439. 'at end of document. (default)',
  440. ['--no-endnotes-end-doc'],
  441. {'default': False,
  442. 'action': 'store_false',
  443. 'dest': 'endnotes_end_doc',
  444. 'validator': frontend.validate_boolean}),
  445. ('Generate a bullet list table of contents, not '
  446. 'an ODF/oowriter table of contents.',
  447. ['--generate-list-toc'],
  448. {'default': True,
  449. 'action': 'store_false',
  450. 'dest': 'generate_oowriter_toc',
  451. 'validator': frontend.validate_boolean}),
  452. ('Generate an ODF/oowriter table of contents, not '
  453. 'a bullet list. (default)',
  454. ['--generate-oowriter-toc'],
  455. {'default': True,
  456. 'action': 'store_true',
  457. 'dest': 'generate_oowriter_toc',
  458. 'validator': frontend.validate_boolean}),
  459. ('Specify the contents of an custom header line. '
  460. 'See odf_odt writer documentation for details '
  461. 'about special field character sequences.',
  462. ['--custom-odt-header'],
  463. { 'default': '',
  464. 'dest': 'custom_header',
  465. }),
  466. ('Specify the contents of an custom footer line. '
  467. 'See odf_odt writer documentation for details '
  468. 'about special field character sequences.',
  469. ['--custom-odt-footer'],
  470. { 'default': '',
  471. 'dest': 'custom_footer',
  472. }),
  473. )
  474. )
  475. settings_defaults = {
  476. 'output_encoding_error_handler': 'xmlcharrefreplace',
  477. }
  478. relative_path_settings = (
  479. 'stylesheet_path',
  480. )
  481. config_section = 'odf_odt writer'
  482. config_section_dependencies = (
  483. 'writers',
  484. )
  485. def __init__(self):
  486. writers.Writer.__init__(self)
  487. self.translator_class = ODFTranslator
  488. def translate(self):
  489. self.settings = self.document.settings
  490. self.visitor = self.translator_class(self.document)
  491. self.visitor.retrieve_styles(self.EXTENSION)
  492. self.document.walkabout(self.visitor)
  493. self.visitor.add_doc_title()
  494. self.assemble_my_parts()
  495. self.output = self.parts['whole']
  496. def assemble_my_parts(self):
  497. """Assemble the `self.parts` dictionary. Extend in subclasses.
  498. """
  499. writers.Writer.assemble_parts(self)
  500. f = tempfile.NamedTemporaryFile()
  501. zfile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
  502. self.write_zip_str(zfile, 'mimetype', self.MIME_TYPE,
  503. compress_type=zipfile.ZIP_STORED)
  504. content = self.visitor.content_astext()
  505. self.write_zip_str(zfile, 'content.xml', content)
  506. s1 = self.create_manifest()
  507. self.write_zip_str(zfile, 'META-INF/manifest.xml', s1)
  508. s1 = self.create_meta()
  509. self.write_zip_str(zfile, 'meta.xml', s1)
  510. s1 = self.get_stylesheet()
  511. self.write_zip_str(zfile, 'styles.xml', s1)
  512. self.store_embedded_files(zfile)
  513. self.copy_from_stylesheet(zfile)
  514. zfile.close()
  515. f.seek(0)
  516. whole = f.read()
  517. f.close()
  518. self.parts['whole'] = whole
  519. self.parts['encoding'] = self.document.settings.output_encoding
  520. self.parts['version'] = docutils.__version__
  521. def write_zip_str(self, zfile, name, bytes, compress_type=zipfile.ZIP_DEFLATED):
  522. localtime = time.localtime(time.time())
  523. zinfo = zipfile.ZipInfo(name, localtime)
  524. # Add some standard UNIX file access permissions (-rw-r--r--).
  525. zinfo.external_attr = (0x81a4 & 0xFFFF) << 16
  526. zinfo.compress_type = compress_type
  527. zfile.writestr(zinfo, bytes)
  528. def store_embedded_files(self, zfile):
  529. embedded_files = self.visitor.get_embedded_file_list()
  530. for source, destination in embedded_files:
  531. if source is None:
  532. continue
  533. try:
  534. # encode/decode
  535. destination1 = destination.decode('latin-1').encode('utf-8')
  536. zfile.write(source, destination1)
  537. except OSError as e:
  538. self.document.reporter.warning(
  539. "Can't open file %s." % (source, ))
  540. def get_settings(self):
  541. """
  542. modeled after get_stylesheet
  543. """
  544. stylespath = self.settings.stylesheet
  545. zfile = zipfile.ZipFile(stylespath, 'r')
  546. s1 = zfile.read('settings.xml')
  547. zfile.close()
  548. return s1
  549. def get_stylesheet(self):
  550. """Get the stylesheet from the visitor.
  551. Ask the visitor to setup the page.
  552. """
  553. s1 = self.visitor.setup_page()
  554. return s1
  555. def copy_from_stylesheet(self, outzipfile):
  556. """Copy images, settings, etc from the stylesheet doc into target doc.
  557. """
  558. stylespath = self.settings.stylesheet
  559. inzipfile = zipfile.ZipFile(stylespath, 'r')
  560. # Copy the styles.
  561. s1 = inzipfile.read('settings.xml')
  562. self.write_zip_str(outzipfile, 'settings.xml', s1)
  563. # Copy the images.
  564. namelist = inzipfile.namelist()
  565. for name in namelist:
  566. if name.startswith('Pictures/'):
  567. imageobj = inzipfile.read(name)
  568. outzipfile.writestr(name, imageobj)
  569. inzipfile.close()
  570. def assemble_parts(self):
  571. pass
  572. def create_manifest(self):
  573. if WhichElementTree == 'lxml':
  574. root = Element('manifest:manifest',
  575. nsmap=MANIFEST_NAMESPACE_DICT,
  576. nsdict=MANIFEST_NAMESPACE_DICT,
  577. )
  578. else:
  579. root = Element('manifest:manifest',
  580. attrib=MANIFEST_NAMESPACE_ATTRIB,
  581. nsdict=MANIFEST_NAMESPACE_DICT,
  582. )
  583. doc = etree.ElementTree(root)
  584. SubElement(root, 'manifest:file-entry', attrib={
  585. 'manifest:media-type': self.MIME_TYPE,
  586. 'manifest:full-path': '/',
  587. }, nsdict=MANNSD)
  588. SubElement(root, 'manifest:file-entry', attrib={
  589. 'manifest:media-type': 'text/xml',
  590. 'manifest:full-path': 'content.xml',
  591. }, nsdict=MANNSD)
  592. SubElement(root, 'manifest:file-entry', attrib={
  593. 'manifest:media-type': 'text/xml',
  594. 'manifest:full-path': 'styles.xml',
  595. }, nsdict=MANNSD)
  596. SubElement(root, 'manifest:file-entry', attrib={
  597. 'manifest:media-type': 'text/xml',
  598. 'manifest:full-path': 'settings.xml',
  599. }, nsdict=MANNSD)
  600. SubElement(root, 'manifest:file-entry', attrib={
  601. 'manifest:media-type': 'text/xml',
  602. 'manifest:full-path': 'meta.xml',
  603. }, nsdict=MANNSD)
  604. s1 = ToString(doc)
  605. doc = minidom.parseString(s1)
  606. s1 = doc.toprettyxml(' ')
  607. return s1
  608. def create_meta(self):
  609. if WhichElementTree == 'lxml':
  610. root = Element('office:document-meta',
  611. nsmap=META_NAMESPACE_DICT,
  612. nsdict=META_NAMESPACE_DICT,
  613. )
  614. else:
  615. root = Element('office:document-meta',
  616. attrib=META_NAMESPACE_ATTRIB,
  617. nsdict=META_NAMESPACE_DICT,
  618. )
  619. doc = etree.ElementTree(root)
  620. root = SubElement(root, 'office:meta', nsdict=METNSD)
  621. el1 = SubElement(root, 'meta:generator', nsdict=METNSD)
  622. el1.text = 'Docutils/rst2odf.py/%s' % (VERSION, )
  623. s1 = os.environ.get('USER', '')
  624. el1 = SubElement(root, 'meta:initial-creator', nsdict=METNSD)
  625. el1.text = s1
  626. s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime())
  627. el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD)
  628. el1.text = s2
  629. el1 = SubElement(root, 'dc:creator', nsdict=METNSD)
  630. el1.text = s1
  631. el1 = SubElement(root, 'dc:date', nsdict=METNSD)
  632. el1.text = s2
  633. el1 = SubElement(root, 'dc:language', nsdict=METNSD)
  634. el1.text = 'en-US'
  635. el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD)
  636. el1.text = '1'
  637. el1 = SubElement(root, 'meta:editing-duration', nsdict=METNSD)
  638. el1.text = 'PT00M01S'
  639. title = self.visitor.get_title()
  640. el1 = SubElement(root, 'dc:title', nsdict=METNSD)
  641. if title:
  642. el1.text = title
  643. else:
  644. el1.text = '[no title]'
  645. meta_dict = self.visitor.get_meta_dict()
  646. keywordstr = meta_dict.get('keywords')
  647. if keywordstr is not None:
  648. keywords = split_words(keywordstr)
  649. for keyword in keywords:
  650. el1 = SubElement(root, 'meta:keyword', nsdict=METNSD)
  651. el1.text = keyword
  652. description = meta_dict.get('description')
  653. if description is not None:
  654. el1 = SubElement(root, 'dc:description', nsdict=METNSD)
  655. el1.text = description
  656. s1 = ToString(doc)
  657. #doc = minidom.parseString(s1)
  658. #s1 = doc.toprettyxml(' ')
  659. return s1
  660. # class ODFTranslator(nodes.SparseNodeVisitor):
  661. class ODFTranslator(nodes.GenericNodeVisitor):
  662. used_styles = (
  663. 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
  664. 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
  665. 'bulletitem', 'bulletlist',
  666. 'caption', 'legend',
  667. 'centeredtextbody', 'codeblock', 'codeblock-indented',
  668. 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
  669. 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
  670. 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
  671. 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
  672. 'epigraph-enumitem', 'epigraph-enumlist', 'footer',
  673. 'footnote', 'citation',
  674. 'header', 'highlights', 'highlights-bulletitem',
  675. 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
  676. 'horizontalline', 'inlineliteral', 'quotation', 'rubric',
  677. 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
  678. 'title',
  679. 'subtitle',
  680. 'heading1',
  681. 'heading2',
  682. 'heading3',
  683. 'heading4',
  684. 'heading5',
  685. 'heading6',
  686. 'heading7',
  687. 'admon-attention-hdr',
  688. 'admon-attention-body',
  689. 'admon-caution-hdr',
  690. 'admon-caution-body',
  691. 'admon-danger-hdr',
  692. 'admon-danger-body',
  693. 'admon-error-hdr',
  694. 'admon-error-body',
  695. 'admon-generic-hdr',
  696. 'admon-generic-body',
  697. 'admon-hint-hdr',
  698. 'admon-hint-body',
  699. 'admon-important-hdr',
  700. 'admon-important-body',
  701. 'admon-note-hdr',
  702. 'admon-note-body',
  703. 'admon-tip-hdr',
  704. 'admon-tip-body',
  705. 'admon-warning-hdr',
  706. 'admon-warning-body',
  707. 'tableoption',
  708. 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
  709. 'Table%d.%c%d',
  710. 'lineblock1',
  711. 'lineblock2',
  712. 'lineblock3',
  713. 'lineblock4',
  714. 'lineblock5',
  715. 'lineblock6',
  716. 'image', 'figureframe',
  717. )
  718. def __init__(self, document):
  719. #nodes.SparseNodeVisitor.__init__(self, document)
  720. nodes.GenericNodeVisitor.__init__(self, document)
  721. self.settings = document.settings
  722. lcode = self.settings.language_code
  723. self.language = languages.get_language(lcode, document.reporter)
  724. self.format_map = { }
  725. if self.settings.odf_config_file:
  726. from configparser import ConfigParser
  727. parser = ConfigParser()
  728. parser.read(self.settings.odf_config_file)
  729. for rststyle, format in parser.items("Formats"):
  730. if rststyle not in self.used_styles:
  731. self.document.reporter.warning(
  732. 'Style "%s" is not a style used by odtwriter.' % (
  733. rststyle, ))
  734. self.format_map[rststyle] = format.decode('utf-8')
  735. self.section_level = 0
  736. self.section_count = 0
  737. # Create ElementTree content and styles documents.
  738. if WhichElementTree == 'lxml':
  739. root = Element(
  740. 'office:document-content',
  741. nsmap=CONTENT_NAMESPACE_DICT,
  742. )
  743. else:
  744. root = Element(
  745. 'office:document-content',
  746. attrib=CONTENT_NAMESPACE_ATTRIB,
  747. )
  748. self.content_tree = etree.ElementTree(element=root)
  749. self.current_element = root
  750. SubElement(root, 'office:scripts')
  751. SubElement(root, 'office:font-face-decls')
  752. el = SubElement(root, 'office:automatic-styles')
  753. self.automatic_styles = el
  754. el = SubElement(root, 'office:body')
  755. el = self.generate_content_element(el)
  756. self.current_element = el
  757. self.body_text_element = el
  758. self.paragraph_style_stack = [self.rststyle('textbody'), ]
  759. self.list_style_stack = []
  760. self.table_count = 0
  761. self.column_count = ord('A') - 1
  762. self.trace_level = -1
  763. self.optiontablestyles_generated = False
  764. self.field_name = None
  765. self.field_element = None
  766. self.title = None
  767. self.image_count = 0
  768. self.image_style_count = 0
  769. self.image_dict = {}
  770. self.embedded_file_list = []
  771. self.syntaxhighlighting = 1
  772. self.syntaxhighlight_lexer = 'python'
  773. self.header_content = []
  774. self.footer_content = []
  775. self.in_header = False
  776. self.in_footer = False
  777. self.blockstyle = ''
  778. self.in_table_of_contents = False
  779. self.table_of_content_index_body = None
  780. self.list_level = 0
  781. self.def_list_level = 0
  782. self.footnote_ref_dict = {}
  783. self.footnote_list = []
  784. self.footnote_chars_idx = 0
  785. self.footnote_level = 0
  786. self.pending_ids = [ ]
  787. self.in_paragraph = False
  788. self.found_doc_title = False
  789. self.bumped_list_level_stack = []
  790. self.meta_dict = {}
  791. self.line_block_level = 0
  792. self.line_indent_level = 0
  793. self.citation_id = None
  794. self.style_index = 0 # use to form unique style names
  795. self.str_stylesheet = ''
  796. self.str_stylesheetcontent = ''
  797. self.dom_stylesheet = None
  798. self.table_styles = None
  799. self.in_citation = False
  800. def get_str_stylesheet(self):
  801. return self.str_stylesheet
  802. def retrieve_styles(self, extension):
  803. """Retrieve the stylesheet from either a .xml file or from
  804. a .odt (zip) file. Return the content as a string.
  805. """
  806. s2 = None
  807. stylespath = self.settings.stylesheet
  808. ext = os.path.splitext(stylespath)[1]
  809. if ext == '.xml':
  810. stylesfile = open(stylespath, 'r')
  811. s1 = stylesfile.read()
  812. stylesfile.close()
  813. elif ext == extension:
  814. zfile = zipfile.ZipFile(stylespath, 'r')
  815. s1 = zfile.read('styles.xml')
  816. s2 = zfile.read('content.xml')
  817. zfile.close()
  818. else:
  819. raise RuntimeError('stylesheet path (%s) must be %s or .xml file' %(stylespath, extension))
  820. self.str_stylesheet = s1
  821. self.str_stylesheetcontent = s2
  822. self.dom_stylesheet = etree.fromstring(self.str_stylesheet)
  823. self.dom_stylesheetcontent = etree.fromstring(self.str_stylesheetcontent)
  824. self.table_styles = self.extract_table_styles(s2)
  825. def extract_table_styles(self, styles_str):
  826. root = etree.fromstring(styles_str)
  827. table_styles = {}
  828. auto_styles = root.find(
  829. '{%s}automatic-styles' % (CNSD['office'], ))
  830. for stylenode in auto_styles:
  831. name = stylenode.get('{%s}name' % (CNSD['style'], ))
  832. tablename = name.split('.')[0]
  833. family = stylenode.get('{%s}family' % (CNSD['style'], ))
  834. if name.startswith(TABLESTYLEPREFIX):
  835. tablestyle = table_styles.get(tablename)
  836. if tablestyle is None:
  837. tablestyle = TableStyle()
  838. table_styles[tablename] = tablestyle
  839. if family == 'table':
  840. properties = stylenode.find(
  841. '{%s}table-properties' % (CNSD['style'], ))
  842. property = properties.get('{%s}%s' % (CNSD['fo'],
  843. 'background-color', ))
  844. if property is not None and property != 'none':
  845. tablestyle.backgroundcolor = property
  846. elif family == 'table-cell':
  847. properties = stylenode.find(
  848. '{%s}table-cell-properties' % (CNSD['style'], ))
  849. if properties is not None:
  850. border = self.get_property(properties)
  851. if border is not None:
  852. tablestyle.border = border
  853. return table_styles
  854. def get_property(self, stylenode):
  855. border = None
  856. for propertyname in TABLEPROPERTYNAMES:
  857. border = stylenode.get('{%s}%s' % (CNSD['fo'], propertyname, ))
  858. if border is not None and border != 'none':
  859. return border
  860. return border
  861. def add_doc_title(self):
  862. text = self.settings.title
  863. if text:
  864. self.title = text
  865. if not self.found_doc_title:
  866. el = Element('text:p', attrib = {
  867. 'text:style-name': self.rststyle('title'),
  868. })
  869. el.text = text
  870. self.body_text_element.insert(0, el)
  871. el = self.find_first_text_p(self.body_text_element)
  872. if el is not None:
  873. self.attach_page_style(el)
  874. def find_first_text_p(self, el):
  875. """Search the generated doc and return the first <text:p> element.
  876. """
  877. if (
  878. el.tag == 'text:p' or
  879. el.tag == 'text:h'
  880. ):
  881. return el
  882. elif el.getchildren():
  883. for child in el.getchildren():
  884. el1 = self.find_first_text_p(child)
  885. if el1 is not None:
  886. return el1
  887. return None
  888. else:
  889. return None
  890. def attach_page_style(self, el):
  891. """Attach the default page style.
  892. Create an automatic-style that refers to the current style
  893. of this element and that refers to the default page style.
  894. """
  895. current_style = el.get('text:style-name')
  896. style_name = 'P1003'
  897. el1 = SubElement(
  898. self.automatic_styles, 'style:style', attrib={
  899. 'style:name': style_name,
  900. 'style:master-page-name': "rststyle-pagedefault",
  901. 'style:family': "paragraph",
  902. }, nsdict=SNSD)
  903. if current_style:
  904. el1.set('style:parent-style-name', current_style)
  905. el.set('text:style-name', style_name)
  906. def rststyle(self, name, parameters=( )):
  907. """
  908. Returns the style name to use for the given style.
  909. If `parameters` is given `name` must contain a matching number of ``%`` and
  910. is used as a format expression with `parameters` as the value.
  911. """
  912. name1 = name % parameters
  913. stylename = self.format_map.get(name1, 'rststyle-%s' % name1)
  914. return stylename
  915. def generate_content_element(self, root):
  916. return SubElement(root, 'office:text')
  917. def setup_page(self):
  918. self.setup_paper(self.dom_stylesheet)
  919. if (len(self.header_content) > 0 or len(self.footer_content) > 0 or
  920. self.settings.custom_header or self.settings.custom_footer):
  921. self.add_header_footer(self.dom_stylesheet)
  922. new_content = etree.tostring(self.dom_stylesheet)
  923. return new_content
  924. def setup_paper(self, root_el):
  925. try:
  926. fin = os.popen("paperconf -s 2> /dev/null")
  927. w, h = list(map(float, fin.read().split()))
  928. fin.close()
  929. except:
  930. w, h = 612, 792 # default to Letter
  931. def walk(el):
  932. if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \
  933. "{%s}page-width" % SNSD["fo"] not in el.attrib:
  934. el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w
  935. el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h
  936. el.attrib["{%s}margin-left" % SNSD["fo"]] = \
  937. el.attrib["{%s}margin-right" % SNSD["fo"]] = \
  938. "%.3fpt" % (.1 * w)
  939. el.attrib["{%s}margin-top" % SNSD["fo"]] = \
  940. el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \
  941. "%.3fpt" % (.1 * h)
  942. else:
  943. for subel in el.getchildren(): walk(subel)
  944. walk(root_el)
  945. def add_header_footer(self, root_el):
  946. automatic_styles = root_el.find(
  947. '{%s}automatic-styles' % SNSD['office'])
  948. path = '{%s}master-styles' % (NAME_SPACE_1, )
  949. master_el = root_el.find(path)
  950. if master_el is None:
  951. return
  952. path = '{%s}master-page' % (SNSD['style'], )
  953. master_el_container = master_el.findall(path)
  954. master_el = None
  955. target_attrib = '{%s}name' % (SNSD['style'], )
  956. target_name = self.rststyle('pagedefault')
  957. for el in master_el_container:
  958. if el.get(target_attrib) == target_name:
  959. master_el = el
  960. break
  961. if master_el is None:
  962. return
  963. el1 = master_el
  964. if self.header_content or self.settings.custom_header:
  965. if WhichElementTree == 'lxml':
  966. el2 = SubElement(el1, 'style:header', nsdict=SNSD)
  967. else:
  968. el2 = SubElement(el1, 'style:header',
  969. attrib=STYLES_NAMESPACE_ATTRIB,
  970. nsdict=STYLES_NAMESPACE_DICT,
  971. )
  972. for el in self.header_content:
  973. attrkey = add_ns('text:style-name', nsdict=SNSD)
  974. el.attrib[attrkey] = self.rststyle('header')
  975. el2.append(el)
  976. if self.settings.custom_header:
  977. elcustom = self.create_custom_headfoot(el2,
  978. self.settings.custom_header, 'header', automatic_styles)
  979. if self.footer_content or self.settings.custom_footer:
  980. if WhichElementTree == 'lxml':
  981. el2 = SubElement(el1, 'style:footer', nsdict=SNSD)
  982. else:
  983. el2 = SubElement(el1, 'style:footer',
  984. attrib=STYLES_NAMESPACE_ATTRIB,
  985. nsdict=STYLES_NAMESPACE_DICT,
  986. )
  987. for el in self.footer_content:
  988. attrkey = add_ns('text:style-name', nsdict=SNSD)
  989. el.attrib[attrkey] = self.rststyle('footer')
  990. el2.append(el)
  991. if self.settings.custom_footer:
  992. elcustom = self.create_custom_headfoot(el2,
  993. self.settings.custom_footer, 'footer', automatic_styles)
  994. code_none, code_field, code_text = list(range(3))
  995. field_pat = re.compile(r'%(..?)%')
  996. def create_custom_headfoot(self, parent, text, style_name, automatic_styles):
  997. parent = SubElement(parent, 'text:p', attrib={
  998. 'text:style-name': self.rststyle(style_name),
  999. })
  1000. current_element = None
  1001. field_iter = self.split_field_specifiers_iter(text)
  1002. for item in field_iter:
  1003. if item[0] == ODFTranslator.code_field:
  1004. if item[1] not in ('p', 'P',
  1005. 't1', 't2', 't3', 't4',
  1006. 'd1', 'd2', 'd3', 'd4', 'd5',
  1007. 's', 't', 'a'):
  1008. msg = 'bad field spec: %%%s%%' % (item[1], )
  1009. raise RuntimeError(msg)
  1010. el1 = self.make_field_element(parent,
  1011. item[1], style_name, automatic_styles)
  1012. if el1 is None:
  1013. msg = 'bad field spec: %%%s%%' % (item[1], )
  1014. raise RuntimeError(msg)
  1015. else:
  1016. current_element = el1
  1017. else:
  1018. if current_element is None:
  1019. parent.text = item[1]
  1020. else:
  1021. current_element.tail = item[1]
  1022. def make_field_element(self, parent, text, style_name, automatic_styles):
  1023. if text == 'p':
  1024. el1 = SubElement(parent, 'text:page-number', attrib={
  1025. #'text:style-name': self.rststyle(style_name),
  1026. 'text:select-page': 'current',
  1027. })
  1028. elif text == 'P':
  1029. el1 = SubElement(parent, 'text:page-count', attrib={
  1030. #'text:style-name': self.rststyle(style_name),
  1031. })
  1032. elif text == 't1':
  1033. self.style_index += 1
  1034. el1 = SubElement(parent, 'text:time', attrib={
  1035. 'text:style-name': self.rststyle(style_name),
  1036. 'text:fixed': 'true',
  1037. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1038. })
  1039. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1040. 'style:name': 'rst-time-style-%d' % self.style_index,
  1041. 'xmlns:number': SNSD['number'],
  1042. 'xmlns:style': SNSD['style'],
  1043. })
  1044. el3 = SubElement(el2, 'number:hours', attrib={
  1045. 'number:style': 'long',
  1046. })
  1047. el3 = SubElement(el2, 'number:text')
  1048. el3.text = ':'
  1049. el3 = SubElement(el2, 'number:minutes', attrib={
  1050. 'number:style': 'long',
  1051. })
  1052. elif text == 't2':
  1053. self.style_index += 1
  1054. el1 = SubElement(parent, 'text:time', attrib={
  1055. 'text:style-name': self.rststyle(style_name),
  1056. 'text:fixed': 'true',
  1057. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1058. })
  1059. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1060. 'style:name': 'rst-time-style-%d' % self.style_index,
  1061. 'xmlns:number': SNSD['number'],
  1062. 'xmlns:style': SNSD['style'],
  1063. })
  1064. el3 = SubElement(el2, 'number:hours', attrib={
  1065. 'number:style': 'long',
  1066. })
  1067. el3 = SubElement(el2, 'number:text')
  1068. el3.text = ':'
  1069. el3 = SubElement(el2, 'number:minutes', attrib={
  1070. 'number:style': 'long',
  1071. })
  1072. el3 = SubElement(el2, 'number:text')
  1073. el3.text = ':'
  1074. el3 = SubElement(el2, 'number:seconds', attrib={
  1075. 'number:style': 'long',
  1076. })
  1077. elif text == 't3':
  1078. self.style_index += 1
  1079. el1 = SubElement(parent, 'text:time', attrib={
  1080. 'text:style-name': self.rststyle(style_name),
  1081. 'text:fixed': 'true',
  1082. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1083. })
  1084. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1085. 'style:name': 'rst-time-style-%d' % self.style_index,
  1086. 'xmlns:number': SNSD['number'],
  1087. 'xmlns:style': SNSD['style'],
  1088. })
  1089. el3 = SubElement(el2, 'number:hours', attrib={
  1090. 'number:style': 'long',
  1091. })
  1092. el3 = SubElement(el2, 'number:text')
  1093. el3.text = ':'
  1094. el3 = SubElement(el2, 'number:minutes', attrib={
  1095. 'number:style': 'long',
  1096. })
  1097. el3 = SubElement(el2, 'number:text')
  1098. el3.text = ' '
  1099. el3 = SubElement(el2, 'number:am-pm')
  1100. elif text == 't4':
  1101. self.style_index += 1
  1102. el1 = SubElement(parent, 'text:time', attrib={
  1103. 'text:style-name': self.rststyle(style_name),
  1104. 'text:fixed': 'true',
  1105. 'style:data-style-name': 'rst-time-style-%d' % self.style_index,
  1106. })
  1107. el2 = SubElement(automatic_styles, 'number:time-style', attrib={
  1108. 'style:name': 'rst-time-style-%d' % self.style_index,
  1109. 'xmlns:number': SNSD['number'],
  1110. 'xmlns:style': SNSD['style'],
  1111. })
  1112. el3 = SubElement(el2, 'number:hours', attrib={
  1113. 'number:style': 'long',
  1114. })
  1115. el3 = SubElement(el2, 'number:text')
  1116. el3.text = ':'
  1117. el3 = SubElement(el2, 'number:minutes', attrib={
  1118. 'number:style': 'long',
  1119. })
  1120. el3 = SubElement(el2, 'number:text')
  1121. el3.text = ':'
  1122. el3 = SubElement(el2, 'number:seconds', attrib={
  1123. 'number:style': 'long',
  1124. })
  1125. el3 = SubElement(el2, 'number:text')
  1126. el3.text = ' '
  1127. el3 = SubElement(el2, 'number:am-pm')
  1128. elif text == 'd1':
  1129. self.style_index += 1
  1130. el1 = SubElement(parent, 'text:date', attrib={
  1131. 'text:style-name': self.rststyle(style_name),
  1132. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1133. })
  1134. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1135. 'style:name': 'rst-date-style-%d' % self.style_index,
  1136. 'number:automatic-order': 'true',
  1137. 'xmlns:number': SNSD['number'],
  1138. 'xmlns:style': SNSD['style'],
  1139. })
  1140. el3 = SubElement(el2, 'number:month', attrib={
  1141. 'number:style': 'long',
  1142. })
  1143. el3 = SubElement(el2, 'number:text')
  1144. el3.text = '/'
  1145. el3 = SubElement(el2, 'number:day', attrib={
  1146. 'number:style': 'long',
  1147. })
  1148. el3 = SubElement(el2, 'number:text')
  1149. el3.text = '/'
  1150. el3 = SubElement(el2, 'number:year')
  1151. elif text == 'd2':
  1152. self.style_index += 1
  1153. el1 = SubElement(parent, 'text:date', attrib={
  1154. 'text:style-name': self.rststyle(style_name),
  1155. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1156. })
  1157. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1158. 'style:name': 'rst-date-style-%d' % self.style_index,
  1159. 'number:automatic-order': 'true',
  1160. 'xmlns:number': SNSD['number'],
  1161. 'xmlns:style': SNSD['style'],
  1162. })
  1163. el3 = SubElement(el2, 'number:month', attrib={
  1164. 'number:style': 'long',
  1165. })
  1166. el3 = SubElement(el2, 'number:text')
  1167. el3.text = '/'
  1168. el3 = SubElement(el2, 'number:day', attrib={
  1169. 'number:style': 'long',
  1170. })
  1171. el3 = SubElement(el2, 'number:text')
  1172. el3.text = '/'
  1173. el3 = SubElement(el2, 'number:year', attrib={
  1174. 'number:style': 'long',
  1175. })
  1176. elif text == 'd3':
  1177. self.style_index += 1
  1178. el1 = SubElement(parent, 'text:date', attrib={
  1179. 'text:style-name': self.rststyle(style_name),
  1180. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1181. })
  1182. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1183. 'style:name': 'rst-date-style-%d' % self.style_index,
  1184. 'number:automatic-order': 'true',
  1185. 'xmlns:number': SNSD['number'],
  1186. 'xmlns:style': SNSD['style'],
  1187. })
  1188. el3 = SubElement(el2, 'number:month', attrib={
  1189. 'number:textual': 'true',
  1190. })
  1191. el3 = SubElement(el2, 'number:text')
  1192. el3.text = ' '
  1193. el3 = SubElement(el2, 'number:day', attrib={
  1194. })
  1195. el3 = SubElement(el2, 'number:text')
  1196. el3.text = ', '
  1197. el3 = SubElement(el2, 'number:year', attrib={
  1198. 'number:style': 'long',
  1199. })
  1200. elif text == 'd4':
  1201. self.style_index += 1
  1202. el1 = SubElement(parent, 'text:date', attrib={
  1203. 'text:style-name': self.rststyle(style_name),
  1204. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1205. })
  1206. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1207. 'style:name': 'rst-date-style-%d' % self.style_index,
  1208. 'number:automatic-order': 'true',
  1209. 'xmlns:number': SNSD['number'],
  1210. 'xmlns:style': SNSD['style'],
  1211. })
  1212. el3 = SubElement(el2, 'number:month', attrib={
  1213. 'number:textual': 'true',
  1214. 'number:style': 'long',
  1215. })
  1216. el3 = SubElement(el2, 'number:text')
  1217. el3.text = ' '
  1218. el3 = SubElement(el2, 'number:day', attrib={
  1219. })
  1220. el3 = SubElement(el2, 'number:text')
  1221. el3.text = ', '
  1222. el3 = SubElement(el2, 'number:year', attrib={
  1223. 'number:style': 'long',
  1224. })
  1225. elif text == 'd5':
  1226. self.style_index += 1
  1227. el1 = SubElement(parent, 'text:date', attrib={
  1228. 'text:style-name': self.rststyle(style_name),
  1229. 'style:data-style-name': 'rst-date-style-%d' % self.style_index,
  1230. })
  1231. el2 = SubElement(automatic_styles, 'number:date-style', attrib={
  1232. 'style:name': 'rst-date-style-%d' % self.style_index,
  1233. 'xmlns:number': SNSD['number'],
  1234. 'xmlns:style': SNSD['style'],
  1235. })
  1236. el3 = SubElement(el2, 'number:year', attrib={
  1237. 'number:style': 'long',
  1238. })
  1239. el3 = SubElement(el2, 'number:text')
  1240. el3.text = '-'
  1241. el3 = SubElement(el2, 'number:month', attrib={
  1242. 'number:style': 'long',
  1243. })
  1244. el3 = SubElement(el2, 'number:text')
  1245. el3.text = '-'
  1246. el3 = SubElement(el2, 'number:day', attrib={
  1247. 'number:style': 'long',
  1248. })
  1249. elif text == 's':
  1250. el1 = SubElement(parent, 'text:subject', attrib={
  1251. 'text:style-name': self.rststyle(style_name),
  1252. })
  1253. elif text == 't':
  1254. el1 = SubElement(parent, 'text:title', attrib={
  1255. 'text:style-name': self.rststyle(style_name),
  1256. })
  1257. elif text == 'a':
  1258. el1 = SubElement(parent, 'text:author-name', attrib={
  1259. 'text:fixed': 'false',
  1260. })
  1261. else:
  1262. el1 = None
  1263. return el1
  1264. def split_field_specifiers_iter(self, text):
  1265. pos1 = 0
  1266. pos_end = len(text)
  1267. while True:
  1268. mo = ODFTranslator.field_pat.search(text, pos1)
  1269. if mo:
  1270. pos2 = mo.start()
  1271. if pos2 > pos1:
  1272. yield (ODFTranslator.code_text, text[pos1:pos2])
  1273. yield (ODFTranslator.code_field, mo.group(1))
  1274. pos1 = mo.end()
  1275. else:
  1276. break
  1277. trailing = text[pos1:]
  1278. if trailing:
  1279. yield (ODFTranslator.code_text, trailing)
  1280. def astext(self):
  1281. root = self.content_tree.getroot()
  1282. et = etree.ElementTree(root)
  1283. s1 = ToString(et)
  1284. return s1
  1285. def content_astext(self):
  1286. return self.astext()
  1287. def set_title(self, title): self.title = title
  1288. def get_title(self): return self.title
  1289. def set_embedded_file_list(self, embedded_file_list):
  1290. self.embedded_file_list = embedded_file_list
  1291. def get_embedded_file_list(self): return self.embedded_file_list
  1292. def get_meta_dict(self): return self.meta_dict
  1293. def process_footnotes(self):
  1294. for node, el1 in self.footnote_list:
  1295. backrefs = node.attributes.get('backrefs', [])
  1296. first = True
  1297. for ref in backrefs:
  1298. el2 = self.footnote_ref_dict.get(ref)
  1299. if el2 is not None:
  1300. if first:
  1301. first = False
  1302. el3 = copy.deepcopy(el1)
  1303. el2.append(el3)
  1304. else:
  1305. children = el2.getchildren()
  1306. if len(children) > 0: # and 'id' in el2.attrib:
  1307. child = children[0]
  1308. ref1 = child.text
  1309. attribkey = add_ns('text:id', nsdict=SNSD)
  1310. id1 = el2.get(attribkey, 'footnote-error')
  1311. if id1 is None:
  1312. id1 = ''
  1313. tag = add_ns('text:note-ref', nsdict=SNSD)
  1314. el2.tag = tag
  1315. if self.settings.endnotes_end_doc:
  1316. note_class = 'endnote'
  1317. else:
  1318. note_class = 'footnote'
  1319. el2.attrib.clear()
  1320. attribkey = add_ns('text:note-class', nsdict=SNSD)
  1321. el2.attrib[attribkey] = note_class
  1322. attribkey = add_ns('text:ref-name', nsdict=SNSD)
  1323. el2.attrib[attribkey] = id1
  1324. attribkey = add_ns('text:reference-format', nsdict=SNSD)
  1325. el2.attrib[attribkey] = 'page'
  1326. el2.text = ref1
  1327. #
  1328. # Utility methods
  1329. def append_child(self, tag, attrib=None, parent=None):
  1330. if parent is None:
  1331. parent = self.current_element
  1332. if attrib is None:
  1333. el = SubElement(parent, tag)
  1334. else:
  1335. el = SubElement(parent, tag, attrib)
  1336. return el
  1337. def append_p(self, style, text=None):
  1338. result = self.append_child('text:p', attrib={
  1339. 'text:style-name': self.rststyle(style)})
  1340. self.append_pending_ids(result)
  1341. if text is not None:
  1342. result.text = text
  1343. return result
  1344. def append_pending_ids(self, el):
  1345. if self.settings.create_links:
  1346. for id in self.pending_ids:
  1347. SubElement(el, 'text:reference-mark', attrib={
  1348. 'text:name': id})
  1349. self.pending_ids = [ ]
  1350. def set_current_element(self, el):
  1351. self.current_element = el
  1352. def set_to_parent(self):
  1353. self.current_element = self.current_element.getparent()
  1354. def generate_labeled_block(self, node, label):
  1355. label = '%s:' % (self.language.labels[label], )
  1356. el = self.append_p('textbody')
  1357. el1 = SubElement(el, 'text:span',
  1358. attrib={'text:style-name': self.rststyle('strong')})
  1359. el1.text = label
  1360. el = self.append_p('blockindent')
  1361. return el
  1362. def generate_labeled_line(self, node, label):
  1363. label = '%s:' % (self.language.labels[label], )
  1364. el = self.append_p('textbody')
  1365. el1 = SubElement(el, 'text:span',
  1366. attrib={'text:style-name': self.rststyle('strong')})
  1367. el1.text = label
  1368. el1.tail = node.astext()
  1369. return el
  1370. def encode(self, text):
  1371. text = text.replace('\u00a0', " ")
  1372. return text
  1373. #
  1374. # Visitor functions
  1375. #
  1376. # In alphabetic order, more or less.
  1377. # See docutils.docutils.nodes.node_class_names.
  1378. #
  1379. def dispatch_visit(self, node):
  1380. """Override to catch basic attributes which many nodes have."""
  1381. self.handle_basic_atts(node)
  1382. nodes.GenericNodeVisitor.dispatch_visit(self, node)
  1383. def handle_basic_atts(self, node):
  1384. if isinstance(node, nodes.Element) and node['ids']:
  1385. self.pending_ids += node['ids']
  1386. def default_visit(self, node):
  1387. self.document.reporter.warning('missing visit_%s' % (node.tagname, ))
  1388. def default_departure(self, node):
  1389. self.document.reporter.warning('missing depart_%s' % (node.tagname, ))
  1390. def visit_Text(self, node):
  1391. # Skip nodes whose text has been processed in parent nodes.
  1392. if isinstance(node.parent, docutils.nodes.literal_block):
  1393. return
  1394. text = node.astext()
  1395. # Are we in mixed content? If so, add the text to the
  1396. # etree tail of the previous sibling element.
  1397. if len(self.current_element.getchildren()) > 0:
  1398. if self.current_element.getchildren()[-1].tail:
  1399. self.current_element.getchildren()[-1].tail += text
  1400. else:
  1401. self.current_element.getchildren()[-1].tail = text
  1402. else:
  1403. if self.current_element.text:
  1404. self.current_element.text += text
  1405. else:
  1406. self.current_element.text = text
  1407. def depart_Text(self, node):
  1408. pass
  1409. #
  1410. # Pre-defined fields
  1411. #
  1412. def visit_address(self, node):
  1413. el = self.generate_labeled_block(node, 'address')
  1414. self.set_current_element(el)
  1415. def depart_address(self, node):
  1416. self.set_to_parent()
  1417. def visit_author(self, node):
  1418. if isinstance(node.parent, nodes.authors):
  1419. el = self.append_p('blockindent')
  1420. else:
  1421. el = self.generate_labeled_block(node, 'author')
  1422. self.set_current_element(el)
  1423. def depart_author(self, node):
  1424. self.set_to_parent()
  1425. def visit_authors(self, node):
  1426. label = '%s:' % (self.language.labels['authors'], )
  1427. el = self.append_p('textbody')
  1428. el1 = SubElement(el, 'text:span',
  1429. attrib={'text:style-name': self.rststyle('strong')})
  1430. el1.text = label
  1431. def depart_authors(self, node):
  1432. pass
  1433. def visit_contact(self, node):
  1434. el = self.generate_labeled_block(node, 'contact')
  1435. self.set_current_element(el)
  1436. def depart_contact(self, node):
  1437. self.set_to_parent()
  1438. def visit_copyright(self, node):
  1439. el = self.generate_labeled_block(node, 'copyright')
  1440. self.set_current_element(el)
  1441. def depart_copyright(self, node):
  1442. self.set_to_parent()
  1443. def visit_date(self, node):
  1444. self.generate_labeled_line(node, 'date')
  1445. def depart_date(self, node):
  1446. pass
  1447. def visit_organization(self, node):
  1448. el = self.generate_labeled_block(node, 'organization')
  1449. self.set_current_element(el)
  1450. def depart_organization(self, node):
  1451. self.set_to_parent()
  1452. def visit_status(self, node):
  1453. el = self.generate_labeled_block(node, 'status')
  1454. self.set_current_element(el)
  1455. def depart_status(self, node):
  1456. self.set_to_parent()
  1457. def visit_revision(self, node):
  1458. el = self.generate_labeled_line(node, 'revision')
  1459. def depart_revision(self, node):
  1460. pass
  1461. def visit_version(self, node):
  1462. el = self.generate_labeled_line(node, 'version')
  1463. #self.set_current_element(el)
  1464. def depart_version(self, node):
  1465. #self.set_to_parent()
  1466. pass
  1467. def visit_attribution(self, node):
  1468. el = self.append_p('attribution', node.astext())
  1469. def depart_attribution(self, node):
  1470. pass
  1471. def visit_block_quote(self, node):
  1472. if 'epigraph' in node.attributes['classes']:
  1473. self.paragraph_style_stack.append(self.rststyle('epigraph'))
  1474. self.blockstyle = self.rststyle('epigraph')
  1475. elif 'highlights' in node.attributes['classes']:
  1476. self.paragraph_style_stack.append(self.rststyle('highlights'))
  1477. self.blockstyle = self.rststyle('highlights')
  1478. else:
  1479. self.paragraph_style_stack.append(self.rststyle('blockquote'))
  1480. self.blockstyle = self.rststyle('blockquote')
  1481. self.line_indent_level += 1
  1482. def depart_block_quote(self, node):
  1483. self.paragraph_style_stack.pop()
  1484. self.blockstyle = ''
  1485. self.line_indent_level -= 1
  1486. def visit_bullet_list(self, node):
  1487. self.list_level +=1
  1488. if self.in_table_of_contents:
  1489. if self.settings.generate_oowriter_toc:
  1490. pass
  1491. else:
  1492. if 'classes' in node and \
  1493. 'auto-toc' in node.attributes['classes']:
  1494. el = SubElement(self.current_element, 'text:list', attrib={
  1495. 'text:style-name': self.rststyle('tocenumlist'),
  1496. })
  1497. self.list_style_stack.append(self.rststyle('enumitem'))
  1498. else:
  1499. el = SubElement(self.current_element, 'text:list', attrib={
  1500. 'text:style-name': self.rststyle('tocbulletlist'),
  1501. })
  1502. self.list_style_stack.append(self.rststyle('bulletitem'))
  1503. self.set_current_element(el)
  1504. else:
  1505. if self.blockstyle == self.rststyle('blockquote'):
  1506. el = SubElement(self.current_element, 'text:list', attrib={
  1507. 'text:style-name': self.rststyle('blockquote-bulletlist'),
  1508. })
  1509. self.list_style_stack.append(
  1510. self.rststyle('blockquote-bulletitem'))
  1511. elif self.blockstyle == self.rststyle('highlights'):
  1512. el = SubElement(self.current_element, 'text:list', attrib={
  1513. 'text:style-name': self.rststyle('highlights-bulletlist'),
  1514. })
  1515. self.list_style_stack.append(
  1516. self.rststyle('highlights-bulletitem'))
  1517. elif self.blockstyle == self.rststyle('epigraph'):
  1518. el = SubElement(self.current_element, 'text:list', attrib={
  1519. 'text:style-name': self.rststyle('epigraph-bulletlist'),
  1520. })
  1521. self.list_style_stack.append(
  1522. self.rststyle('epigraph-bulletitem'))
  1523. else:
  1524. el = SubElement(self.current_element, 'text:list', attrib={
  1525. 'text:style-name': self.rststyle('bulletlist'),
  1526. })
  1527. self.list_style_stack.append(self.rststyle('bulletitem'))
  1528. self.set_current_element(el)
  1529. def depart_bullet_list(self, node):
  1530. if self.in_table_of_contents:
  1531. if self.settings.generate_oowriter_toc:
  1532. pass
  1533. else:
  1534. self.set_to_parent()
  1535. self.list_style_stack.pop()
  1536. else:
  1537. self.set_to_parent()
  1538. self.list_style_stack.pop()
  1539. self.list_level -=1
  1540. def visit_caption(self, node):
  1541. raise nodes.SkipChildren()
  1542. pass
  1543. def depart_caption(self, node):
  1544. pass
  1545. def visit_comment(self, node):
  1546. el = self.append_p('textbody')
  1547. el1 = SubElement(el, 'office:annotation', attrib={})
  1548. el2 = SubElement(el1, 'dc:creator', attrib={})
  1549. s1 = os.environ.get('USER', '')
  1550. el2.text = s1
  1551. el2 = SubElement(el1, 'text:p', attrib={})
  1552. el2.text = node.astext()
  1553. def depart_comment(self, node):
  1554. pass
  1555. def visit_compound(self, node):
  1556. # The compound directive currently receives no special treatment.
  1557. pass
  1558. def depart_compound(self, node):
  1559. pass
  1560. def visit_container(self, node):
  1561. styles = node.attributes.get('classes', ())
  1562. if len(styles) > 0:
  1563. self.paragraph_style_stack.append(self.rststyle(styles[0]))
  1564. def depart_container(self, node):
  1565. styles = node.attributes.get('classes', ())
  1566. if len(styles) > 0:
  1567. self.paragraph_style_stack.pop()
  1568. def visit_decoration(self, node):
  1569. pass
  1570. def depart_decoration(self, node):
  1571. pass
  1572. def visit_definition_list(self, node):
  1573. self.def_list_level +=1
  1574. if self.list_level > 5:
  1575. raise RuntimeError(
  1576. 'max definition list nesting level exceeded')
  1577. def depart_definition_list(self, node):
  1578. self.def_list_level -=1
  1579. def visit_definition_list_item(self, node):
  1580. pass
  1581. def depart_definition_list_item(self, node):
  1582. pass
  1583. def visit_term(self, node):
  1584. el = self.append_p('deflist-term-%d' % self.def_list_level)
  1585. el.text = node.astext()
  1586. self.set_current_element(el)
  1587. raise nodes.SkipChildren()
  1588. def depart_term(self, node):
  1589. self.set_to_parent()
  1590. def visit_definition(self, node):
  1591. self.paragraph_style_stack.append(
  1592. self.rststyle('deflist-def-%d' % self.def_list_level))
  1593. self.bumped_list_level_stack.append(ListLevel(1))
  1594. def depart_definition(self, node):
  1595. self.paragraph_style_stack.pop()
  1596. self.bumped_list_level_stack.pop()
  1597. def visit_classifier(self, node):
  1598. els = self.current_element.getchildren()
  1599. if len(els) > 0:
  1600. el = els[-1]
  1601. el1 = SubElement(el, 'text:span',
  1602. attrib={'text:style-name': self.rststyle('emphasis')
  1603. })
  1604. el1.text = ' (%s)' % (node.astext(), )
  1605. def depart_classifier(self, node):
  1606. pass
  1607. def visit_document(self, node):
  1608. pass
  1609. def depart_document(self, node):
  1610. self.process_footnotes()
  1611. def visit_docinfo(self, node):
  1612. self.section_level += 1
  1613. self.section_count += 1
  1614. if self.settings.create_sections:
  1615. el = self.append_child('text:section', attrib={
  1616. 'text:name': 'Section%d' % self.section_count,
  1617. 'text:style-name': 'Sect%d' % self.section_level,
  1618. })
  1619. self.set_current_element(el)
  1620. def depart_docinfo(self, node):
  1621. self.section_level -= 1
  1622. if self.settings.create_sections:
  1623. self.set_to_parent()
  1624. def visit_emphasis(self, node):
  1625. el = SubElement(self.current_element, 'text:span',
  1626. attrib={'text:style-name': self.rststyle('emphasis')})
  1627. self.set_current_element(el)
  1628. def depart_emphasis(self, node):
  1629. self.set_to_parent()
  1630. def visit_enumerated_list(self, node):
  1631. el1 = self.current_element
  1632. if self.blockstyle == self.rststyle('blockquote'):
  1633. el2 = SubElement(el1, 'text:list', attrib={
  1634. 'text:style-name': self.rststyle('blockquote-enumlist'),
  1635. })
  1636. self.list_style_stack.append(self.rststyle('blockquote-enumitem'))
  1637. elif self.blockstyle == self.rststyle('highlights'):
  1638. el2 = SubElement(el1, 'text:list', attrib={
  1639. 'text:style-name': self.rststyle('highlights-enumlist'),
  1640. })
  1641. self.list_style_stack.append(self.rststyle('highlights-enumitem'))
  1642. elif self.blockstyle == self.rststyle('epigraph'):
  1643. el2 = SubElement(el1, 'text:list', attrib={
  1644. 'text:style-name': self.rststyle('epigraph-enumlist'),
  1645. })
  1646. self.list_style_stack.append(self.rststyle('epigraph-enumitem'))
  1647. else:
  1648. liststylename = 'enumlist-%s' % (node.get('enumtype', 'arabic'), )
  1649. el2 = SubElement(el1, 'text:list', attrib={
  1650. 'text:style-name': self.rststyle(liststylename),
  1651. })
  1652. self.list_style_stack.append(self.rststyle('enumitem'))
  1653. self.set_current_element(el2)
  1654. def depart_enumerated_list(self, node):
  1655. self.set_to_parent()
  1656. self.list_style_stack.pop()
  1657. def visit_list_item(self, node):
  1658. # If we are in a "bumped" list level, then wrap this
  1659. # list in an outer lists in order to increase the
  1660. # indentation level.
  1661. if self.in_table_of_contents:
  1662. if self.settings.generate_oowriter_toc:
  1663. self.paragraph_style_stack.append(
  1664. self.rststyle('contents-%d' % (self.list_level, )))
  1665. else:
  1666. el1 = self.append_child('text:list-item')
  1667. self.set_current_element(el1)
  1668. else:
  1669. el1 = self.append_child('text:list-item')
  1670. el3 = el1
  1671. if len(self.bumped_list_level_stack) > 0:
  1672. level_obj = self.bumped_list_level_stack[-1]
  1673. if level_obj.get_sibling():
  1674. level_obj.set_nested(False)
  1675. for level_obj1 in self.bumped_list_level_stack:
  1676. for idx in range(level_obj1.get_level()):
  1677. el2 = self.append_child('text:list', parent=el3)
  1678. el3 = self.append_child(
  1679. 'text:list-item', parent=el2)
  1680. self.paragraph_style_stack.append(self.list_style_stack[-1])
  1681. self.set_current_element(el3)
  1682. def depart_list_item(self, node):
  1683. if self.in_table_of_contents:
  1684. if self.settings.generate_oowriter_toc:
  1685. self.paragraph_style_stack.pop()
  1686. else:
  1687. self.set_to_parent()
  1688. else:
  1689. if len(self.bumped_list_level_stack) > 0:
  1690. level_obj = self.bumped_list_level_stack[-1]
  1691. if level_obj.get_sibling():
  1692. level_obj.set_nested(True)
  1693. for level_obj1 in self.bumped_list_level_stack:
  1694. for idx in range(level_obj1.get_level()):
  1695. self.set_to_parent()
  1696. self.set_to_parent()
  1697. self.paragraph_style_stack.pop()
  1698. self.set_to_parent()
  1699. def visit_header(self, node):
  1700. self.in_header = True
  1701. def depart_header(self, node):
  1702. self.in_header = False
  1703. def visit_footer(self, node):
  1704. self.in_footer = True
  1705. def depart_footer(self, node):
  1706. self.in_footer = False
  1707. def visit_field(self, node):
  1708. pass
  1709. def depart_field(self, node):
  1710. pass
  1711. def visit_field_list(self, node):
  1712. pass
  1713. def depart_field_list(self, node):
  1714. pass
  1715. def visit_field_name(self, node):
  1716. el = self.append_p('textbody')
  1717. el1 = SubElement(el, 'text:span',
  1718. attrib={'text:style-name': self.rststyle('strong')})
  1719. el1.text = node.astext()
  1720. def depart_field_name(self, node):
  1721. pass
  1722. def visit_field_body(self, node):
  1723. self.paragraph_style_stack.append(self.rststyle('blockindent'))
  1724. def depart_field_body(self, node):
  1725. self.paragraph_style_stack.pop()
  1726. def visit_figure(self, node):
  1727. pass
  1728. def depart_figure(self, node):
  1729. pass
  1730. def visit_footnote(self, node):
  1731. self.footnote_level += 1
  1732. self.save_footnote_current = self.current_element
  1733. el1 = Element('text:note-body')
  1734. self.current_element = el1
  1735. self.footnote_list.append((node, el1))
  1736. if isinstance(node, docutils.nodes.citation):
  1737. self.paragraph_style_stack.append(self.rststyle('citation'))
  1738. else:
  1739. self.paragraph_style_stack.append(self.rststyle('footnote'))
  1740. def depart_footnote(self, node):
  1741. self.paragraph_style_stack.pop()
  1742. self.current_element = self.save_footnote_current
  1743. self.footnote_level -= 1
  1744. footnote_chars = [
  1745. '*', '**', '***',
  1746. '++', '+++',
  1747. '##', '###',
  1748. '@@', '@@@',
  1749. ]
  1750. def visit_footnote_reference(self, node):
  1751. if self.footnote_level <= 0:
  1752. id = node.attributes['ids'][0]
  1753. refid = node.attributes.get('refid')
  1754. if refid is None:
  1755. refid = ''
  1756. if self.settings.endnotes_end_doc:
  1757. note_class = 'endnote'
  1758. else:
  1759. note_class = 'footnote'
  1760. el1 = self.append_child('text:note', attrib={
  1761. 'text:id': '%s' % (refid, ),
  1762. 'text:note-class': note_class,
  1763. })
  1764. note_auto = str(node.attributes.get('auto', 1))
  1765. if isinstance(node, docutils.nodes.citation_reference):
  1766. citation = '[%s]' % node.astext()
  1767. el2 = SubElement(el1, 'text:note-citation', attrib={
  1768. 'text:label': citation,
  1769. })
  1770. el2.text = citation
  1771. elif note_auto == '1':
  1772. el2 = SubElement(el1, 'text:note-citation', attrib={
  1773. 'text:label': node.astext(),
  1774. })
  1775. el2.text = node.astext()
  1776. elif note_auto == '*':
  1777. if self.footnote_chars_idx >= len(
  1778. ODFTranslator.footnote_chars):
  1779. self.footnote_chars_idx = 0
  1780. footnote_char = ODFTranslator.footnote_chars[
  1781. self.footnote_chars_idx]
  1782. self.footnote_chars_idx += 1
  1783. el2 = SubElement(el1, 'text:note-citation', attrib={
  1784. 'text:label': footnote_char,
  1785. })
  1786. el2.text = footnote_char
  1787. self.footnote_ref_dict[id] = el1
  1788. raise nodes.SkipChildren()
  1789. def depart_footnote_reference(self, node):
  1790. pass
  1791. def visit_citation(self, node):
  1792. self.in_citation = True
  1793. for id in node.attributes['ids']:
  1794. self.citation_id = id
  1795. break
  1796. self.paragraph_style_stack.append(self.rststyle('blockindent'))
  1797. self.bumped_list_level_stack.append(ListLevel(1))
  1798. def depart_citation(self, node):
  1799. self.citation_id = None
  1800. self.paragraph_style_stack.pop()
  1801. self.bumped_list_level_stack.pop()
  1802. self.in_citation = False
  1803. def visit_citation_reference(self, node):
  1804. if self.settings.create_links:
  1805. id = node.attributes['refid']
  1806. el = self.append_child('text:reference-ref', attrib={
  1807. 'text:ref-name': '%s' % (id, ),
  1808. 'text:reference-format': 'text',
  1809. })
  1810. el.text = '['
  1811. self.set_current_element(el)
  1812. elif self.current_element.text is None:
  1813. self.current_element.text = '['
  1814. else:
  1815. self.current_element.text += '['
  1816. def depart_citation_reference(self, node):
  1817. self.current_element.text += ']'
  1818. if self.settings.create_links:
  1819. self.set_to_parent()
  1820. def visit_label(self, node):
  1821. if isinstance(node.parent, docutils.nodes.footnote):
  1822. raise nodes.SkipChildren()
  1823. elif self.citation_id is not None:
  1824. el = self.append_p('textbody')
  1825. self.set_current_element(el)
  1826. if self.settings.create_links:
  1827. el0 = SubElement(el, 'text:span')
  1828. el0.text = '['
  1829. el1 = self.append_child('text:reference-mark-start', attrib={
  1830. 'text:name': '%s' % (self.citation_id, ),
  1831. })
  1832. else:
  1833. el.text = '['
  1834. def depart_label(self, node):
  1835. if isinstance(node.parent, docutils.nodes.footnote):
  1836. pass
  1837. elif self.citation_id is not None:
  1838. if self.settings.create_links:
  1839. el = self.append_child('text:reference-mark-end', attrib={
  1840. 'text:name': '%s' % (self.citation_id, ),
  1841. })
  1842. el0 = SubElement(self.current_element, 'text:span')
  1843. el0.text = ']'
  1844. else:
  1845. self.current_element.text += ']'
  1846. self.set_to_parent()
  1847. def visit_generated(self, node):
  1848. pass
  1849. def depart_generated(self, node):
  1850. pass
  1851. def check_file_exists(self, path):
  1852. if os.path.exists(path):
  1853. return 1
  1854. else:
  1855. return 0
  1856. def visit_image(self, node):
  1857. # Capture the image file.
  1858. if 'uri' in node.attributes:
  1859. source = node.attributes['uri']
  1860. if not source.startswith('http:'):
  1861. if not source.startswith(os.sep):
  1862. docsource, line = utils.get_source_line(node)
  1863. if docsource:
  1864. dirname = os.path.dirname(docsource)
  1865. if dirname:
  1866. source = '%s%s%s' % (dirname, os.sep, source, )
  1867. if not self.check_file_exists(source):
  1868. self.document.reporter.warning(
  1869. 'Cannot find image file %s.' % (source, ))
  1870. return
  1871. else:
  1872. return
  1873. if source in self.image_dict:
  1874. filename, destination = self.image_dict[source]
  1875. else:
  1876. self.image_count += 1
  1877. filename = os.path.split(source)[1]
  1878. destination = 'Pictures/1%08x%s' % (self.image_count, filename, )
  1879. if source.startswith('http:'):
  1880. try:
  1881. imgfile = urllib.request.urlopen(source)
  1882. content = imgfile.read()
  1883. imgfile.close()
  1884. imgfile2 = tempfile.NamedTemporaryFile('wb', delete=False)
  1885. imgfile2.write(content)
  1886. imgfile2.close()
  1887. imgfilename = imgfile2.name
  1888. source = imgfilename
  1889. except urllib.error.HTTPError as e:
  1890. self.document.reporter.warning(
  1891. "Can't open image url %s." % (source, ))
  1892. spec = (source, destination,)
  1893. else:
  1894. spec = (os.path.abspath(source), destination,)
  1895. self.embedded_file_list.append(spec)
  1896. self.image_dict[source] = (source, destination,)
  1897. # Is this a figure (containing an image) or just a plain image?
  1898. if self.in_paragraph:
  1899. el1 = self.current_element
  1900. else:
  1901. el1 = SubElement(self.current_element, 'text:p',
  1902. attrib={'text:style-name': self.rststyle('textbody')})
  1903. el2 = el1
  1904. if isinstance(node.parent, docutils.nodes.figure):
  1905. el3, el4, el5, caption = self.generate_figure(node, source,
  1906. destination, el2)
  1907. attrib = {}
  1908. el6, width = self.generate_image(node, source, destination,
  1909. el5, attrib)
  1910. if caption is not None:
  1911. el6.tail = caption
  1912. else: #if isinstance(node.parent, docutils.nodes.image):
  1913. el3 = self.generate_image(node, source, destination, el2)
  1914. def depart_image(self, node):
  1915. pass
  1916. def get_image_width_height(self, node, attr):
  1917. size = None
  1918. if attr in node.attributes:
  1919. size = node.attributes[attr]
  1920. unit = size[-2:]
  1921. if unit.isalpha():
  1922. size = size[:-2]
  1923. else:
  1924. unit = 'px'
  1925. try:
  1926. size = float(size)
  1927. except ValueError as e:
  1928. self.document.reporter.warning(
  1929. 'Invalid %s for image: "%s"' % (
  1930. attr, node.attributes[attr]))
  1931. size = [size, unit]
  1932. return size
  1933. def get_image_scale(self, node):
  1934. if 'scale' in node.attributes:
  1935. try:
  1936. scale = int(node.attributes['scale'])
  1937. if scale < 1: # or scale > 100:
  1938. self.document.reporter.warning(
  1939. 'scale out of range (%s), using 1.' % (scale, ))
  1940. scale = 1
  1941. scale = scale * 0.01
  1942. except ValueError as e:
  1943. self.document.reporter.warning(
  1944. 'Invalid scale for image: "%s"' % (
  1945. node.attributes['scale'], ))
  1946. else:
  1947. scale = 1.0
  1948. return scale
  1949. def get_image_scaled_width_height(self, node, source):
  1950. scale = self.get_image_scale(node)
  1951. width = self.get_image_width_height(node, 'width')
  1952. height = self.get_image_width_height(node, 'height')
  1953. dpi = (72, 72)
  1954. if PIL is not None and source in self.image_dict:
  1955. filename, destination = self.image_dict[source]
  1956. imageobj = PIL.Image.open(filename, 'r')
  1957. dpi = imageobj.info.get('dpi', dpi)
  1958. # dpi information can be (xdpi, ydpi) or xydpi
  1959. try: iter(dpi)
  1960. except: dpi = (dpi, dpi)
  1961. else:
  1962. imageobj = None
  1963. if width is None or height is None:
  1964. if imageobj is None:
  1965. raise RuntimeError(
  1966. 'image size not fully specified and PIL not installed')
  1967. if width is None: width = [imageobj.size[0], 'px']
  1968. if height is None: height = [imageobj.size[1], 'px']
  1969. width[0] *= scale
  1970. height[0] *= scale
  1971. if width[1] == 'px': width = [width[0] / dpi[0], 'in']
  1972. if height[1] == 'px': height = [height[0] / dpi[1], 'in']
  1973. width[0] = str(width[0])
  1974. height[0] = str(height[0])
  1975. return ''.join(width), ''.join(height)
  1976. def generate_figure(self, node, source, destination, current_element):
  1977. caption = None
  1978. width, height = self.get_image_scaled_width_height(node, source)
  1979. for node1 in node.parent.children:
  1980. if node1.tagname == 'caption':
  1981. caption = node1.astext()
  1982. self.image_style_count += 1
  1983. #
  1984. # Add the style for the caption.
  1985. if caption is not None:
  1986. attrib = {
  1987. 'style:class': 'extra',
  1988. 'style:family': 'paragraph',
  1989. 'style:name': 'Caption',
  1990. 'style:parent-style-name': 'Standard',
  1991. }
  1992. el1 = SubElement(self.automatic_styles, 'style:style',
  1993. attrib=attrib, nsdict=SNSD)
  1994. attrib = {
  1995. 'fo:margin-bottom': '0.0835in',
  1996. 'fo:margin-top': '0.0835in',
  1997. 'text:line-number': '0',
  1998. 'text:number-lines': 'false',
  1999. }
  2000. el2 = SubElement(el1, 'style:paragraph-properties',
  2001. attrib=attrib, nsdict=SNSD)
  2002. attrib = {
  2003. 'fo:font-size': '12pt',
  2004. 'fo:font-style': 'italic',
  2005. 'style:font-name': 'Times',
  2006. 'style:font-name-complex': 'Lucidasans1',
  2007. 'style:font-size-asian': '12pt',
  2008. 'style:font-size-complex': '12pt',
  2009. 'style:font-style-asian': 'italic',
  2010. 'style:font-style-complex': 'italic',
  2011. }
  2012. el2 = SubElement(el1, 'style:text-properties',
  2013. attrib=attrib, nsdict=SNSD)
  2014. style_name = 'rstframestyle%d' % self.image_style_count
  2015. # Add the styles
  2016. attrib = {
  2017. 'style:name': style_name,
  2018. 'style:family': 'graphic',
  2019. 'style:parent-style-name': self.rststyle('figureframe'),
  2020. }
  2021. el1 = SubElement(self.automatic_styles,
  2022. 'style:style', attrib=attrib, nsdict=SNSD)
  2023. halign = 'center'
  2024. valign = 'top'
  2025. if 'align' in node.attributes:
  2026. align = node.attributes['align'].split()
  2027. for val in align:
  2028. if val in ('left', 'center', 'right'):
  2029. halign = val
  2030. elif val in ('top', 'middle', 'bottom'):
  2031. valign = val
  2032. attrib = {}
  2033. wrap = False
  2034. classes = node.parent.attributes.get('classes')
  2035. if classes and 'wrap' in classes:
  2036. wrap = True
  2037. if wrap:
  2038. attrib['style:wrap'] = 'dynamic'
  2039. else:
  2040. attrib['style:wrap'] = 'none'
  2041. el2 = SubElement(el1,
  2042. 'style:graphic-properties', attrib=attrib, nsdict=SNSD)
  2043. attrib = {
  2044. 'draw:style-name': style_name,
  2045. 'draw:name': 'Frame1',
  2046. 'text:anchor-type': 'paragraph',
  2047. 'draw:z-index': '0',
  2048. }
  2049. attrib['svg:width'] = width
  2050. el3 = SubElement(current_element, 'draw:frame', attrib=attrib)
  2051. attrib = {}
  2052. el4 = SubElement(el3, 'draw:text-box', attrib=attrib)
  2053. attrib = {
  2054. 'text:style-name': self.rststyle('caption'),
  2055. }
  2056. el5 = SubElement(el4, 'text:p', attrib=attrib)
  2057. return el3, el4, el5, caption
  2058. def generate_image(self, node, source, destination, current_element,
  2059. frame_attrs=None):
  2060. width, height = self.get_image_scaled_width_height(node, source)
  2061. self.image_style_count += 1
  2062. style_name = 'rstframestyle%d' % self.image_style_count
  2063. # Add the style.
  2064. attrib = {
  2065. 'style:name': style_name,
  2066. 'style:family': 'graphic',
  2067. 'style:parent-style-name': self.rststyle('image'),
  2068. }
  2069. el1 = SubElement(self.automatic_styles,
  2070. 'style:style', attrib=attrib, nsdict=SNSD)
  2071. halign = None
  2072. valign = None
  2073. if 'align' in node.attributes:
  2074. align = node.attributes['align'].split()
  2075. for val in align:
  2076. if val in ('left', 'center', 'right'):
  2077. halign = val
  2078. elif val in ('top', 'middle', 'bottom'):
  2079. valign = val
  2080. if frame_attrs is None:
  2081. attrib = {
  2082. 'style:vertical-pos': 'top',
  2083. 'style:vertical-rel': 'paragraph',
  2084. 'style:horizontal-rel': 'paragraph',
  2085. 'style:mirror': 'none',
  2086. 'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
  2087. 'draw:luminance': '0%',
  2088. 'draw:contrast': '0%',
  2089. 'draw:red': '0%',
  2090. 'draw:green': '0%',
  2091. 'draw:blue': '0%',
  2092. 'draw:gamma': '100%',
  2093. 'draw:color-inversion': 'false',
  2094. 'draw:image-opacity': '100%',
  2095. 'draw:color-mode': 'standard',
  2096. }
  2097. else:
  2098. attrib = frame_attrs
  2099. if halign is not None:
  2100. attrib['style:horizontal-pos'] = halign
  2101. if valign is not None:
  2102. attrib['style:vertical-pos'] = valign
  2103. # If there is a classes/wrap directive or we are
  2104. # inside a table, add a no-wrap style.
  2105. wrap = False
  2106. classes = node.attributes.get('classes')
  2107. if classes and 'wrap' in classes:
  2108. wrap = True
  2109. if wrap:
  2110. attrib['style:wrap'] = 'dynamic'
  2111. else:
  2112. attrib['style:wrap'] = 'none'
  2113. # If we are inside a table, add a no-wrap style.
  2114. if self.is_in_table(node):
  2115. attrib['style:wrap'] = 'none'
  2116. el2 = SubElement(el1,
  2117. 'style:graphic-properties', attrib=attrib, nsdict=SNSD)
  2118. # Add the content.
  2119. #el = SubElement(current_element, 'text:p',
  2120. # attrib={'text:style-name': self.rststyle('textbody')})
  2121. attrib={
  2122. 'draw:style-name': style_name,
  2123. 'draw:name': 'graphics2',
  2124. 'draw:z-index': '1',
  2125. }
  2126. if isinstance(node.parent, nodes.TextElement):
  2127. attrib['text:anchor-type'] = 'as-char' #vds
  2128. else:
  2129. attrib['text:anchor-type'] = 'paragraph'
  2130. attrib['svg:width'] = width
  2131. attrib['svg:height'] = height
  2132. el1 = SubElement(current_element, 'draw:frame', attrib=attrib)
  2133. el2 = SubElement(el1, 'draw:image', attrib={
  2134. 'xlink:href': '%s' % (destination, ),
  2135. 'xlink:type': 'simple',
  2136. 'xlink:show': 'embed',
  2137. 'xlink:actuate': 'onLoad',
  2138. })
  2139. return el1, width
  2140. def is_in_table(self, node):
  2141. node1 = node.parent
  2142. while node1:
  2143. if isinstance(node1, docutils.nodes.entry):
  2144. return True
  2145. node1 = node1.parent
  2146. return False
  2147. def visit_legend(self, node):
  2148. if isinstance(node.parent, docutils.nodes.figure):
  2149. el1 = self.current_element[-1]
  2150. el1 = el1[0][0]
  2151. self.current_element = el1
  2152. self.paragraph_style_stack.append(self.rststyle('legend'))
  2153. def depart_legend(self, node):
  2154. if isinstance(node.parent, docutils.nodes.figure):
  2155. self.paragraph_style_stack.pop()
  2156. self.set_to_parent()
  2157. self.set_to_parent()
  2158. self.set_to_parent()
  2159. def visit_line_block(self, node):
  2160. self.line_indent_level += 1
  2161. self.line_block_level += 1
  2162. def depart_line_block(self, node):
  2163. self.line_indent_level -= 1
  2164. self.line_block_level -= 1
  2165. def visit_line(self, node):
  2166. style = 'lineblock%d' % self.line_indent_level
  2167. el1 = SubElement(self.current_element, 'text:p', attrib={
  2168. 'text:style-name': self.rststyle(style),
  2169. })
  2170. self.current_element = el1
  2171. def depart_line(self, node):
  2172. self.set_to_parent()
  2173. def visit_literal(self, node):
  2174. el = SubElement(self.current_element, 'text:span',
  2175. attrib={'text:style-name': self.rststyle('inlineliteral')})
  2176. self.set_current_element(el)
  2177. def depart_literal(self, node):
  2178. self.set_to_parent()
  2179. def visit_inline(self, node):
  2180. styles = node.attributes.get('classes', ())
  2181. if len(styles) > 0:
  2182. inline_style = styles[0]
  2183. el = SubElement(self.current_element, 'text:span',
  2184. attrib={'text:style-name': self.rststyle(inline_style)})
  2185. self.set_current_element(el)
  2186. def depart_inline(self, node):
  2187. self.set_to_parent()
  2188. def _calculate_code_block_padding(self, line):
  2189. count = 0
  2190. matchobj = SPACES_PATTERN.match(line)
  2191. if matchobj:
  2192. pad = matchobj.group()
  2193. count = len(pad)
  2194. else:
  2195. matchobj = TABS_PATTERN.match(line)
  2196. if matchobj:
  2197. pad = matchobj.group()
  2198. count = len(pad) * 8
  2199. return count
  2200. def _add_syntax_highlighting(self, insource, language):
  2201. lexer = pygments.lexers.get_lexer_by_name(language, stripall=True)
  2202. if language in ('latex', 'tex'):
  2203. fmtr = OdtPygmentsLaTeXFormatter(lambda name, parameters=():
  2204. self.rststyle(name, parameters),
  2205. escape_function=escape_cdata)
  2206. else:
  2207. fmtr = OdtPygmentsProgFormatter(lambda name, parameters=():
  2208. self.rststyle(name, parameters),
  2209. escape_function=escape_cdata)
  2210. outsource = pygments.highlight(insource, lexer, fmtr)
  2211. return outsource
  2212. def fill_line(self, line):
  2213. line = FILL_PAT1.sub(self.fill_func1, line)
  2214. line = FILL_PAT2.sub(self.fill_func2, line)
  2215. return line
  2216. def fill_func1(self, matchobj):
  2217. spaces = matchobj.group(0)
  2218. repl = '<text:s text:c="%d"/>' % (len(spaces), )
  2219. return repl
  2220. def fill_func2(self, matchobj):
  2221. spaces = matchobj.group(0)
  2222. repl = ' <text:s text:c="%d"/>' % (len(spaces) - 1, )
  2223. return repl
  2224. def visit_literal_block(self, node):
  2225. if len(self.paragraph_style_stack) > 1:
  2226. wrapper1 = '<text:p text:style-name="%s">%%s</text:p>' % (
  2227. self.rststyle('codeblock-indented'), )
  2228. else:
  2229. wrapper1 = '<text:p text:style-name="%s">%%s</text:p>' % (
  2230. self.rststyle('codeblock'), )
  2231. source = node.astext()
  2232. if (pygments and
  2233. self.settings.add_syntax_highlighting
  2234. #and
  2235. #node.get('hilight', False)
  2236. ):
  2237. language = node.get('language', 'python')
  2238. source = self._add_syntax_highlighting(source, language)
  2239. else:
  2240. source = escape_cdata(source)
  2241. lines = source.split('\n')
  2242. # If there is an empty last line, remove it.
  2243. if lines[-1] == '':
  2244. del lines[-1]
  2245. lines1 = ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
  2246. my_lines = []
  2247. for my_line in lines:
  2248. my_line = self.fill_line(my_line)
  2249. my_line = my_line.replace("&#10;", "\n")
  2250. my_lines.append(my_line)
  2251. my_lines_str = '<text:line-break/>'.join(my_lines)
  2252. my_lines_str2 = wrapper1 % (my_lines_str, )
  2253. lines1.append(my_lines_str2)
  2254. lines1.append('</wrappertag1>')
  2255. s1 = ''.join(lines1)
  2256. if WhichElementTree != "lxml":
  2257. s1 = s1.encode("utf-8")
  2258. el1 = etree.fromstring(s1)
  2259. children = el1.getchildren()
  2260. for child in children:
  2261. self.current_element.append(child)
  2262. def depart_literal_block(self, node):
  2263. pass
  2264. visit_doctest_block = visit_literal_block
  2265. depart_doctest_block = depart_literal_block
  2266. # placeholder for math (see docs/dev/todo.txt)
  2267. def visit_math(self, node):
  2268. self.document.reporter.warning('"math" role not supported',
  2269. base_node=node)
  2270. self.visit_literal(node)
  2271. def depart_math(self, node):
  2272. self.depart_literal(node)
  2273. def visit_math_block(self, node):
  2274. self.document.reporter.warning('"math" directive not supported',
  2275. base_node=node)
  2276. self.visit_literal_block(node)
  2277. def depart_math_block(self, node):
  2278. self.depart_literal_block(node)
  2279. def visit_meta(self, node):
  2280. name = node.attributes.get('name')
  2281. content = node.attributes.get('content')
  2282. if name is not None and content is not None:
  2283. self.meta_dict[name] = content
  2284. def depart_meta(self, node):
  2285. pass
  2286. def visit_option_list(self, node):
  2287. table_name = 'tableoption'
  2288. #
  2289. # Generate automatic styles
  2290. if not self.optiontablestyles_generated:
  2291. self.optiontablestyles_generated = True
  2292. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2293. 'style:name': self.rststyle(table_name),
  2294. 'style:family': 'table'}, nsdict=SNSD)
  2295. el1 = SubElement(el, 'style:table-properties', attrib={
  2296. 'style:width': '17.59cm',
  2297. 'table:align': 'left',
  2298. 'style:shadow': 'none'}, nsdict=SNSD)
  2299. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2300. 'style:name': self.rststyle('%s.%%c' % table_name, ( 'A', )),
  2301. 'style:family': 'table-column'}, nsdict=SNSD)
  2302. el1 = SubElement(el, 'style:table-column-properties', attrib={
  2303. 'style:column-width': '4.999cm'}, nsdict=SNSD)
  2304. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2305. 'style:name': self.rststyle('%s.%%c' % table_name, ( 'B', )),
  2306. 'style:family': 'table-column'}, nsdict=SNSD)
  2307. el1 = SubElement(el, 'style:table-column-properties', attrib={
  2308. 'style:column-width': '12.587cm'}, nsdict=SNSD)
  2309. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2310. 'style:name': self.rststyle(
  2311. '%s.%%c%%d' % table_name, ( 'A', 1, )),
  2312. 'style:family': 'table-cell'}, nsdict=SNSD)
  2313. el1 = SubElement(el, 'style:table-cell-properties', attrib={
  2314. 'fo:background-color': 'transparent',
  2315. 'fo:padding': '0.097cm',
  2316. 'fo:border-left': '0.035cm solid #000000',
  2317. 'fo:border-right': 'none',
  2318. 'fo:border-top': '0.035cm solid #000000',
  2319. 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD)
  2320. el2 = SubElement(el1, 'style:background-image', nsdict=SNSD)
  2321. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2322. 'style:name': self.rststyle(
  2323. '%s.%%c%%d' % table_name, ( 'B', 1, )),
  2324. 'style:family': 'table-cell'}, nsdict=SNSD)
  2325. el1 = SubElement(el, 'style:table-cell-properties', attrib={
  2326. 'fo:padding': '0.097cm',
  2327. 'fo:border': '0.035cm solid #000000'}, nsdict=SNSD)
  2328. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2329. 'style:name': self.rststyle(
  2330. '%s.%%c%%d' % table_name, ( 'A', 2, )),
  2331. 'style:family': 'table-cell'}, nsdict=SNSD)
  2332. el1 = SubElement(el, 'style:table-cell-properties', attrib={
  2333. 'fo:padding': '0.097cm',
  2334. 'fo:border-left': '0.035cm solid #000000',
  2335. 'fo:border-right': 'none',
  2336. 'fo:border-top': 'none',
  2337. 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD)
  2338. el = SubElement(self.automatic_styles, 'style:style', attrib={
  2339. 'style:name': self.rststyle(
  2340. '%s.%%c%%d' % table_name, ( 'B', 2, )),
  2341. 'style:family': 'table-cell'}, nsdict=SNSD)
  2342. el1 = SubElement(el, 'style:table-cell-properties', attrib={
  2343. 'fo:padding': '0.097cm',
  2344. 'fo:border-left': '0.035cm solid #000000',
  2345. 'fo:border-right': '0.035cm solid #000000',
  2346. 'fo:border-top': 'none',
  2347. 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD)
  2348. #
  2349. # Generate table data
  2350. el = self.append_child('table:table', attrib={
  2351. 'table:name': self.rststyle(table_name),
  2352. 'table:style-name': self.rststyle(table_name),
  2353. })
  2354. el1 = SubElement(el, 'table:table-column', attrib={
  2355. 'table:style-name': self.rststyle(
  2356. '%s.%%c' % table_name, ( 'A', ))})
  2357. el1 = SubElement(el, 'table:table-column', attrib={
  2358. 'table:style-name': self.rststyle(
  2359. '%s.%%c' % table_name, ( 'B', ))})
  2360. el1 = SubElement(el, 'table:table-header-rows')
  2361. el2 = SubElement(el1, 'table:table-row')
  2362. el3 = SubElement(el2, 'table:table-cell', attrib={
  2363. 'table:style-name': self.rststyle(
  2364. '%s.%%c%%d' % table_name, ( 'A', 1, )),
  2365. 'office:value-type': 'string'})
  2366. el4 = SubElement(el3, 'text:p', attrib={
  2367. 'text:style-name': 'Table_20_Heading'})
  2368. el4.text= 'Option'
  2369. el3 = SubElement(el2, 'table:table-cell', attrib={
  2370. 'table:style-name': self.rststyle(
  2371. '%s.%%c%%d' % table_name, ( 'B', 1, )),
  2372. 'office:value-type': 'string'})
  2373. el4 = SubElement(el3, 'text:p', attrib={
  2374. 'text:style-name': 'Table_20_Heading'})
  2375. el4.text= 'Description'
  2376. self.set_current_element(el)
  2377. def depart_option_list(self, node):
  2378. self.set_to_parent()
  2379. def visit_option_list_item(self, node):
  2380. el = self.append_child('table:table-row')
  2381. self.set_current_element(el)
  2382. def depart_option_list_item(self, node):
  2383. self.set_to_parent()
  2384. def visit_option_group(self, node):
  2385. el = self.append_child('table:table-cell', attrib={
  2386. 'table:style-name': 'Table%d.A2' % self.table_count,
  2387. 'office:value-type': 'string',
  2388. })
  2389. self.set_current_element(el)
  2390. def depart_option_group(self, node):
  2391. self.set_to_parent()
  2392. def visit_option(self, node):
  2393. el = self.append_child('text:p', attrib={
  2394. 'text:style-name': 'Table_20_Contents'})
  2395. el.text = node.astext()
  2396. def depart_option(self, node):
  2397. pass
  2398. def visit_option_string(self, node):
  2399. pass
  2400. def depart_option_string(self, node):
  2401. pass
  2402. def visit_option_argument(self, node):
  2403. pass
  2404. def depart_option_argument(self, node):
  2405. pass
  2406. def visit_description(self, node):
  2407. el = self.append_child('table:table-cell', attrib={
  2408. 'table:style-name': 'Table%d.B2' % self.table_count,
  2409. 'office:value-type': 'string',
  2410. })
  2411. el1 = SubElement(el, 'text:p', attrib={
  2412. 'text:style-name': 'Table_20_Contents'})
  2413. el1.text = node.astext()
  2414. raise nodes.SkipChildren()
  2415. def depart_description(self, node):
  2416. pass
  2417. def visit_paragraph(self, node):
  2418. self.in_paragraph = True
  2419. if self.in_header:
  2420. el = self.append_p('header')
  2421. elif self.in_footer:
  2422. el = self.append_p('footer')
  2423. else:
  2424. style_name = self.paragraph_style_stack[-1]
  2425. el = self.append_child('text:p',
  2426. attrib={'text:style-name': style_name})
  2427. self.append_pending_ids(el)
  2428. self.set_current_element(el)
  2429. def depart_paragraph(self, node):
  2430. self.in_paragraph = False
  2431. self.set_to_parent()
  2432. if self.in_header:
  2433. self.header_content.append(
  2434. self.current_element.getchildren()[-1])
  2435. self.current_element.remove(
  2436. self.current_element.getchildren()[-1])
  2437. elif self.in_footer:
  2438. self.footer_content.append(
  2439. self.current_element.getchildren()[-1])
  2440. self.current_element.remove(
  2441. self.current_element.getchildren()[-1])
  2442. def visit_problematic(self, node):
  2443. pass
  2444. def depart_problematic(self, node):
  2445. pass
  2446. def visit_raw(self, node):
  2447. if 'format' in node.attributes:
  2448. formats = node.attributes['format']
  2449. formatlist = formats.split()
  2450. if 'odt' in formatlist:
  2451. rawstr = node.astext()
  2452. attrstr = ' '.join(['%s="%s"' % (k, v, )
  2453. for k,v in list(CONTENT_NAMESPACE_ATTRIB.items())])
  2454. contentstr = '<stuff %s>%s</stuff>' % (attrstr, rawstr, )
  2455. if WhichElementTree != "lxml":
  2456. contentstr = contentstr.encode("utf-8")
  2457. content = etree.fromstring(contentstr)
  2458. elements = content.getchildren()
  2459. if len(elements) > 0:
  2460. el1 = elements[0]
  2461. if self.in_header:
  2462. pass
  2463. elif self.in_footer:
  2464. pass
  2465. else:
  2466. self.current_element.append(el1)
  2467. raise nodes.SkipChildren()
  2468. def depart_raw(self, node):
  2469. if self.in_header:
  2470. pass
  2471. elif self.in_footer:
  2472. pass
  2473. else:
  2474. pass
  2475. def visit_reference(self, node):
  2476. text = node.astext()
  2477. if self.settings.create_links:
  2478. if 'refuri' in node:
  2479. href = node['refuri']
  2480. if ( self.settings.cloak_email_addresses
  2481. and href.startswith('mailto:')):
  2482. href = self.cloak_mailto(href)
  2483. el = self.append_child('text:a', attrib={
  2484. 'xlink:href': '%s' % href,
  2485. 'xlink:type': 'simple',
  2486. })
  2487. self.set_current_element(el)
  2488. elif 'refid' in node:
  2489. if self.settings.create_links:
  2490. href = node['refid']
  2491. el = self.append_child('text:reference-ref', attrib={
  2492. 'text:ref-name': '%s' % href,
  2493. 'text:reference-format': 'text',
  2494. })
  2495. else:
  2496. self.document.reporter.warning(
  2497. 'References must have "refuri" or "refid" attribute.')
  2498. if (self.in_table_of_contents and
  2499. len(node.children) >= 1 and
  2500. isinstance(node.children[0], docutils.nodes.generated)):
  2501. node.remove(node.children[0])
  2502. def depart_reference(self, node):
  2503. if self.settings.create_links:
  2504. if 'refuri' in node:
  2505. self.set_to_parent()
  2506. def visit_rubric(self, node):
  2507. style_name = self.rststyle('rubric')
  2508. classes = node.get('classes')
  2509. if classes:
  2510. class1 = classes[0]
  2511. if class1:
  2512. style_name = class1
  2513. el = SubElement(self.current_element, 'text:h', attrib = {
  2514. #'text:outline-level': '%d' % section_level,
  2515. #'text:style-name': 'Heading_20_%d' % section_level,
  2516. 'text:style-name': style_name,
  2517. })
  2518. text = node.astext()
  2519. el.text = self.encode(text)
  2520. def depart_rubric(self, node):
  2521. pass
  2522. def visit_section(self, node, move_ids=1):
  2523. self.section_level += 1
  2524. self.section_count += 1
  2525. if self.settings.create_sections:
  2526. el = self.append_child('text:section', attrib={
  2527. 'text:name': 'Section%d' % self.section_count,
  2528. 'text:style-name': 'Sect%d' % self.section_level,
  2529. })
  2530. self.set_current_element(el)
  2531. def depart_section(self, node):
  2532. self.section_level -= 1
  2533. if self.settings.create_sections:
  2534. self.set_to_parent()
  2535. def visit_strong(self, node):
  2536. el = SubElement(self.current_element, 'text:span',
  2537. attrib={'text:style-name': self.rststyle('strong')})
  2538. self.set_current_element(el)
  2539. def depart_strong(self, node):
  2540. self.set_to_parent()
  2541. def visit_substitution_definition(self, node):
  2542. raise nodes.SkipChildren()
  2543. def depart_substitution_definition(self, node):
  2544. pass
  2545. def visit_system_message(self, node):
  2546. pass
  2547. def depart_system_message(self, node):
  2548. pass
  2549. def get_table_style(self, node):
  2550. table_style = None
  2551. table_name = None
  2552. use_predefined_table_style = False
  2553. str_classes = node.get('classes')
  2554. if str_classes is not None:
  2555. for str_class in str_classes:
  2556. if str_class.startswith(TABLESTYLEPREFIX):
  2557. table_name = str_class
  2558. use_predefined_table_style = True
  2559. break
  2560. if table_name is not None:
  2561. table_style = self.table_styles.get(table_name)
  2562. if table_style is None:
  2563. # If we can't find the table style, issue warning
  2564. # and use the default table style.
  2565. self.document.reporter.warning(
  2566. 'Can\'t find table style "%s". Using default.' % (
  2567. table_name, ))
  2568. table_name = TABLENAMEDEFAULT
  2569. table_style = self.table_styles.get(table_name)
  2570. if table_style is None:
  2571. # If we can't find the default table style, issue a warning
  2572. # and use a built-in default style.
  2573. self.document.reporter.warning(
  2574. 'Can\'t find default table style "%s". Using built-in default.' % (
  2575. table_name, ))
  2576. table_style = BUILTIN_DEFAULT_TABLE_STYLE
  2577. else:
  2578. table_name = TABLENAMEDEFAULT
  2579. table_style = self.table_styles.get(table_name)
  2580. if table_style is None:
  2581. # If we can't find the default table style, issue a warning
  2582. # and use a built-in default style.
  2583. self.document.reporter.warning(
  2584. 'Can\'t find default table style "%s". Using built-in default.' % (
  2585. table_name, ))
  2586. table_style = BUILTIN_DEFAULT_TABLE_STYLE
  2587. return table_style
  2588. def visit_table(self, node):
  2589. self.table_count += 1
  2590. table_style = self.get_table_style(node)
  2591. table_name = '%s%%d' % TABLESTYLEPREFIX
  2592. el1 = SubElement(self.automatic_styles, 'style:style', attrib={
  2593. 'style:name': self.rststyle(
  2594. '%s' % table_name, ( self.table_count, )),
  2595. 'style:family': 'table',
  2596. }, nsdict=SNSD)
  2597. if table_style.backgroundcolor is None:
  2598. el1_1 = SubElement(el1, 'style:table-properties', attrib={
  2599. #'style:width': '17.59cm',
  2600. #'table:align': 'margins',
  2601. 'table:align': 'left',
  2602. 'fo:margin-top': '0in',
  2603. 'fo:margin-bottom': '0.10in',
  2604. }, nsdict=SNSD)
  2605. else:
  2606. el1_1 = SubElement(el1, 'style:table-properties', attrib={
  2607. #'style:width': '17.59cm',
  2608. 'table:align': 'margins',
  2609. 'fo:margin-top': '0in',
  2610. 'fo:margin-bottom': '0.10in',
  2611. 'fo:background-color': table_style.backgroundcolor,
  2612. }, nsdict=SNSD)
  2613. # We use a single cell style for all cells in this table.
  2614. # That's probably not correct, but seems to work.
  2615. el2 = SubElement(self.automatic_styles, 'style:style', attrib={
  2616. 'style:name': self.rststyle(
  2617. '%s.%%c%%d' % table_name, ( self.table_count, 'A', 1, )),
  2618. 'style:family': 'table-cell',
  2619. }, nsdict=SNSD)
  2620. thickness = self.settings.table_border_thickness
  2621. if thickness is None:
  2622. line_style1 = table_style.border
  2623. else:
  2624. line_style1 = '0.%03dcm solid #000000' % (thickness, )
  2625. el2_1 = SubElement(el2, 'style:table-cell-properties', attrib={
  2626. 'fo:padding': '0.049cm',
  2627. 'fo:border-left': line_style1,
  2628. 'fo:border-right': line_style1,
  2629. 'fo:border-top': line_style1,
  2630. 'fo:border-bottom': line_style1,
  2631. }, nsdict=SNSD)
  2632. title = None
  2633. for child in node.children:
  2634. if child.tagname == 'title':
  2635. title = child.astext()
  2636. break
  2637. if title is not None:
  2638. el3 = self.append_p('table-title', title)
  2639. else:
  2640. pass
  2641. el4 = SubElement(self.current_element, 'table:table', attrib={
  2642. 'table:name': self.rststyle(
  2643. '%s' % table_name, ( self.table_count, )),
  2644. 'table:style-name': self.rststyle(
  2645. '%s' % table_name, ( self.table_count, )),
  2646. })
  2647. self.set_current_element(el4)
  2648. self.current_table_style = el1
  2649. self.table_width = 0.0
  2650. def depart_table(self, node):
  2651. attribkey = add_ns('style:width', nsdict=SNSD)
  2652. attribval = '%.4fin' % (self.table_width, )
  2653. el1 = self.current_table_style
  2654. el2 = el1[0]
  2655. el2.attrib[attribkey] = attribval
  2656. self.set_to_parent()
  2657. def visit_tgroup(self, node):
  2658. self.column_count = ord('A') - 1
  2659. def depart_tgroup(self, node):
  2660. pass
  2661. def visit_colspec(self, node):
  2662. self.column_count += 1
  2663. colspec_name = self.rststyle(
  2664. '%s%%d.%%s' % TABLESTYLEPREFIX,
  2665. (self.table_count, chr(self.column_count), )
  2666. )
  2667. colwidth = node['colwidth'] / 12.0
  2668. el1 = SubElement(self.automatic_styles, 'style:style', attrib={
  2669. 'style:name': colspec_name,
  2670. 'style:family': 'table-column',
  2671. }, nsdict=SNSD)
  2672. el1_1 = SubElement(el1, 'style:table-column-properties', attrib={
  2673. 'style:column-width': '%.4fin' % colwidth
  2674. },
  2675. nsdict=SNSD)
  2676. el2 = self.append_child('table:table-column', attrib={
  2677. 'table:style-name': colspec_name,
  2678. })
  2679. self.table_width += colwidth
  2680. def depart_colspec(self, node):
  2681. pass
  2682. def visit_thead(self, node):
  2683. el = self.append_child('table:table-header-rows')
  2684. self.set_current_element(el)
  2685. self.in_thead = True
  2686. self.paragraph_style_stack.append('Table_20_Heading')
  2687. def depart_thead(self, node):
  2688. self.set_to_parent()
  2689. self.in_thead = False
  2690. self.paragraph_style_stack.pop()
  2691. def visit_row(self, node):
  2692. self.column_count = ord('A') - 1
  2693. el = self.append_child('table:table-row')
  2694. self.set_current_element(el)
  2695. def depart_row(self, node):
  2696. self.set_to_parent()
  2697. def visit_entry(self, node):
  2698. self.column_count += 1
  2699. cellspec_name = self.rststyle(
  2700. '%s%%d.%%c%%d' % TABLESTYLEPREFIX,
  2701. (self.table_count, 'A', 1, )
  2702. )
  2703. attrib={
  2704. 'table:style-name': cellspec_name,
  2705. 'office:value-type': 'string',
  2706. }
  2707. morecols = node.get('morecols', 0)
  2708. if morecols > 0:
  2709. attrib['table:number-columns-spanned'] = '%d' % (morecols + 1,)
  2710. self.column_count += morecols
  2711. morerows = node.get('morerows', 0)
  2712. if morerows > 0:
  2713. attrib['table:number-rows-spanned'] = '%d' % (morerows + 1,)
  2714. el1 = self.append_child('table:table-cell', attrib=attrib)
  2715. self.set_current_element(el1)
  2716. def depart_entry(self, node):
  2717. self.set_to_parent()
  2718. def visit_tbody(self, node):
  2719. pass
  2720. def depart_tbody(self, node):
  2721. pass
  2722. def visit_target(self, node):
  2723. #
  2724. # I don't know how to implement targets in ODF.
  2725. # How do we create a target in oowriter? A cross-reference?
  2726. if not ('refuri' in node or 'refid' in node
  2727. or 'refname' in node):
  2728. pass
  2729. else:
  2730. pass
  2731. def depart_target(self, node):
  2732. pass
  2733. def visit_title(self, node, move_ids=1, title_type='title'):
  2734. if isinstance(node.parent, docutils.nodes.section):
  2735. section_level = self.section_level
  2736. if section_level > 7:
  2737. self.document.reporter.warning(
  2738. 'Heading/section levels greater than 7 not supported.')
  2739. self.document.reporter.warning(
  2740. ' Reducing to heading level 7 for heading: "%s"' % (
  2741. node.astext(), ))
  2742. section_level = 7
  2743. el1 = self.append_child('text:h', attrib = {
  2744. 'text:outline-level': '%d' % section_level,
  2745. #'text:style-name': 'Heading_20_%d' % section_level,
  2746. 'text:style-name': self.rststyle(
  2747. 'heading%d', (section_level, )),
  2748. })
  2749. self.append_pending_ids(el1)
  2750. self.set_current_element(el1)
  2751. elif isinstance(node.parent, docutils.nodes.document):
  2752. # text = self.settings.title
  2753. #else:
  2754. # text = node.astext()
  2755. el1 = SubElement(self.current_element, 'text:p', attrib = {
  2756. 'text:style-name': self.rststyle(title_type),
  2757. })
  2758. self.append_pending_ids(el1)
  2759. text = node.astext()
  2760. self.title = text
  2761. self.found_doc_title = True
  2762. self.set_current_element(el1)
  2763. def depart_title(self, node):
  2764. if (isinstance(node.parent, docutils.nodes.section) or
  2765. isinstance(node.parent, docutils.nodes.document)):
  2766. self.set_to_parent()
  2767. def visit_subtitle(self, node, move_ids=1):
  2768. self.visit_title(node, move_ids, title_type='subtitle')
  2769. def depart_subtitle(self, node):
  2770. self.depart_title(node)
  2771. def visit_title_reference(self, node):
  2772. el = self.append_child('text:span', attrib={
  2773. 'text:style-name': self.rststyle('quotation')})
  2774. el.text = self.encode(node.astext())
  2775. raise nodes.SkipChildren()
  2776. def depart_title_reference(self, node):
  2777. pass
  2778. def generate_table_of_content_entry_template(self, el1):
  2779. for idx in range(1, 11):
  2780. el2 = SubElement(el1,
  2781. 'text:table-of-content-entry-template',
  2782. attrib={
  2783. 'text:outline-level': "%d" % (idx, ),
  2784. 'text:style-name': self.rststyle('contents-%d' % (idx, )),
  2785. })
  2786. el3 = SubElement(el2, 'text:index-entry-chapter')
  2787. el3 = SubElement(el2, 'text:index-entry-text')
  2788. el3 = SubElement(el2, 'text:index-entry-tab-stop', attrib={
  2789. 'style:leader-char': ".",
  2790. 'style:type': "right",
  2791. })
  2792. el3 = SubElement(el2, 'text:index-entry-page-number')
  2793. def find_title_label(self, node, class_type, label_key):
  2794. label = ''
  2795. title_node = None
  2796. for child in node.children:
  2797. if isinstance(child, class_type):
  2798. title_node = child
  2799. break
  2800. if title_node is not None:
  2801. label = title_node.astext()
  2802. else:
  2803. label = self.language.labels[label_key]
  2804. return label
  2805. def visit_topic(self, node):
  2806. if 'classes' in node.attributes:
  2807. if 'contents' in node.attributes['classes']:
  2808. label = self.find_title_label(node, docutils.nodes.title,
  2809. 'contents')
  2810. if self.settings.generate_oowriter_toc:
  2811. el1 = self.append_child('text:table-of-content', attrib={
  2812. 'text:name': 'Table of Contents1',
  2813. 'text:protected': 'true',
  2814. 'text:style-name': 'Sect1',
  2815. })
  2816. el2 = SubElement(el1,
  2817. 'text:table-of-content-source',
  2818. attrib={
  2819. 'text:outline-level': '10',
  2820. })
  2821. el3 =SubElement(el2, 'text:index-title-template', attrib={
  2822. 'text:style-name': 'Contents_20_Heading',
  2823. })
  2824. el3.text = label
  2825. self.generate_table_of_content_entry_template(el2)
  2826. el4 = SubElement(el1, 'text:index-body')
  2827. el5 = SubElement(el4, 'text:index-title')
  2828. el6 = SubElement(el5, 'text:p', attrib={
  2829. 'text:style-name': self.rststyle('contents-heading'),
  2830. })
  2831. el6.text = label
  2832. self.save_current_element = self.current_element
  2833. self.table_of_content_index_body = el4
  2834. self.set_current_element(el4)
  2835. else:
  2836. el = self.append_p('horizontalline')
  2837. el = self.append_p('centeredtextbody')
  2838. el1 = SubElement(el, 'text:span',
  2839. attrib={'text:style-name': self.rststyle('strong')})
  2840. el1.text = label
  2841. self.in_table_of_contents = True
  2842. elif 'abstract' in node.attributes['classes']:
  2843. el = self.append_p('horizontalline')
  2844. el = self.append_p('centeredtextbody')
  2845. el1 = SubElement(el, 'text:span',
  2846. attrib={'text:style-name': self.rststyle('strong')})
  2847. label = self.find_title_label(node, docutils.nodes.title,
  2848. 'abstract')
  2849. el1.text = label
  2850. elif 'dedication' in node.attributes['classes']:
  2851. el = self.append_p('horizontalline')
  2852. el = self.append_p('centeredtextbody')
  2853. el1 = SubElement(el, 'text:span',
  2854. attrib={'text:style-name': self.rststyle('strong')})
  2855. label = self.find_title_label(node, docutils.nodes.title,
  2856. 'dedication')
  2857. el1.text = label
  2858. def depart_topic(self, node):
  2859. if 'classes' in node.attributes:
  2860. if 'contents' in node.attributes['classes']:
  2861. if self.settings.generate_oowriter_toc:
  2862. self.update_toc_page_numbers(
  2863. self.table_of_content_index_body)
  2864. self.set_current_element(self.save_current_element)
  2865. else:
  2866. el = self.append_p('horizontalline')
  2867. self.in_table_of_contents = False
  2868. def update_toc_page_numbers(self, el):
  2869. collection = []
  2870. self.update_toc_collect(el, 0, collection)
  2871. self.update_toc_add_numbers(collection)
  2872. def update_toc_collect(self, el, level, collection):
  2873. collection.append((level, el))
  2874. level += 1
  2875. for child_el in el.getchildren():
  2876. if child_el.tag != 'text:index-body':
  2877. self.update_toc_collect(child_el, level, collection)
  2878. def update_toc_add_numbers(self, collection):
  2879. for level, el1 in collection:
  2880. if (el1.tag == 'text:p' and
  2881. el1.text != 'Table of Contents'):
  2882. el2 = SubElement(el1, 'text:tab')
  2883. el2.tail = '9999'
  2884. def visit_transition(self, node):
  2885. el = self.append_p('horizontalline')
  2886. def depart_transition(self, node):
  2887. pass
  2888. #
  2889. # Admonitions
  2890. #
  2891. def visit_warning(self, node):
  2892. self.generate_admonition(node, 'warning')
  2893. def depart_warning(self, node):
  2894. self.paragraph_style_stack.pop()
  2895. def visit_attention(self, node):
  2896. self.generate_admonition(node, 'attention')
  2897. depart_attention = depart_warning
  2898. def visit_caution(self, node):
  2899. self.generate_admonition(node, 'caution')
  2900. depart_caution = depart_warning
  2901. def visit_danger(self, node):
  2902. self.generate_admonition(node, 'danger')
  2903. depart_danger = depart_warning
  2904. def visit_error(self, node):
  2905. self.generate_admonition(node, 'error')
  2906. depart_error = depart_warning
  2907. def visit_hint(self, node):
  2908. self.generate_admonition(node, 'hint')
  2909. depart_hint = depart_warning
  2910. def visit_important(self, node):
  2911. self.generate_admonition(node, 'important')
  2912. depart_important = depart_warning
  2913. def visit_note(self, node):
  2914. self.generate_admonition(node, 'note')
  2915. depart_note = depart_warning
  2916. def visit_tip(self, node):
  2917. self.generate_admonition(node, 'tip')
  2918. depart_tip = depart_warning
  2919. def visit_admonition(self, node):
  2920. title = None
  2921. for child in node.children:
  2922. if child.tagname == 'title':
  2923. title = child.astext()
  2924. if title is None:
  2925. classes1 = node.get('classes')
  2926. if classes1:
  2927. title = classes1[0]
  2928. self.generate_admonition(node, 'generic', title)
  2929. depart_admonition = depart_warning
  2930. def generate_admonition(self, node, label, title=None):
  2931. el1 = SubElement(self.current_element, 'text:p', attrib = {
  2932. 'text:style-name': self.rststyle('admon-%s-hdr', ( label, )),
  2933. })
  2934. if title:
  2935. el1.text = title
  2936. else:
  2937. el1.text = '%s!' % (label.capitalize(), )
  2938. s1 = self.rststyle('admon-%s-body', ( label, ))
  2939. self.paragraph_style_stack.append(s1)
  2940. #
  2941. # Roles (e.g. subscript, superscript, strong, ...
  2942. #
  2943. def visit_subscript(self, node):
  2944. el = self.append_child('text:span', attrib={
  2945. 'text:style-name': 'rststyle-subscript',
  2946. })
  2947. self.set_current_element(el)
  2948. def depart_subscript(self, node):
  2949. self.set_to_parent()
  2950. def visit_superscript(self, node):
  2951. el = self.append_child('text:span', attrib={
  2952. 'text:style-name': 'rststyle-superscript',
  2953. })
  2954. self.set_current_element(el)
  2955. def depart_superscript(self, node):
  2956. self.set_to_parent()
  2957. # Use an own reader to modify transformations done.
  2958. class Reader(standalone.Reader):
  2959. def get_transforms(self):
  2960. default = standalone.Reader.get_transforms(self)
  2961. if self.settings.create_links:
  2962. return default
  2963. return [ i
  2964. for i in default
  2965. if i is not references.DanglingReferences ]