about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/docutils/writers/odf_odt')
-rw-r--r--.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/__init__.py3461
-rwxr-xr-x.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/prepstyles.py78
-rw-r--r--.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/pygmentsformatter.py109
-rw-r--r--.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/styles.odtbin0 -> 16500 bytes
4 files changed, 3648 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/__init__.py b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/__init__.py
new file mode 100644
index 00000000..c538af34
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/__init__.py
@@ -0,0 +1,3461 @@
+# $Id: __init__.py 9541 2024-02-17 10:37:13Z milde $
+# Author: Dave Kuhlman <dkuhlman@davekuhlman.org>
+# Copyright: This module has been placed in the public domain.
+
+"""
+Open Document Format (ODF) Writer.
+
+This module is provisional:
+the API is not settled and may change with any minor Docutils version.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+from configparser import ConfigParser
+import copy
+from io import StringIO
+import itertools
+import locale
+import os
+import os.path
+from pathlib import Path
+import re
+import subprocess
+import tempfile
+import time
+import urllib
+import weakref
+from xml.etree import ElementTree as etree
+from xml.dom import minidom
+import zipfile
+
+import docutils
+from docutils import frontend, nodes, utils, writers, languages
+from docutils.parsers.rst.directives.images import PIL  # optional
+from docutils.readers import standalone
+from docutils.transforms import references
+
+# Import pygments and odtwriter pygments formatters if possible.
+try:
+    import pygments
+    import pygments.lexers
+    from .pygmentsformatter import (OdtPygmentsProgFormatter,
+                                    OdtPygmentsLaTeXFormatter)
+except (ImportError, SyntaxError):
+    pygments = None
+
+# import warnings
+# warnings.warn('importing IPShellEmbed', UserWarning)
+# from IPython.Shell import IPShellEmbed
+# args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', '   .\\D.: ',
+#         '-po', 'Out<\\#>: ', '-nosep']
+# ipshell = IPShellEmbed(args,
+#                        banner = 'Entering IPython.  Press Ctrl-D to exit.',
+#                        exit_msg = 'Leaving Interpreter, back to program.')
+
+VERSION = '1.0a'
+
+IMAGE_NAME_COUNTER = itertools.count()
+
+
+#
+# ElementTree does not support getparent method (lxml does).
+# This wrapper class and the following support functions provide
+#   that support for the ability to get the parent of an element.
+#
+_parents = weakref.WeakKeyDictionary()
+if isinstance(etree.Element, type):
+    _ElementInterface = etree.Element
+else:
+    _ElementInterface = etree._ElementInterface
+
+
+class _ElementInterfaceWrapper(_ElementInterface):
+    def __init__(self, tag, attrib=None):
+        _ElementInterface.__init__(self, tag, attrib)
+        _parents[self] = None
+
+    def setparent(self, parent):
+        _parents[self] = parent
+
+    def getparent(self):
+        return _parents[self]
+
+
+#
+# Constants and globals
+
+SPACES_PATTERN = re.compile(r'( +)')
+TABS_PATTERN = re.compile(r'(\t+)')
+FILL_PAT1 = re.compile(r'^ +')
+FILL_PAT2 = re.compile(r' {2,}')
+
+TABLESTYLEPREFIX = 'rststyle-table-'
+TABLENAMEDEFAULT = '%s0' % TABLESTYLEPREFIX
+TABLEPROPERTYNAMES = (
+    'border', 'border-top', 'border-left',
+    'border-right', 'border-bottom', )
+
+GENERATOR_DESC = 'Docutils.org/odf_odt'
+
+NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
+
+CONTENT_NAMESPACE_DICT = CNSD = {
+    # 'office:version': '1.0',
+    'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
+    'dc': 'http://purl.org/dc/elements/1.1/',
+    'dom': 'http://www.w3.org/2001/xml-events',
+    'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
+    'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
+    'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
+    'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
+    'math': 'http://www.w3.org/1998/Math/MathML',
+    'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
+    'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
+    'office': NAME_SPACE_1,
+    'ooo': 'http://openoffice.org/2004/office',
+    'oooc': 'http://openoffice.org/2004/calc',
+    'ooow': 'http://openoffice.org/2004/writer',
+    'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
+
+    'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
+    'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
+    'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
+    'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
+    'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
+    'xforms': 'http://www.w3.org/2002/xforms',
+    'xlink': 'http://www.w3.org/1999/xlink',
+    'xsd': 'http://www.w3.org/2001/XMLSchema',
+    'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
+}
+
+STYLES_NAMESPACE_DICT = SNSD = {
+    # 'office:version': '1.0',
+    'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
+    'dc': 'http://purl.org/dc/elements/1.1/',
+    'dom': 'http://www.w3.org/2001/xml-events',
+    'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
+    'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
+    'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
+    'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
+    'math': 'http://www.w3.org/1998/Math/MathML',
+    'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
+    'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
+    'office': NAME_SPACE_1,
+    'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
+    'ooo': 'http://openoffice.org/2004/office',
+    'oooc': 'http://openoffice.org/2004/calc',
+    'ooow': 'http://openoffice.org/2004/writer',
+    'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
+    'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
+    'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
+    'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
+    'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
+    'xlink': 'http://www.w3.org/1999/xlink',
+}
+
+MANIFEST_NAMESPACE_DICT = MANNSD = {
+    'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
+}
+
+META_NAMESPACE_DICT = METNSD = {
+    # 'office:version': '1.0',
+    'dc': 'http://purl.org/dc/elements/1.1/',
+    'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
+    'office': NAME_SPACE_1,
+    'ooo': 'http://openoffice.org/2004/office',
+    'xlink': 'http://www.w3.org/1999/xlink',
+}
+
+# Attribute dictionaries for use with ElementTree, which
+# does not support use of nsmap parameter on Element() and SubElement().
+
+CONTENT_NAMESPACE_ATTRIB = {
+    # 'office:version': '1.0',
+    'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
+    'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
+    'xmlns:dom': 'http://www.w3.org/2001/xml-events',
+    'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
+    'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
+    'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
+    'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
+    'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
+    'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
+    'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
+    'xmlns:office': NAME_SPACE_1,
+    'xmlns:presentation':
+        'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
+    'xmlns:ooo': 'http://openoffice.org/2004/office',
+    'xmlns:oooc': 'http://openoffice.org/2004/calc',
+    'xmlns:ooow': 'http://openoffice.org/2004/writer',
+    'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
+    'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
+    'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
+    'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
+    'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
+    'xmlns:xforms': 'http://www.w3.org/2002/xforms',
+    'xmlns:xlink': 'http://www.w3.org/1999/xlink',
+    'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
+    'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
+}
+
+STYLES_NAMESPACE_ATTRIB = {
+    # 'office:version': '1.0',
+    'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
+    'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
+    'xmlns:dom': 'http://www.w3.org/2001/xml-events',
+    'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
+    'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
+    'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
+    'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
+    'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
+    'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
+    'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
+    'xmlns:office': NAME_SPACE_1,
+    'xmlns:presentation':
+        'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
+    'xmlns:ooo': 'http://openoffice.org/2004/office',
+    'xmlns:oooc': 'http://openoffice.org/2004/calc',
+    'xmlns:ooow': 'http://openoffice.org/2004/writer',
+    'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
+    'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
+    'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
+    'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
+    'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
+    'xmlns:xlink': 'http://www.w3.org/1999/xlink',
+}
+
+MANIFEST_NAMESPACE_ATTRIB = {
+    'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
+}
+
+META_NAMESPACE_ATTRIB = {
+    # 'office:version': '1.0',
+    'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
+    'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
+    'xmlns:office': NAME_SPACE_1,
+    'xmlns:ooo': 'http://openoffice.org/2004/office',
+    'xmlns:xlink': 'http://www.w3.org/1999/xlink',
+}
+
+
+#
+# Functions
+#
+
+#
+# ElementTree support functions.
+# In order to be able to get the parent of elements, must use these
+#   instead of the functions with same name provided by ElementTree.
+#
+def Element(tag, attrib=None, nsmap=None, nsdict=CNSD):
+    if attrib is None:
+        attrib = {}
+    tag, attrib = fix_ns(tag, attrib, nsdict)
+    return _ElementInterfaceWrapper(tag, attrib)
+
+
+def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD):
+    if attrib is None:
+        attrib = {}
+    tag, attrib = fix_ns(tag, attrib, nsdict)
+    el = _ElementInterfaceWrapper(tag, attrib)
+    parent.append(el)
+    el.setparent(parent)
+    return el
+
+
+def fix_ns(tag, attrib, nsdict):
+    nstag = add_ns(tag, nsdict)
+    nsattrib = {}
+    for key, val in list(attrib.items()):
+        nskey = add_ns(key, nsdict)
+        nsattrib[nskey] = val
+    return nstag, nsattrib
+
+
+def add_ns(tag, nsdict=CNSD):
+    return tag
+
+
+def ToString(et):
+    outstream = StringIO()
+    et.write(outstream, encoding="unicode")
+    s1 = outstream.getvalue()
+    outstream.close()
+    return s1
+
+
+def escape_cdata(text):
+    text = text.replace("&", "&amp;")
+    text = text.replace("<", "&lt;")
+    text = text.replace(">", "&gt;")
+    ascii = ''
+    for char in text:
+        if ord(char) >= ord("\x7f"):
+            ascii += "&#x%X;" % (ord(char), )
+        else:
+            ascii += char
+    return ascii
+
+
+#
+# Classes
+#
+
+
+class TableStyle:
+    def __init__(self, border=None, backgroundcolor=None):
+        self.border = border
+        self.backgroundcolor = backgroundcolor
+
+    def get_border_(self):
+        return self.border_
+
+    def set_border_(self, border):
+        self.border_ = border
+
+    border = property(get_border_, set_border_)
+
+    def get_backgroundcolor_(self):
+        return self.backgroundcolor_
+
+    def set_backgroundcolor_(self, backgroundcolor):
+        self.backgroundcolor_ = backgroundcolor
+    backgroundcolor = property(get_backgroundcolor_, set_backgroundcolor_)
+
+
+BUILTIN_DEFAULT_TABLE_STYLE = TableStyle(
+    border='0.0007in solid #000000')
+
+
+#
+# Information about the indentation level for lists nested inside
+#   other contexts, e.g. dictionary lists.
+class ListLevel:
+    def __init__(self, level, sibling_level=True, nested_level=True):
+        self.level = level
+        self.sibling_level = sibling_level
+        self.nested_level = nested_level
+
+    def set_sibling(self, sibling_level):
+        self.sibling_level = sibling_level
+
+    def get_sibling(self):
+        return self.sibling_level
+
+    def set_nested(self, nested_level):
+        self.nested_level = nested_level
+
+    def get_nested(self):
+        return self.nested_level
+
+    def set_level(self, level):
+        self.level = level
+
+    def get_level(self):
+        return self.level
+
+
+class Writer(writers.Writer):
+
+    MIME_TYPE = 'application/vnd.oasis.opendocument.text'
+    EXTENSION = '.odt'
+
+    supported = ('odt', )
+    """Formats this writer supports."""
+
+    default_stylesheet = 'styles' + EXTENSION
+
+    default_stylesheet_path = utils.relative_path(
+        os.path.join(os.getcwd(), 'dummy'),
+        os.path.join(os.path.dirname(__file__), default_stylesheet))
+
+    default_template = 'template.txt'
+
+    default_template_path = utils.relative_path(
+        os.path.join(os.getcwd(), 'dummy'),
+        os.path.join(os.path.dirname(__file__), default_template))
+
+    settings_spec = (
+        'ODF-Specific Options.',
+        None,
+        (
+            ('Specify a stylesheet.  '
+                'Default: "%s"' % default_stylesheet_path,
+                ['--stylesheet'],
+                {
+                    'default': default_stylesheet_path,
+                    'dest': 'stylesheet'
+                }),
+            ('Specify an ODF-specific configuration/mapping file '
+                'relative to the current working directory.',
+                ['--odf-config-file'],
+                {'metavar': '<file>'}),
+            ('Obfuscate email addresses to confuse harvesters.',
+                ['--cloak-email-addresses'],
+                {'default': False,
+                    'action': 'store_true',
+                    'dest': 'cloak_email_addresses',
+                    'validator': frontend.validate_boolean}),
+            ('Do not obfuscate email addresses.',
+                ['--no-cloak-email-addresses'],
+                {'default': False,
+                    'action': 'store_false',
+                    'dest': 'cloak_email_addresses',
+                    'validator': frontend.validate_boolean}),
+            ('Specify the thickness of table borders in thousands of a cm. '
+                'Default is 35.',
+                ['--table-border-thickness'],
+                {'default': None,
+                    'metavar': '<int>',
+                    'validator': frontend.validate_nonnegative_int}),
+            ('Add syntax highlighting in literal code blocks.',
+                ['--add-syntax-highlighting'],
+                {'default': False,
+                    'action': 'store_true',
+                    'dest': 'add_syntax_highlighting',
+                    'validator': frontend.validate_boolean}),
+            ('Do not add syntax highlighting in '
+                'literal code blocks. (default)',
+                ['--no-syntax-highlighting'],
+                {'default': False,
+                    'action': 'store_false',
+                    'dest': 'add_syntax_highlighting',
+                    'validator': frontend.validate_boolean}),
+            ('Create sections for headers.  (default)',
+                ['--create-sections'],
+                {'default': True,
+                    'action': 'store_true',
+                    'dest': 'create_sections',
+                    'validator': frontend.validate_boolean}),
+            ('Do not create sections for headers.',
+                ['--no-sections'],
+                {'default': True,
+                    'action': 'store_false',
+                    'dest': 'create_sections',
+                    'validator': frontend.validate_boolean}),
+            ('Create links.',
+                ['--create-links'],
+                {'default': False,
+                    'action': 'store_true',
+                    'dest': 'create_links',
+                    'validator': frontend.validate_boolean}),
+            ('Do not create links.  (default)',
+                ['--no-links'],
+                {'default': False,
+                    'action': 'store_false',
+                    'dest': 'create_links',
+                    'validator': frontend.validate_boolean}),
+            ('Generate endnotes at end of document, not footnotes '
+                'at bottom of page.',
+                ['--endnotes-end-doc'],
+                {'default': False,
+                    'action': 'store_true',
+                    'dest': 'endnotes_end_doc',
+                    'validator': frontend.validate_boolean}),
+            ('Generate footnotes at bottom of page, not endnotes '
+                'at end of document. (default)',
+                ['--no-endnotes-end-doc'],
+                {'default': False,
+                    'action': 'store_false',
+                    'dest': 'endnotes_end_doc',
+                    'validator': frontend.validate_boolean}),
+            ('Generate a bullet list table of contents, '
+                'not a native ODF table of contents.',
+                ['--generate-list-toc'],
+                {'action': 'store_false',
+                    'dest': 'generate_oowriter_toc',
+                    'validator': frontend.validate_boolean}),
+            ('Generate a native ODF table of contents, '
+                'not a bullet list. (default)',
+                ['--generate-oowriter-toc'],
+                {'default': True,
+                    'action': 'store_true',
+                    'dest': 'generate_oowriter_toc',
+                    'validator': frontend.validate_boolean}),
+            ('Specify the contents of an custom header line.  '
+                'See ODF/ODT writer documentation for details '
+                'about special field character sequences.',
+                ['--custom-odt-header'],
+                {'default': '',
+                    'dest': 'custom_header',
+                    'metavar': '<custom header>'}),
+            ('Specify the contents of an custom footer line.  '
+                'See ODF/ODT writer documentation for details.',
+                ['--custom-odt-footer'],
+                {'default': '',
+                    'dest': 'custom_footer',
+                    'metavar': '<custom footer>'}),
+        )
+    )
+
+    settings_defaults = {
+        'output_encoding_error_handler': 'xmlcharrefreplace',
+    }
+
+    relative_path_settings = ('odf_config_file', 'stylesheet',)
+
+    config_section = 'odf_odt writer'
+    config_section_dependencies = ('writers',)
+
+    def __init__(self):
+        writers.Writer.__init__(self)
+        self.translator_class = ODFTranslator
+
+    def translate(self):
+        self.settings = self.document.settings
+        self.visitor = self.translator_class(self.document)
+        self.visitor.retrieve_styles(self.EXTENSION)
+        self.document.walkabout(self.visitor)
+        self.visitor.add_doc_title()
+        self.assemble_my_parts()
+        self.output = self.parts['whole']
+
+    def assemble_my_parts(self):
+        """Assemble the `self.parts` dictionary.  Extend in subclasses.
+        """
+        writers.Writer.assemble_parts(self)
+        f = tempfile.NamedTemporaryFile()
+        zfile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
+        self.write_zip_str(
+            zfile, 'mimetype', self.MIME_TYPE,
+            compress_type=zipfile.ZIP_STORED)
+        content = self.visitor.content_astext()
+        self.write_zip_str(zfile, 'content.xml', content)
+        s1 = self.create_manifest()
+        self.write_zip_str(zfile, 'META-INF/manifest.xml', s1)
+        s1 = self.create_meta()
+        self.write_zip_str(zfile, 'meta.xml', s1)
+        s1 = self.get_stylesheet()
+        # Set default language in document to be generated.
+        # Language is specified by the -l/--language command line option.
+        # The format is described in BCP 47.  If region is omitted, we use
+        # local.normalize(ll) to obtain a region.
+        language_code = None
+        region_code = None
+        if self.visitor.language_code:
+            language_ids = self.visitor.language_code.replace('_', '-')
+            language_ids = language_ids.split('-')
+            # first tag is primary language tag
+            language_code = language_ids[0].lower()
+            # 2-letter region subtag may follow in 2nd or 3rd position
+            for subtag in language_ids[1:]:
+                if len(subtag) == 2 and subtag.isalpha():
+                    region_code = subtag.upper()
+                    break
+                elif len(subtag) == 1:
+                    break   # 1-letter tag is never before valid region tag
+            if region_code is None:
+                try:
+                    rcode = locale.normalize(language_code)
+                except NameError:
+                    rcode = language_code
+                rcode = rcode.split('_')
+                if len(rcode) > 1:
+                    rcode = rcode[1].split('.')
+                    region_code = rcode[0]
+                if region_code is None:
+                    self.document.reporter.warning(
+                        'invalid language-region.\n'
+                        '  Could not find region with locale.normalize().\n'
+                        '  Please specify both language and region (ll-RR).\n'
+                        '  Examples: es-MX (Spanish, Mexico),\n'
+                        '  en-AU (English, Australia).')
+        # Update the style ElementTree with the language and region.
+        # Note that we keep a reference to the modified node because
+        # it is possible that ElementTree will throw away the Python
+        # representation of the updated node if we do not.
+        updated, new_dom_styles, updated_node = self.update_stylesheet(
+            self.visitor.get_dom_stylesheet(), language_code, region_code)
+        if updated:
+            s1 = etree.tostring(new_dom_styles)
+        self.write_zip_str(zfile, 'styles.xml', s1)
+        self.store_embedded_files(zfile)
+        self.copy_from_stylesheet(zfile)
+        zfile.close()
+        f.seek(0)
+        whole = f.read()
+        f.close()
+        self.parts['whole'] = whole
+        self.parts['encoding'] = self.document.settings.output_encoding
+        self.parts['version'] = docutils.__version__
+
+    def update_stylesheet(self, stylesheet_root, language_code, region_code):
+        """Update xml style sheet element with language and region/country."""
+        updated = False
+        modified_nodes = set()
+        if language_code is not None or region_code is not None:
+            n1 = stylesheet_root.find(
+                '{urn:oasis:names:tc:opendocument:xmlns:office:1.0}'
+                'styles')
+            if n1 is None:
+                raise RuntimeError(
+                    "Cannot find 'styles' element in styles.odt/styles.xml")
+            n2_nodes = n1.findall(
+                '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}'
+                'default-style')
+            if not n2_nodes:
+                raise RuntimeError(
+                    "Cannot find 'default-style' "
+                    "element in styles.xml")
+            for node in n2_nodes:
+                family = node.attrib.get(
+                    '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}'
+                    'family')
+                if family == 'paragraph' or family == 'graphic':
+                    n3 = node.find(
+                        '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}'
+                        'text-properties')
+                    if n3 is None:
+                        raise RuntimeError(
+                            "Cannot find 'text-properties' "
+                            "element in styles.xml")
+                    if language_code is not None:
+                        n3.attrib[
+                            '{urn:oasis:names:tc:opendocument:xmlns:'
+                            'xsl-fo-compatible:1.0}language'] = language_code
+                        n3.attrib[
+                            '{urn:oasis:names:tc:opendocument:xmlns:'
+                            'style:1.0}language-complex'] = language_code
+                        updated = True
+                        modified_nodes.add(n3)
+                    if region_code is not None:
+                        n3.attrib[
+                            '{urn:oasis:names:tc:opendocument:xmlns:'
+                            'xsl-fo-compatible:1.0}country'] = region_code
+                        n3.attrib[
+                            '{urn:oasis:names:tc:opendocument:xmlns:'
+                            'style:1.0}country-complex'] = region_code
+                        updated = True
+                        modified_nodes.add(n3)
+        return updated, stylesheet_root, modified_nodes
+
+    def write_zip_str(
+            self, zfile, name, bytes, compress_type=zipfile.ZIP_DEFLATED):
+        localtime = time.localtime(time.time())
+        zinfo = zipfile.ZipInfo(name, localtime)
+        # Add some standard UNIX file access permissions (-rw-r--r--).
+        zinfo.external_attr = (0x81a4 & 0xFFFF) << 16
+        zinfo.compress_type = compress_type
+        zfile.writestr(zinfo, bytes)
+
+    def store_embedded_files(self, zfile):
+        embedded_files = self.visitor.get_embedded_file_list()
+        for source, destination in embedded_files:
+            if source is None:
+                continue
+            try:
+                zfile.write(source, destination)
+            except OSError:
+                self.document.reporter.warning(
+                    "Can't open file %s." % (source, ))
+
+    def get_settings(self):
+        """
+        modeled after get_stylesheet
+        """
+        stylespath = self.settings.stylesheet
+        zfile = zipfile.ZipFile(stylespath, 'r')
+        s1 = zfile.read('settings.xml')
+        zfile.close()
+        return s1
+
+    def get_stylesheet(self):
+        """Get the stylesheet from the visitor.
+        Ask the visitor to setup the page.
+        """
+        return self.visitor.setup_page()
+
+    def copy_from_stylesheet(self, outzipfile):
+        """Copy images, settings, etc from the stylesheet doc into target doc.
+        """
+        stylespath = self.settings.stylesheet
+        inzipfile = zipfile.ZipFile(stylespath, 'r')
+        # Copy the styles.
+        s1 = inzipfile.read('settings.xml')
+        self.write_zip_str(outzipfile, 'settings.xml', s1)
+        # Copy the images.
+        namelist = inzipfile.namelist()
+        for name in namelist:
+            if name.startswith('Pictures/'):
+                imageobj = inzipfile.read(name)
+                outzipfile.writestr(name, imageobj)
+        inzipfile.close()
+
+    def assemble_parts(self):
+        pass
+
+    def create_manifest(self):
+        root = Element(
+            'manifest:manifest',
+            attrib=MANIFEST_NAMESPACE_ATTRIB,
+            nsdict=MANIFEST_NAMESPACE_DICT,
+        )
+        doc = etree.ElementTree(root)
+        SubElement(root, 'manifest:file-entry', attrib={
+            'manifest:media-type': self.MIME_TYPE,
+            'manifest:full-path': '/',
+        }, nsdict=MANNSD)
+        SubElement(root, 'manifest:file-entry', attrib={
+            'manifest:media-type': 'text/xml',
+            'manifest:full-path': 'content.xml',
+        }, nsdict=MANNSD)
+        SubElement(root, 'manifest:file-entry', attrib={
+            'manifest:media-type': 'text/xml',
+            'manifest:full-path': 'styles.xml',
+        }, nsdict=MANNSD)
+        SubElement(root, 'manifest:file-entry', attrib={
+            'manifest:media-type': 'text/xml',
+            'manifest:full-path': 'settings.xml',
+        }, nsdict=MANNSD)
+        SubElement(root, 'manifest:file-entry', attrib={
+            'manifest:media-type': 'text/xml',
+            'manifest:full-path': 'meta.xml',
+        }, nsdict=MANNSD)
+        s1 = ToString(doc)
+        doc = minidom.parseString(s1)
+        return doc.toprettyxml('  ')
+
+    def create_meta(self):
+        root = Element(
+            'office:document-meta',
+            attrib=META_NAMESPACE_ATTRIB,
+            nsdict=META_NAMESPACE_DICT,
+        )
+        doc = etree.ElementTree(root)
+        root = SubElement(root, 'office:meta', nsdict=METNSD)
+        el1 = SubElement(root, 'meta:generator', nsdict=METNSD)
+        el1.text = 'Docutils/rst2odf.py/%s' % (VERSION, )
+        s1 = os.environ.get('USER', '')
+        el1 = SubElement(root, 'meta:initial-creator', nsdict=METNSD)
+        el1.text = s1
+        s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime())
+        el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD)
+        el1.text = s2
+        el1 = SubElement(root, 'dc:creator', nsdict=METNSD)
+        el1.text = s1
+        el1 = SubElement(root, 'dc:date', nsdict=METNSD)
+        el1.text = s2
+        el1 = SubElement(root, 'dc:language', nsdict=METNSD)
+        el1.text = 'en-US'
+        el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD)
+        el1.text = '1'
+        el1 = SubElement(root, 'meta:editing-duration', nsdict=METNSD)
+        el1.text = 'PT00M01S'
+        title = self.visitor.get_title()
+        el1 = SubElement(root, 'dc:title', nsdict=METNSD)
+        if title:
+            el1.text = title
+        else:
+            el1.text = '[no title]'
+        for prop, value in self.visitor.get_meta_dict().items():
+            # 'keywords', 'description', and 'subject' have their own fields:
+            if prop == 'keywords':
+                keywords = re.split(', *', value)
+                for keyword in keywords:
+                    el1 = SubElement(root, 'meta:keyword', nsdict=METNSD)
+                    el1.text = keyword
+            elif prop == 'description':
+                el1 = SubElement(root, 'dc:description', nsdict=METNSD)
+                el1.text = value
+            elif prop == 'subject':
+                el1 = SubElement(root, 'dc:subject', nsdict=METNSD)
+                el1.text = value
+            else:  # Store remaining properties as custom/user-defined
+                el1 = SubElement(root, 'meta:user-defined',
+                                 attrib={'meta:name': prop}, nsdict=METNSD)
+                el1.text = value
+        s1 = ToString(doc)
+        # doc = minidom.parseString(s1)
+        # s1 = doc.toprettyxml('  ')
+        return s1
+
+
+# class ODFTranslator(nodes.SparseNodeVisitor):
+class ODFTranslator(nodes.GenericNodeVisitor):
+
+    used_styles = (
+        'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
+        'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
+        'bulletitem', 'bulletlist',
+        'caption', 'legend',
+        'centeredtextbody', 'codeblock', 'codeblock-indented',
+        'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
+        'codeblock-keyword', 'codeblock-name', 'codeblock-number',
+        'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
+        'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
+        'epigraph-enumitem', 'epigraph-enumlist', 'footer',
+        'footnote', 'citation',
+        'header', 'highlights', 'highlights-bulletitem',
+        'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
+        'horizontalline', 'inlineliteral', 'quotation', 'rubric',
+        'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
+        'title',
+        'subtitle',
+        'heading1',
+        'heading2',
+        'heading3',
+        'heading4',
+        'heading5',
+        'heading6',
+        'heading7',
+        'admon-attention-hdr',
+        'admon-attention-body',
+        'admon-caution-hdr',
+        'admon-caution-body',
+        'admon-danger-hdr',
+        'admon-danger-body',
+        'admon-error-hdr',
+        'admon-error-body',
+        'admon-generic-hdr',
+        'admon-generic-body',
+        'admon-hint-hdr',
+        'admon-hint-body',
+        'admon-important-hdr',
+        'admon-important-body',
+        'admon-note-hdr',
+        'admon-note-body',
+        'admon-tip-hdr',
+        'admon-tip-body',
+        'admon-warning-hdr',
+        'admon-warning-body',
+        'tableoption',
+        'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
+        'Table%d.%c%d',
+        'lineblock1',
+        'lineblock2',
+        'lineblock3',
+        'lineblock4',
+        'lineblock5',
+        'lineblock6',
+        'image', 'figureframe',
+    )
+
+    def __init__(self, document):
+        # nodes.SparseNodeVisitor.__init__(self, document)
+        nodes.GenericNodeVisitor.__init__(self, document)
+        self.settings = document.settings
+        self.language_code = self.settings.language_code
+        self.language = languages.get_language(
+            self.language_code,
+            document.reporter)
+        self.format_map = {}
+        if self.settings.odf_config_file:
+            parser = ConfigParser()
+            parser.read(self.settings.odf_config_file)
+            for rststyle, format in parser.items("Formats"):
+                if rststyle not in self.used_styles:
+                    self.document.reporter.warning(
+                        'Style "%s" is not a style used by odtwriter.' % (
+                            rststyle, ))
+                self.format_map[rststyle] = format
+        self.section_level = 0
+        self.section_count = 0
+        # Create ElementTree content and styles documents.
+        root = Element(
+            'office:document-content',
+            attrib=CONTENT_NAMESPACE_ATTRIB,
+        )
+        self.content_tree = etree.ElementTree(element=root)
+        self.current_element = root
+        SubElement(root, 'office:scripts')
+        SubElement(root, 'office:font-face-decls')
+        el = SubElement(root, 'office:automatic-styles')
+        self.automatic_styles = el
+        el = SubElement(root, 'office:body')
+        el = self.generate_content_element(el)
+        self.current_element = el
+        self.body_text_element = el
+        self.paragraph_style_stack = [self.rststyle('textbody'), ]
+        self.list_style_stack = []
+        self.table_count = 0
+        self.column_count = ord('A') - 1
+        self.trace_level = -1
+        self.optiontablestyles_generated = False
+        self.field_name = None
+        self.field_element = None
+        self.title = None
+        self.image_count = 0
+        self.image_style_count = 0
+        self.image_dict = {}
+        self.embedded_file_list = []
+        self.syntaxhighlighting = 1
+        self.syntaxhighlight_lexer = 'python'
+        self.header_content = []
+        self.footer_content = []
+        self.in_header = False
+        self.in_footer = False
+        self.blockstyle = ''
+        self.in_table_of_contents = False
+        self.table_of_content_index_body = None
+        self.list_level = 0
+        self.def_list_level = 0
+        self.footnote_ref_dict = {}
+        self.footnote_list = []
+        self.footnote_chars_idx = 0
+        self.footnote_level = 0
+        self.pending_ids = []
+        self.in_paragraph = False
+        self.found_doc_title = False
+        self.bumped_list_level_stack = []
+        self.meta_dict = {}
+        self.line_block_level = 0
+        self.line_indent_level = 0
+        self.citation_id = None
+        self.style_index = 0        # use to form unique style names
+        self.str_stylesheet = ''
+        self.str_stylesheetcontent = ''
+        self.dom_stylesheet = None
+        self.table_styles = None
+        self.in_citation = False
+
+        # Keep track of nested styling classes
+        self.inline_style_count_stack = []
+
+    def get_str_stylesheet(self):
+        return self.str_stylesheet
+
+    def retrieve_styles(self, extension):
+        """Retrieve the stylesheet from either a .xml file or from
+        a .odt (zip) file.  Return the content as a string.
+        """
+        s2 = None
+        stylespath = self.settings.stylesheet
+        ext = os.path.splitext(stylespath)[1]
+        if ext == '.xml':
+            with open(stylespath, 'r', encoding='utf-8') as stylesfile:
+                s1 = stylesfile.read()
+        elif ext == extension:
+            zfile = zipfile.ZipFile(stylespath, 'r')
+            s1 = zfile.read('styles.xml')
+            s2 = zfile.read('content.xml')
+            zfile.close()
+        else:
+            raise RuntimeError('stylesheet path (%s) must be %s or '
+                               '.xml file' % (stylespath, extension))
+        self.str_stylesheet = s1
+        self.str_stylesheetcontent = s2
+        self.dom_stylesheet = etree.fromstring(self.str_stylesheet)
+        self.dom_stylesheetcontent = etree.fromstring(
+            self.str_stylesheetcontent)
+        self.table_styles = self.extract_table_styles(s2)
+
+    def extract_table_styles(self, styles_str):
+        root = etree.fromstring(styles_str)
+        table_styles = {}
+        auto_styles = root.find(
+            '{%s}automatic-styles' % (CNSD['office'], ))
+        for stylenode in auto_styles:
+            name = stylenode.get('{%s}name' % (CNSD['style'], ))
+            tablename = name.split('.')[0]
+            family = stylenode.get('{%s}family' % (CNSD['style'], ))
+            if name.startswith(TABLESTYLEPREFIX):
+                tablestyle = table_styles.get(tablename)
+                if tablestyle is None:
+                    tablestyle = TableStyle()
+                    table_styles[tablename] = tablestyle
+                if family == 'table':
+                    properties = stylenode.find(
+                        '{%s}table-properties' % (CNSD['style'], ))
+                    property = properties.get(
+                        '{%s}%s' % (CNSD['fo'], 'background-color', ))
+                    if property is not None and property != 'none':
+                        tablestyle.backgroundcolor = property
+                elif family == 'table-cell':
+                    properties = stylenode.find(
+                        '{%s}table-cell-properties' % (CNSD['style'], ))
+                    if properties is not None:
+                        border = self.get_property(properties)
+                        if border is not None:
+                            tablestyle.border = border
+        return table_styles
+
+    def get_property(self, stylenode):
+        border = None
+        for propertyname in TABLEPROPERTYNAMES:
+            border = stylenode.get('{%s}%s' % (CNSD['fo'], propertyname, ))
+            if border is not None and border != 'none':
+                return border
+        return border
+
+    def add_doc_title(self):
+        text = self.settings.title
+        if text:
+            self.title = text
+            if not self.found_doc_title:
+                el = Element('text:p', attrib={
+                    'text:style-name': self.rststyle('title'),
+                })
+                el.text = text
+                self.body_text_element.insert(0, el)
+        el = self.find_first_text_p(self.body_text_element)
+        if el is not None:
+            self.attach_page_style(el)
+
+    def find_first_text_p(self, el):
+        """Search the generated doc and return the first <text:p> element.
+        """
+        if el.tag == 'text:p' or el.tag == 'text:h':
+            return el
+        else:
+            for child in el:
+                el1 = self.find_first_text_p(child)
+                if el1 is not None:
+                    return el1
+            return None
+
+    def attach_page_style(self, el):
+        """Attach the default page style.
+
+        Create an automatic-style that refers to the current style
+        of this element and that refers to the default page style.
+        """
+        current_style = el.get('text:style-name')
+        style_name = 'P1003'
+        el1 = SubElement(
+            self.automatic_styles, 'style:style', attrib={
+                'style:name': style_name,
+                'style:master-page-name': "rststyle-pagedefault",
+                'style:family': "paragraph",
+            }, nsdict=SNSD)
+        if current_style:
+            el1.set('style:parent-style-name', current_style)
+        el.set('text:style-name', style_name)
+
+    def rststyle(self, name, parameters=()):
+        """
+        Returns the style name to use for the given style.
+
+        If `parameters` is given `name` must contain a matching number of
+        ``%`` and is used as a format expression with `parameters` as
+        the value.
+        """
+        name1 = name % parameters
+        return self.format_map.get(name1, 'rststyle-%s' % name1)
+
+    def generate_content_element(self, root):
+        return SubElement(root, 'office:text')
+
+    def setup_page(self):
+        self.setup_paper(self.dom_stylesheet)
+        if (len(self.header_content) > 0
+            or len(self.footer_content) > 0
+            or self.settings.custom_header
+            or self.settings.custom_footer):
+            self.add_header_footer(self.dom_stylesheet)
+        return etree.tostring(self.dom_stylesheet)
+
+    def get_dom_stylesheet(self):
+        return self.dom_stylesheet
+
+    def setup_paper(self, root_el):
+        # TODO: only call paperconf, if it is actually used
+        # (i.e. page size removed from "styles.odt" with rst2odt_prepstyles.py
+        # cf. conditional in walk() below)?
+        try:
+            dimensions = subprocess.check_output(('paperconf', '-s'),
+                                                 stderr=subprocess.STDOUT)
+            w, h = (float(s) for s in dimensions.split())
+        except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
+            self.document.reporter.info(
+                'Cannot use `paperconf`, defaulting to Letter.')
+            w, h = 612, 792     # default to Letter
+
+        def walk(el):
+            if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \
+                    "{%s}page-width" % SNSD["fo"] not in el.attrib:
+                el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w
+                el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h
+                el.attrib["{%s}margin-left" % SNSD["fo"]] = \
+                    el.attrib["{%s}margin-right" % SNSD["fo"]] = \
+                    "%.3fpt" % (.1 * w)
+                el.attrib["{%s}margin-top" % SNSD["fo"]] = \
+                    el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \
+                    "%.3fpt" % (.1 * h)
+            else:
+                for subel in el:
+                    walk(subel)
+        walk(root_el)
+
+    def add_header_footer(self, root_el):
+        automatic_styles = root_el.find(
+            '{%s}automatic-styles' % SNSD['office'])
+        path = '{%s}master-styles' % (NAME_SPACE_1, )
+        master_el = root_el.find(path)
+        if master_el is None:
+            return
+        path = '{%s}master-page' % (SNSD['style'], )
+        master_el_container = master_el.findall(path)
+        master_el = None
+        target_attrib = '{%s}name' % (SNSD['style'], )
+        target_name = self.rststyle('pagedefault')
+        for el in master_el_container:
+            if el.get(target_attrib) == target_name:
+                master_el = el
+                break
+        if master_el is None:
+            return
+        el1 = master_el
+        if self.header_content or self.settings.custom_header:
+            el2 = SubElement(
+                el1, 'style:header',
+                attrib=STYLES_NAMESPACE_ATTRIB,
+                nsdict=STYLES_NAMESPACE_DICT,
+            )
+            for el in self.header_content:
+                attrkey = add_ns('text:style-name', nsdict=SNSD)
+                el.attrib[attrkey] = self.rststyle('header')
+                el2.append(el)
+            if self.settings.custom_header:
+                self.create_custom_headfoot(
+                    el2,
+                    self.settings.custom_header, 'header', automatic_styles)
+        if self.footer_content or self.settings.custom_footer:
+            el2 = SubElement(
+                el1, 'style:footer',
+                attrib=STYLES_NAMESPACE_ATTRIB,
+                nsdict=STYLES_NAMESPACE_DICT,
+            )
+            for el in self.footer_content:
+                attrkey = add_ns('text:style-name', nsdict=SNSD)
+                el.attrib[attrkey] = self.rststyle('footer')
+                el2.append(el)
+            if self.settings.custom_footer:
+                self.create_custom_headfoot(
+                    el2,
+                    self.settings.custom_footer, 'footer', automatic_styles)
+
+    code_none, code_field, code_text = list(range(3))
+    field_pat = re.compile(r'%(..?)%')
+
+    def create_custom_headfoot(
+            self, parent, text, style_name, automatic_styles):
+        parent = SubElement(parent, 'text:p', attrib={
+            'text:style-name': self.rststyle(style_name),
+        })
+        current_element = None
+        field_iter = self.split_field_specifiers_iter(text)
+        for item in field_iter:
+            if item[0] == ODFTranslator.code_field:
+                if item[1] not in (
+                        'p', 'P',
+                        't1', 't2', 't3', 't4',
+                        'd1', 'd2', 'd3', 'd4', 'd5',
+                        's', 't', 'a'):
+                    msg = 'bad field spec: %%%s%%' % (item[1], )
+                    raise RuntimeError(msg)
+                el1 = self.make_field_element(
+                    parent,
+                    item[1], style_name, automatic_styles)
+                if el1 is None:
+                    msg = 'bad field spec: %%%s%%' % (item[1], )
+                    raise RuntimeError(msg)
+                else:
+                    current_element = el1
+            else:
+                if current_element is None:
+                    parent.text = item[1]
+                else:
+                    current_element.tail = item[1]
+
+    def make_field_element(self, parent, text, style_name, automatic_styles):
+        if text == 'p':
+            el1 = SubElement(parent, 'text:page-number', attrib={
+                # 'text:style-name': self.rststyle(style_name),
+                'text:select-page': 'current',
+            })
+        elif text == 'P':
+            el1 = SubElement(parent, 'text:page-count', attrib={
+                # 'text:style-name': self.rststyle(style_name),
+            })
+        elif text == 't1':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:time', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'text:fixed': 'true',
+                'style:data-style-name':
+                    'rst-time-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:time-style', attrib={
+                'style:name': 'rst-time-style-%d' % self.style_index,
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:hours', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ':'
+            el3 = SubElement(el2, 'number:minutes', attrib={
+                'number:style': 'long',
+            })
+        elif text == 't2':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:time', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'text:fixed': 'true',
+                'style:data-style-name':
+                    'rst-time-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:time-style', attrib={
+                'style:name': 'rst-time-style-%d' % self.style_index,
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:hours', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ':'
+            el3 = SubElement(el2, 'number:minutes', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ':'
+            el3 = SubElement(el2, 'number:seconds', attrib={
+                'number:style': 'long',
+            })
+        elif text == 't3':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:time', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'text:fixed': 'true',
+                'style:data-style-name':
+                    'rst-time-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:time-style', attrib={
+                'style:name': 'rst-time-style-%d' % self.style_index,
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:hours', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ':'
+            el3 = SubElement(el2, 'number:minutes', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ' '
+            el3 = SubElement(el2, 'number:am-pm')
+        elif text == 't4':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:time', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'text:fixed': 'true',
+                'style:data-style-name':
+                    'rst-time-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:time-style', attrib={
+                'style:name': 'rst-time-style-%d' % self.style_index,
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:hours', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ':'
+            el3 = SubElement(el2, 'number:minutes', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ':'
+            el3 = SubElement(el2, 'number:seconds', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ' '
+            el3 = SubElement(el2, 'number:am-pm')
+        elif text == 'd1':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:date', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'style:data-style-name':
+                    'rst-date-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:date-style', attrib={
+                'style:name': 'rst-date-style-%d' % self.style_index,
+                'number:automatic-order': 'true',
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:month', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = '/'
+            el3 = SubElement(el2, 'number:day', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = '/'
+            el3 = SubElement(el2, 'number:year')
+        elif text == 'd2':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:date', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'style:data-style-name':
+                    'rst-date-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:date-style', attrib={
+                'style:name': 'rst-date-style-%d' % self.style_index,
+                'number:automatic-order': 'true',
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:month', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = '/'
+            el3 = SubElement(el2, 'number:day', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = '/'
+            el3 = SubElement(el2, 'number:year', attrib={
+                'number:style': 'long',
+            })
+        elif text == 'd3':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:date', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'style:data-style-name':
+                    'rst-date-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:date-style', attrib={
+                'style:name': 'rst-date-style-%d' % self.style_index,
+                'number:automatic-order': 'true',
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:month', attrib={
+                'number:textual': 'true',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ' '
+            el3 = SubElement(el2, 'number:day', attrib={})
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ', '
+            el3 = SubElement(el2, 'number:year', attrib={
+                'number:style': 'long',
+            })
+        elif text == 'd4':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:date', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'style:data-style-name':
+                    'rst-date-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:date-style', attrib={
+                'style:name': 'rst-date-style-%d' % self.style_index,
+                'number:automatic-order': 'true',
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:month', attrib={
+                'number:textual': 'true',
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ' '
+            el3 = SubElement(el2, 'number:day', attrib={})
+            el3 = SubElement(el2, 'number:text')
+            el3.text = ', '
+            el3 = SubElement(el2, 'number:year', attrib={
+                'number:style': 'long',
+            })
+        elif text == 'd5':
+            self.style_index += 1
+            el1 = SubElement(parent, 'text:date', attrib={
+                'text:style-name': self.rststyle(style_name),
+                'style:data-style-name':
+                    'rst-date-style-%d' % self.style_index,
+            })
+            el2 = SubElement(automatic_styles, 'number:date-style', attrib={
+                'style:name': 'rst-date-style-%d' % self.style_index,
+                'xmlns:number': SNSD['number'],
+                'xmlns:style': SNSD['style'],
+            })
+            el3 = SubElement(el2, 'number:year', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = '-'
+            el3 = SubElement(el2, 'number:month', attrib={
+                'number:style': 'long',
+            })
+            el3 = SubElement(el2, 'number:text')
+            el3.text = '-'
+            el3 = SubElement(el2, 'number:day', attrib={
+                'number:style': 'long',
+            })
+        elif text == 's':
+            el1 = SubElement(parent, 'text:subject', attrib={
+                'text:style-name': self.rststyle(style_name),
+            })
+        elif text == 't':
+            el1 = SubElement(parent, 'text:title', attrib={
+                'text:style-name': self.rststyle(style_name),
+            })
+        elif text == 'a':
+            el1 = SubElement(parent, 'text:author-name', attrib={
+                'text:fixed': 'false',
+            })
+        else:
+            el1 = None
+        return el1
+
+    def split_field_specifiers_iter(self, text):
+        pos1 = 0
+        while True:
+            mo = ODFTranslator.field_pat.search(text, pos1)
+            if mo:
+                pos2 = mo.start()
+                if pos2 > pos1:
+                    yield ODFTranslator.code_text, text[pos1:pos2]
+                yield ODFTranslator.code_field, mo.group(1)
+                pos1 = mo.end()
+            else:
+                break
+        trailing = text[pos1:]
+        if trailing:
+            yield ODFTranslator.code_text, trailing
+
+    def astext(self):
+        root = self.content_tree.getroot()
+        et = etree.ElementTree(root)
+        return ToString(et)
+
+    def content_astext(self):
+        return self.astext()
+
+    def set_title(self, title):
+        self.title = title
+
+    def get_title(self):
+        return self.title
+
+    def set_embedded_file_list(self, embedded_file_list):
+        self.embedded_file_list = embedded_file_list
+
+    def get_embedded_file_list(self):
+        return self.embedded_file_list
+
+    def get_meta_dict(self):
+        return self.meta_dict
+
+    def process_footnotes(self):
+        for node, el1 in self.footnote_list:
+            backrefs = node.attributes.get('backrefs', [])
+            first = True
+            for ref in backrefs:
+                el2 = self.footnote_ref_dict.get(ref)
+                if el2 is not None:
+                    if first:
+                        first = False
+                        el3 = copy.deepcopy(el1)
+                        el2.append(el3)
+                    else:
+                        if len(el2) > 0:   # and 'id' in el2.attrib:
+                            child = el2[0]
+                            ref1 = child.text
+                            attribkey = add_ns('text:id', nsdict=SNSD)
+                            id1 = el2.get(attribkey, 'footnote-error')
+                            if id1 is None:
+                                id1 = ''
+                            tag = add_ns('text:note-ref', nsdict=SNSD)
+                            el2.tag = tag
+                            if self.settings.endnotes_end_doc:
+                                note_class = 'endnote'
+                            else:
+                                note_class = 'footnote'
+                            el2.attrib.clear()
+                            attribkey = add_ns('text:note-class', nsdict=SNSD)
+                            el2.attrib[attribkey] = note_class
+                            attribkey = add_ns('text:ref-name', nsdict=SNSD)
+                            el2.attrib[attribkey] = id1
+                            attribkey = add_ns(
+                                'text:reference-format', nsdict=SNSD)
+                            el2.attrib[attribkey] = 'page'
+                            el2.text = ref1
+
+    #
+    # Utility methods
+
+    def append_child(self, tag, attrib=None, parent=None):
+        if parent is None:
+            parent = self.current_element
+        return SubElement(parent, tag, attrib)
+
+    def append_p(self, style, text=None):
+        result = self.append_child('text:p', attrib={
+            'text:style-name': self.rststyle(style)})
+        self.append_pending_ids(result)
+        if text is not None:
+            result.text = text
+        return result
+
+    def append_pending_ids(self, el):
+        if self.settings.create_links:
+            for id in self.pending_ids:
+                SubElement(el, 'text:reference-mark', attrib={
+                    'text:name': id})
+        self.pending_ids = []
+
+    def set_current_element(self, el):
+        self.current_element = el
+
+    def set_to_parent(self):
+        self.current_element = self.current_element.getparent()
+
+    def generate_labeled_block(self, node, label):
+        label = '%s:' % (self.language.labels[label], )
+        el = self.append_p('textbody')
+        el1 = SubElement(
+            el, 'text:span',
+            attrib={'text:style-name': self.rststyle('strong')})
+        el1.text = label
+        return self.append_p('blockindent')
+
+    def generate_labeled_line(self, node, label):
+        label = '%s:' % (self.language.labels[label], )
+        el = self.append_p('textbody')
+        el1 = SubElement(
+            el, 'text:span',
+            attrib={'text:style-name': self.rststyle('strong')})
+        el1.text = label
+        el1.tail = node.astext()
+        return el
+
+    def encode(self, text):
+        return text.replace('\n', " ")
+
+    #
+    # Visitor functions
+    #
+    # In alphabetic order, more or less.
+    #   See docutils.docutils.nodes.node_class_names.
+    #
+
+    def dispatch_visit(self, node):
+        """Override to catch basic attributes which many nodes have."""
+        self.handle_basic_atts(node)
+        nodes.GenericNodeVisitor.dispatch_visit(self, node)
+
+    def handle_basic_atts(self, node):
+        if isinstance(node, nodes.Element) and node['ids']:
+            self.pending_ids += node['ids']
+
+    def default_visit(self, node):
+        self.document.reporter.warning('missing visit_%s' % (node.tagname, ))
+
+    def default_departure(self, node):
+        self.document.reporter.warning('missing depart_%s' % (node.tagname, ))
+
+    def visit_Text(self, node):
+        # Skip nodes whose text has been processed in parent nodes.
+        if isinstance(node.parent, docutils.nodes.literal_block):
+            return
+        text = node.astext()
+        # Are we in mixed content?  If so, add the text to the
+        #   etree tail of the previous sibling element.
+        if len(self.current_element) > 0:
+            if self.current_element[-1].tail:
+                self.current_element[-1].tail += text
+            else:
+                self.current_element[-1].tail = text
+        else:
+            if self.current_element.text:
+                self.current_element.text += text
+            else:
+                self.current_element.text = text
+
+    def depart_Text(self, node):
+        pass
+
+    #
+    # Pre-defined fields
+    #
+
+    def visit_address(self, node):
+        el = self.generate_labeled_block(node, 'address')
+        self.set_current_element(el)
+
+    def depart_address(self, node):
+        self.set_to_parent()
+
+    def visit_author(self, node):
+        if isinstance(node.parent, nodes.authors):
+            el = self.append_p('blockindent')
+        else:
+            el = self.generate_labeled_block(node, 'author')
+        self.set_current_element(el)
+
+    def depart_author(self, node):
+        self.set_to_parent()
+
+    def visit_authors(self, node):
+        label = '%s:' % (self.language.labels['authors'], )
+        el = self.append_p('textbody')
+        el1 = SubElement(
+            el, 'text:span',
+            attrib={'text:style-name': self.rststyle('strong')})
+        el1.text = label
+
+    def depart_authors(self, node):
+        pass
+
+    def visit_contact(self, node):
+        el = self.generate_labeled_block(node, 'contact')
+        self.set_current_element(el)
+
+    def depart_contact(self, node):
+        self.set_to_parent()
+
+    def visit_copyright(self, node):
+        el = self.generate_labeled_block(node, 'copyright')
+        self.set_current_element(el)
+
+    def depart_copyright(self, node):
+        self.set_to_parent()
+
+    def visit_date(self, node):
+        self.generate_labeled_line(node, 'date')
+
+    def depart_date(self, node):
+        pass
+
+    def visit_organization(self, node):
+        el = self.generate_labeled_block(node, 'organization')
+        self.set_current_element(el)
+
+    def depart_organization(self, node):
+        self.set_to_parent()
+
+    def visit_status(self, node):
+        el = self.generate_labeled_block(node, 'status')
+        self.set_current_element(el)
+
+    def depart_status(self, node):
+        self.set_to_parent()
+
+    def visit_revision(self, node):
+        self.generate_labeled_line(node, 'revision')
+
+    def depart_revision(self, node):
+        pass
+
+    def visit_version(self, node):
+        self.generate_labeled_line(node, 'version')
+        # self.set_current_element(el)
+
+    def depart_version(self, node):
+        # self.set_to_parent()
+        pass
+
+    def visit_attribution(self, node):
+        self.append_p('attribution', node.astext())
+
+    def depart_attribution(self, node):
+        pass
+
+    def visit_block_quote(self, node):
+        if 'epigraph' in node.attributes['classes']:
+            self.paragraph_style_stack.append(self.rststyle('epigraph'))
+            self.blockstyle = self.rststyle('epigraph')
+        elif 'highlights' in node.attributes['classes']:
+            self.paragraph_style_stack.append(self.rststyle('highlights'))
+            self.blockstyle = self.rststyle('highlights')
+        else:
+            self.paragraph_style_stack.append(self.rststyle('blockquote'))
+            self.blockstyle = self.rststyle('blockquote')
+        self.line_indent_level += 1
+
+    def depart_block_quote(self, node):
+        self.paragraph_style_stack.pop()
+        self.blockstyle = ''
+        self.line_indent_level -= 1
+
+    def visit_bullet_list(self, node):
+        self.list_level += 1
+        if self.in_table_of_contents:
+            if self.settings.generate_oowriter_toc:
+                pass
+            else:
+                if 'classes' in node and \
+                        'auto-toc' in node.attributes['classes']:
+                    el = SubElement(self.current_element, 'text:list', attrib={
+                        'text:style-name': self.rststyle('tocenumlist'),
+                    })
+                    self.list_style_stack.append(self.rststyle('enumitem'))
+                else:
+                    el = SubElement(self.current_element, 'text:list', attrib={
+                        'text:style-name': self.rststyle('tocbulletlist'),
+                    })
+                    self.list_style_stack.append(self.rststyle('bulletitem'))
+                self.set_current_element(el)
+        else:
+            if self.blockstyle == self.rststyle('blockquote'):
+                el = SubElement(self.current_element, 'text:list', attrib={
+                    'text:style-name': self.rststyle('blockquote-bulletlist'),
+                })
+                self.list_style_stack.append(
+                    self.rststyle('blockquote-bulletitem'))
+            elif self.blockstyle == self.rststyle('highlights'):
+                el = SubElement(self.current_element, 'text:list', attrib={
+                    'text:style-name': self.rststyle('highlights-bulletlist'),
+                })
+                self.list_style_stack.append(
+                    self.rststyle('highlights-bulletitem'))
+            elif self.blockstyle == self.rststyle('epigraph'):
+                el = SubElement(self.current_element, 'text:list', attrib={
+                    'text:style-name': self.rststyle('epigraph-bulletlist'),
+                })
+                self.list_style_stack.append(
+                    self.rststyle('epigraph-bulletitem'))
+            else:
+                el = SubElement(self.current_element, 'text:list', attrib={
+                    'text:style-name': self.rststyle('bulletlist'),
+                })
+                self.list_style_stack.append(self.rststyle('bulletitem'))
+            self.set_current_element(el)
+
+    def depart_bullet_list(self, node):
+        if self.in_table_of_contents:
+            if self.settings.generate_oowriter_toc:
+                pass
+            else:
+                self.set_to_parent()
+                self.list_style_stack.pop()
+        else:
+            self.set_to_parent()
+            self.list_style_stack.pop()
+        self.list_level -= 1
+
+    def visit_caption(self, node):
+        raise nodes.SkipChildren()
+
+    def depart_caption(self, node):
+        pass
+
+    def visit_comment(self, node):
+        el = self.append_p('textbody')
+        el1 = SubElement(el, 'office:annotation', attrib={})
+        el2 = SubElement(el1, 'dc:creator', attrib={})
+        s1 = os.environ.get('USER', '')
+        el2.text = s1
+        el2 = SubElement(el1, 'text:p', attrib={})
+        el2.text = node.astext()
+
+    def depart_comment(self, node):
+        pass
+
+    def visit_compound(self, node):
+        # The compound directive currently receives no special treatment.
+        pass
+
+    def depart_compound(self, node):
+        pass
+
+    def visit_container(self, node):
+        styles = node.attributes.get('classes', ())
+        if len(styles) > 0:
+            self.paragraph_style_stack.append(self.rststyle(styles[0]))
+
+    def depart_container(self, node):
+        styles = node.attributes.get('classes', ())
+        if len(styles) > 0:
+            self.paragraph_style_stack.pop()
+
+    def visit_decoration(self, node):
+        pass
+
+    def depart_decoration(self, node):
+        pass
+
+    def visit_definition_list(self, node):
+        self.def_list_level += 1
+        if self.list_level > 5:
+            raise RuntimeError(
+                'max definition list nesting level exceeded')
+
+    def depart_definition_list(self, node):
+        self.def_list_level -= 1
+
+    def visit_definition_list_item(self, node):
+        pass
+
+    def depart_definition_list_item(self, node):
+        pass
+
+    def visit_term(self, node):
+        el = self.append_p('deflist-term-%d' % self.def_list_level)
+        el.text = node.astext()
+        self.set_current_element(el)
+        raise nodes.SkipChildren()
+
+    def depart_term(self, node):
+        self.set_to_parent()
+
+    def visit_definition(self, node):
+        self.paragraph_style_stack.append(
+            self.rststyle('deflist-def-%d' % self.def_list_level))
+        self.bumped_list_level_stack.append(ListLevel(1))
+
+    def depart_definition(self, node):
+        self.paragraph_style_stack.pop()
+        self.bumped_list_level_stack.pop()
+
+    def visit_classifier(self, node):
+        if len(self.current_element) > 0:
+            el = self.current_element[-1]
+            el1 = SubElement(
+                el, 'text:span',
+                attrib={'text:style-name': self.rststyle('emphasis')})
+            el1.text = ' (%s)' % (node.astext(), )
+
+    def depart_classifier(self, node):
+        pass
+
+    def visit_document(self, node):
+        pass
+
+    def depart_document(self, node):
+        self.process_footnotes()
+
+    def visit_docinfo(self, node):
+        self.section_level += 1
+        self.section_count += 1
+        if self.settings.create_sections:
+            el = self.append_child(
+                'text:section', attrib={
+                    'text:name': 'Section%d' % self.section_count,
+                    'text:style-name': 'Sect%d' % self.section_level,
+                }
+            )
+            self.set_current_element(el)
+
+    def depart_docinfo(self, node):
+        self.section_level -= 1
+        if self.settings.create_sections:
+            self.set_to_parent()
+
+    def visit_emphasis(self, node):
+        el = SubElement(
+            self.current_element, 'text:span',
+            attrib={'text:style-name': self.rststyle('emphasis')})
+        self.set_current_element(el)
+
+    def depart_emphasis(self, node):
+        self.set_to_parent()
+
+    def visit_enumerated_list(self, node):
+        el1 = self.current_element
+        if self.blockstyle == self.rststyle('blockquote'):
+            el2 = SubElement(el1, 'text:list', attrib={
+                'text:style-name': self.rststyle('blockquote-enumlist'),
+            })
+            self.list_style_stack.append(self.rststyle('blockquote-enumitem'))
+        elif self.blockstyle == self.rststyle('highlights'):
+            el2 = SubElement(el1, 'text:list', attrib={
+                'text:style-name': self.rststyle('highlights-enumlist'),
+            })
+            self.list_style_stack.append(self.rststyle('highlights-enumitem'))
+        elif self.blockstyle == self.rststyle('epigraph'):
+            el2 = SubElement(el1, 'text:list', attrib={
+                'text:style-name': self.rststyle('epigraph-enumlist'),
+            })
+            self.list_style_stack.append(self.rststyle('epigraph-enumitem'))
+        else:
+            liststylename = 'enumlist-%s' % (node.get('enumtype', 'arabic'), )
+            el2 = SubElement(el1, 'text:list', attrib={
+                'text:style-name': self.rststyle(liststylename),
+            })
+            self.list_style_stack.append(self.rststyle('enumitem'))
+        self.set_current_element(el2)
+
+    def depart_enumerated_list(self, node):
+        self.set_to_parent()
+        self.list_style_stack.pop()
+
+    def visit_list_item(self, node):
+        # If we are in a "bumped" list level, then wrap this
+        #   list in an outer lists in order to increase the
+        #   indentation level.
+        if self.in_table_of_contents:
+            if self.settings.generate_oowriter_toc:
+                self.paragraph_style_stack.append(
+                    self.rststyle('contents-%d' % (self.list_level, )))
+            else:
+                el1 = self.append_child('text:list-item')
+                self.set_current_element(el1)
+        else:
+            el1 = self.append_child('text:list-item')
+            el3 = el1
+            if len(self.bumped_list_level_stack) > 0:
+                level_obj = self.bumped_list_level_stack[-1]
+                if level_obj.get_sibling():
+                    level_obj.set_nested(False)
+                    for level_obj1 in self.bumped_list_level_stack:
+                        for idx in range(level_obj1.get_level()):
+                            el2 = self.append_child('text:list', parent=el3)
+                            el3 = self.append_child(
+                                'text:list-item', parent=el2)
+            self.paragraph_style_stack.append(self.list_style_stack[-1])
+            self.set_current_element(el3)
+
+    def depart_list_item(self, node):
+        if self.in_table_of_contents:
+            if self.settings.generate_oowriter_toc:
+                self.paragraph_style_stack.pop()
+            else:
+                self.set_to_parent()
+        else:
+            if len(self.bumped_list_level_stack) > 0:
+                level_obj = self.bumped_list_level_stack[-1]
+                if level_obj.get_sibling():
+                    level_obj.set_nested(True)
+                    for level_obj1 in self.bumped_list_level_stack:
+                        for idx in range(level_obj1.get_level()):
+                            self.set_to_parent()
+                            self.set_to_parent()
+            self.paragraph_style_stack.pop()
+            self.set_to_parent()
+
+    def visit_header(self, node):
+        self.in_header = True
+
+    def depart_header(self, node):
+        self.in_header = False
+
+    def visit_footer(self, node):
+        self.in_footer = True
+
+    def depart_footer(self, node):
+        self.in_footer = False
+
+    def visit_field(self, node):
+        pass
+
+    def depart_field(self, node):
+        pass
+
+    def visit_field_list(self, node):
+        pass
+
+    def depart_field_list(self, node):
+        pass
+
+    def visit_field_name(self, node):
+        el = self.append_p('textbody')
+        el1 = SubElement(
+            el, 'text:span',
+            attrib={'text:style-name': self.rststyle('strong')})
+        el1.text = node.astext()
+
+    def depart_field_name(self, node):
+        pass
+
+    def visit_field_body(self, node):
+        self.paragraph_style_stack.append(self.rststyle('blockindent'))
+
+    def depart_field_body(self, node):
+        self.paragraph_style_stack.pop()
+
+    def visit_figure(self, node):
+        pass
+
+    def depart_figure(self, node):
+        pass
+
+    def visit_footnote(self, node):
+        self.footnote_level += 1
+        self.save_footnote_current = self.current_element
+        el1 = Element('text:note-body')
+        self.current_element = el1
+        self.footnote_list.append((node, el1))
+        if isinstance(node, docutils.nodes.citation):
+            self.paragraph_style_stack.append(self.rststyle('citation'))
+        else:
+            self.paragraph_style_stack.append(self.rststyle('footnote'))
+
+    def depart_footnote(self, node):
+        self.paragraph_style_stack.pop()
+        self.current_element = self.save_footnote_current
+        self.footnote_level -= 1
+
+    footnote_chars = [
+        '*', '**', '***',
+        '++', '+++',
+        '##', '###',
+        '@@', '@@@',
+    ]
+
+    def visit_footnote_reference(self, node):
+        if self.footnote_level <= 0:
+            id = node.attributes['ids'][0]
+            refid = node.attributes.get('refid')
+            if refid is None:
+                refid = ''
+            if self.settings.endnotes_end_doc:
+                note_class = 'endnote'
+            else:
+                note_class = 'footnote'
+            el1 = self.append_child('text:note', attrib={
+                'text:id': '%s' % (refid, ),
+                'text:note-class': note_class,
+            })
+            note_auto = str(node.attributes.get('auto', 1))
+            if isinstance(node, docutils.nodes.citation_reference):
+                citation = '[%s]' % node.astext()
+                el2 = SubElement(el1, 'text:note-citation', attrib={
+                    'text:label': citation,
+                })
+                el2.text = citation
+            elif note_auto == '1':
+                el2 = SubElement(el1, 'text:note-citation', attrib={
+                    'text:label': node.astext(),
+                })
+                el2.text = node.astext()
+            elif note_auto == '*':
+                if self.footnote_chars_idx >= len(
+                        ODFTranslator.footnote_chars):
+                    self.footnote_chars_idx = 0
+                footnote_char = ODFTranslator.footnote_chars[
+                    self.footnote_chars_idx]
+                self.footnote_chars_idx += 1
+                el2 = SubElement(el1, 'text:note-citation', attrib={
+                    'text:label': footnote_char,
+                })
+                el2.text = footnote_char
+            self.footnote_ref_dict[id] = el1
+        raise nodes.SkipChildren()
+
+    def depart_footnote_reference(self, node):
+        pass
+
+    def visit_citation(self, node):
+        self.in_citation = True
+        for id in node.attributes['ids']:
+            self.citation_id = id
+            break
+        self.paragraph_style_stack.append(self.rststyle('blockindent'))
+        self.bumped_list_level_stack.append(ListLevel(1))
+
+    def depart_citation(self, node):
+        self.citation_id = None
+        self.paragraph_style_stack.pop()
+        self.bumped_list_level_stack.pop()
+        self.in_citation = False
+
+    def visit_citation_reference(self, node):
+        if self.settings.create_links:
+            id = node.attributes['refid']
+            el = self.append_child('text:reference-ref', attrib={
+                'text:ref-name': '%s' % (id, ),
+                'text:reference-format': 'text',
+            })
+            el.text = '['
+            self.set_current_element(el)
+        elif self.current_element.text is None:
+            self.current_element.text = '['
+        else:
+            self.current_element.text += '['
+
+    def depart_citation_reference(self, node):
+        self.current_element.text += ']'
+        if self.settings.create_links:
+            self.set_to_parent()
+
+    def visit_label(self, node):
+        if isinstance(node.parent, docutils.nodes.footnote):
+            raise nodes.SkipChildren()
+        elif self.citation_id is not None:
+            el = self.append_p('textbody')
+            self.set_current_element(el)
+            if self.settings.create_links:
+                el0 = SubElement(el, 'text:span')
+                el0.text = '['
+                self.append_child('text:reference-mark-start', attrib={
+                    'text:name': '%s' % (self.citation_id, ),
+                })
+            else:
+                el.text = '['
+
+    def depart_label(self, node):
+        if isinstance(node.parent, docutils.nodes.footnote):
+            pass
+        elif self.citation_id is not None:
+            if self.settings.create_links:
+                self.append_child('text:reference-mark-end', attrib={
+                    'text:name': '%s' % (self.citation_id, ),
+                })
+                el0 = SubElement(self.current_element, 'text:span')
+                el0.text = ']'
+            else:
+                self.current_element.text += ']'
+            self.set_to_parent()
+
+    def visit_generated(self, node):
+        pass
+
+    def depart_generated(self, node):
+        pass
+
+    def check_file_exists(self, path):
+        if os.path.exists(path):
+            return 1
+        else:
+            return 0
+
+    def visit_image(self, node):
+        # Capture the image file.
+        source = node['uri']
+        uri_parts = urllib.parse.urlparse(source)
+        if uri_parts.scheme in ('', 'file'):
+            source = urllib.parse.unquote(uri_parts.path)
+            if source.startswith('/'):
+                root_prefix = Path(self.settings.root_prefix)
+                source = (root_prefix/source[1:]).as_posix()
+            else:
+                # adapt relative paths
+                docsource, line = utils.get_source_line(node)
+                if docsource:
+                    dirname = os.path.dirname(docsource)
+                    if dirname:
+                        source = os.path.join(dirname, source)
+            if not self.check_file_exists(source):
+                self.document.reporter.warning(
+                    f'Cannot find image file "{source}".')
+                return
+        if source in self.image_dict:
+            filename, destination = self.image_dict[source]
+        else:
+            self.image_count += 1
+            filename = os.path.split(source)[1]
+            destination = 'Pictures/1%08x%s' % (self.image_count, filename)
+            if uri_parts.scheme in ('', 'file'):
+                spec = (os.path.abspath(source), destination,)
+            else:
+                try:
+                    with urllib.request.urlopen(source) as imgfile:
+                        content = imgfile.read()
+                except urllib.error.URLError as err:
+                    self.document.reporter.warning(
+                        f'Cannot open image URL "{source}". {err}')
+                    return
+                with tempfile.NamedTemporaryFile('wb',
+                                                 delete=False) as imgfile2:
+                    imgfile2.write(content)
+                source = imgfile2.name
+                spec = (source, destination,)
+            self.embedded_file_list.append(spec)
+            self.image_dict[source] = (source, destination,)
+        # Is this a figure (containing an image) or just a plain image?
+        if self.in_paragraph:
+            el1 = self.current_element
+        else:
+            el1 = SubElement(
+                self.current_element, 'text:p',
+                attrib={'text:style-name': self.rststyle('textbody')})
+        el2 = el1
+        if isinstance(node.parent, docutils.nodes.figure):
+            el3, el4, el5, caption = self.generate_figure(
+                node, source,
+                destination, el2)
+            attrib = {}
+            el6, width = self.generate_image(
+                node, source, destination,
+                el5, attrib)
+            if caption is not None:
+                el6.tail = caption
+        else:   # if isinstance(node.parent, docutils.nodes.image):
+            self.generate_image(node, source, destination, el2)
+
+    def depart_image(self, node):
+        pass
+
+    def get_image_width_height(self, node, attr):
+        size = None
+        unit = None
+        if attr in node.attributes:
+            size = node.attributes[attr]
+            size = size.strip()
+            # For conversion factors, see:
+            # http://www.unitconversion.org/unit_converter/typography-ex.html
+            try:
+                if size.endswith('%'):
+                    if attr == 'height':
+                        # Percentage allowed for width but not height.
+                        raise ValueError('percentage not allowed for height')
+                    size = size.rstrip(' %')
+                    size = float(size) / 100.0
+                    unit = '%'
+                else:
+                    size, unit = self.convert_to_cm(size)
+            except ValueError as exp:
+                self.document.reporter.warning(
+                    'Invalid %s for image: "%s".  '
+                    'Error: "%s".' % (
+                        attr, node.attributes[attr], exp))
+        return size, unit
+
+    def convert_to_cm(self, size):
+        """Convert various units to centimeters.
+
+        Note that a call to this method should be wrapped in:
+            try: except ValueError:
+        """
+        size = size.strip()
+        if size.endswith('px'):
+            size = float(size[:-2]) * 0.026     # convert px to cm
+        elif size.endswith('in'):
+            size = float(size[:-2]) * 2.54      # convert in to cm
+        elif size.endswith('pt'):
+            size = float(size[:-2]) * 0.035     # convert pt to cm
+        elif size.endswith('pc'):
+            size = float(size[:-2]) * 2.371     # convert pc to cm
+        elif size.endswith('mm'):
+            size = float(size[:-2]) * 0.1       # convert mm to cm
+        elif size.endswith('cm'):
+            size = float(size[:-2])
+        else:
+            raise ValueError('unknown unit type')
+        unit = 'cm'
+        return size, unit
+
+    def get_image_scale(self, node):
+        if 'scale' in node.attributes:
+            scale = node.attributes['scale']
+            try:
+                scale = int(scale)
+            except ValueError:
+                self.document.reporter.warning(
+                    'Invalid scale for image: "%s"' % (
+                        node.attributes['scale'], ))
+            if scale < 1:       # or scale > 100:
+                self.document.reporter.warning(
+                    'scale out of range (%s), using 1.' % (scale, ))
+                scale = 1
+            scale = scale * 0.01
+        else:
+            scale = 1.0
+        return scale
+
+    def get_image_scaled_width_height(self, node, source):
+        """Return the image size in centimeters adjusted by image attrs."""
+        scale = self.get_image_scale(node)
+        width, width_unit = self.get_image_width_height(node, 'width')
+        height, _ = self.get_image_width_height(node, 'height')
+        dpi = (72, 72)
+        if PIL is not None and source in self.image_dict:
+            filename, destination = self.image_dict[source]
+            with PIL.Image.open(filename, 'r') as img:
+                img_size = img.size
+                dpi = img.info.get('dpi', dpi)
+            # dpi information can be (xdpi, ydpi) or xydpi
+            try:
+                iter(dpi)
+            except TypeError:
+                dpi = (dpi, dpi)
+        else:
+            img_size = None
+        if width is None or height is None:
+            if img_size is None:
+                raise RuntimeError(
+                    'image size not fully specified and PIL not installed')
+            if width is None:
+                width = img_size[0]
+                width = float(width) * 0.026        # convert px to cm
+            if height is None:
+                height = img_size[1]
+                height = float(height) * 0.026      # convert px to cm
+            if width_unit == '%':
+                factor = width
+                image_width = img_size[0]
+                image_width = float(image_width) * 0.026    # convert px to cm
+                image_height = img_size[1]
+                image_height = float(image_height) * 0.026  # convert px to cm
+                line_width = self.get_page_width()
+                width = factor * line_width
+                factor = (factor * line_width) / image_width
+                height = factor * image_height
+        width *= scale
+        height *= scale
+        width = '%.2fcm' % width
+        height = '%.2fcm' % height
+        return width, height
+
+    def get_page_width(self):
+        """Return the document's page width in centimeters."""
+        root = self.get_dom_stylesheet()
+        nodes = root.iterfind(
+            './/{urn:oasis:names:tc:opendocument:xmlns:style:1.0}'
+            'page-layout/'
+            '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}'
+            'page-layout-properties')
+        width = None
+        for node in nodes:
+            page_width = node.get(
+                '{urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0}'
+                'page-width')
+            margin_left = node.get(
+                '{urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0}'
+                'margin-left')
+            margin_right = node.get(
+                '{urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0}'
+                'margin-right')
+            if (page_width is None
+                or margin_left is None
+                or margin_right is None):
+                continue
+            try:
+                page_width, _ = self.convert_to_cm(page_width)
+                margin_left, _ = self.convert_to_cm(margin_left)
+                margin_right, _ = self.convert_to_cm(margin_right)
+            except ValueError:
+                self.document.reporter.warning(
+                    'Stylesheet file contains invalid page width '
+                    'or margin size.')
+            width = page_width - margin_left - margin_right
+        if width is None:
+            # We can't find the width in styles, so we make a guess.
+            # Use a width of 6 in = 15.24 cm.
+            width = 15.24
+        return width
+
+    def generate_figure(self, node, source, destination, current_element):
+        caption = None
+        width, height = self.get_image_scaled_width_height(node, source)
+        for node1 in node.parent.children:
+            if node1.tagname == 'caption':
+                caption = node1.astext()
+        self.image_style_count += 1
+        #
+        # Add the style for the caption.
+        if caption is not None:
+            attrib = {
+                'style:class': 'extra',
+                'style:family': 'paragraph',
+                'style:name': 'Caption',
+                'style:parent-style-name': 'Standard',
+            }
+            el1 = SubElement(self.automatic_styles, 'style:style',
+                             attrib=attrib, nsdict=SNSD)
+            attrib = {
+                'fo:margin-bottom': '0.0835in',
+                'fo:margin-top': '0.0835in',
+                'text:line-number': '0',
+                'text:number-lines': 'false',
+            }
+            SubElement(el1, 'style:paragraph-properties',
+                       attrib=attrib, nsdict=SNSD)
+            attrib = {
+                'fo:font-size': '12pt',
+                'fo:font-style': 'italic',
+                'style:font-name': 'Times',
+                'style:font-name-complex': 'Lucidasans1',
+                'style:font-size-asian': '12pt',
+                'style:font-size-complex': '12pt',
+                'style:font-style-asian': 'italic',
+                'style:font-style-complex': 'italic',
+            }
+            SubElement(el1, 'style:text-properties',
+                       attrib=attrib, nsdict=SNSD)
+        style_name = 'rstframestyle%d' % self.image_style_count
+        draw_name = 'graphics%d' % next(IMAGE_NAME_COUNTER)
+        # Add the styles
+        attrib = {
+            'style:name': style_name,
+            'style:family': 'graphic',
+            'style:parent-style-name': self.rststyle('figureframe'),
+        }
+        el1 = SubElement(self.automatic_styles,
+                         'style:style', attrib=attrib, nsdict=SNSD)
+        attrib = {}
+        wrap = False
+        classes = node.parent.attributes.get('classes')
+        if classes and 'wrap' in classes:
+            wrap = True
+        if wrap:
+            attrib['style:wrap'] = 'dynamic'
+        else:
+            attrib['style:wrap'] = 'none'
+        SubElement(el1, 'style:graphic-properties',
+                   attrib=attrib, nsdict=SNSD)
+        attrib = {
+            'draw:style-name': style_name,
+            'draw:name': draw_name,
+            'text:anchor-type': 'paragraph',
+            'draw:z-index': '0',
+        }
+        attrib['svg:width'] = width
+        el3 = SubElement(current_element, 'draw:frame', attrib=attrib)
+        attrib = {}
+        el4 = SubElement(el3, 'draw:text-box', attrib=attrib)
+        attrib = {
+            'text:style-name': self.rststyle('caption'),
+        }
+        el5 = SubElement(el4, 'text:p', attrib=attrib)
+        return el3, el4, el5, caption
+
+    def generate_image(self, node, source, destination, current_element,
+                       frame_attrs=None):
+        width, height = self.get_image_scaled_width_height(node, source)
+        self.image_style_count += 1
+        style_name = 'rstframestyle%d' % self.image_style_count
+        # Add the style.
+        attrib = {
+            'style:name': style_name,
+            'style:family': 'graphic',
+            'style:parent-style-name': self.rststyle('image'),
+        }
+        el1 = SubElement(self.automatic_styles,
+                         'style:style', attrib=attrib, nsdict=SNSD)
+        halign = None
+        valign = None
+        if 'align' in node.attributes:
+            align = node.attributes['align'].split()
+            for val in align:
+                if val in ('left', 'center', 'right'):
+                    halign = val
+                elif val in ('top', 'middle', 'bottom'):
+                    valign = val
+        if frame_attrs is None:
+            attrib = {
+                'style:vertical-pos': 'top',
+                'style:vertical-rel': 'paragraph',
+                'style:horizontal-rel': 'paragraph',
+                'style:mirror': 'none',
+                'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
+                'draw:luminance': '0%',
+                'draw:contrast': '0%',
+                'draw:red': '0%',
+                'draw:green': '0%',
+                'draw:blue': '0%',
+                'draw:gamma': '100%',
+                'draw:color-inversion': 'false',
+                'draw:image-opacity': '100%',
+                'draw:color-mode': 'standard',
+            }
+        else:
+            attrib = frame_attrs
+        if halign is not None:
+            attrib['style:horizontal-pos'] = halign
+        if valign is not None:
+            attrib['style:vertical-pos'] = valign
+        # If there is a classes/wrap directive or we are
+        #   inside a table, add a no-wrap style.
+        wrap = False
+        classes = node.attributes.get('classes')
+        if classes and 'wrap' in classes:
+            wrap = True
+        if wrap:
+            attrib['style:wrap'] = 'dynamic'
+        else:
+            attrib['style:wrap'] = 'none'
+        # If we are inside a table, add a no-wrap style.
+        if self.is_in_table(node):
+            attrib['style:wrap'] = 'none'
+        SubElement(el1, 'style:graphic-properties',
+                   attrib=attrib, nsdict=SNSD)
+        draw_name = 'graphics%d' % next(IMAGE_NAME_COUNTER)
+        # Add the content.
+        # el = SubElement(current_element, 'text:p',
+        #     attrib={'text:style-name': self.rststyle('textbody')})
+        attrib = {
+            'draw:style-name': style_name,
+            'draw:name': draw_name,
+            'draw:z-index': '1',
+        }
+        if isinstance(node.parent, nodes.TextElement):
+            attrib['text:anchor-type'] = 'as-char'      # vds
+        else:
+            attrib['text:anchor-type'] = 'paragraph'
+        attrib['svg:width'] = width
+        attrib['svg:height'] = height
+        el1 = SubElement(current_element, 'draw:frame', attrib=attrib)
+        SubElement(el1, 'draw:image', attrib={
+            'xlink:href': '%s' % (destination, ),
+            'xlink:type': 'simple',
+            'xlink:show': 'embed',
+            'xlink:actuate': 'onLoad',
+        })
+        return el1, width
+
+    def is_in_table(self, node):
+        node1 = node.parent
+        while node1:
+            if isinstance(node1, docutils.nodes.entry):
+                return True
+            node1 = node1.parent
+        return False
+
+    def visit_legend(self, node):
+        if isinstance(node.parent, docutils.nodes.figure):
+            el1 = self.current_element[-1]
+            el1 = el1[0][0]
+            self.current_element = el1
+            self.paragraph_style_stack.append(self.rststyle('legend'))
+
+    def depart_legend(self, node):
+        if isinstance(node.parent, docutils.nodes.figure):
+            self.paragraph_style_stack.pop()
+            self.set_to_parent()
+            self.set_to_parent()
+            self.set_to_parent()
+
+    def visit_line_block(self, node):
+        self.line_indent_level += 1
+        self.line_block_level += 1
+
+    def depart_line_block(self, node):
+        self.line_indent_level -= 1
+        self.line_block_level -= 1
+
+    def visit_line(self, node):
+        style = 'lineblock%d' % self.line_indent_level
+        el1 = SubElement(self.current_element, 'text:p',
+                         attrib={'text:style-name': self.rststyle(style), })
+        self.current_element = el1
+
+    def depart_line(self, node):
+        self.set_to_parent()
+
+    def visit_literal(self, node):
+        el = SubElement(
+            self.current_element, 'text:span',
+            attrib={'text:style-name': self.rststyle('inlineliteral')})
+        self.set_current_element(el)
+
+    def depart_literal(self, node):
+        self.set_to_parent()
+
+    def visit_inline(self, node):
+        styles = node.attributes.get('classes', ())
+        if styles:
+            el = self.current_element
+            for inline_style in styles:
+                el = SubElement(el, 'text:span',
+                                attrib={'text:style-name':
+                                        self.rststyle(inline_style)})
+            count = len(styles)
+        else:
+            # No style was specified so use a default style (old code
+            # crashed if no style was given)
+            el = SubElement(self.current_element, 'text:span')
+            count = 1
+
+        self.set_current_element(el)
+        self.inline_style_count_stack.append(count)
+
+    def depart_inline(self, node):
+        count = self.inline_style_count_stack.pop()
+        for x in range(count):
+            self.set_to_parent()
+
+    def _calculate_code_block_padding(self, line):
+        count = 0
+        matchobj = SPACES_PATTERN.match(line)
+        if matchobj:
+            pad = matchobj.group()
+            count = len(pad)
+        else:
+            matchobj = TABS_PATTERN.match(line)
+            if matchobj:
+                pad = matchobj.group()
+                count = len(pad) * 8
+        return count
+
+    def _add_syntax_highlighting(self, insource, language):
+        lexer = pygments.lexers.get_lexer_by_name(language, stripall=True)
+        if language in ('latex', 'tex'):
+            fmtr = OdtPygmentsLaTeXFormatter(
+                lambda name, parameters=():
+                self.rststyle(name, parameters),
+                escape_function=escape_cdata)
+        else:
+            fmtr = OdtPygmentsProgFormatter(
+                lambda name, parameters=():
+                self.rststyle(name, parameters),
+                escape_function=escape_cdata)
+        return pygments.highlight(insource, lexer, fmtr)
+
+    def fill_line(self, line):
+        line = FILL_PAT1.sub(self.fill_func1, line)
+        return FILL_PAT2.sub(self.fill_func2, line)
+
+    def fill_func1(self, matchobj):
+        spaces = matchobj.group(0)
+        return '<text:s text:c="%d"/>' % (len(spaces), )
+
+    def fill_func2(self, matchobj):
+        spaces = matchobj.group(0)
+        return ' <text:s text:c="%d"/>' % (len(spaces) - 1, )
+
+    def visit_literal_block(self, node):
+        if len(self.paragraph_style_stack) > 1:
+            wrapper1 = '<text:p text:style-name="%s">%%s</text:p>' % (
+                self.rststyle('codeblock-indented'), )
+        else:
+            wrapper1 = '<text:p text:style-name="%s">%%s</text:p>' % (
+                self.rststyle('codeblock'), )
+        source = node.astext()
+        if (pygments and self.settings.add_syntax_highlighting):
+            language = node.get('language', 'python')
+            source = self._add_syntax_highlighting(source, language)
+        else:
+            source = escape_cdata(source)
+        lines = source.split('\n')
+        # If there is an empty last line, remove it.
+        if lines[-1] == '':
+            del lines[-1]
+        lines1 = ['<wrappertag1 xmlns:text="urn:oasis:names:tc:'
+                  'opendocument:xmlns:text:1.0">']
+        my_lines = []
+        for my_line in lines:
+            my_line = self.fill_line(my_line)
+            my_line = my_line.replace("&#10;", "\n")
+            my_lines.append(my_line)
+        my_lines_str = '<text:line-break/>'.join(my_lines)
+        my_lines_str2 = wrapper1 % (my_lines_str, )
+        lines1.append(my_lines_str2)
+        lines1.append('</wrappertag1>')
+        s1 = ''.join(lines1)
+        s1 = s1.encode("utf-8")
+        el1 = etree.fromstring(s1)
+        for child in el1:
+            self.current_element.append(child)
+
+    def depart_literal_block(self, node):
+        pass
+
+    visit_doctest_block = visit_literal_block
+    depart_doctest_block = depart_literal_block
+
+    # placeholder for math (see docs/dev/todo.txt)
+    def visit_math(self, node):
+        self.document.reporter.warning('"math" role not supported',
+                                       base_node=node)
+        self.visit_literal(node)
+
+    def depart_math(self, node):
+        self.depart_literal(node)
+
+    def visit_math_block(self, node):
+        self.document.reporter.warning('"math" directive not supported',
+                                       base_node=node)
+        self.visit_literal_block(node)
+
+    def depart_math_block(self, node):
+        self.depart_literal_block(node)
+
+    def visit_meta(self, node):
+        name = node.attributes.get('name')
+        content = node.attributes.get('content')
+        if name is not None and content is not None:
+            self.meta_dict[name] = content
+
+    def depart_meta(self, node):
+        pass
+
+    def visit_option_list(self, node):
+        table_name = 'tableoption'
+        #
+        # Generate automatic styles
+        if not self.optiontablestyles_generated:
+            self.optiontablestyles_generated = True
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle(table_name),
+                'style:family': 'table'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-properties', attrib={
+                'style:width': '17.59cm',
+                'table:align': 'left',
+                'style:shadow': 'none'}, nsdict=SNSD)
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle('%s.%%c' % table_name, ('A', )),
+                'style:family': 'table-column'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-column-properties', attrib={
+                'style:column-width': '4.999cm'}, nsdict=SNSD)
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle('%s.%%c' % table_name, ('B', )),
+                'style:family': 'table-column'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-column-properties', attrib={
+                'style:column-width': '12.587cm'}, nsdict=SNSD)
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle(
+                    '%s.%%c%%d' % table_name, ('A', 1, )),
+                'style:family': 'table-cell'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-cell-properties', attrib={
+                'fo:background-color': 'transparent',
+                'fo:padding': '0.097cm',
+                'fo:border-left': '0.035cm solid #000000',
+                'fo:border-right': 'none',
+                'fo:border-top': '0.035cm solid #000000',
+                'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD)
+            el2 = SubElement(el1, 'style:background-image', nsdict=SNSD)
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle(
+                    '%s.%%c%%d' % table_name, ('B', 1, )),
+                'style:family': 'table-cell'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-cell-properties', attrib={
+                'fo:padding': '0.097cm',
+                'fo:border': '0.035cm solid #000000'}, nsdict=SNSD)
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle(
+                    '%s.%%c%%d' % table_name, ('A', 2, )),
+                'style:family': 'table-cell'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-cell-properties', attrib={
+                'fo:padding': '0.097cm',
+                'fo:border-left': '0.035cm solid #000000',
+                'fo:border-right': 'none',
+                'fo:border-top': 'none',
+                'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD)
+            el = SubElement(self.automatic_styles, 'style:style', attrib={
+                'style:name': self.rststyle(
+                    '%s.%%c%%d' % table_name, ('B', 2, )),
+                'style:family': 'table-cell'}, nsdict=SNSD)
+            el1 = SubElement(el, 'style:table-cell-properties', attrib={
+                'fo:padding': '0.097cm',
+                'fo:border-left': '0.035cm solid #000000',
+                'fo:border-right': '0.035cm solid #000000',
+                'fo:border-top': 'none',
+                'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD)
+        #
+        # Generate table data
+        el = self.append_child('table:table', attrib={
+            'table:name': self.rststyle(table_name),
+            'table:style-name': self.rststyle(table_name),
+        })
+        el1 = SubElement(el, 'table:table-column', attrib={
+            'table:style-name': self.rststyle(
+                '%s.%%c' % table_name, ('A', ))})
+        el1 = SubElement(el, 'table:table-column', attrib={
+            'table:style-name': self.rststyle(
+                '%s.%%c' % table_name, ('B', ))})
+        el1 = SubElement(el, 'table:table-header-rows')
+        el2 = SubElement(el1, 'table:table-row')
+        el3 = SubElement(el2, 'table:table-cell', attrib={
+            'table:style-name': self.rststyle(
+                '%s.%%c%%d' % table_name, ('A', 1, )),
+            'office:value-type': 'string'})
+        el4 = SubElement(el3, 'text:p', attrib={
+            'text:style-name': 'Table_20_Heading'})
+        el4.text = 'Option'
+        el3 = SubElement(el2, 'table:table-cell', attrib={
+            'table:style-name': self.rststyle(
+                '%s.%%c%%d' % table_name, ('B', 1, )),
+            'office:value-type': 'string'})
+        el4 = SubElement(el3, 'text:p', attrib={
+            'text:style-name': 'Table_20_Heading'})
+        el4.text = 'Description'
+        self.set_current_element(el)
+
+    def depart_option_list(self, node):
+        self.set_to_parent()
+
+    def visit_option_list_item(self, node):
+        el = self.append_child('table:table-row')
+        self.set_current_element(el)
+
+    def depart_option_list_item(self, node):
+        self.set_to_parent()
+
+    def visit_option_group(self, node):
+        el = self.append_child('table:table-cell', attrib={
+            'table:style-name': 'Table%d.A2' % self.table_count,
+            'office:value-type': 'string',
+        })
+        self.set_current_element(el)
+
+    def depart_option_group(self, node):
+        self.set_to_parent()
+
+    def visit_option(self, node):
+        el = self.append_child('text:p', attrib={
+            'text:style-name': 'Table_20_Contents'})
+        el.text = node.astext()
+
+    def depart_option(self, node):
+        pass
+
+    def visit_option_string(self, node):
+        pass
+
+    def depart_option_string(self, node):
+        pass
+
+    def visit_option_argument(self, node):
+        pass
+
+    def depart_option_argument(self, node):
+        pass
+
+    def visit_description(self, node):
+        el = self.append_child('table:table-cell', attrib={
+            'table:style-name': 'Table%d.B2' % self.table_count,
+            'office:value-type': 'string',
+        })
+        el1 = SubElement(el, 'text:p', attrib={
+            'text:style-name': 'Table_20_Contents'})
+        el1.text = node.astext()
+        raise nodes.SkipChildren()
+
+    def depart_description(self, node):
+        pass
+
+    def visit_paragraph(self, node):
+        self.in_paragraph = True
+        if self.in_header:
+            el = self.append_p('header')
+        elif self.in_footer:
+            el = self.append_p('footer')
+        else:
+            style_name = self.paragraph_style_stack[-1]
+            el = self.append_child(
+                'text:p',
+                attrib={'text:style-name': style_name})
+            self.append_pending_ids(el)
+        self.set_current_element(el)
+
+    def depart_paragraph(self, node):
+        self.in_paragraph = False
+        self.set_to_parent()
+        if self.in_header:
+            self.header_content.append(self.current_element[-1])
+            self.current_element.remove(self.current_element[-1])
+        elif self.in_footer:
+            self.footer_content.append(self.current_element[-1])
+            self.current_element.remove(self.current_element[-1])
+
+    def visit_problematic(self, node):
+        pass
+
+    def depart_problematic(self, node):
+        pass
+
+    def visit_raw(self, node):
+        if 'format' in node.attributes:
+            formats = node.attributes['format']
+            formatlist = formats.split()
+            if 'odt' in formatlist:
+                rawstr = node.astext()
+                attrstr = ' '.join(
+                    '%s="%s"' % (k, v, )
+                    for k, v in list(CONTENT_NAMESPACE_ATTRIB.items()))
+                contentstr = '<stuff %s>%s</stuff>' % (attrstr, rawstr, )
+                contentstr = contentstr.encode("utf-8")
+                content = etree.fromstring(contentstr)
+                if len(content) > 0:
+                    el1 = content[0]
+                    if self.in_header:
+                        pass
+                    elif self.in_footer:
+                        pass
+                    else:
+                        self.current_element.append(el1)
+        raise nodes.SkipChildren()
+
+    def depart_raw(self, node):
+        if self.in_header:
+            pass
+        elif self.in_footer:
+            pass
+        else:
+            pass
+
+    def visit_reference(self, node):
+        # text = node.astext()
+        if self.settings.create_links:
+            if 'refuri' in node:
+                href = node['refuri']
+                if (self.settings.cloak_email_addresses
+                    and href.startswith('mailto:')):
+                    href = self.cloak_mailto(href)
+                el = self.append_child('text:a', attrib={
+                    'xlink:href': '%s' % href,
+                    'xlink:type': 'simple',
+                })
+                self.set_current_element(el)
+            elif 'refid' in node:
+                if self.settings.create_links:
+                    href = node['refid']
+                    el = self.append_child('text:reference-ref', attrib={
+                        'text:ref-name': '%s' % href,
+                        'text:reference-format': 'text',
+                    })
+            else:
+                self.document.reporter.warning(
+                    'References must have "refuri" or "refid" attribute.')
+        if (self.in_table_of_contents
+            and len(node.children) >= 1
+            and isinstance(node.children[0], docutils.nodes.generated)):
+            node.remove(node.children[0])
+
+    def depart_reference(self, node):
+        if self.settings.create_links:
+            if 'refuri' in node:
+                self.set_to_parent()
+
+    def visit_rubric(self, node):
+        style_name = self.rststyle('rubric')
+        classes = node.get('classes')
+        if classes:
+            class1 = classes[0]
+            if class1:
+                style_name = class1
+        el = SubElement(self.current_element, 'text:h', attrib={
+            # 'text:outline-level': '%d' % section_level,
+            # 'text:style-name': 'Heading_20_%d' % section_level,
+            'text:style-name': style_name,
+        })
+        text = node.astext()
+        el.text = self.encode(text)
+
+    def depart_rubric(self, node):
+        pass
+
+    def visit_section(self, node, move_ids=1):
+        self.section_level += 1
+        self.section_count += 1
+        if self.settings.create_sections:
+            el = self.append_child('text:section', attrib={
+                'text:name': 'Section%d' % self.section_count,
+                'text:style-name': 'Sect%d' % self.section_level,
+            })
+            self.set_current_element(el)
+
+    def depart_section(self, node):
+        self.section_level -= 1
+        if self.settings.create_sections:
+            self.set_to_parent()
+
+    def visit_strong(self, node):
+        el = SubElement(self.current_element, 'text:span',
+                        attrib={'text:style-name': self.rststyle('strong')})
+        self.set_current_element(el)
+
+    def depart_strong(self, node):
+        self.set_to_parent()
+
+    def visit_substitution_definition(self, node):
+        raise nodes.SkipChildren()
+
+    def depart_substitution_definition(self, node):
+        pass
+
+    def visit_system_message(self, node):
+        pass
+
+    def depart_system_message(self, node):
+        pass
+
+    def get_table_style(self, node):
+        table_style = None
+        table_name = None
+        str_classes = node.get('classes')
+        if str_classes is not None:
+            for str_class in str_classes:
+                if str_class.startswith(TABLESTYLEPREFIX):
+                    table_name = str_class
+                    break
+        if table_name is not None:
+            table_style = self.table_styles.get(table_name)
+            if table_style is None:
+                # If we can't find the table style, issue warning
+                #   and use the default table style.
+                self.document.reporter.warning(
+                    'Can\'t find table style "%s".  Using default.' % (
+                        table_name, ))
+                table_name = TABLENAMEDEFAULT
+                table_style = self.table_styles.get(table_name)
+                if table_style is None:
+                    # If we can't find the default table style, issue a warning
+                    #   and use a built-in default style.
+                    self.document.reporter.warning(
+                        'Can\'t find default table style "%s".  '
+                        'Using built-in default.' % (
+                            table_name, ))
+                    table_style = BUILTIN_DEFAULT_TABLE_STYLE
+        else:
+            table_name = TABLENAMEDEFAULT
+            table_style = self.table_styles.get(table_name)
+            if table_style is None:
+                # If we can't find the default table style, issue a warning
+                #   and use a built-in default style.
+                self.document.reporter.warning(
+                    'Can\'t find default table style "%s".  '
+                    'Using built-in default.' % (
+                        table_name, ))
+                table_style = BUILTIN_DEFAULT_TABLE_STYLE
+        return table_style
+
+    def visit_table(self, node):
+        self.table_count += 1
+        table_style = self.get_table_style(node)
+        table_name = '%s%%d' % TABLESTYLEPREFIX
+        el1 = SubElement(self.automatic_styles, 'style:style', attrib={
+            'style:name': self.rststyle(
+                '%s' % table_name, (self.table_count, )),
+            'style:family': 'table',
+        }, nsdict=SNSD)
+        if table_style.backgroundcolor is None:
+            SubElement(el1, 'style:table-properties', attrib={
+                # 'style:width': '17.59cm',
+                # 'table:align': 'margins',
+                'table:align': 'left',
+                'fo:margin-top': '0in',
+                'fo:margin-bottom': '0.10in',
+            }, nsdict=SNSD)
+        else:
+            SubElement(el1, 'style:table-properties', attrib={
+                # 'style:width': '17.59cm',
+                'table:align': 'margins',
+                'fo:margin-top': '0in',
+                'fo:margin-bottom': '0.10in',
+                'fo:background-color': table_style.backgroundcolor,
+            }, nsdict=SNSD)
+        # We use a single cell style for all cells in this table.
+        # That's probably not correct, but seems to work.
+        el2 = SubElement(self.automatic_styles, 'style:style', attrib={
+            'style:name': self.rststyle(
+                '%s.%%c%%d' % table_name, (self.table_count, 'A', 1, )),
+            'style:family': 'table-cell',
+        }, nsdict=SNSD)
+        thickness = self.settings.table_border_thickness
+        if thickness is None:
+            line_style1 = table_style.border
+        else:
+            line_style1 = '0.%03dcm solid #000000' % (thickness, )
+        SubElement(el2, 'style:table-cell-properties', attrib={
+            'fo:padding': '0.049cm',
+            'fo:border-left': line_style1,
+            'fo:border-right': line_style1,
+            'fo:border-top': line_style1,
+            'fo:border-bottom': line_style1,
+        }, nsdict=SNSD)
+        title = None
+        for child in node.children:
+            if child.tagname == 'title':
+                title = child.astext()
+                break
+        if title is not None:
+            self.append_p('table-title', title)
+        else:
+            pass
+        el4 = SubElement(self.current_element, 'table:table', attrib={
+            'table:name': self.rststyle(
+                '%s' % table_name, (self.table_count, )),
+            'table:style-name': self.rststyle(
+                '%s' % table_name, (self.table_count, )),
+        })
+        self.set_current_element(el4)
+        self.current_table_style = el1
+        self.table_width = 0.0
+
+    def depart_table(self, node):
+        attribkey = add_ns('style:width', nsdict=SNSD)
+        attribval = '%.4fin' % (self.table_width, )
+        el1 = self.current_table_style
+        el2 = el1[0]
+        el2.attrib[attribkey] = attribval
+        self.set_to_parent()
+
+    def visit_tgroup(self, node):
+        self.column_count = ord('A') - 1
+
+    def depart_tgroup(self, node):
+        pass
+
+    def visit_colspec(self, node):
+        self.column_count += 1
+        colspec_name = self.rststyle(
+            '%s%%d.%%s' % TABLESTYLEPREFIX,
+            (self.table_count, chr(self.column_count), )
+        )
+        colwidth = node['colwidth'] / 12.0
+        el1 = SubElement(self.automatic_styles, 'style:style', attrib={
+            'style:name': colspec_name,
+            'style:family': 'table-column',
+        }, nsdict=SNSD)
+        SubElement(el1, 'style:table-column-properties',
+                   attrib={'style:column-width': '%.4fin' % colwidth},
+                   nsdict=SNSD)
+        self.append_child('table:table-column',
+                          attrib={'table:style-name': colspec_name, })
+        self.table_width += colwidth
+
+    def depart_colspec(self, node):
+        pass
+
+    def visit_thead(self, node):
+        el = self.append_child('table:table-header-rows')
+        self.set_current_element(el)
+        self.in_thead = True
+        self.paragraph_style_stack.append('Table_20_Heading')
+
+    def depart_thead(self, node):
+        self.set_to_parent()
+        self.in_thead = False
+        self.paragraph_style_stack.pop()
+
+    def visit_row(self, node):
+        self.column_count = ord('A') - 1
+        el = self.append_child('table:table-row')
+        self.set_current_element(el)
+
+    def depart_row(self, node):
+        self.set_to_parent()
+
+    def visit_entry(self, node):
+        self.column_count += 1
+        cellspec_name = self.rststyle(
+            '%s%%d.%%c%%d' % TABLESTYLEPREFIX,
+            (self.table_count, 'A', 1, )
+        )
+        attrib = {
+            'table:style-name': cellspec_name,
+            'office:value-type': 'string',
+        }
+        morecols = node.get('morecols', 0)
+        if morecols > 0:
+            attrib['table:number-columns-spanned'] = '%d' % (morecols + 1,)
+            self.column_count += morecols
+        morerows = node.get('morerows', 0)
+        if morerows > 0:
+            attrib['table:number-rows-spanned'] = '%d' % (morerows + 1,)
+        el1 = self.append_child('table:table-cell', attrib=attrib)
+        self.set_current_element(el1)
+
+    def depart_entry(self, node):
+        self.set_to_parent()
+
+    def visit_tbody(self, node):
+        pass
+
+    def depart_tbody(self, node):
+        pass
+
+    def visit_target(self, node):
+        #
+        # I don't know how to implement targets in ODF.
+        # How do we create a target in oowriter?  A cross-reference?
+        if ('refuri' not in node
+                and 'refid' not in node
+                and 'refname' not in node):
+            pass
+        else:
+            pass
+
+    def depart_target(self, node):
+        pass
+
+    def visit_title(self, node, move_ids=1, title_type='title'):
+        if isinstance(node.parent, docutils.nodes.section):
+            section_level = self.section_level
+            if section_level > 7:
+                self.document.reporter.warning(
+                    'Heading/section levels greater than 7 not supported.')
+                self.document.reporter.warning(
+                    '    Reducing to heading level 7 for heading: "%s"' % (
+                        node.astext(), ))
+                section_level = 7
+            el1 = self.append_child(
+                'text:h', attrib={
+                    'text:outline-level': '%d' % section_level,
+                    # 'text:style-name': 'Heading_20_%d' % section_level,
+                    'text:style-name': self.rststyle(
+                        'heading%d', (section_level, )),
+                })
+            self.append_pending_ids(el1)
+            self.set_current_element(el1)
+        elif isinstance(node.parent, docutils.nodes.document):
+            # text = self.settings.title
+            # else:
+            #     text = node.astext()
+            el1 = SubElement(self.current_element, 'text:p', attrib={
+                'text:style-name': self.rststyle(title_type),
+            })
+            self.append_pending_ids(el1)
+            text = node.astext()
+            self.title = text
+            self.found_doc_title = True
+            self.set_current_element(el1)
+
+    def depart_title(self, node):
+        if (isinstance(node.parent, docutils.nodes.section)
+            or isinstance(node.parent, docutils.nodes.document)):
+            self.set_to_parent()
+
+    def visit_subtitle(self, node, move_ids=1):
+        self.visit_title(node, move_ids, title_type='subtitle')
+
+    def depart_subtitle(self, node):
+        self.depart_title(node)
+
+    def visit_title_reference(self, node):
+        el = self.append_child('text:span', attrib={
+            'text:style-name': self.rststyle('quotation')})
+        el.text = self.encode(node.astext())
+        raise nodes.SkipChildren()
+
+    def depart_title_reference(self, node):
+        pass
+
+    def generate_table_of_content_entry_template(self, el1):
+        for idx in range(1, 11):
+            el2 = SubElement(
+                el1,
+                'text:table-of-content-entry-template',
+                attrib={
+                    'text:outline-level': "%d" % (idx, ),
+                    'text:style-name': self.rststyle('contents-%d' % (idx, )),
+                })
+            SubElement(el2, 'text:index-entry-chapter')
+            SubElement(el2, 'text:index-entry-text')
+            SubElement(el2, 'text:index-entry-tab-stop', attrib={
+                'style:leader-char': ".",
+                'style:type': "right",
+            })
+            SubElement(el2, 'text:index-entry-page-number')
+
+    def find_title_label(self, node, class_type, label_key):
+        label = ''
+        title_node = None
+        for child in node.children:
+            if isinstance(child, class_type):
+                title_node = child
+                break
+        if title_node is not None:
+            label = title_node.astext()
+        else:
+            label = self.language.labels[label_key]
+        return label
+
+    def visit_topic(self, node):
+        if 'classes' in node.attributes:
+            if 'contents' in node.attributes['classes']:
+                label = self.find_title_label(
+                    node, docutils.nodes.title, 'contents')
+                if self.settings.generate_oowriter_toc:
+                    el1 = self.append_child('text:table-of-content', attrib={
+                        'text:name': 'Table of Contents1',
+                        'text:protected': 'true',
+                        'text:style-name': 'Sect1',
+                    })
+                    el2 = SubElement(
+                        el1,
+                        'text:table-of-content-source',
+                        attrib={
+                            'text:outline-level': '10',
+                        })
+                    el3 = SubElement(el2, 'text:index-title-template', attrib={
+                        'text:style-name': 'Contents_20_Heading',
+                    })
+                    el3.text = label
+                    self.generate_table_of_content_entry_template(el2)
+                    el4 = SubElement(el1, 'text:index-body')
+                    el5 = SubElement(el4, 'text:index-title')
+                    el6 = SubElement(el5, 'text:p', attrib={
+                        'text:style-name': self.rststyle('contents-heading'),
+                    })
+                    el6.text = label
+                    self.save_current_element = self.current_element
+                    self.table_of_content_index_body = el4
+                    self.set_current_element(el4)
+                else:
+                    el = self.append_p('horizontalline')
+                    el = self.append_p('centeredtextbody')
+                    el1 = SubElement(
+                        el, 'text:span',
+                        attrib={'text:style-name': self.rststyle('strong')})
+                    el1.text = label
+                self.in_table_of_contents = True
+            elif 'abstract' in node.attributes['classes']:
+                el = self.append_p('horizontalline')
+                el = self.append_p('centeredtextbody')
+                el1 = SubElement(
+                    el, 'text:span',
+                    attrib={'text:style-name': self.rststyle('strong')})
+                label = self.find_title_label(
+                    node, docutils.nodes.title,
+                    'abstract')
+                el1.text = label
+            elif 'dedication' in node.attributes['classes']:
+                el = self.append_p('horizontalline')
+                el = self.append_p('centeredtextbody')
+                el1 = SubElement(
+                    el, 'text:span',
+                    attrib={'text:style-name': self.rststyle('strong')})
+                label = self.find_title_label(
+                    node, docutils.nodes.title,
+                    'dedication')
+                el1.text = label
+
+    def depart_topic(self, node):
+        if 'classes' in node.attributes:
+            if 'contents' in node.attributes['classes']:
+                if self.settings.generate_oowriter_toc:
+                    self.update_toc_page_numbers(
+                        self.table_of_content_index_body)
+                    self.set_current_element(self.save_current_element)
+                else:
+                    self.append_p('horizontalline')
+                self.in_table_of_contents = False
+
+    def update_toc_page_numbers(self, el):
+        collection = []
+        self.update_toc_collect(el, 0, collection)
+        self.update_toc_add_numbers(collection)
+
+    def update_toc_collect(self, el, level, collection):
+        collection.append((level, el))
+        level += 1
+        for child_el in el:
+            if child_el.tag != 'text:index-body':
+                self.update_toc_collect(child_el, level, collection)
+
+    def update_toc_add_numbers(self, collection):
+        for level, el1 in collection:
+            if (el1.tag == 'text:p'
+                and el1.text != 'Table of Contents'):
+                el2 = SubElement(el1, 'text:tab')
+                el2.tail = '9999'
+
+    def visit_transition(self, node):
+        self.append_p('horizontalline')
+
+    def depart_transition(self, node):
+        pass
+
+    #
+    # Admonitions
+    #
+    def visit_warning(self, node):
+        self.generate_admonition(node, 'warning')
+
+    def depart_warning(self, node):
+        self.paragraph_style_stack.pop()
+
+    def visit_attention(self, node):
+        self.generate_admonition(node, 'attention')
+
+    depart_attention = depart_warning
+
+    def visit_caution(self, node):
+        self.generate_admonition(node, 'caution')
+
+    depart_caution = depart_warning
+
+    def visit_danger(self, node):
+        self.generate_admonition(node, 'danger')
+
+    depart_danger = depart_warning
+
+    def visit_error(self, node):
+        self.generate_admonition(node, 'error')
+
+    depart_error = depart_warning
+
+    def visit_hint(self, node):
+        self.generate_admonition(node, 'hint')
+
+    depart_hint = depart_warning
+
+    def visit_important(self, node):
+        self.generate_admonition(node, 'important')
+
+    depart_important = depart_warning
+
+    def visit_note(self, node):
+        self.generate_admonition(node, 'note')
+
+    depart_note = depart_warning
+
+    def visit_tip(self, node):
+        self.generate_admonition(node, 'tip')
+
+    depart_tip = depart_warning
+
+    def visit_admonition(self, node):
+        title = None
+        for child in node.children:
+            if child.tagname == 'title':
+                title = child.astext()
+        if title is None:
+            classes1 = node.get('classes')
+            if classes1:
+                title = classes1[0]
+        self.generate_admonition(node, 'generic', title)
+
+    depart_admonition = depart_warning
+
+    def generate_admonition(self, node, label, title=None):
+        if hasattr(self.language, 'labels'):
+            translated_label = self.language.labels.get(label, label)
+        else:
+            translated_label = label
+        el1 = SubElement(self.current_element, 'text:p', attrib={
+            'text:style-name': self.rststyle(
+                'admon-%s-hdr', (label, )),
+        })
+        if title:
+            el1.text = title
+        else:
+            el1.text = '%s!' % (translated_label.capitalize(), )
+        s1 = self.rststyle('admon-%s-body', (label, ))
+        self.paragraph_style_stack.append(s1)
+
+    #
+    # Roles (e.g. subscript, superscript, strong, ...
+    #
+    def visit_subscript(self, node):
+        el = self.append_child('text:span', attrib={
+            'text:style-name': 'rststyle-subscript',
+        })
+        self.set_current_element(el)
+
+    def depart_subscript(self, node):
+        self.set_to_parent()
+
+    def visit_superscript(self, node):
+        el = self.append_child('text:span', attrib={
+            'text:style-name': 'rststyle-superscript',
+        })
+        self.set_current_element(el)
+
+    def depart_superscript(self, node):
+        self.set_to_parent()
+
+    def visit_abbreviation(self, node):
+        pass
+
+    def depart_abbreviation(self, node):
+        pass
+
+    def visit_acronym(self, node):
+        pass
+
+    def depart_acronym(self, node):
+        pass
+
+    def visit_sidebar(self, node):
+        pass
+
+    def depart_sidebar(self, node):
+        pass
+
+
+# Use an own reader to modify transformations done.
+class Reader(standalone.Reader):
+
+    def get_transforms(self):
+        transforms = super().get_transforms()
+        if not self.settings.create_links:
+            transforms.remove(references.DanglingReferences)
+        return transforms
diff --git a/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/prepstyles.py b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/prepstyles.py
new file mode 100755
index 00000000..b59490f2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/prepstyles.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+
+# $Id: prepstyles.py 9386 2023-05-16 14:49:31Z milde $
+# Author: Dave Kuhlman <dkuhlman@rexx.com>
+# Copyright: This module has been placed in the public domain.
+
+"""
+Adapt a word-processor-generated styles.odt for odtwriter use:
+
+Drop page size specifications from styles.xml in STYLE_FILE.odt.
+See https://docutils.sourceforge.io/docs/user/odt.html#page-size
+"""
+
+# Author: Michael Schutte <michi@uiae.at>
+
+from xml.etree import ElementTree as etree
+
+import sys
+import zipfile
+from tempfile import mkstemp
+import shutil
+import os
+
+NAMESPACES = {
+    "style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
+    "fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
+}
+
+
+def prepstyle(filename):
+
+    zin = zipfile.ZipFile(filename)
+    styles = zin.open("styles.xml")
+
+    root = None
+    # some extra effort to preserve namespace prefixes
+    for event, elem in etree.iterparse(styles, events=("start", "start-ns")):
+        if event == "start-ns":
+            etree.register_namespace(elem[0], elem[1])
+        elif event == "start":
+            if root is None:
+                root = elem
+
+    styles.close()
+
+    for el in root.findall(".//style:page-layout-properties",
+                           namespaces=NAMESPACES):
+        for attr in list(el.attrib):
+            if attr.startswith("{%s}" % NAMESPACES["fo"]):
+                del el.attrib[attr]
+
+    tempname = mkstemp()
+    zout = zipfile.ZipFile(os.fdopen(tempname[0], "wb"), "w",
+                           zipfile.ZIP_DEFLATED)
+
+    for item in zin.infolist():
+        if item.filename == "styles.xml":
+            zout.writestr(item, etree.tostring(root, encoding="UTF-8"))
+        else:
+            zout.writestr(item, zin.read(item.filename))
+
+    zout.close()
+    zin.close()
+    shutil.move(tempname[1], filename)
+
+
+def main():
+    args = sys.argv[1:]
+    if len(args) != 1 or args[0] in ('-h', '--help'):
+        print(__doc__, file=sys.stderr)
+        print("Usage: %s STYLE_FILE.odt\n" % sys.argv[0], file=sys.stderr)
+        sys.exit(1)
+    filename = args[0]
+    prepstyle(filename)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/pygmentsformatter.py b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/pygmentsformatter.py
new file mode 100644
index 00000000..7880651b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/pygmentsformatter.py
@@ -0,0 +1,109 @@
+# $Id: pygmentsformatter.py 9015 2022-03-03 22:15:00Z milde $
+# Author: Dave Kuhlman <dkuhlman@rexx.com>
+# Copyright: This module has been placed in the public domain.
+
+"""
+
+Additional support for Pygments formatter.
+
+"""
+
+
+import pygments
+import pygments.formatter
+
+
+class OdtPygmentsFormatter(pygments.formatter.Formatter):
+    def __init__(self, rststyle_function, escape_function):
+        pygments.formatter.Formatter.__init__(self)
+        self.rststyle_function = rststyle_function
+        self.escape_function = escape_function
+
+    def rststyle(self, name, parameters=()):
+        return self.rststyle_function(name, parameters)
+
+
+class OdtPygmentsProgFormatter(OdtPygmentsFormatter):
+    def format(self, tokensource, outfile):
+        tokenclass = pygments.token.Token
+        for ttype, value in tokensource:
+            value = self.escape_function(value)
+            if ttype == tokenclass.Keyword:
+                s2 = self.rststyle('codeblock-keyword')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Literal.String:
+                s2 = self.rststyle('codeblock-string')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype in (
+                    tokenclass.Literal.Number.Integer,
+                    tokenclass.Literal.Number.Integer.Long,
+                    tokenclass.Literal.Number.Float,
+                    tokenclass.Literal.Number.Hex,
+                    tokenclass.Literal.Number.Oct,
+                    tokenclass.Literal.Number,
+                    ):
+                s2 = self.rststyle('codeblock-number')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Operator:
+                s2 = self.rststyle('codeblock-operator')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Comment:
+                s2 = self.rststyle('codeblock-comment')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Name.Class:
+                s2 = self.rststyle('codeblock-classname')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Name.Function:
+                s2 = self.rststyle('codeblock-functionname')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Name:
+                s2 = self.rststyle('codeblock-name')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            else:
+                s1 = value
+            outfile.write(s1)
+
+
+class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter):
+    def format(self, tokensource, outfile):
+        tokenclass = pygments.token.Token
+        for ttype, value in tokensource:
+            value = self.escape_function(value)
+            if ttype == tokenclass.Keyword:
+                s2 = self.rststyle('codeblock-keyword')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype in (tokenclass.Literal.String,
+                           tokenclass.Literal.String.Backtick,
+                           ):
+                s2 = self.rststyle('codeblock-string')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Name.Attribute:
+                s2 = self.rststyle('codeblock-operator')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            elif ttype == tokenclass.Comment:
+                if value[-1] == '\n':
+                    s2 = self.rststyle('codeblock-comment')
+                    s1 = '<text:span text:style-name="%s">%s</text:span>\n' % \
+                        (s2, value[:-1], )
+                else:
+                    s2 = self.rststyle('codeblock-comment')
+                    s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                        (s2, value, )
+            elif ttype == tokenclass.Name.Builtin:
+                s2 = self.rststyle('codeblock-name')
+                s1 = '<text:span text:style-name="%s">%s</text:span>' % \
+                    (s2, value, )
+            else:
+                s1 = value
+            outfile.write(s1)
diff --git a/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/styles.odt b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/styles.odt
new file mode 100644
index 00000000..e17b0072
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docutils/writers/odf_odt/styles.odt
Binary files differ