PageRenderTime 73ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

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

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