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.
6
Open Document Format (ODF) Writer.
12
__docformat__ = 'reStructuredText'
20
from xml.dom import minidom
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
37
#from lxml import etree
38
#WhichElementTree = 'lxml'
39
raise ImportError('Ignoring lxml')
40
except ImportError, e:
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:
47
# 3. Try to use a version of ElementTree installed as a separate
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.'
57
# Import pygments and odtwriter pygments formatters if possible.
60
import pygments.lexers
61
from pygmentsformatter import OdtPygmentsProgFormatter, \
62
OdtPygmentsLaTeXFormatter
63
except ImportError, exp:
67
# Is the PIL imaging library installed?
70
except ImportError, exp:
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.')
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.
88
if WhichElementTree == 'elementtree':
89
class _ElementInterfaceWrapper(etree._ElementInterface):
90
def __init__(self, tag, attrib=None):
91
etree._ElementInterface.__init__(self, tag, attrib)
95
def setparent(self, parent):
102
# Constants and globals
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,}')
109
TableStylePrefix = 'Table'
111
GENERATOR_DESC = 'Docutils.org/odf_odt'
113
NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
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',
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',
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',
169
MANIFEST_NAMESPACE_DICT = MANNSD = {
170
'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
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',
183
# Attribute dictionaries for use with ElementTree (not lxml), which
184
# does not support use of nsmap parameter on Element() and SubElement().
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',
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',
239
MANIFEST_NAMESPACE_ATTRIB = {
240
'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
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',
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.
262
def Element(tag, attrib=None, nsmap=None, nsdict=CNSD):
265
tag, attrib = fix_ns(tag, attrib, nsdict)
266
if WhichElementTree == 'lxml':
267
el = etree.Element(tag, attrib, nsmap=nsmap)
269
el = _ElementInterfaceWrapper(tag, attrib)
272
def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD):
275
tag, attrib = fix_ns(tag, attrib, nsdict)
276
if WhichElementTree == 'lxml':
277
el = etree.SubElement(parent, tag, attrib, nsmap=nsmap)
279
el = _ElementInterfaceWrapper(tag, attrib)
284
def fix_ns(tag, attrib, nsdict):
285
nstag = add_ns(tag, nsdict)
287
for key, val in attrib.iteritems():
288
nskey = add_ns(key, nsdict)
289
nsattrib[nskey] = val
290
return nstag, nsattrib
292
def add_ns(tag, nsdict=CNSD):
293
if WhichElementTree == 'lxml':
294
nstag, name = tag.split(':')
295
ns = nsdict.get(nstag)
297
raise RuntimeError, 'Invalid namespace prefix: %s' % nstag
298
tag = '{%s}%s' % (ns, name,)
302
outstream = StringIO.StringIO()
303
if sys.version_info >= (3, 2):
304
et.write(outstream, encoding="unicode")
307
s1 = outstream.getvalue()
312
def escape_cdata(text):
313
text = text.replace("&", "&")
314
text = text.replace("<", "<")
315
text = text.replace(">", ">")
318
if ord(char) >= ord("\x7f"):
319
ascii += "&#x%X;" % ( ord(char), )
331
WORD_SPLIT_PAT1 = re.compile(r'\b(\w*)\b\W*')
333
def split_words(line):
334
# We need whitespace at the end of the string for our regexpr.
338
mo = WORD_SPLIT_PAT1.search(line, pos1)
339
while mo is not None:
340
word = mo.groups()[0]
343
mo = WORD_SPLIT_PAT1.search(line, pos1)
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):
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
363
class Writer(writers.Writer):
365
MIME_TYPE = 'application/vnd.oasis.opendocument.text'
368
supported = ('odt', )
369
"""Formats this writer supports."""
371
default_stylesheet = 'styles' + EXTENSION
373
default_stylesheet_path = utils.relative_path(
374
os.path.join(os.getcwd(), 'dummy'),
375
os.path.join(os.path.dirname(__file__), default_stylesheet))
377
default_template = 'template.txt'
379
default_template_path = utils.relative_path(
380
os.path.join(os.getcwd(), 'dummy'),
381
os.path.join(os.path.dirname(__file__), default_template))
384
'ODF-Specific Options',
387
('Specify a stylesheet. '
388
'Default: "%s"' % default_stylesheet_path,
391
'default': default_stylesheet_path,
394
('Specify a configuration/mapping file relative to the '
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'],
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'],
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. '
420
['--table-border-thickness'],
422
'validator': frontend.validate_nonnegative_int}),
423
('Add syntax highlighting in literal code blocks.',
424
['--add-syntax-highlighting'],
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'],
432
'action': 'store_false',
433
'dest': 'add_syntax_highlighting',
434
'validator': frontend.validate_boolean}),
435
('Create sections for headers. (default)',
436
['--create-sections'],
438
'action': 'store_true',
439
'dest': 'create_sections',
440
'validator': frontend.validate_boolean}),
441
('Do not create sections for headers.',
444
'action': 'store_false',
445
'dest': 'create_sections',
446
'validator': frontend.validate_boolean}),
450
'action': 'store_true',
451
'dest': 'create_links',
452
'validator': frontend.validate_boolean}),
453
('Do not create links. (default)',
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'],
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'],
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'],
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'],
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'],
492
'dest': 'custom_header',
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'],
499
'dest': 'custom_footer',
504
settings_defaults = {
505
'output_encoding_error_handler': 'xmlcharrefreplace',
508
relative_path_settings = (
512
config_section = 'opendocument odf writer'
513
config_section_dependencies = (
518
writers.Writer.__init__(self)
519
self.translator_class = ODFTranslator
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']
529
def assemble_my_parts(self):
530
"""Assemble the `self.parts` dictionary. Extend in subclasses.
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)
551
self.parts['whole'] = whole
552
self.parts['encoding'] = self.document.settings.output_encoding
553
self.parts['version'] = docutils.__version__
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)
563
def store_embedded_files(self, zfile):
564
embedded_files = self.visitor.get_embedded_file_list()
565
for source, destination in embedded_files:
570
destination1 = destination.decode('latin-1').encode('utf-8')
571
zfile.write(source, destination1, zipfile.ZIP_STORED)
573
self.document.reporter.warning(
574
"Can't open file %s." % (source, ))
576
def get_settings(self):
578
modeled after get_stylesheet
580
stylespath = self.settings.stylesheet
581
zfile = zipfile.ZipFile(stylespath, 'r')
582
s1 = zfile.read('settings.xml')
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.
590
stylespath = self.settings.stylesheet
591
ext = os.path.splitext(stylespath)[1]
593
stylesfile = open(stylespath, 'r')
594
s1 = stylesfile.read()
596
elif ext == self.EXTENSION:
597
zfile = zipfile.ZipFile(stylespath, 'r')
598
s1 = zfile.read('styles.xml')
601
raise RuntimeError, 'stylesheet path (%s) must be %s or .xml file' %(stylespath, self.EXTENSION)
602
s1 = self.visitor.setup_page(s1)
605
def assemble_parts(self):
608
def create_manifest(self):
609
if WhichElementTree == 'lxml':
610
root = Element('manifest:manifest',
611
nsmap=MANIFEST_NAMESPACE_DICT,
612
nsdict=MANIFEST_NAMESPACE_DICT,
615
root = Element('manifest:manifest',
616
attrib=MANIFEST_NAMESPACE_ATTRIB,
617
nsdict=MANIFEST_NAMESPACE_DICT,
619
doc = etree.ElementTree(root)
620
SubElement(root, 'manifest:file-entry', attrib={
621
'manifest:media-type': self.MIME_TYPE,
622
'manifest:full-path': '/',
624
SubElement(root, 'manifest:file-entry', attrib={
625
'manifest:media-type': 'text/xml',
626
'manifest:full-path': 'content.xml',
628
SubElement(root, 'manifest:file-entry', attrib={
629
'manifest:media-type': 'text/xml',
630
'manifest:full-path': 'styles.xml',
632
SubElement(root, 'manifest:file-entry', attrib={
633
'manifest:media-type': 'text/xml',
634
'manifest:full-path': 'meta.xml',
637
doc = minidom.parseString(s1)
638
s1 = doc.toprettyxml(' ')
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,
648
root = Element('office:document-meta',
649
attrib=META_NAMESPACE_ATTRIB,
650
nsdict=META_NAMESPACE_DICT,
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)
659
s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime())
660
el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD)
662
el1 = SubElement(root, 'dc:creator', nsdict=METNSD)
664
el1 = SubElement(root, 'dc:date', nsdict=METNSD)
666
el1 = SubElement(root, 'dc:language', nsdict=METNSD)
668
el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD)
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)
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)
685
description = meta_dict.get('description')
686
if description is not None:
687
el1 = SubElement(root, 'dc:description', nsdict=METNSD)
688
el1.text = description
690
#doc = minidom.parseString(s1)
691
#s1 = doc.toprettyxml(' ')
694
# class ODFTranslator(nodes.SparseNodeVisitor):
696
class ODFTranslator(nodes.GenericNodeVisitor):
699
'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
700
'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
701
'bulletitem', 'bulletlist',
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',
723
'admon-attention-hdr',
724
'admon-attention-body',
726
'admon-caution-body',
732
'admon-generic-body',
735
'admon-important-hdr',
736
'admon-important-body',
742
'admon-warning-body',
744
'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
752
'image', 'figureframe',
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
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.' % (
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':
776
'office:document-content',
777
nsmap=CONTENT_NAMESPACE_DICT,
781
'office:document-content',
782
attrib=CONTENT_NAMESPACE_ATTRIB,
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 = []
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
804
self.image_style_count = 0
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
814
self.in_table_of_contents = False
815
self.table_of_content_index_body = None
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 = []
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
831
def add_doc_title(self):
832
text = self.settings.title
835
if not self.found_doc_title:
836
el = Element('text:p', attrib = {
837
'text:style-name': self.rststyle('title'),
840
self.body_text_element.insert(0, el)
842
def rststyle(self, name, parameters=( )):
844
Returns the style name to use for the given style.
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.
849
name1 = name % parameters
850
stylename = self.format_map.get(name1, 'rststyle-%s' % name1)
853
def generate_content_element(self, root):
854
return SubElement(root, 'office:text')
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)
865
def setup_paper(self, root_el):
867
fin = os.popen("paperconf -s 2> /dev/null")
868
w, h = map(float, fin.read().split())
871
w, h = 612, 792 # default to Letter
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"]] = \
880
el.attrib["{%s}margin-top" % SNSD["fo"]] = \
881
el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \
884
for subel in el.getchildren(): walk(subel)
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:
894
path = '{%s}master-page' % (SNSD['style'], )
895
master_el = master_el.find(path)
896
if master_el is None:
899
if self.header_content or self.settings.custom_header:
900
if WhichElementTree == 'lxml':
901
el2 = SubElement(el1, 'style:header', nsdict=SNSD)
903
el2 = SubElement(el1, 'style:header',
904
attrib=STYLES_NAMESPACE_ATTRIB,
905
nsdict=STYLES_NAMESPACE_DICT,
907
for el in self.header_content:
908
attrkey = add_ns('text:style-name', nsdict=SNSD)
909
el.attrib[attrkey] = self.rststyle('header')
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)
918
el2 = SubElement(el1, 'style:footer',
919
attrib=STYLES_NAMESPACE_ATTRIB,
920
nsdict=STYLES_NAMESPACE_DICT,
922
for el in self.footer_content:
923
attrkey = add_ns('text:style-name', nsdict=SNSD)
924
el.attrib[attrkey] = self.rststyle('footer')
926
if self.settings.custom_footer:
927
elcustom = self.create_custom_headfoot(el2,
928
self.settings.custom_footer, 'footer', automatic_styles)
930
code_none, code_field, code_text = range(3)
931
field_pat = re.compile(r'%(..?)%')
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',
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),
948
el1 = self.make_field_element(parent,
949
item[1], style_name, automatic_styles)
951
msg = 'bad field spec: %%%s%%' % (item[1], )
952
raise RuntimeError, msg
954
current_element = el1
956
if current_element is None:
957
parent = SubElement(parent, 'text:p', attrib={
958
'text:style-name': self.rststyle(style_name),
960
parent.text = item[1]
962
current_element.tail = item[1]
964
def make_field_element(self, parent, text, style_name, automatic_styles):
966
el1 = SubElement(parent, 'text:page-number', attrib={
967
'text:style-name': self.rststyle(style_name),
968
'text:select-page': 'current',
971
el1 = SubElement(parent, 'text:page-count', attrib={
972
'text:style-name': self.rststyle(style_name),
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,
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'],
986
el3 = SubElement(el2, 'number:hours', attrib={
987
'number:style': 'long',
989
el3 = SubElement(el2, 'number:text')
991
el3 = SubElement(el2, 'number:minutes', attrib={
992
'number:style': 'long',
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,
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'],
1006
el3 = SubElement(el2, 'number:hours', attrib={
1007
'number:style': 'long',
1009
el3 = SubElement(el2, 'number:text')
1011
el3 = SubElement(el2, 'number:minutes', attrib={
1012
'number:style': 'long',
1014
el3 = SubElement(el2, 'number:text')
1016
el3 = SubElement(el2, 'number:seconds', attrib={
1017
'number:style': 'long',
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,
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'],
1031
el3 = SubElement(el2, 'number:hours', attrib={
1032
'number:style': 'long',
1034
el3 = SubElement(el2, 'number:text')
1036
el3 = SubElement(el2, 'number:minutes', attrib={
1037
'number:style': 'long',
1039
el3 = SubElement(el2, 'number:text')
1041
el3 = SubElement(el2, 'number:am-pm')
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,
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'],
1054
el3 = SubElement(el2, 'number:hours', attrib={
1055
'number:style': 'long',
1057
el3 = SubElement(el2, 'number:text')
1059
el3 = SubElement(el2, 'number:minutes', attrib={
1060
'number:style': 'long',
1062
el3 = SubElement(el2, 'number:text')
1064
el3 = SubElement(el2, 'number:seconds', attrib={
1065
'number:style': 'long',
1067
el3 = SubElement(el2, 'number:text')
1069
el3 = SubElement(el2, 'number:am-pm')
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,
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'],
1082
el3 = SubElement(el2, 'number:month', attrib={
1083
'number:style': 'long',
1085
el3 = SubElement(el2, 'number:text')
1087
el3 = SubElement(el2, 'number:day', attrib={
1088
'number:style': 'long',
1090
el3 = SubElement(el2, 'number:text')
1092
el3 = SubElement(el2, 'number:year')
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,
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'],
1105
el3 = SubElement(el2, 'number:month', attrib={
1106
'number:style': 'long',
1108
el3 = SubElement(el2, 'number:text')
1110
el3 = SubElement(el2, 'number:day', attrib={
1111
'number:style': 'long',
1113
el3 = SubElement(el2, 'number:text')
1115
el3 = SubElement(el2, 'number:year', attrib={
1116
'number:style': 'long',
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,
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'],
1130
el3 = SubElement(el2, 'number:month', attrib={
1131
'number:textual': 'true',
1133
el3 = SubElement(el2, 'number:text')
1135
el3 = SubElement(el2, 'number:day', attrib={
1137
el3 = SubElement(el2, 'number:text')
1139
el3 = SubElement(el2, 'number:year', attrib={
1140
'number:style': 'long',
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,
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'],
1154
el3 = SubElement(el2, 'number:month', attrib={
1155
'number:textual': 'true',
1156
'number:style': 'long',
1158
el3 = SubElement(el2, 'number:text')
1160
el3 = SubElement(el2, 'number:day', attrib={
1162
el3 = SubElement(el2, 'number:text')
1164
el3 = SubElement(el2, 'number:year', attrib={
1165
'number:style': 'long',
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,
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'],
1178
el3 = SubElement(el2, 'number:year', attrib={
1179
'number:style': 'long',
1181
el3 = SubElement(el2, 'number:text')
1183
el3 = SubElement(el2, 'number:month', attrib={
1184
'number:style': 'long',
1186
el3 = SubElement(el2, 'number:text')
1188
el3 = SubElement(el2, 'number:day', attrib={
1189
'number:style': 'long',
1192
el1 = SubElement(parent, 'text:subject', attrib={
1193
'text:style-name': self.rststyle(style_name),
1196
el1 = SubElement(parent, 'text:title', attrib={
1197
'text:style-name': self.rststyle(style_name),
1200
el1 = SubElement(parent, 'text:author-name', attrib={
1201
'text:fixed': 'false',
1207
def split_field_specifiers_iter(self, text):
1211
mo = ODFTranslator.field_pat.search(text, pos1)
1215
yield (ODFTranslator.code_text, text[pos1:pos2])
1216
yield (ODFTranslator.code_field, mo.group(1))
1220
trailing = text[pos1:]
1222
yield (ODFTranslator.code_text, trailing)
1226
root = self.content_tree.getroot()
1227
et = etree.ElementTree(root)
1231
def content_astext(self):
1232
return self.astext()
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
1241
def process_footnotes(self):
1242
for node, el1 in self.footnote_list:
1243
backrefs = node.attributes.get('backrefs', [])
1245
for ref in backrefs:
1246
el2 = self.footnote_ref_dict.get(ref)
1250
el3 = copy.deepcopy(el1)
1253
children = el2.getchildren()
1254
if len(children) > 0: # and 'id' in el2.attrib:
1257
attribkey = add_ns('text:id', nsdict=SNSD)
1258
id1 = el2.get(attribkey, 'footnote-error')
1261
tag = add_ns('text:note-ref', nsdict=SNSD)
1263
if self.settings.endnotes_end_doc:
1264
note_class = 'endnote'
1266
note_class = 'footnote'
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'
1279
def append_child(self, tag, attrib=None, parent=None):
1281
parent = self.current_element
1283
el = SubElement(parent, tag)
1285
el = SubElement(parent, tag, attrib)
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:
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={
1301
self.pending_ids = [ ]
1303
def set_current_element(self, el):
1304
self.current_element = el
1306
def set_to_parent(self):
1307
self.current_element = self.current_element.getparent()
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')})
1314
el = self.append_p('blockindent')
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')})
1322
el1.tail = node.astext()
1325
def encode(self, text):
1326
text = text.replace(u'\u00a0', " ")
1332
# In alphabetic order, more or less.
1333
# See docutils.docutils.nodes.node_class_names.
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)
1341
def handle_basic_atts(self, node):
1342
if isinstance(node, nodes.Element) and node['ids']:
1343
self.pending_ids += node['ids']
1345
def default_visit(self, node):
1346
self.document.reporter.warning('missing visit_%s' % (node.tagname, ))
1348
def default_departure(self, node):
1349
self.document.reporter.warning('missing depart_%s' % (node.tagname, ))
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):
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
1362
self.current_element.getchildren()[-1].tail = text
1364
if self.current_element.text:
1365
self.current_element.text += text
1367
self.current_element.text = text
1369
def depart_Text(self, node):
1373
# Pre-defined fields
1376
def visit_address(self, node):
1377
el = self.generate_labeled_block(node, 'Address: ')
1378
self.set_current_element(el)
1380
def depart_address(self, node):
1381
self.set_to_parent()
1383
def visit_author(self, node):
1384
if isinstance(node.parent, nodes.authors):
1385
el = self.append_p('blockindent')
1387
el = self.generate_labeled_block(node, 'Author: ')
1388
self.set_current_element(el)
1390
def depart_author(self, node):
1391
self.set_to_parent()
1393
def visit_authors(self, node):
1395
el = self.append_p('textbody')
1396
el1 = SubElement(el, 'text:span',
1397
attrib={'text:style-name': self.rststyle('strong')})
1400
def depart_authors(self, node):
1403
def visit_contact(self, node):
1404
el = self.generate_labeled_block(node, 'Contact: ')
1405
self.set_current_element(el)
1407
def depart_contact(self, node):
1408
self.set_to_parent()
1410
def visit_copyright(self, node):
1411
el = self.generate_labeled_block(node, 'Copyright: ')
1412
self.set_current_element(el)
1414
def depart_copyright(self, node):
1415
self.set_to_parent()
1417
def visit_date(self, node):
1418
self.generate_labeled_line(node, 'Date: ')
1420
def depart_date(self, node):
1423
def visit_organization(self, node):
1424
el = self.generate_labeled_block(node, 'Organization: ')
1425
self.set_current_element(el)
1427
def depart_organization(self, node):
1428
self.set_to_parent()
1430
def visit_status(self, node):
1431
el = self.generate_labeled_block(node, 'Status: ')
1432
self.set_current_element(el)
1434
def depart_status(self, node):
1435
self.set_to_parent()
1437
def visit_revision(self, node):
1438
self.generate_labeled_line(node, 'Revision: ')
1440
def depart_revision(self, node):
1443
def visit_version(self, node):
1444
el = self.generate_labeled_line(node, 'Version: ')
1445
#self.set_current_element(el)
1447
def depart_version(self, node):
1448
#self.set_to_parent()
1451
def visit_attribution(self, node):
1452
el = self.append_p('attribution', node.astext())
1454
def depart_attribution(self, node):
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')
1465
self.paragraph_style_stack.append(self.rststyle('blockquote'))
1466
self.blockstyle = self.rststyle('blockquote')
1467
self.line_indent_level += 1
1469
def depart_block_quote(self, node):
1470
self.paragraph_style_stack.pop()
1471
self.blockstyle = ''
1472
self.line_indent_level -= 1
1474
def visit_bullet_list(self, node):
1476
if self.in_table_of_contents:
1477
if self.settings.generate_oowriter_toc:
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'),
1485
self.list_style_stack.append(self.rststyle('enumitem'))
1487
el = SubElement(self.current_element, 'text:list', attrib={
1488
'text:style-name': self.rststyle('tocbulletlist'),
1490
self.list_style_stack.append(self.rststyle('bulletitem'))
1491
self.set_current_element(el)
1493
if self.blockstyle == self.rststyle('blockquote'):
1494
el = SubElement(self.current_element, 'text:list', attrib={
1495
'text:style-name': self.rststyle('blockquote-bulletlist'),
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'),
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'),
1509
self.list_style_stack.append(
1510
self.rststyle('epigraph-bulletitem'))
1512
el = SubElement(self.current_element, 'text:list', attrib={
1513
'text:style-name': self.rststyle('bulletlist'),
1515
self.list_style_stack.append(self.rststyle('bulletitem'))
1516
self.set_current_element(el)
1518
def depart_bullet_list(self, node):
1519
if self.in_table_of_contents:
1520
if self.settings.generate_oowriter_toc:
1523
self.set_to_parent()
1524
self.list_style_stack.pop()
1526
self.set_to_parent()
1527
self.list_style_stack.pop()
1530
def visit_caption(self, node):
1531
raise nodes.SkipChildren()
1534
def depart_caption(self, node):
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()
1543
def depart_comment(self, node):
1546
def visit_compound(self, node):
1547
# The compound directive currently receives no special treatment.
1550
def depart_compound(self, node):
1553
def visit_container(self, node):
1554
styles = node.attributes.get('classes', ())
1556
self.paragraph_style_stack.append(self.rststyle(styles[0]))
1558
def depart_container(self, node):
1559
styles = node.attributes.get('classes', ())
1561
self.paragraph_style_stack.pop()
1563
def visit_decoration(self, node):
1566
def depart_decoration(self, node):
1569
def visit_definition(self, node):
1570
self.paragraph_style_stack.append(self.rststyle('blockindent'))
1571
self.bumped_list_level_stack.append(ListLevel(1))
1573
def depart_definition(self, node):
1574
self.paragraph_style_stack.pop()
1575
self.bumped_list_level_stack.pop()
1577
def visit_definition_list(self, node):
1580
def depart_definition_list(self, node):
1583
def visit_definition_list_item(self, node):
1586
def depart_definition_list_item(self, node):
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)
1596
def depart_term(self, node):
1597
self.set_to_parent()
1598
self.set_to_parent()
1600
def visit_classifier(self, node):
1601
els = self.current_element.getchildren()
1604
el1 = SubElement(el, 'text:span',
1605
attrib={'text:style-name': self.rststyle('emphasis')
1607
el1.text = ' (%s)' % (node.astext(), )
1609
def depart_classifier(self, node):
1612
def visit_document(self, node):
1615
def depart_document(self, node):
1616
self.process_footnotes()
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,
1626
self.set_current_element(el)
1628
def depart_docinfo(self, node):
1629
self.section_level -= 1
1630
if self.settings.create_sections:
1631
self.set_to_parent()
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)
1638
def depart_emphasis(self, node):
1639
self.set_to_parent()
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'),
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'),
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'),
1657
self.list_style_stack.append(self.rststyle('epigraph-enumitem'))
1659
liststylename = 'enumlist-%s' % (node.get('enumtype', 'arabic'), )
1660
el2 = SubElement(el1, 'text:list', attrib={
1661
'text:style-name': self.rststyle(liststylename),
1663
self.list_style_stack.append(self.rststyle('enumitem'))
1664
self.set_current_element(el2)
1666
def depart_enumerated_list(self, node):
1667
self.set_to_parent()
1668
self.list_style_stack.pop()
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, )))
1679
el1 = self.append_child('text:list-item')
1680
self.set_current_element(el1)
1682
el1 = self.append_child('text:list-item')
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)
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()
1701
self.set_to_parent()
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()
1714
def visit_header(self, node):
1715
self.in_header = True
1717
def depart_header(self, node):
1718
self.in_header = False
1720
def visit_footer(self, node):
1721
self.in_footer = True
1723
def depart_footer(self, node):
1724
self.in_footer = False
1726
def visit_field(self, node):
1729
def depart_field(self, node):
1732
def visit_field_list(self, node):
1735
def depart_field_list(self, node):
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()
1744
def depart_field_name(self, node):
1747
def visit_field_body(self, node):
1748
self.paragraph_style_stack.append(self.rststyle('blockindent'))
1750
def depart_field_body(self, node):
1751
self.paragraph_style_stack.pop()
1753
def visit_figure(self, node):
1756
def depart_figure(self, node):
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'))
1768
self.paragraph_style_stack.append(self.rststyle('footnote'))
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
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')
1788
if self.settings.endnotes_end_doc:
1789
note_class = 'endnote'
1791
note_class = 'footnote'
1792
el1 = self.append_child('text:note', attrib={
1793
'text:id': '%s' % (refid, ),
1794
'text:note-class': note_class,
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,
1803
elif note_auto == '1':
1804
el2 = SubElement(el1, 'text:note-citation', attrib={
1805
'text:label': node.astext(),
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,
1818
el2.text = footnote_char
1819
self.footnote_ref_dict[id] = el1
1820
raise nodes.SkipChildren()
1822
def depart_footnote_reference(self, node):
1825
def visit_citation(self, node):
1826
for id in node.attributes['ids']:
1827
self.citation_id = id
1829
self.paragraph_style_stack.append(self.rststyle('blockindent'))
1830
self.bumped_list_level_stack.append(ListLevel(1))
1832
def depart_citation(self, node):
1833
self.citation_id = None
1834
self.paragraph_style_stack.pop()
1835
self.bumped_list_level_stack.pop()
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',
1845
self.set_current_element(el)
1846
elif self.current_element.text is None:
1847
self.current_element.text = '['
1849
self.current_element.text += '['
1851
def depart_citation_reference(self, node):
1852
self.current_element.text += ']'
1853
if self.settings.create_links:
1854
self.set_to_parent()
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)
1863
if self.settings.create_links:
1864
el1 = self.append_child('text:reference-mark-start', attrib={
1865
'text:name': '%s' % (self.citation_id, ),
1868
def depart_label(self, node):
1869
if isinstance(node.parent, docutils.nodes.footnote):
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, ),
1877
self.set_to_parent()
1879
def visit_generated(self, node):
1882
def depart_generated(self, node):
1885
def check_file_exists(self, path):
1886
if os.path.exists(path):
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, ))
1901
if source in self.image_dict:
1902
filename, destination = self.image_dict[source]
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,)
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
1915
el1 = SubElement(self.current_element, 'text:p',
1916
attrib={'text:style-name': self.rststyle('textbody')})
1918
if isinstance(node.parent, docutils.nodes.figure):
1919
el3, el4, el5, caption = self.generate_figure(node, source,
1922
el6, width = self.generate_image(node, source, destination,
1924
if caption is not None:
1926
else: #if isinstance(node.parent, docutils.nodes.image):
1927
el3 = self.generate_image(node, source, destination, el2)
1929
def depart_image(self, node):
1932
def get_image_width_height(self, node, attr):
1934
if attr in node.attributes:
1935
size = node.attributes[attr]
1943
except ValueError, e:
1944
self.document.reporter.warning(
1945
'Invalid %s for image: "%s"' % (
1946
attr, node.attributes[attr]))
1950
def get_image_scale(self, node):
1951
if 'scale' in node.attributes:
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, ))
1958
scale = scale * 0.01
1959
except ValueError, e:
1960
self.document.reporter.warning(
1961
'Invalid scale for image: "%s"' % (
1962
node.attributes['scale'], ))
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')
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
1979
except: dpi = (dpi, dpi)
1983
if width is None or height is None:
1984
if imageobj is None:
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']
1992
if width[1] == 'px': width = [width[0] / dpi[0], 'in']
1993
if height[1] == 'px': height = [height[0] / dpi[1], 'in']
1995
width[0] = str(width[0])
1996
height[0] = str(height[0])
1997
return ''.join(width), ''.join(height)
1999
def generate_figure(self, node, source, destination, current_element):
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
2007
# Add the style for the caption.
2008
if caption is not None:
2010
'style:class': 'extra',
2011
'style:family': 'paragraph',
2012
'style:name': 'Caption',
2013
'style:parent-style-name': 'Standard',
2015
el1 = SubElement(self.automatic_styles, 'style:style',
2016
attrib=attrib, nsdict=SNSD)
2018
'fo:margin-bottom': '0.0835in',
2019
'fo:margin-top': '0.0835in',
2020
'text:line-number': '0',
2021
'text:number-lines': 'false',
2023
el2 = SubElement(el1, 'style:paragraph-properties',
2024
attrib=attrib, nsdict=SNSD)
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',
2035
el2 = SubElement(el1, 'style:text-properties',
2036
attrib=attrib, nsdict=SNSD)
2037
style_name = 'rstframestyle%d' % self.image_style_count
2040
'style:name': style_name,
2041
'style:family': 'graphic',
2042
'style:parent-style-name': self.rststyle('figureframe'),
2044
el1 = SubElement(self.automatic_styles,
2045
'style:style', attrib=attrib, nsdict=SNSD)
2048
if 'align' in node.attributes:
2049
align = node.attributes['align'].split()
2051
if val in ('left', 'center', 'right'):
2053
elif val in ('top', 'middle', 'bottom'):
2057
classes = node.parent.attributes.get('classes')
2058
if classes and 'wrap' in classes:
2061
attrib['style:wrap'] = 'dynamic'
2063
attrib['style:wrap'] = 'none'
2064
el2 = SubElement(el1,
2065
'style:graphic-properties', attrib=attrib, nsdict=SNSD)
2067
'draw:style-name': style_name,
2068
'draw:name': 'Frame1',
2069
'text:anchor-type': 'paragraph',
2070
'draw:z-index': '0',
2072
attrib['svg:width'] = width
2074
#attrib['svg:height'] = height
2075
el3 = SubElement(current_element, 'draw:frame', attrib=attrib)
2077
el4 = SubElement(el3, 'draw:text-box', attrib=attrib)
2079
'text:style-name': self.rststyle('caption'),
2081
el5 = SubElement(el4, 'text:p', attrib=attrib)
2082
return el3, el4, el5, caption
2084
def generate_image(self, node, source, destination, current_element,
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
2091
'style:name': style_name,
2092
'style:family': 'graphic',
2093
'style:parent-style-name': self.rststyle('image'),
2095
el1 = SubElement(self.automatic_styles,
2096
'style:style', attrib=attrib, nsdict=SNSD)
2099
if 'align' in node.attributes:
2100
align = node.attributes['align'].split()
2102
if val in ('left', 'center', 'right'):
2104
elif val in ('top', 'middle', 'bottom'):
2106
if frame_attrs is None:
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%',
2118
'draw:gamma': '100%',
2119
'draw:color-inversion': 'false',
2120
'draw:image-opacity': '100%',
2121
'draw:color-mode': 'standard',
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.
2132
classes = node.attributes.get('classes')
2133
if classes and 'wrap' in classes:
2136
attrib['style:wrap'] = 'dynamic'
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)
2145
#el = SubElement(current_element, 'text:p',
2146
# attrib={'text:style-name': self.rststyle('textbody')})
2148
'draw:style-name': style_name,
2149
'draw:name': 'graphics2',
2150
'draw:z-index': '1',
2152
if isinstance(node.parent, nodes.TextElement):
2153
attrib['text:anchor-type'] = 'as-char' #vds
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',
2167
def is_in_table(self, node):
2170
if isinstance(node1, docutils.nodes.entry):
2172
node1 = node1.parent
2175
def visit_legend(self, node):
2176
if isinstance(node.parent, docutils.nodes.figure):
2177
el1 = self.current_element[-1]
2179
self.current_element = el1
2180
self.paragraph_style_stack.append(self.rststyle('legend'))
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()
2189
def visit_line_block(self, node):
2190
self.line_indent_level += 1
2191
self.line_block_level += 1
2193
def depart_line_block(self, node):
2194
self.line_indent_level -= 1
2195
self.line_block_level -= 1
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),
2202
self.current_element = el1
2204
def depart_line(self, node):
2205
self.set_to_parent()
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)
2212
def depart_literal(self, node):
2213
self.set_to_parent()
2215
def visit_inline(self, node):
2216
styles = node.attributes.get('classes', ())
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)
2223
def depart_inline(self, node):
2224
self.set_to_parent()
2226
def _calculate_code_block_padding(self, line):
2228
matchobj = SPACES_PATTERN.match(line)
2230
pad = matchobj.group()
2233
matchobj = TABS_PATTERN.match(line)
2235
pad = matchobj.group()
2236
count = len(pad) * 8
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)
2246
fmtr = OdtPygmentsProgFormatter(lambda name, parameters=():
2247
self.rststyle(name, parameters),
2248
escape_function=escape_cdata)
2249
outsource = pygments.highlight(insource, lexer, fmtr)
2252
def fill_line(self, line):
2253
line = FILL_PAT1.sub(self.fill_func1, line)
2254
line = FILL_PAT2.sub(self.fill_func2, line)
2257
def fill_func1(self, matchobj):
2258
spaces = matchobj.group(0)
2259
repl = '<text:s text:c="%d"/>' % (len(spaces), )
2262
def fill_func2(self, matchobj):
2263
spaces = matchobj.group(0)
2264
repl = ' <text:s text:c="%d"/>' % (len(spaces) - 1, )
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()
2272
self.settings.add_syntax_highlighting
2274
#node.get('hilight', False)
2276
language = node.get('language', 'python')
2277
source = self._add_syntax_highlighting(source, language)
2279
source = escape_cdata(source)
2280
lines = source.split('\n')
2281
lines1 = ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
2284
for my_line in lines:
2285
my_line = self.fill_line(my_line)
2286
my_line = my_line.replace(" ", "\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)
2300
def depart_literal_block(self, node):
2303
visit_doctest_block = visit_literal_block
2304
depart_doctest_block = depart_literal_block
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
2312
def depart_meta(self, node):
2315
def visit_option_list(self, node):
2316
table_name = 'tableoption'
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)
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),
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'})
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)
2407
def depart_option_list(self, node):
2408
self.set_to_parent()
2410
def visit_option_list_item(self, node):
2411
el = self.append_child('table:table-row')
2412
self.set_current_element(el)
2414
def depart_option_list_item(self, node):
2415
self.set_to_parent()
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',
2422
self.set_current_element(el)
2424
def depart_option_group(self, node):
2425
self.set_to_parent()
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()
2432
def depart_option(self, node):
2435
def visit_option_string(self, node):
2438
def depart_option_string(self, node):
2441
def visit_option_argument(self, node):
2444
def depart_option_argument(self, node):
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',
2452
el1 = SubElement(el, 'text:p', attrib={
2453
'text:style-name': 'Table_20_Contents'})
2454
el1.text = node.astext()
2455
raise nodes.SkipChildren()
2457
def depart_description(self, node):
2460
def visit_paragraph(self, node):
2461
self.in_paragraph = True
2463
el = self.append_p('header')
2464
elif self.in_footer:
2465
el = self.append_p('footer')
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)
2473
def depart_paragraph(self, node):
2474
self.in_paragraph = False
2475
self.set_to_parent()
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])
2487
def visit_problematic(self, node):
2490
def depart_problematic(self, node):
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:
2510
elif self.in_footer:
2513
self.current_element.append(el1)
2514
raise nodes.SkipChildren()
2516
def depart_raw(self, node):
2519
elif self.in_footer:
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',
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',
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])
2552
def depart_reference(self, node):
2553
if self.settings.create_links:
2554
if node.has_key('refuri'):
2555
self.set_to_parent()
2557
def visit_rubric(self, node):
2558
style_name = self.rststyle('rubric')
2559
classes = node.get('classes')
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,
2569
text = node.astext()
2570
el.text = self.encode(text)
2572
def depart_rubric(self, node):
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,
2583
self.set_current_element(el)
2585
def depart_section(self, node):
2586
self.section_level -= 1
2587
if self.settings.create_sections:
2588
self.set_to_parent()
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)
2595
def depart_strong(self, node):
2596
self.set_to_parent()
2598
def visit_substitution_definition(self, node):
2599
raise nodes.SkipChildren()
2601
def depart_substitution_definition(self, node):
2604
def visit_system_message(self, node):
2607
def depart_system_message(self, node):
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',
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',
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',
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,
2641
for child in node.children:
2642
if child.tagname == 'title':
2643
title = child.astext()
2645
if title is not None:
2646
el3 = self.append_p('table-title', title)
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, )),
2655
self.set_current_element(el4)
2656
self.current_table_style = el1
2657
self.table_width = 0
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()
2665
def visit_tgroup(self, node):
2666
self.column_count = ord('A') - 1
2668
def depart_tgroup(self, node):
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), )
2677
colwidth = node['colwidth']
2678
el1 = SubElement(self.automatic_styles, 'style:style', attrib={
2679
'style:name': colspec_name,
2680
'style:family': 'table-column',
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,
2687
self.table_width += colwidth
2689
def depart_colspec(self, node):
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')
2698
def depart_thead(self, node):
2699
self.set_to_parent()
2700
self.in_thead = False
2701
self.paragraph_style_stack.pop()
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)
2708
def depart_row(self, node):
2709
self.set_to_parent()
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, )
2718
'table:style-name': cellspec_name,
2719
'office:value-type': 'string',
2721
morecols = node.get('morecols', 0)
2723
attrib['table:number-columns-spanned'] = '%d' % (morecols + 1,)
2724
self.column_count += morecols
2725
morerows = node.get('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)
2731
def depart_entry(self, node):
2732
self.set_to_parent()
2734
def visit_tbody(self, node):
2737
def depart_tbody(self, node):
2740
def visit_target(self, node):
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')):
2750
def depart_target(self, node):
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"' % (
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, )),
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
2774
# text = node.astext()
2775
el1 = SubElement(self.current_element, 'text:p', attrib = {
2776
'text:style-name': self.rststyle(title_type),
2778
self.append_pending_ids(el1)
2779
text = node.astext()
2781
self.found_doc_title = True
2782
self.set_current_element(el1)
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()
2789
def visit_subtitle(self, node, move_ids=1):
2790
self.visit_title(node, move_ids, title_type='subtitle')
2792
def depart_subtitle(self, node):
2793
self.depart_title(node)
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()
2801
def depart_title_reference(self, node):
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',
2809
'text:outline-level': "%d" % (idx, ),
2810
'text:style-name': self.rststyle('contents-%d' % (idx, )),
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",
2818
el3 = SubElement(el2, 'text:index-entry-page-number')
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',
2829
el2 = SubElement(el1,
2830
'text:table-of-content-source',
2832
'text:outline-level': '10',
2834
el3 =SubElement(el2, 'text:index-title-template', attrib={
2835
'text:style-name': 'Contents_20_Heading',
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'),
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)
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'
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)
2870
el = self.append_p('horizontalline')
2871
self.in_table_of_contents = False
2873
def update_toc_page_numbers(self, el):
2875
self.update_toc_collect(el, 0, collection)
2876
self.update_toc_add_numbers(collection)
2878
def update_toc_collect(self, el, level, collection):
2879
collection.append((level, el))
2881
for child_el in el.getchildren():
2882
if child_el.tag != 'text:index-body':
2883
self.update_toc_collect(child_el, level, collection)
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')
2893
def visit_transition(self, node):
2894
el = self.append_p('horizontalline')
2896
def depart_transition(self, node):
2902
def visit_warning(self, node):
2903
self.generate_admonition(node, 'warning')
2905
def depart_warning(self, node):
2906
self.paragraph_style_stack.pop()
2908
def visit_attention(self, node):
2909
self.generate_admonition(node, 'attention')
2911
depart_attention = depart_warning
2913
def visit_caution(self, node):
2914
self.generate_admonition(node, 'caution')
2916
depart_caution = depart_warning
2918
def visit_danger(self, node):
2919
self.generate_admonition(node, 'danger')
2921
depart_danger = depart_warning
2923
def visit_error(self, node):
2924
self.generate_admonition(node, 'error')
2926
depart_error = depart_warning
2928
def visit_hint(self, node):
2929
self.generate_admonition(node, 'hint')
2931
depart_hint = depart_warning
2933
def visit_important(self, node):
2934
self.generate_admonition(node, 'important')
2936
depart_important = depart_warning
2938
def visit_note(self, node):
2939
self.generate_admonition(node, 'note')
2941
depart_note = depart_warning
2943
def visit_tip(self, node):
2944
self.generate_admonition(node, 'tip')
2946
depart_tip = depart_warning
2948
def visit_admonition(self, node):
2949
#import pdb; pdb.set_trace()
2951
for child in node.children:
2952
if child.tagname == 'title':
2953
title = child.astext()
2955
classes1 = node.get('classes')
2958
self.generate_admonition(node, 'generic', title)
2960
depart_admonition = depart_warning
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, )),
2969
el1.text = '%s!' % (label.capitalize(), )
2970
s1 = self.rststyle('admon-%s-body', ( label, ))
2971
self.paragraph_style_stack.append(s1)
2974
# Roles (e.g. subscript, superscript, strong, ...
2976
def visit_subscript(self, node):
2977
el = self.append_child('text:span', attrib={
2978
'text:style-name': 'rststyle-subscript',
2980
self.set_current_element(el)
2982
def depart_subscript(self, node):
2983
self.set_to_parent()
2985
def visit_superscript(self, node):
2986
el = self.append_child('text:span', attrib={
2987
'text:style-name': 'rststyle-superscript',
2989
self.set_current_element(el)
2991
def depart_superscript(self, node):
2992
self.set_to_parent()
2995
# Use an own reader to modify transformations done.
2996
class Reader(standalone.Reader):
2998
def get_transforms(self):
2999
default = standalone.Reader.get_transforms(self)
3000
if self.settings.create_links:
3004
if i is not references.DanglingReferences ]