~ubuntu-branches/ubuntu/saucy/python-docutils/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/move-data-to-usr-share.diff/docutils/writers/odf_odt/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Jakub Wilk
  • Date: 2011-08-07 23:24:56 UTC
  • mfrom: (11.1.2 experimental)
  • Revision ID: james.westby@ubuntu.com-20110807232456-ikquearlmix8hkp1
Tags: 0.7-4
Upload to unstable.

Show diffs side-by-side

added added

removed removed

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