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
= 'rststyle-table-'
110 TABLENAMEDEFAULT
= '%s0' % TABLESTYLEPREFIX
111 TABLEPROPERTYNAMES
= ('border', 'border-top', 'border-left',
112 'border-right', 'border-bottom', )
114 GENERATOR_DESC
= 'Docutils.org/odf_odt'
116 NAME_SPACE_1
= 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
118 CONTENT_NAMESPACE_DICT
= CNSD
= {
119 # 'office:version': '1.0',
120 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
121 'dc': 'http://purl.org/dc/elements/1.1/',
122 'dom': 'http://www.w3.org/2001/xml-events',
123 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
124 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
125 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
126 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
127 'math': 'http://www.w3.org/1998/Math/MathML',
128 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
129 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
130 'office': NAME_SPACE_1
,
131 'ooo': 'http://openoffice.org/2004/office',
132 'oooc': 'http://openoffice.org/2004/calc',
133 'ooow': 'http://openoffice.org/2004/writer',
134 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
136 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
137 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
138 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
139 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
140 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
141 'xforms': 'http://www.w3.org/2002/xforms',
142 'xlink': 'http://www.w3.org/1999/xlink',
143 'xsd': 'http://www.w3.org/2001/XMLSchema',
144 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
147 STYLES_NAMESPACE_DICT
= SNSD
= {
148 # 'office:version': '1.0',
149 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
150 'dc': 'http://purl.org/dc/elements/1.1/',
151 'dom': 'http://www.w3.org/2001/xml-events',
152 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
153 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
154 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
155 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
156 'math': 'http://www.w3.org/1998/Math/MathML',
157 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
158 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
159 'office': NAME_SPACE_1
,
160 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
161 'ooo': 'http://openoffice.org/2004/office',
162 'oooc': 'http://openoffice.org/2004/calc',
163 'ooow': 'http://openoffice.org/2004/writer',
164 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
165 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
166 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
167 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
168 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
169 'xlink': 'http://www.w3.org/1999/xlink',
172 MANIFEST_NAMESPACE_DICT
= MANNSD
= {
173 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
176 META_NAMESPACE_DICT
= METNSD
= {
177 # 'office:version': '1.0',
178 'dc': 'http://purl.org/dc/elements/1.1/',
179 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
180 'office': NAME_SPACE_1
,
181 'ooo': 'http://openoffice.org/2004/office',
182 'xlink': 'http://www.w3.org/1999/xlink',
186 # Attribute dictionaries for use with ElementTree (not lxml), which
187 # does not support use of nsmap parameter on Element() and SubElement().
189 CONTENT_NAMESPACE_ATTRIB
= {
190 'office:version': '1.0',
191 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
192 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
193 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
194 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
195 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
196 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
197 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
198 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
199 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
200 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
201 'xmlns:office': NAME_SPACE_1
,
202 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
203 'xmlns:ooo': 'http://openoffice.org/2004/office',
204 'xmlns:oooc': 'http://openoffice.org/2004/calc',
205 'xmlns:ooow': 'http://openoffice.org/2004/writer',
206 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
207 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
208 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
209 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
210 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
211 'xmlns:xforms': 'http://www.w3.org/2002/xforms',
212 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
213 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
214 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
217 STYLES_NAMESPACE_ATTRIB
= {
218 'office:version': '1.0',
219 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
220 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
221 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
222 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
223 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
224 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
225 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
226 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
227 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
228 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
229 'xmlns:office': NAME_SPACE_1
,
230 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
231 'xmlns:ooo': 'http://openoffice.org/2004/office',
232 'xmlns:oooc': 'http://openoffice.org/2004/calc',
233 'xmlns:ooow': 'http://openoffice.org/2004/writer',
234 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
235 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
236 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
237 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
238 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
239 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
242 MANIFEST_NAMESPACE_ATTRIB
= {
243 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
246 META_NAMESPACE_ATTRIB
= {
247 'office:version': '1.0',
248 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
249 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
250 'xmlns:office': NAME_SPACE_1
,
251 'xmlns:ooo': 'http://openoffice.org/2004/office',
252 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
261 # ElementTree support functions.
262 # In order to be able to get the parent of elements, must use these
263 # instead of the functions with same name provided by ElementTree.
265 def Element(tag
, attrib
=None, nsmap
=None, nsdict
=CNSD
):
268 tag
, attrib
= fix_ns(tag
, attrib
, nsdict
)
269 if WhichElementTree
== 'lxml':
270 el
= etree
.Element(tag
, attrib
, nsmap
=nsmap
)
272 el
= _ElementInterfaceWrapper(tag
, attrib
)
275 def SubElement(parent
, tag
, attrib
=None, nsmap
=None, nsdict
=CNSD
):
278 tag
, attrib
= fix_ns(tag
, attrib
, nsdict
)
279 if WhichElementTree
== 'lxml':
280 el
= etree
.SubElement(parent
, tag
, attrib
, nsmap
=nsmap
)
282 el
= _ElementInterfaceWrapper(tag
, attrib
)
287 def fix_ns(tag
, attrib
, nsdict
):
288 nstag
= add_ns(tag
, nsdict
)
290 for key
, val
in attrib
.iteritems():
291 nskey
= add_ns(key
, nsdict
)
292 nsattrib
[nskey
] = val
293 return nstag
, nsattrib
295 def add_ns(tag
, nsdict
=CNSD
):
296 if WhichElementTree
== 'lxml':
297 nstag
, name
= tag
.split(':')
298 ns
= nsdict
.get(nstag
)
300 raise RuntimeError, 'Invalid namespace prefix: %s' % nstag
301 tag
= '{%s}%s' % (ns
, name
,)
305 outstream
= StringIO
.StringIO()
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
), )
326 WORD_SPLIT_PAT1
= re
.compile(r
'\b(\w*)\b\W*')
328 def split_words(line
):
329 # We need whitespace at the end of the string for our regexpr.
333 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
334 while mo
is not None:
335 word
= mo
.groups()[0]
338 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
347 class TableStyle(object):
348 def __init__(self
, border
=None, backgroundcolor
=None):
350 self
.backgroundcolor
= backgroundcolor
351 def get_border_(self
):
353 def set_border_(self
, border
):
354 self
.border_
= border
355 border
= property(get_border_
, set_border_
)
356 def get_backgroundcolor_(self
):
357 return self
.backgroundcolor_
358 def set_backgroundcolor_(self
, backgroundcolor
):
359 self
.backgroundcolor_
= backgroundcolor
360 backgroundcolor
= property(get_backgroundcolor_
, set_backgroundcolor_
)
362 BUILTIN_DEFAULT_TABLE_STYLE
= TableStyle(
363 border
= '0.0007in solid #000000')
366 # Information about the indentation level for lists nested inside
367 # other contexts, e.g. dictionary lists.
368 class ListLevel(object):
369 def __init__(self
, level
, sibling_level
=True, nested_level
=True):
371 self
.sibling_level
= sibling_level
372 self
.nested_level
= nested_level
373 def set_sibling(self
, sibling_level
): self
.sibling_level
= sibling_level
374 def get_sibling(self
): return self
.sibling_level
375 def set_nested(self
, nested_level
): self
.nested_level
= nested_level
376 def get_nested(self
): return self
.nested_level
377 def set_level(self
, level
): self
.level
= level
378 def get_level(self
): return self
.level
381 class Writer(writers
.Writer
):
383 MIME_TYPE
= 'application/vnd.oasis.opendocument.text'
386 supported
= ('odt', )
387 """Formats this writer supports."""
389 default_stylesheet
= 'styles' + EXTENSION
391 default_stylesheet_path
= utils
.relative_path(
392 os
.path
.join(os
.getcwd(), 'dummy'),
393 os
.path
.join(os
.path
.dirname(__file__
), default_stylesheet
))
395 default_template
= 'template.txt'
397 default_template_path
= utils
.relative_path(
398 os
.path
.join(os
.getcwd(), 'dummy'),
399 os
.path
.join(os
.path
.dirname(__file__
), default_template
))
402 'ODF-Specific Options',
405 ('Specify a stylesheet. '
406 'Default: "%s"' % default_stylesheet_path
,
409 'default': default_stylesheet_path
,
412 ('Specify a configuration/mapping file relative to the '
414 'directory for additional ODF options. '
415 'In particular, this file may contain a section named '
416 '"Formats" that maps default style names to '
417 'names to be used in the resulting output file allowing for '
418 'adhering to external standards. '
419 'For more info and the format of the configuration/mapping file, '
420 'see the odtwriter doc.',
421 ['--odf-config-file'],
422 {'metavar': '<file>'}),
423 ('Obfuscate email addresses to confuse harvesters while still '
424 'keeping email links usable with standards-compliant browsers.',
425 ['--cloak-email-addresses'],
427 'action': 'store_true',
428 'dest': 'cloak_email_addresses',
429 'validator': frontend
.validate_boolean
}),
430 ('Do not obfuscate email addresses.',
431 ['--no-cloak-email-addresses'],
433 'action': 'store_false',
434 'dest': 'cloak_email_addresses',
435 'validator': frontend
.validate_boolean
}),
436 ('Specify the thickness of table borders in thousands of a cm. '
438 ['--table-border-thickness'],
440 'validator': frontend
.validate_nonnegative_int
}),
441 ('Add syntax highlighting in literal code blocks.',
442 ['--add-syntax-highlighting'],
444 'action': 'store_true',
445 'dest': 'add_syntax_highlighting',
446 'validator': frontend
.validate_boolean
}),
447 ('Do not add syntax highlighting in literal code blocks. (default)',
448 ['--no-syntax-highlighting'],
450 'action': 'store_false',
451 'dest': 'add_syntax_highlighting',
452 'validator': frontend
.validate_boolean
}),
453 ('Create sections for headers. (default)',
454 ['--create-sections'],
456 'action': 'store_true',
457 'dest': 'create_sections',
458 'validator': frontend
.validate_boolean
}),
459 ('Do not create sections for headers.',
462 'action': 'store_false',
463 'dest': 'create_sections',
464 'validator': frontend
.validate_boolean
}),
468 'action': 'store_true',
469 'dest': 'create_links',
470 'validator': frontend
.validate_boolean
}),
471 ('Do not create links. (default)',
474 'action': 'store_false',
475 'dest': 'create_links',
476 'validator': frontend
.validate_boolean
}),
477 ('Generate endnotes at end of document, not footnotes '
478 'at bottom of page.',
479 ['--endnotes-end-doc'],
481 'action': 'store_true',
482 'dest': 'endnotes_end_doc',
483 'validator': frontend
.validate_boolean
}),
484 ('Generate footnotes at bottom of page, not endnotes '
485 'at end of document. (default)',
486 ['--no-endnotes-end-doc'],
488 'action': 'store_false',
489 'dest': 'endnotes_end_doc',
490 'validator': frontend
.validate_boolean
}),
491 ('Generate a bullet list table of contents, not '
492 'an ODF/oowriter table of contents.',
493 ['--generate-list-toc'],
495 'action': 'store_false',
496 'dest': 'generate_oowriter_toc',
497 'validator': frontend
.validate_boolean
}),
498 ('Generate an ODF/oowriter table of contents, not '
499 'a bullet list. (default)',
500 ['--generate-oowriter-toc'],
502 'action': 'store_true',
503 'dest': 'generate_oowriter_toc',
504 'validator': frontend
.validate_boolean
}),
505 ('Specify the contents of an custom header line. '
506 'See odf_odt writer documentation for details '
507 'about special field character sequences.',
508 ['--custom-odt-header'],
510 'dest': 'custom_header',
512 ('Specify the contents of an custom footer line. '
513 'See odf_odt writer documentation for details '
514 'about special field character sequences.',
515 ['--custom-odt-footer'],
517 'dest': 'custom_footer',
522 settings_defaults
= {
523 'output_encoding_error_handler': 'xmlcharrefreplace',
526 relative_path_settings
= (
530 config_section
= 'opendocument odf writer'
531 config_section_dependencies
= (
536 writers
.Writer
.__init
__(self
)
537 self
.translator_class
= ODFTranslator
540 self
.settings
= self
.document
.settings
541 self
.visitor
= self
.translator_class(self
.document
)
542 self
.visitor
.retrieve_styles(self
.EXTENSION
)
543 self
.document
.walkabout(self
.visitor
)
544 self
.visitor
.add_doc_title()
545 self
.assemble_my_parts()
546 self
.output
= self
.parts
['whole']
548 def assemble_my_parts(self
):
549 """Assemble the `self.parts` dictionary. Extend in subclasses.
551 writers
.Writer
.assemble_parts(self
)
552 f
= tempfile
.NamedTemporaryFile()
553 zfile
= zipfile
.ZipFile(f
, 'w', zipfile
.ZIP_DEFLATED
)
554 content
= self
.visitor
.content_astext()
555 self
.write_zip_str(zfile
, 'content.xml', content
)
556 self
.write_zip_str(zfile
, 'mimetype', self
.MIME_TYPE
)
557 s1
= self
.create_manifest()
558 self
.write_zip_str(zfile
, 'META-INF/manifest.xml', s1
)
559 s1
= self
.create_meta()
560 self
.write_zip_str(zfile
, 'meta.xml', s1
)
561 s1
= self
.get_stylesheet()
562 self
.write_zip_str(zfile
, 'styles.xml', s1
)
563 s1
= self
.get_settings()
564 self
.write_zip_str(zfile
, 'settings.xml', s1
)
565 self
.store_embedded_files(zfile
)
570 self
.parts
['whole'] = whole
571 self
.parts
['encoding'] = self
.document
.settings
.output_encoding
572 self
.parts
['version'] = docutils
.__version
__
574 def write_zip_str(self
, zfile
, name
, bytes
):
575 localtime
= time
.localtime(time
.time())
576 zinfo
= zipfile
.ZipInfo(name
, localtime
)
577 # Add some standard UNIX file access permissions (-rw-r--r--).
578 zinfo
.external_attr
= (0x81a4 & 0xFFFF) << 16L
579 zinfo
.compress_type
= zipfile
.ZIP_DEFLATED
580 zfile
.writestr(zinfo
, bytes
)
582 def store_embedded_files(self
, zfile
):
583 embedded_files
= self
.visitor
.get_embedded_file_list()
584 for source
, destination
in embedded_files
:
589 destination1
= destination
.decode('latin-1').encode('utf-8')
590 zfile
.write(source
, destination1
, zipfile
.ZIP_STORED
)
592 self
.document
.reporter
.warning(
593 "Can't open file %s." % (source
, ))
595 def get_settings(self
):
597 modeled after get_stylesheet
599 stylespath
= self
.settings
.stylesheet
600 zfile
= zipfile
.ZipFile(stylespath
, 'r')
601 s1
= zfile
.read('settings.xml')
605 def get_stylesheet(self
):
606 """Get the stylesheet from the visitor.
607 Ask the visitor to setup the page.
609 s1
= self
.visitor
.setup_page()
612 def assemble_parts(self
):
615 def create_manifest(self
):
616 if WhichElementTree
== 'lxml':
617 root
= Element('manifest:manifest',
618 nsmap
=MANIFEST_NAMESPACE_DICT
,
619 nsdict
=MANIFEST_NAMESPACE_DICT
,
622 root
= Element('manifest:manifest',
623 attrib
=MANIFEST_NAMESPACE_ATTRIB
,
624 nsdict
=MANIFEST_NAMESPACE_DICT
,
626 doc
= etree
.ElementTree(root
)
627 SubElement(root
, 'manifest:file-entry', attrib
={
628 'manifest:media-type': self
.MIME_TYPE
,
629 'manifest:full-path': '/',
631 SubElement(root
, 'manifest:file-entry', attrib
={
632 'manifest:media-type': 'text/xml',
633 'manifest:full-path': 'content.xml',
635 SubElement(root
, 'manifest:file-entry', attrib
={
636 'manifest:media-type': 'text/xml',
637 'manifest:full-path': 'styles.xml',
639 SubElement(root
, 'manifest:file-entry', attrib
={
640 'manifest:media-type': 'text/xml',
641 'manifest:full-path': 'meta.xml',
644 doc
= minidom
.parseString(s1
)
645 s1
= doc
.toprettyxml(' ')
648 def create_meta(self
):
649 if WhichElementTree
== 'lxml':
650 root
= Element('office:document-meta',
651 nsmap
=META_NAMESPACE_DICT
,
652 nsdict
=META_NAMESPACE_DICT
,
655 root
= Element('office:document-meta',
656 attrib
=META_NAMESPACE_ATTRIB
,
657 nsdict
=META_NAMESPACE_DICT
,
659 doc
= etree
.ElementTree(root
)
660 root
= SubElement(root
, 'office:meta', nsdict
=METNSD
)
661 el1
= SubElement(root
, 'meta:generator', nsdict
=METNSD
)
662 el1
.text
= 'Docutils/rst2odf.py/%s' % (VERSION
, )
663 s1
= os
.environ
.get('USER', '')
664 el1
= SubElement(root
, 'meta:initial-creator', nsdict
=METNSD
)
666 s2
= time
.strftime('%Y-%m-%dT%H:%M:%S', time
.localtime())
667 el1
= SubElement(root
, 'meta:creation-date', nsdict
=METNSD
)
669 el1
= SubElement(root
, 'dc:creator', nsdict
=METNSD
)
671 el1
= SubElement(root
, 'dc:date', nsdict
=METNSD
)
673 el1
= SubElement(root
, 'dc:language', nsdict
=METNSD
)
675 el1
= SubElement(root
, 'meta:editing-cycles', nsdict
=METNSD
)
677 el1
= SubElement(root
, 'meta:editing-duration', nsdict
=METNSD
)
678 el1
.text
= 'PT00M01S'
679 title
= self
.visitor
.get_title()
680 el1
= SubElement(root
, 'dc:title', nsdict
=METNSD
)
684 el1
.text
= '[no title]'
685 meta_dict
= self
.visitor
.get_meta_dict()
686 keywordstr
= meta_dict
.get('keywords')
687 if keywordstr
is not None:
688 keywords
= split_words(keywordstr
)
689 for keyword
in keywords
:
690 el1
= SubElement(root
, 'meta:keyword', nsdict
=METNSD
)
692 description
= meta_dict
.get('description')
693 if description
is not None:
694 el1
= SubElement(root
, 'dc:description', nsdict
=METNSD
)
695 el1
.text
= description
697 #doc = minidom.parseString(s1)
698 #s1 = doc.toprettyxml(' ')
701 # class ODFTranslator(nodes.SparseNodeVisitor):
703 class ODFTranslator(nodes
.GenericNodeVisitor
):
706 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
707 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
708 'bulletitem', 'bulletlist',
710 'centeredtextbody', 'codeblock',
711 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
712 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
713 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
714 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
715 'epigraph-enumitem', 'epigraph-enumlist', 'footer',
716 'footnote', 'citation',
717 'header', 'highlights', 'highlights-bulletitem',
718 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
719 'horizontalline', 'inlineliteral', 'quotation', 'rubric',
720 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
730 'admon-attention-hdr',
731 'admon-attention-body',
733 'admon-caution-body',
739 'admon-generic-body',
742 'admon-important-hdr',
743 'admon-important-body',
749 'admon-warning-body',
751 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
759 'image', 'figureframe',
762 def __init__(self
, document
):
763 #nodes.SparseNodeVisitor.__init__(self, document)
764 nodes
.GenericNodeVisitor
.__init
__(self
, document
)
765 self
.settings
= document
.settings
766 self
.format_map
= { }
767 if self
.settings
.odf_config_file
:
768 from ConfigParser
import ConfigParser
770 parser
= ConfigParser()
771 parser
.read(self
.settings
.odf_config_file
)
772 for rststyle
, format
in parser
.items("Formats"):
773 if rststyle
not in self
.used_styles
:
774 self
.document
.reporter
.warning(
775 'Style "%s" is not a style used by odtwriter.' % (
777 self
.format_map
[rststyle
] = format
778 self
.section_level
= 0
779 self
.section_count
= 0
780 # Create ElementTree content and styles documents.
781 if WhichElementTree
== 'lxml':
783 'office:document-content',
784 nsmap
=CONTENT_NAMESPACE_DICT
,
788 'office:document-content',
789 attrib
=CONTENT_NAMESPACE_ATTRIB
,
791 self
.content_tree
= etree
.ElementTree(element
=root
)
792 self
.current_element
= root
793 SubElement(root
, 'office:scripts')
794 SubElement(root
, 'office:font-face-decls')
795 el
= SubElement(root
, 'office:automatic-styles')
796 self
.automatic_styles
= el
797 el
= SubElement(root
, 'office:body')
798 el
= self
.generate_content_element(el
)
799 self
.current_element
= el
800 self
.body_text_element
= el
801 self
.paragraph_style_stack
= [self
.rststyle('textbody'), ]
802 self
.list_style_stack
= []
804 self
.column_count
= ord('A') - 1
805 self
.trace_level
= -1
806 self
.optiontablestyles_generated
= False
807 self
.field_name
= None
808 self
.field_element
= None
811 self
.image_style_count
= 0
813 self
.embedded_file_list
= []
814 self
.syntaxhighlighting
= 1
815 self
.syntaxhighlight_lexer
= 'python'
816 self
.header_content
= []
817 self
.footer_content
= []
818 self
.in_header
= False
819 self
.in_footer
= False
821 self
.in_table_of_contents
= False
822 self
.table_of_content_index_body
= None
824 self
.footnote_ref_dict
= {}
825 self
.footnote_list
= []
826 self
.footnote_chars_idx
= 0
827 self
.footnote_level
= 0
828 self
.pending_ids
= [ ]
829 self
.in_paragraph
= False
830 self
.found_doc_title
= False
831 self
.bumped_list_level_stack
= []
833 self
.line_block_level
= 0
834 self
.line_indent_level
= 0
835 self
.citation_id
= None
836 self
.style_index
= 0 # use to form unique style names
837 self
.str_stylesheet
= ''
838 self
.str_stylesheetcontent
= ''
839 self
.dom_stylesheet
= None
840 self
.table_styles
= None
842 def get_str_stylesheet(self
):
843 return self
.str_stylesheet
845 def retrieve_styles(self
, extension
):
846 """Retrieve the stylesheet from either a .xml file or from
847 a .odt (zip) file. Return the content as a string.
850 stylespath
= self
.settings
.stylesheet
851 ext
= os
.path
.splitext(stylespath
)[1]
853 stylesfile
= open(stylespath
, 'r')
854 s1
= stylesfile
.read()
856 elif ext
== extension
:
857 zfile
= zipfile
.ZipFile(stylespath
, 'r')
858 s1
= zfile
.read('styles.xml')
859 s2
= zfile
.read('content.xml')
862 raise RuntimeError, 'stylesheet path (%s) must be %s or .xml file' %(stylespath
, extension
)
863 self
.str_stylesheet
= s1
864 self
.str_stylesheetcontent
= s2
865 self
.dom_stylesheet
= etree
.fromstring(self
.str_stylesheet
)
866 self
.dom_stylesheetcontent
= etree
.fromstring(self
.str_stylesheetcontent
)
867 self
.table_styles
= self
.extract_table_styles(s2
)
869 def extract_table_styles(self
, styles_str
):
870 root
= etree
.fromstring(styles_str
)
872 auto_styles
= root
.find(
873 '{%s}automatic-styles' % (CNSD
['office'], ))
874 for stylenode
in auto_styles
:
875 name
= stylenode
.get('{%s}name' % (CNSD
['style'], ))
876 tablename
= name
.split('.')[0]
877 family
= stylenode
.get('{%s}family' % (CNSD
['style'], ))
878 if name
.startswith(TABLESTYLEPREFIX
):
879 tablestyle
= table_styles
.get(tablename
)
880 if tablestyle
is None:
881 tablestyle
= TableStyle()
882 table_styles
[tablename
] = tablestyle
883 if family
== 'table':
884 properties
= stylenode
.find(
885 '{%s}table-properties' % (CNSD
['style'], ))
886 property = properties
.get('{%s}%s' % (CNSD
['fo'],
887 'background-color', ))
888 if property is not None and property != 'none':
889 tablestyle
.backgroundcolor
= property
890 elif family
== 'table-cell':
891 properties
= stylenode
.find(
892 '{%s}table-cell-properties' % (CNSD
['style'], ))
893 if properties
is not None:
894 border
= self
.get_property(properties
)
895 if border
is not None:
896 tablestyle
.border
= border
899 def get_property(self
, stylenode
):
901 for propertyname
in TABLEPROPERTYNAMES
:
902 border
= stylenode
.get('{%s}%s' % (CNSD
['fo'], propertyname
, ))
903 if border
is not None and border
!= 'none':
907 def add_doc_title(self
):
908 text
= self
.settings
.title
911 if not self
.found_doc_title
:
912 el
= Element('text:p', attrib
= {
913 'text:style-name': self
.rststyle('title'),
916 self
.body_text_element
.insert(0, el
)
918 def rststyle(self
, name
, parameters
=( )):
920 Returns the style name to use for the given style.
922 If `parameters` is given `name` must contain a matching number of ``%`` and
923 is used as a format expression with `parameters` as the value.
925 name1
= name
% parameters
926 stylename
= self
.format_map
.get(name1
, 'rststyle-%s' % name1
)
929 def generate_content_element(self
, root
):
930 return SubElement(root
, 'office:text')
932 def setup_page(self
):
933 self
.setup_paper(self
.dom_stylesheet
)
934 if (len(self
.header_content
) > 0 or len(self
.footer_content
) > 0 or
935 self
.settings
.custom_header
or self
.settings
.custom_footer
):
936 self
.add_header_footer(self
.dom_stylesheet
)
937 new_content
= etree
.tostring(self
.dom_stylesheet
)
940 def setup_paper(self
, root_el
):
942 fin
= os
.popen("paperconf -s 2> /dev/null")
943 w
, h
= map(float, fin
.read().split())
946 w
, h
= 612, 792 # default to Letter
948 if el
.tag
== "{%s}page-layout-properties" % SNSD
["style"] and \
949 not el
.attrib
.has_key("{%s}page-width" % SNSD
["fo"]):
950 el
.attrib
["{%s}page-width" % SNSD
["fo"]] = "%.3fpt" % w
951 el
.attrib
["{%s}page-height" % SNSD
["fo"]] = "%.3fpt" % h
952 el
.attrib
["{%s}margin-left" % SNSD
["fo"]] = \
953 el
.attrib
["{%s}margin-right" % SNSD
["fo"]] = \
955 el
.attrib
["{%s}margin-top" % SNSD
["fo"]] = \
956 el
.attrib
["{%s}margin-bottom" % SNSD
["fo"]] = \
959 for subel
in el
.getchildren(): walk(subel
)
962 def add_header_footer(self
, root_el
):
963 automatic_styles
= root_el
.find(
964 '{%s}automatic-styles' % SNSD
['office'])
965 path
= '{%s}master-styles' % (NAME_SPACE_1
, )
966 master_el
= root_el
.find(path
)
967 if master_el
is None:
969 path
= '{%s}master-page' % (SNSD
['style'], )
970 master_el
= master_el
.find(path
)
971 if master_el
is None:
974 if self
.header_content
or self
.settings
.custom_header
:
975 if WhichElementTree
== 'lxml':
976 el2
= SubElement(el1
, 'style:header', nsdict
=SNSD
)
978 el2
= SubElement(el1
, 'style:header',
979 attrib
=STYLES_NAMESPACE_ATTRIB
,
980 nsdict
=STYLES_NAMESPACE_DICT
,
982 for el
in self
.header_content
:
983 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
984 el
.attrib
[attrkey
] = self
.rststyle('header')
986 if self
.settings
.custom_header
:
987 elcustom
= self
.create_custom_headfoot(el2
,
988 self
.settings
.custom_header
, 'header', automatic_styles
)
989 if self
.footer_content
or self
.settings
.custom_footer
:
990 if WhichElementTree
== 'lxml':
991 el2
= SubElement(el1
, 'style:footer', nsdict
=SNSD
)
993 el2
= SubElement(el1
, 'style:footer',
994 attrib
=STYLES_NAMESPACE_ATTRIB
,
995 nsdict
=STYLES_NAMESPACE_DICT
,
997 for el
in self
.footer_content
:
998 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
999 el
.attrib
[attrkey
] = self
.rststyle('footer')
1001 if self
.settings
.custom_footer
:
1002 elcustom
= self
.create_custom_headfoot(el2
,
1003 self
.settings
.custom_footer
, 'footer', automatic_styles
)
1005 code_none
, code_field
, code_text
= range(3)
1006 field_pat
= re
.compile(r
'%(..?)%')
1008 def create_custom_headfoot(self
, parent
, text
, style_name
, automatic_styles
):
1009 parent
= SubElement(parent
, 'text:p', attrib
={
1010 'text:style-name': self
.rststyle(style_name
),
1012 current_element
= None
1013 field_iter
= self
.split_field_specifiers_iter(text
)
1014 for item
in field_iter
:
1015 if item
[0] == ODFTranslator
.code_field
:
1016 if item
[1] not in ('p', 'P',
1017 't1', 't2', 't3', 't4',
1018 'd1', 'd2', 'd3', 'd4', 'd5',
1020 msg
= 'bad field spec: %%%s%%' % (item
[1], )
1021 raise RuntimeError, msg
1022 el1
= self
.make_field_element(parent
,
1023 item
[1], style_name
, automatic_styles
)
1025 msg
= 'bad field spec: %%%s%%' % (item
[1], )
1026 raise RuntimeError, msg
1028 current_element
= el1
1030 if current_element
is None:
1031 parent
.text
= item
[1]
1033 current_element
.tail
= item
[1]
1035 def make_field_element(self
, parent
, text
, style_name
, automatic_styles
):
1037 el1
= SubElement(parent
, 'text:page-number', attrib
={
1038 #'text:style-name': self.rststyle(style_name),
1039 'text:select-page': 'current',
1042 el1
= SubElement(parent
, 'text:page-count', attrib
={
1043 #'text:style-name': self.rststyle(style_name),
1046 self
.style_index
+= 1
1047 el1
= SubElement(parent
, 'text:time', attrib
={
1048 'text:style-name': self
.rststyle(style_name
),
1049 'text:fixed': 'true',
1050 'style:data-style-name': 'rst-time-style-%d' % self
.style_index
,
1052 el2
= SubElement(automatic_styles
, 'number:time-style', attrib
={
1053 'style:name': 'rst-time-style-%d' % self
.style_index
,
1054 'xmlns:number': SNSD
['number'],
1055 'xmlns:style': SNSD
['style'],
1057 el3
= SubElement(el2
, 'number:hours', attrib
={
1058 'number:style': 'long',
1060 el3
= SubElement(el2
, 'number:text')
1062 el3
= SubElement(el2
, 'number:minutes', attrib
={
1063 'number:style': 'long',
1066 self
.style_index
+= 1
1067 el1
= SubElement(parent
, 'text:time', attrib
={
1068 'text:style-name': self
.rststyle(style_name
),
1069 'text:fixed': 'true',
1070 'style:data-style-name': 'rst-time-style-%d' % self
.style_index
,
1072 el2
= SubElement(automatic_styles
, 'number:time-style', attrib
={
1073 'style:name': 'rst-time-style-%d' % self
.style_index
,
1074 'xmlns:number': SNSD
['number'],
1075 'xmlns:style': SNSD
['style'],
1077 el3
= SubElement(el2
, 'number:hours', attrib
={
1078 'number:style': 'long',
1080 el3
= SubElement(el2
, 'number:text')
1082 el3
= SubElement(el2
, 'number:minutes', attrib
={
1083 'number:style': 'long',
1085 el3
= SubElement(el2
, 'number:text')
1087 el3
= SubElement(el2
, 'number:seconds', attrib
={
1088 'number:style': 'long',
1091 self
.style_index
+= 1
1092 el1
= SubElement(parent
, 'text:time', attrib
={
1093 'text:style-name': self
.rststyle(style_name
),
1094 'text:fixed': 'true',
1095 'style:data-style-name': 'rst-time-style-%d' % self
.style_index
,
1097 el2
= SubElement(automatic_styles
, 'number:time-style', attrib
={
1098 'style:name': 'rst-time-style-%d' % self
.style_index
,
1099 'xmlns:number': SNSD
['number'],
1100 'xmlns:style': SNSD
['style'],
1102 el3
= SubElement(el2
, 'number:hours', attrib
={
1103 'number:style': 'long',
1105 el3
= SubElement(el2
, 'number:text')
1107 el3
= SubElement(el2
, 'number:minutes', attrib
={
1108 'number:style': 'long',
1110 el3
= SubElement(el2
, 'number:text')
1112 el3
= SubElement(el2
, 'number:am-pm')
1114 self
.style_index
+= 1
1115 el1
= SubElement(parent
, 'text:time', attrib
={
1116 'text:style-name': self
.rststyle(style_name
),
1117 'text:fixed': 'true',
1118 'style:data-style-name': 'rst-time-style-%d' % self
.style_index
,
1120 el2
= SubElement(automatic_styles
, 'number:time-style', attrib
={
1121 'style:name': 'rst-time-style-%d' % self
.style_index
,
1122 'xmlns:number': SNSD
['number'],
1123 'xmlns:style': SNSD
['style'],
1125 el3
= SubElement(el2
, 'number:hours', attrib
={
1126 'number:style': 'long',
1128 el3
= SubElement(el2
, 'number:text')
1130 el3
= SubElement(el2
, 'number:minutes', attrib
={
1131 'number:style': 'long',
1133 el3
= SubElement(el2
, 'number:text')
1135 el3
= SubElement(el2
, 'number:seconds', attrib
={
1136 'number:style': 'long',
1138 el3
= SubElement(el2
, 'number:text')
1140 el3
= SubElement(el2
, 'number:am-pm')
1142 self
.style_index
+= 1
1143 el1
= SubElement(parent
, 'text:date', attrib
={
1144 'text:style-name': self
.rststyle(style_name
),
1145 'style:data-style-name': 'rst-date-style-%d' % self
.style_index
,
1147 el2
= SubElement(automatic_styles
, 'number:date-style', attrib
={
1148 'style:name': 'rst-date-style-%d' % self
.style_index
,
1149 'number:automatic-order': 'true',
1150 'xmlns:number': SNSD
['number'],
1151 'xmlns:style': SNSD
['style'],
1153 el3
= SubElement(el2
, 'number:month', attrib
={
1154 'number:style': 'long',
1156 el3
= SubElement(el2
, 'number:text')
1158 el3
= SubElement(el2
, 'number:day', attrib
={
1159 'number:style': 'long',
1161 el3
= SubElement(el2
, 'number:text')
1163 el3
= SubElement(el2
, 'number:year')
1165 self
.style_index
+= 1
1166 el1
= SubElement(parent
, 'text:date', attrib
={
1167 'text:style-name': self
.rststyle(style_name
),
1168 'style:data-style-name': 'rst-date-style-%d' % self
.style_index
,
1170 el2
= SubElement(automatic_styles
, 'number:date-style', attrib
={
1171 'style:name': 'rst-date-style-%d' % self
.style_index
,
1172 'number:automatic-order': 'true',
1173 'xmlns:number': SNSD
['number'],
1174 'xmlns:style': SNSD
['style'],
1176 el3
= SubElement(el2
, 'number:month', attrib
={
1177 'number:style': 'long',
1179 el3
= SubElement(el2
, 'number:text')
1181 el3
= SubElement(el2
, 'number:day', attrib
={
1182 'number:style': 'long',
1184 el3
= SubElement(el2
, 'number:text')
1186 el3
= SubElement(el2
, 'number:year', attrib
={
1187 'number:style': 'long',
1190 self
.style_index
+= 1
1191 el1
= SubElement(parent
, 'text:date', attrib
={
1192 'text:style-name': self
.rststyle(style_name
),
1193 'style:data-style-name': 'rst-date-style-%d' % self
.style_index
,
1195 el2
= SubElement(automatic_styles
, 'number:date-style', attrib
={
1196 'style:name': 'rst-date-style-%d' % self
.style_index
,
1197 'number:automatic-order': 'true',
1198 'xmlns:number': SNSD
['number'],
1199 'xmlns:style': SNSD
['style'],
1201 el3
= SubElement(el2
, 'number:month', attrib
={
1202 'number:textual': 'true',
1204 el3
= SubElement(el2
, 'number:text')
1206 el3
= SubElement(el2
, 'number:day', attrib
={
1208 el3
= SubElement(el2
, 'number:text')
1210 el3
= SubElement(el2
, 'number:year', attrib
={
1211 'number:style': 'long',
1214 self
.style_index
+= 1
1215 el1
= SubElement(parent
, 'text:date', attrib
={
1216 'text:style-name': self
.rststyle(style_name
),
1217 'style:data-style-name': 'rst-date-style-%d' % self
.style_index
,
1219 el2
= SubElement(automatic_styles
, 'number:date-style', attrib
={
1220 'style:name': 'rst-date-style-%d' % self
.style_index
,
1221 'number:automatic-order': 'true',
1222 'xmlns:number': SNSD
['number'],
1223 'xmlns:style': SNSD
['style'],
1225 el3
= SubElement(el2
, 'number:month', attrib
={
1226 'number:textual': 'true',
1227 'number:style': 'long',
1229 el3
= SubElement(el2
, 'number:text')
1231 el3
= SubElement(el2
, 'number:day', attrib
={
1233 el3
= SubElement(el2
, 'number:text')
1235 el3
= SubElement(el2
, 'number:year', attrib
={
1236 'number:style': 'long',
1239 self
.style_index
+= 1
1240 el1
= SubElement(parent
, 'text:date', attrib
={
1241 'text:style-name': self
.rststyle(style_name
),
1242 'style:data-style-name': 'rst-date-style-%d' % self
.style_index
,
1244 el2
= SubElement(automatic_styles
, 'number:date-style', attrib
={
1245 'style:name': 'rst-date-style-%d' % self
.style_index
,
1246 'xmlns:number': SNSD
['number'],
1247 'xmlns:style': SNSD
['style'],
1249 el3
= SubElement(el2
, 'number:year', attrib
={
1250 'number:style': 'long',
1252 el3
= SubElement(el2
, 'number:text')
1254 el3
= SubElement(el2
, 'number:month', attrib
={
1255 'number:style': 'long',
1257 el3
= SubElement(el2
, 'number:text')
1259 el3
= SubElement(el2
, 'number:day', attrib
={
1260 'number:style': 'long',
1263 el1
= SubElement(parent
, 'text:subject', attrib
={
1264 'text:style-name': self
.rststyle(style_name
),
1267 el1
= SubElement(parent
, 'text:title', attrib
={
1268 'text:style-name': self
.rststyle(style_name
),
1271 el1
= SubElement(parent
, 'text:author-name', attrib
={
1272 'text:fixed': 'false',
1278 def split_field_specifiers_iter(self
, text
):
1282 mo
= ODFTranslator
.field_pat
.search(text
, pos1
)
1286 yield (ODFTranslator
.code_text
, text
[pos1
:pos2
])
1287 yield (ODFTranslator
.code_field
, mo
.group(1))
1291 trailing
= text
[pos1
:]
1293 yield (ODFTranslator
.code_text
, trailing
)
1297 root
= self
.content_tree
.getroot()
1298 et
= etree
.ElementTree(root
)
1302 def content_astext(self
):
1303 return self
.astext()
1305 def set_title(self
, title
): self
.title
= title
1306 def get_title(self
): return self
.title
1307 def set_embedded_file_list(self
, embedded_file_list
):
1308 self
.embedded_file_list
= embedded_file_list
1309 def get_embedded_file_list(self
): return self
.embedded_file_list
1310 def get_meta_dict(self
): return self
.meta_dict
1312 def process_footnotes(self
):
1313 for node
, el1
in self
.footnote_list
:
1314 backrefs
= node
.attributes
.get('backrefs', [])
1316 for ref
in backrefs
:
1317 el2
= self
.footnote_ref_dict
.get(ref
)
1321 el3
= copy
.deepcopy(el1
)
1324 children
= el2
.getchildren()
1325 if len(children
) > 0: # and 'id' in el2.attrib:
1328 attribkey
= add_ns('text:id', nsdict
=SNSD
)
1329 id1
= el2
.get(attribkey
, 'footnote-error')
1332 tag
= add_ns('text:note-ref', nsdict
=SNSD
)
1334 if self
.settings
.endnotes_end_doc
:
1335 note_class
= 'endnote'
1337 note_class
= 'footnote'
1339 attribkey
= add_ns('text:note-class', nsdict
=SNSD
)
1340 el2
.attrib
[attribkey
] = note_class
1341 attribkey
= add_ns('text:ref-name', nsdict
=SNSD
)
1342 el2
.attrib
[attribkey
] = id1
1343 attribkey
= add_ns('text:reference-format', nsdict
=SNSD
)
1344 el2
.attrib
[attribkey
] = 'page'
1350 def append_child(self
, tag
, attrib
=None, parent
=None):
1352 parent
= self
.current_element
1354 el
= SubElement(parent
, tag
)
1356 el
= SubElement(parent
, tag
, attrib
)
1359 def append_p(self
, style
, text
=None):
1360 result
= self
.append_child('text:p', attrib
={
1361 'text:style-name': self
.rststyle(style
)})
1362 self
.append_pending_ids(result
)
1363 if text
is not None:
1367 def append_pending_ids(self
, el
):
1368 if self
.settings
.create_links
:
1369 for id in self
.pending_ids
:
1370 SubElement(el
, 'text:reference-mark', attrib
={
1372 self
.pending_ids
= [ ]
1374 def set_current_element(self
, el
):
1375 self
.current_element
= el
1377 def set_to_parent(self
):
1378 self
.current_element
= self
.current_element
.getparent()
1380 def generate_labeled_block(self
, node
, label
):
1381 el
= self
.append_p('textbody')
1382 el1
= SubElement(el
, 'text:span',
1383 attrib
={'text:style-name': self
.rststyle('strong')})
1385 el
= self
.append_p('blockindent')
1388 def generate_labeled_line(self
, node
, label
):
1389 el
= self
.append_p('textbody')
1390 el1
= SubElement(el
, 'text:span',
1391 attrib
={'text:style-name': self
.rststyle('strong')})
1393 el1
.tail
= node
.astext()
1396 def encode(self
, text
):
1397 text
= text
.replace(u
'\u00a0', " ")
1403 # In alphabetic order, more or less.
1404 # See docutils.docutils.nodes.node_class_names.
1407 def dispatch_visit(self
, node
):
1408 """Override to catch basic attributes which many nodes have."""
1409 self
.handle_basic_atts(node
)
1410 nodes
.GenericNodeVisitor
.dispatch_visit(self
, node
)
1412 def handle_basic_atts(self
, node
):
1413 if isinstance(node
, nodes
.Element
) and node
['ids']:
1414 self
.pending_ids
+= node
['ids']
1416 def default_visit(self
, node
):
1417 self
.document
.reporter
.warning('missing visit_%s' % (node
.tagname
, ))
1419 def default_departure(self
, node
):
1420 self
.document
.reporter
.warning('missing depart_%s' % (node
.tagname
, ))
1422 def visit_Text(self
, node
):
1423 # Skip nodes whose text has been processed in parent nodes.
1424 if isinstance(node
.parent
, docutils
.nodes
.literal_block
):
1426 text
= node
.astext()
1427 # Are we in mixed content? If so, add the text to the
1428 # etree tail of the previous sibling element.
1429 if len(self
.current_element
.getchildren()) > 0:
1430 if self
.current_element
.getchildren()[-1].tail
:
1431 self
.current_element
.getchildren()[-1].tail
+= text
1433 self
.current_element
.getchildren()[-1].tail
= text
1435 if self
.current_element
.text
:
1436 self
.current_element
.text
+= text
1438 self
.current_element
.text
= text
1440 def depart_Text(self
, node
):
1444 # Pre-defined fields
1447 def visit_address(self
, node
):
1448 el
= self
.generate_labeled_block(node
, 'Address: ')
1449 self
.set_current_element(el
)
1451 def depart_address(self
, node
):
1452 self
.set_to_parent()
1454 def visit_author(self
, node
):
1455 if isinstance(node
.parent
, nodes
.authors
):
1456 el
= self
.append_p('blockindent')
1458 el
= self
.generate_labeled_block(node
, 'Author: ')
1459 self
.set_current_element(el
)
1461 def depart_author(self
, node
):
1462 self
.set_to_parent()
1464 def visit_authors(self
, node
):
1466 el
= self
.append_p('textbody')
1467 el1
= SubElement(el
, 'text:span',
1468 attrib
={'text:style-name': self
.rststyle('strong')})
1471 def depart_authors(self
, node
):
1474 def visit_contact(self
, node
):
1475 el
= self
.generate_labeled_block(node
, 'Contact: ')
1476 self
.set_current_element(el
)
1478 def depart_contact(self
, node
):
1479 self
.set_to_parent()
1481 def visit_copyright(self
, node
):
1482 el
= self
.generate_labeled_block(node
, 'Copyright: ')
1483 self
.set_current_element(el
)
1485 def depart_copyright(self
, node
):
1486 self
.set_to_parent()
1488 def visit_date(self
, node
):
1489 self
.generate_labeled_line(node
, 'Date: ')
1491 def depart_date(self
, node
):
1494 def visit_organization(self
, node
):
1495 el
= self
.generate_labeled_block(node
, 'Organization: ')
1496 self
.set_current_element(el
)
1498 def depart_organization(self
, node
):
1499 self
.set_to_parent()
1501 def visit_status(self
, node
):
1502 el
= self
.generate_labeled_block(node
, 'Status: ')
1503 self
.set_current_element(el
)
1505 def depart_status(self
, node
):
1506 self
.set_to_parent()
1508 def visit_revision(self
, node
):
1509 self
.generate_labeled_line(node
, 'Revision: ')
1511 def depart_revision(self
, node
):
1514 def visit_version(self
, node
):
1515 el
= self
.generate_labeled_line(node
, 'Version: ')
1516 #self.set_current_element(el)
1518 def depart_version(self
, node
):
1519 #self.set_to_parent()
1522 def visit_attribution(self
, node
):
1523 el
= self
.append_p('attribution', node
.astext())
1525 def depart_attribution(self
, node
):
1528 def visit_block_quote(self
, node
):
1529 if 'epigraph' in node
.attributes
['classes']:
1530 self
.paragraph_style_stack
.append(self
.rststyle('epigraph'))
1531 self
.blockstyle
= self
.rststyle('epigraph')
1532 elif 'highlights' in node
.attributes
['classes']:
1533 self
.paragraph_style_stack
.append(self
.rststyle('highlights'))
1534 self
.blockstyle
= self
.rststyle('highlights')
1536 self
.paragraph_style_stack
.append(self
.rststyle('blockquote'))
1537 self
.blockstyle
= self
.rststyle('blockquote')
1538 self
.line_indent_level
+= 1
1540 def depart_block_quote(self
, node
):
1541 self
.paragraph_style_stack
.pop()
1542 self
.blockstyle
= ''
1543 self
.line_indent_level
-= 1
1545 def visit_bullet_list(self
, node
):
1547 if self
.in_table_of_contents
:
1548 if self
.settings
.generate_oowriter_toc
:
1551 if node
.has_key('classes') and \
1552 'auto-toc' in node
.attributes
['classes']:
1553 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1554 'text:style-name': self
.rststyle('tocenumlist'),
1556 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1558 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1559 'text:style-name': self
.rststyle('tocbulletlist'),
1561 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1562 self
.set_current_element(el
)
1564 if self
.blockstyle
== self
.rststyle('blockquote'):
1565 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1566 'text:style-name': self
.rststyle('blockquote-bulletlist'),
1568 self
.list_style_stack
.append(
1569 self
.rststyle('blockquote-bulletitem'))
1570 elif self
.blockstyle
== self
.rststyle('highlights'):
1571 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1572 'text:style-name': self
.rststyle('highlights-bulletlist'),
1574 self
.list_style_stack
.append(
1575 self
.rststyle('highlights-bulletitem'))
1576 elif self
.blockstyle
== self
.rststyle('epigraph'):
1577 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1578 'text:style-name': self
.rststyle('epigraph-bulletlist'),
1580 self
.list_style_stack
.append(
1581 self
.rststyle('epigraph-bulletitem'))
1583 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1584 'text:style-name': self
.rststyle('bulletlist'),
1586 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1587 self
.set_current_element(el
)
1589 def depart_bullet_list(self
, node
):
1590 if self
.in_table_of_contents
:
1591 if self
.settings
.generate_oowriter_toc
:
1594 self
.set_to_parent()
1595 self
.list_style_stack
.pop()
1597 self
.set_to_parent()
1598 self
.list_style_stack
.pop()
1601 def visit_caption(self
, node
):
1602 raise nodes
.SkipChildren()
1605 def depart_caption(self
, node
):
1608 def visit_comment(self
, node
):
1609 el
= self
.append_p('textbody')
1610 el1
= SubElement(el
, 'office:annotation', attrib
={})
1611 el2
= SubElement(el1
, 'text:p', attrib
={})
1612 el2
.text
= node
.astext()
1614 def depart_comment(self
, node
):
1617 def visit_compound(self
, node
):
1618 # The compound directive currently receives no special treatment.
1621 def depart_compound(self
, node
):
1624 def visit_container(self
, node
):
1625 styles
= node
.attributes
.get('classes', ())
1627 self
.paragraph_style_stack
.append(self
.rststyle(styles
[0]))
1629 def depart_container(self
, node
):
1630 styles
= node
.attributes
.get('classes', ())
1632 self
.paragraph_style_stack
.pop()
1634 def visit_decoration(self
, node
):
1637 def depart_decoration(self
, node
):
1640 def visit_definition(self
, node
):
1641 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1642 self
.bumped_list_level_stack
.append(ListLevel(1))
1644 def depart_definition(self
, node
):
1645 self
.paragraph_style_stack
.pop()
1646 self
.bumped_list_level_stack
.pop()
1648 def visit_definition_list(self
, node
):
1651 def depart_definition_list(self
, node
):
1654 def visit_definition_list_item(self
, node
):
1657 def depart_definition_list_item(self
, node
):
1660 def visit_term(self
, node
):
1661 el
= self
.append_p('textbody')
1662 el1
= SubElement(el
, 'text:span',
1663 attrib
={'text:style-name': self
.rststyle('strong')})
1664 #el1.text = node.astext()
1665 self
.set_current_element(el1
)
1667 def depart_term(self
, node
):
1668 self
.set_to_parent()
1669 self
.set_to_parent()
1671 def visit_classifier(self
, node
):
1672 els
= self
.current_element
.getchildren()
1675 el1
= SubElement(el
, 'text:span',
1676 attrib
={'text:style-name': self
.rststyle('emphasis')
1678 el1
.text
= ' (%s)' % (node
.astext(), )
1680 def depart_classifier(self
, node
):
1683 def visit_document(self
, node
):
1686 def depart_document(self
, node
):
1687 self
.process_footnotes()
1689 def visit_docinfo(self
, node
):
1690 self
.section_level
+= 1
1691 self
.section_count
+= 1
1692 if self
.settings
.create_sections
:
1693 el
= self
.append_child('text:section', attrib
={
1694 'text:name': 'Section%d' % self
.section_count
,
1695 'text:style-name': 'Sect%d' % self
.section_level
,
1697 self
.set_current_element(el
)
1699 def depart_docinfo(self
, node
):
1700 self
.section_level
-= 1
1701 if self
.settings
.create_sections
:
1702 self
.set_to_parent()
1704 def visit_emphasis(self
, node
):
1705 el
= SubElement(self
.current_element
, 'text:span',
1706 attrib
={'text:style-name': self
.rststyle('emphasis')})
1707 self
.set_current_element(el
)
1709 def depart_emphasis(self
, node
):
1710 self
.set_to_parent()
1712 def visit_enumerated_list(self
, node
):
1713 el1
= self
.current_element
1714 if self
.blockstyle
== self
.rststyle('blockquote'):
1715 el2
= SubElement(el1
, 'text:list', attrib
={
1716 'text:style-name': self
.rststyle('blockquote-enumlist'),
1718 self
.list_style_stack
.append(self
.rststyle('blockquote-enumitem'))
1719 elif self
.blockstyle
== self
.rststyle('highlights'):
1720 el2
= SubElement(el1
, 'text:list', attrib
={
1721 'text:style-name': self
.rststyle('highlights-enumlist'),
1723 self
.list_style_stack
.append(self
.rststyle('highlights-enumitem'))
1724 elif self
.blockstyle
== self
.rststyle('epigraph'):
1725 el2
= SubElement(el1
, 'text:list', attrib
={
1726 'text:style-name': self
.rststyle('epigraph-enumlist'),
1728 self
.list_style_stack
.append(self
.rststyle('epigraph-enumitem'))
1730 liststylename
= 'enumlist-%s' % (node
.get('enumtype', 'arabic'), )
1731 el2
= SubElement(el1
, 'text:list', attrib
={
1732 'text:style-name': self
.rststyle(liststylename
),
1734 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1735 self
.set_current_element(el2
)
1737 def depart_enumerated_list(self
, node
):
1738 self
.set_to_parent()
1739 self
.list_style_stack
.pop()
1741 def visit_list_item(self
, node
):
1742 # If we are in a "bumped" list level, then wrap this
1743 # list in an outer lists in order to increase the
1744 # indentation level.
1745 if self
.in_table_of_contents
:
1746 if self
.settings
.generate_oowriter_toc
:
1747 self
.paragraph_style_stack
.append(
1748 self
.rststyle('contents-%d' % (self
.list_level
, )))
1750 el1
= self
.append_child('text:list-item')
1751 self
.set_current_element(el1
)
1753 el1
= self
.append_child('text:list-item')
1755 if len(self
.bumped_list_level_stack
) > 0:
1756 level_obj
= self
.bumped_list_level_stack
[-1]
1757 if level_obj
.get_sibling():
1758 level_obj
.set_nested(False)
1759 for level_obj1
in self
.bumped_list_level_stack
:
1760 for idx
in range(level_obj1
.get_level()):
1761 el2
= self
.append_child('text:list', parent
=el3
)
1762 el3
= self
.append_child(
1763 'text:list-item', parent
=el2
)
1764 self
.paragraph_style_stack
.append(self
.list_style_stack
[-1])
1765 self
.set_current_element(el3
)
1767 def depart_list_item(self
, node
):
1768 if self
.in_table_of_contents
:
1769 if self
.settings
.generate_oowriter_toc
:
1770 self
.paragraph_style_stack
.pop()
1772 self
.set_to_parent()
1774 if len(self
.bumped_list_level_stack
) > 0:
1775 level_obj
= self
.bumped_list_level_stack
[-1]
1776 if level_obj
.get_sibling():
1777 level_obj
.set_nested(True)
1778 for level_obj1
in self
.bumped_list_level_stack
:
1779 for idx
in range(level_obj1
.get_level()):
1780 self
.set_to_parent()
1781 self
.set_to_parent()
1782 self
.paragraph_style_stack
.pop()
1783 self
.set_to_parent()
1785 def visit_header(self
, node
):
1786 self
.in_header
= True
1788 def depart_header(self
, node
):
1789 self
.in_header
= False
1791 def visit_footer(self
, node
):
1792 self
.in_footer
= True
1794 def depart_footer(self
, node
):
1795 self
.in_footer
= False
1797 def visit_field(self
, node
):
1800 def depart_field(self
, node
):
1803 def visit_field_list(self
, node
):
1806 def depart_field_list(self
, node
):
1809 def visit_field_name(self
, node
):
1810 el
= self
.append_p('textbody')
1811 el1
= SubElement(el
, 'text:span',
1812 attrib
={'text:style-name': self
.rststyle('strong')})
1813 el1
.text
= node
.astext()
1815 def depart_field_name(self
, node
):
1818 def visit_field_body(self
, node
):
1819 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1821 def depart_field_body(self
, node
):
1822 self
.paragraph_style_stack
.pop()
1824 def visit_figure(self
, node
):
1827 def depart_figure(self
, node
):
1830 def visit_footnote(self
, node
):
1831 self
.footnote_level
+= 1
1832 self
.save_footnote_current
= self
.current_element
1833 el1
= Element('text:note-body')
1834 self
.current_element
= el1
1835 self
.footnote_list
.append((node
, el1
))
1836 if isinstance(node
, docutils
.nodes
.citation
):
1837 self
.paragraph_style_stack
.append(self
.rststyle('citation'))
1839 self
.paragraph_style_stack
.append(self
.rststyle('footnote'))
1841 def depart_footnote(self
, node
):
1842 self
.paragraph_style_stack
.pop()
1843 self
.current_element
= self
.save_footnote_current
1844 self
.footnote_level
-= 1
1853 def visit_footnote_reference(self
, node
):
1854 if self
.footnote_level
<= 0:
1855 id = node
.attributes
['ids'][0]
1856 refid
= node
.attributes
.get('refid')
1859 if self
.settings
.endnotes_end_doc
:
1860 note_class
= 'endnote'
1862 note_class
= 'footnote'
1863 el1
= self
.append_child('text:note', attrib
={
1864 'text:id': '%s' % (refid
, ),
1865 'text:note-class': note_class
,
1867 note_auto
= str(node
.attributes
.get('auto', 1))
1868 if isinstance(node
, docutils
.nodes
.citation_reference
):
1869 citation
= '[%s]' % node
.astext()
1870 el2
= SubElement(el1
, 'text:note-citation', attrib
={
1871 'text:label': citation
,
1874 elif note_auto
== '1':
1875 el2
= SubElement(el1
, 'text:note-citation', attrib
={
1876 'text:label': node
.astext(),
1878 el2
.text
= node
.astext()
1879 elif note_auto
== '*':
1880 if self
.footnote_chars_idx
>= len(
1881 ODFTranslator
.footnote_chars
):
1882 self
.footnote_chars_idx
= 0
1883 footnote_char
= ODFTranslator
.footnote_chars
[
1884 self
.footnote_chars_idx
]
1885 self
.footnote_chars_idx
+= 1
1886 el2
= SubElement(el1
, 'text:note-citation', attrib
={
1887 'text:label': footnote_char
,
1889 el2
.text
= footnote_char
1890 self
.footnote_ref_dict
[id] = el1
1891 raise nodes
.SkipChildren()
1893 def depart_footnote_reference(self
, node
):
1896 def visit_citation(self
, node
):
1897 for id in node
.attributes
['ids']:
1898 self
.citation_id
= id
1900 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1901 self
.bumped_list_level_stack
.append(ListLevel(1))
1903 def depart_citation(self
, node
):
1904 self
.citation_id
= None
1905 self
.paragraph_style_stack
.pop()
1906 self
.bumped_list_level_stack
.pop()
1908 def visit_citation_reference(self
, node
):
1909 if self
.settings
.create_links
:
1910 id = node
.attributes
['refid']
1911 el
= self
.append_child('text:reference-ref', attrib
={
1912 'text:ref-name': '%s' % (id, ),
1913 'text:reference-format': 'text',
1916 self
.set_current_element(el
)
1917 elif self
.current_element
.text
is None:
1918 self
.current_element
.text
= '['
1920 self
.current_element
.text
+= '['
1922 def depart_citation_reference(self
, node
):
1923 self
.current_element
.text
+= ']'
1924 if self
.settings
.create_links
:
1925 self
.set_to_parent()
1927 def visit_label(self
, node
):
1928 if isinstance(node
.parent
, docutils
.nodes
.footnote
):
1929 raise nodes
.SkipChildren()
1930 elif self
.citation_id
is not None:
1931 el
= self
.append_p('textbody')
1932 self
.set_current_element(el
)
1934 if self
.settings
.create_links
:
1935 el1
= self
.append_child('text:reference-mark-start', attrib
={
1936 'text:name': '%s' % (self
.citation_id
, ),
1939 def depart_label(self
, node
):
1940 if isinstance(node
.parent
, docutils
.nodes
.footnote
):
1942 elif self
.citation_id
is not None:
1943 self
.current_element
.text
+= ']'
1944 if self
.settings
.create_links
:
1945 el
= self
.append_child('text:reference-mark-end', attrib
={
1946 'text:name': '%s' % (self
.citation_id
, ),
1948 self
.set_to_parent()
1950 def visit_generated(self
, node
):
1953 def depart_generated(self
, node
):
1956 def check_file_exists(self
, path
):
1957 if os
.path
.exists(path
):
1962 def visit_image(self
, node
):
1963 # Capture the image file.
1964 if 'uri' in node
.attributes
:
1965 source
= node
.attributes
['uri']
1966 if not self
.check_file_exists(source
):
1967 self
.document
.reporter
.warning(
1968 'Cannot find image file %s.' % (source
, ))
1972 if source
in self
.image_dict
:
1973 filename
, destination
= self
.image_dict
[source
]
1975 self
.image_count
+= 1
1976 filename
= os
.path
.split(source
)[1]
1977 destination
= 'Pictures/1%08x%s' % (self
.image_count
, filename
, )
1978 spec
= (os
.path
.abspath(source
), destination
,)
1980 self
.embedded_file_list
.append(spec
)
1981 self
.image_dict
[source
] = (source
, destination
,)
1982 # Is this a figure (containing an image) or just a plain image?
1983 if self
.in_paragraph
:
1984 el1
= self
.current_element
1986 el1
= SubElement(self
.current_element
, 'text:p',
1987 attrib
={'text:style-name': self
.rststyle('textbody')})
1989 if isinstance(node
.parent
, docutils
.nodes
.figure
):
1990 el3
, el4
, el5
, caption
= self
.generate_figure(node
, source
,
1993 el6
, width
= self
.generate_image(node
, source
, destination
,
1995 if caption
is not None:
1997 else: #if isinstance(node.parent, docutils.nodes.image):
1998 el3
= self
.generate_image(node
, source
, destination
, el2
)
2000 def depart_image(self
, node
):
2003 def get_image_width_height(self
, node
, attr
):
2005 if attr
in node
.attributes
:
2006 size
= node
.attributes
[attr
]
2014 except ValueError, e
:
2015 self
.document
.reporter
.warning(
2016 'Invalid %s for image: "%s"' % (
2017 attr
, node
.attributes
[attr
]))
2021 def get_image_scale(self
, node
):
2022 if 'scale' in node
.attributes
:
2024 scale
= int(node
.attributes
['scale'])
2025 if scale
< 1: # or scale > 100:
2026 self
.document
.reporter
.warning(
2027 'scale out of range (%s), using 1.' % (scale
, ))
2029 scale
= scale
* 0.01
2030 except ValueError, e
:
2031 self
.document
.reporter
.warning(
2032 'Invalid scale for image: "%s"' % (
2033 node
.attributes
['scale'], ))
2038 def get_image_scaled_width_height(self
, node
, source
):
2039 scale
= self
.get_image_scale(node
)
2040 width
= self
.get_image_width_height(node
, 'width')
2041 height
= self
.get_image_width_height(node
, 'height')
2044 if Image
is not None and source
in self
.image_dict
:
2045 filename
, destination
= self
.image_dict
[source
]
2046 imageobj
= Image
.open(filename
, 'r')
2047 dpi
= imageobj
.info
.get('dpi', dpi
)
2048 # dpi information can be (xdpi, ydpi) or xydpi
2050 except: dpi
= (dpi
, dpi
)
2054 if width
is None or height
is None:
2055 if imageobj
is None:
2057 'image size not fully specified and PIL not installed')
2058 if width
is None: width
= [imageobj
.size
[0], 'px']
2059 if height
is None: height
= [imageobj
.size
[1], 'px']
2063 if width
[1] == 'px': width
= [width
[0] / dpi
[0], 'in']
2064 if height
[1] == 'px': height
= [height
[0] / dpi
[1], 'in']
2066 width
[0] = str(width
[0])
2067 height
[0] = str(height
[0])
2068 return ''.join(width
), ''.join(height
)
2070 def generate_figure(self
, node
, source
, destination
, current_element
):
2072 width
, height
= self
.get_image_scaled_width_height(node
, source
)
2073 for node1
in node
.parent
.children
:
2074 if node1
.tagname
== 'caption':
2075 caption
= node1
.astext()
2076 self
.image_style_count
+= 1
2078 # Add the style for the caption.
2079 if caption
is not None:
2081 'style:class': 'extra',
2082 'style:family': 'paragraph',
2083 'style:name': 'Caption',
2084 'style:parent-style-name': 'Standard',
2086 el1
= SubElement(self
.automatic_styles
, 'style:style',
2087 attrib
=attrib
, nsdict
=SNSD
)
2089 'fo:margin-bottom': '0.0835in',
2090 'fo:margin-top': '0.0835in',
2091 'text:line-number': '0',
2092 'text:number-lines': 'false',
2094 el2
= SubElement(el1
, 'style:paragraph-properties',
2095 attrib
=attrib
, nsdict
=SNSD
)
2097 'fo:font-size': '12pt',
2098 'fo:font-style': 'italic',
2099 'style:font-name': 'Times',
2100 'style:font-name-complex': 'Lucidasans1',
2101 'style:font-size-asian': '12pt',
2102 'style:font-size-complex': '12pt',
2103 'style:font-style-asian': 'italic',
2104 'style:font-style-complex': 'italic',
2106 el2
= SubElement(el1
, 'style:text-properties',
2107 attrib
=attrib
, nsdict
=SNSD
)
2108 style_name
= 'rstframestyle%d' % self
.image_style_count
2111 'style:name': style_name
,
2112 'style:family': 'graphic',
2113 'style:parent-style-name': self
.rststyle('figureframe'),
2115 el1
= SubElement(self
.automatic_styles
,
2116 'style:style', attrib
=attrib
, nsdict
=SNSD
)
2119 if 'align' in node
.attributes
:
2120 align
= node
.attributes
['align'].split()
2122 if val
in ('left', 'center', 'right'):
2124 elif val
in ('top', 'middle', 'bottom'):
2128 classes
= node
.parent
.attributes
.get('classes')
2129 if classes
and 'wrap' in classes
:
2132 attrib
['style:wrap'] = 'dynamic'
2134 attrib
['style:wrap'] = 'none'
2135 el2
= SubElement(el1
,
2136 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
2138 'draw:style-name': style_name
,
2139 'draw:name': 'Frame1',
2140 'text:anchor-type': 'paragraph',
2141 'draw:z-index': '0',
2143 attrib
['svg:width'] = width
2145 #attrib['svg:height'] = height
2146 el3
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
2148 el4
= SubElement(el3
, 'draw:text-box', attrib
=attrib
)
2150 'text:style-name': self
.rststyle('caption'),
2152 el5
= SubElement(el4
, 'text:p', attrib
=attrib
)
2153 return el3
, el4
, el5
, caption
2155 def generate_image(self
, node
, source
, destination
, current_element
,
2157 width
, height
= self
.get_image_scaled_width_height(node
, source
)
2158 self
.image_style_count
+= 1
2159 style_name
= 'rstframestyle%d' % self
.image_style_count
2162 'style:name': style_name
,
2163 'style:family': 'graphic',
2164 'style:parent-style-name': self
.rststyle('image'),
2166 el1
= SubElement(self
.automatic_styles
,
2167 'style:style', attrib
=attrib
, nsdict
=SNSD
)
2170 if 'align' in node
.attributes
:
2171 align
= node
.attributes
['align'].split()
2173 if val
in ('left', 'center', 'right'):
2175 elif val
in ('top', 'middle', 'bottom'):
2177 if frame_attrs
is None:
2179 'style:vertical-pos': 'top',
2180 'style:vertical-rel': 'paragraph',
2181 'style:horizontal-rel': 'paragraph',
2182 'style:mirror': 'none',
2183 'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
2184 'draw:luminance': '0%',
2185 'draw:contrast': '0%',
2189 'draw:gamma': '100%',
2190 'draw:color-inversion': 'false',
2191 'draw:image-opacity': '100%',
2192 'draw:color-mode': 'standard',
2195 attrib
= frame_attrs
2196 if halign
is not None:
2197 attrib
['style:horizontal-pos'] = halign
2198 if valign
is not None:
2199 attrib
['style:vertical-pos'] = valign
2200 # If there is a classes/wrap directive or we are
2201 # inside a table, add a no-wrap style.
2203 classes
= node
.attributes
.get('classes')
2204 if classes
and 'wrap' in classes
:
2207 attrib
['style:wrap'] = 'dynamic'
2209 attrib
['style:wrap'] = 'none'
2210 # If we are inside a table, add a no-wrap style.
2211 if self
.is_in_table(node
):
2212 attrib
['style:wrap'] = 'none'
2213 el2
= SubElement(el1
,
2214 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
2216 #el = SubElement(current_element, 'text:p',
2217 # attrib={'text:style-name': self.rststyle('textbody')})
2219 'draw:style-name': style_name
,
2220 'draw:name': 'graphics2',
2221 'draw:z-index': '1',
2223 if isinstance(node
.parent
, nodes
.TextElement
):
2224 attrib
['text:anchor-type'] = 'as-char' #vds
2226 attrib
['text:anchor-type'] = 'paragraph'
2227 attrib
['svg:width'] = width
2228 attrib
['svg:height'] = height
2229 el1
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
2230 el2
= SubElement(el1
, 'draw:image', attrib
={
2231 'xlink:href': '%s' % (destination
, ),
2232 'xlink:type': 'simple',
2233 'xlink:show': 'embed',
2234 'xlink:actuate': 'onLoad',
2238 def is_in_table(self
, node
):
2241 if isinstance(node1
, docutils
.nodes
.entry
):
2243 node1
= node1
.parent
2246 def visit_legend(self
, node
):
2247 if isinstance(node
.parent
, docutils
.nodes
.figure
):
2248 el1
= self
.current_element
[-1]
2250 self
.current_element
= el1
2251 self
.paragraph_style_stack
.append(self
.rststyle('legend'))
2253 def depart_legend(self
, node
):
2254 if isinstance(node
.parent
, docutils
.nodes
.figure
):
2255 self
.paragraph_style_stack
.pop()
2256 self
.set_to_parent()
2257 self
.set_to_parent()
2258 self
.set_to_parent()
2260 def visit_line_block(self
, node
):
2261 self
.line_indent_level
+= 1
2262 self
.line_block_level
+= 1
2264 def depart_line_block(self
, node
):
2265 self
.line_indent_level
-= 1
2266 self
.line_block_level
-= 1
2268 def visit_line(self
, node
):
2269 style
= 'lineblock%d' % self
.line_indent_level
2270 el1
= SubElement(self
.current_element
, 'text:p', attrib
={
2271 'text:style-name': self
.rststyle(style
),
2273 self
.current_element
= el1
2275 def depart_line(self
, node
):
2276 self
.set_to_parent()
2278 def visit_literal(self
, node
):
2279 el
= SubElement(self
.current_element
, 'text:span',
2280 attrib
={'text:style-name': self
.rststyle('inlineliteral')})
2281 self
.set_current_element(el
)
2283 def depart_literal(self
, node
):
2284 self
.set_to_parent()
2286 def visit_inline(self
, node
):
2287 styles
= node
.attributes
.get('classes', ())
2289 inline_style
= styles
[0]
2290 el
= SubElement(self
.current_element
, 'text:span',
2291 attrib
={'text:style-name': self
.rststyle(inline_style
)})
2292 self
.set_current_element(el
)
2294 def depart_inline(self
, node
):
2295 self
.set_to_parent()
2297 def _calculate_code_block_padding(self
, line
):
2299 matchobj
= SPACES_PATTERN
.match(line
)
2301 pad
= matchobj
.group()
2304 matchobj
= TABS_PATTERN
.match(line
)
2306 pad
= matchobj
.group()
2307 count
= len(pad
) * 8
2310 def _add_syntax_highlighting(self
, insource
, language
):
2311 lexer
= pygments
.lexers
.get_lexer_by_name(language
, stripall
=True)
2312 if language
in ('latex', 'tex'):
2313 fmtr
= OdtPygmentsLaTeXFormatter(lambda name
, parameters
=():
2314 self
.rststyle(name
, parameters
),
2315 escape_function
=escape_cdata
)
2317 fmtr
= OdtPygmentsProgFormatter(lambda name
, parameters
=():
2318 self
.rststyle(name
, parameters
),
2319 escape_function
=escape_cdata
)
2320 outsource
= pygments
.highlight(insource
, lexer
, fmtr
)
2323 def fill_line(self
, line
):
2324 line
= FILL_PAT1
.sub(self
.fill_func1
, line
)
2325 line
= FILL_PAT2
.sub(self
.fill_func2
, line
)
2328 def fill_func1(self
, matchobj
):
2329 spaces
= matchobj
.group(0)
2330 repl
= '<text:s text:c="%d"/>' % (len(spaces
), )
2333 def fill_func2(self
, matchobj
):
2334 spaces
= matchobj
.group(0)
2335 repl
= ' <text:s text:c="%d"/>' % (len(spaces
) - 1, )
2338 def visit_literal_block(self
, node
):
2339 wrapper1
= '<text:p text:style-name="%s">%%s</text:p>' % (
2340 self
.rststyle('codeblock'), )
2341 source
= node
.astext()
2343 self
.settings
.add_syntax_highlighting
2345 #node.get('hilight', False)
2347 language
= node
.get('language', 'python')
2348 source
= self
._add
_syntax
_highlighting
(source
, language
)
2350 source
= escape_cdata(source
)
2351 lines
= source
.split('\n')
2352 lines1
= ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
2355 for my_line
in lines
:
2356 my_line
= self
.fill_line(my_line
)
2357 my_line
= my_line
.replace(" ", "\n")
2358 my_lines
.append(my_line
)
2359 my_lines_str
= '<text:line-break/>'.join(my_lines
)
2360 my_lines_str2
= wrapper1
% (my_lines_str
, )
2361 lines1
.append(my_lines_str2
)
2362 lines1
.append('</wrappertag1>')
2363 s1
= ''.join(lines1
)
2364 if WhichElementTree
!= "lxml":
2365 s1
= s1
.encode("utf-8")
2366 el1
= etree
.fromstring(s1
)
2367 children
= el1
.getchildren()
2368 for child
in children
:
2369 self
.current_element
.append(child
)
2371 def depart_literal_block(self
, node
):
2374 visit_doctest_block
= visit_literal_block
2375 depart_doctest_block
= depart_literal_block
2377 def visit_meta(self
, node
):
2378 name
= node
.attributes
.get('name')
2379 content
= node
.attributes
.get('content')
2380 if name
is not None and content
is not None:
2381 self
.meta_dict
[name
] = content
2383 def depart_meta(self
, node
):
2386 def visit_option_list(self
, node
):
2387 table_name
= 'tableoption'
2389 # Generate automatic styles
2390 if not self
.optiontablestyles_generated
:
2391 self
.optiontablestyles_generated
= True
2392 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2393 'style:name': self
.rststyle(table_name
),
2394 'style:family': 'table'}, nsdict
=SNSD
)
2395 el1
= SubElement(el
, 'style:table-properties', attrib
={
2396 'style:width': '17.59cm',
2397 'table:align': 'left',
2398 'style:shadow': 'none'}, nsdict
=SNSD
)
2399 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2400 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'A', )),
2401 'style:family': 'table-column'}, nsdict
=SNSD
)
2402 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2403 'style:column-width': '4.999cm'}, nsdict
=SNSD
)
2404 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2405 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'B', )),
2406 'style:family': 'table-column'}, nsdict
=SNSD
)
2407 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2408 'style:column-width': '12.587cm'}, nsdict
=SNSD
)
2409 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2410 'style:name': self
.rststyle(
2411 '%s.%%c%%d' % table_name
, ( 'A', 1, )),
2412 'style:family': 'table-cell'}, nsdict
=SNSD
)
2413 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2414 'fo:background-color': 'transparent',
2415 'fo:padding': '0.097cm',
2416 'fo:border-left': '0.035cm solid #000000',
2417 'fo:border-right': 'none',
2418 'fo:border-top': '0.035cm solid #000000',
2419 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2420 el2
= SubElement(el1
, 'style:background-image', nsdict
=SNSD
)
2421 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2422 'style:name': self
.rststyle(
2423 '%s.%%c%%d' % table_name
, ( 'B', 1, )),
2424 'style:family': 'table-cell'}, nsdict
=SNSD
)
2425 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2426 'fo:padding': '0.097cm',
2427 'fo:border': '0.035cm solid #000000'}, nsdict
=SNSD
)
2428 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2429 'style:name': self
.rststyle(
2430 '%s.%%c%%d' % table_name
, ( 'A', 2, )),
2431 'style:family': 'table-cell'}, nsdict
=SNSD
)
2432 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2433 'fo:padding': '0.097cm',
2434 'fo:border-left': '0.035cm solid #000000',
2435 'fo:border-right': 'none',
2436 'fo:border-top': 'none',
2437 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2438 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2439 'style:name': self
.rststyle(
2440 '%s.%%c%%d' % table_name
, ( 'B', 2, )),
2441 'style:family': 'table-cell'}, nsdict
=SNSD
)
2442 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2443 'fo:padding': '0.097cm',
2444 'fo:border-left': '0.035cm solid #000000',
2445 'fo:border-right': '0.035cm solid #000000',
2446 'fo:border-top': 'none',
2447 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2449 # Generate table data
2450 el
= self
.append_child('table:table', attrib
={
2451 'table:name': self
.rststyle(table_name
),
2452 'table:style-name': self
.rststyle(table_name
),
2454 el1
= SubElement(el
, 'table:table-column', attrib
={
2455 'table:style-name': self
.rststyle(
2456 '%s.%%c' % table_name
, ( 'A', ))})
2457 el1
= SubElement(el
, 'table:table-column', attrib
={
2458 'table:style-name': self
.rststyle(
2459 '%s.%%c' % table_name
, ( 'B', ))})
2460 el1
= SubElement(el
, 'table:table-header-rows')
2461 el2
= SubElement(el1
, 'table:table-row')
2462 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2463 'table:style-name': self
.rststyle(
2464 '%s.%%c%%d' % table_name
, ( 'A', 1, )),
2465 'office:value-type': 'string'})
2466 el4
= SubElement(el3
, 'text:p', attrib
={
2467 'text:style-name': 'Table_20_Heading'})
2469 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2470 'table:style-name': self
.rststyle(
2471 '%s.%%c%%d' % table_name
, ( 'B', 1, )),
2472 'office:value-type': 'string'})
2473 el4
= SubElement(el3
, 'text:p', attrib
={
2474 'text:style-name': 'Table_20_Heading'})
2475 el4
.text
= 'Description'
2476 self
.set_current_element(el
)
2478 def depart_option_list(self
, node
):
2479 self
.set_to_parent()
2481 def visit_option_list_item(self
, node
):
2482 el
= self
.append_child('table:table-row')
2483 self
.set_current_element(el
)
2485 def depart_option_list_item(self
, node
):
2486 self
.set_to_parent()
2488 def visit_option_group(self
, node
):
2489 el
= self
.append_child('table:table-cell', attrib
={
2490 'table:style-name': 'Table%d.A2' % self
.table_count
,
2491 'office:value-type': 'string',
2493 self
.set_current_element(el
)
2495 def depart_option_group(self
, node
):
2496 self
.set_to_parent()
2498 def visit_option(self
, node
):
2499 el
= self
.append_child('text:p', attrib
={
2500 'text:style-name': 'Table_20_Contents'})
2501 el
.text
= node
.astext()
2503 def depart_option(self
, node
):
2506 def visit_option_string(self
, node
):
2509 def depart_option_string(self
, node
):
2512 def visit_option_argument(self
, node
):
2515 def depart_option_argument(self
, node
):
2518 def visit_description(self
, node
):
2519 el
= self
.append_child('table:table-cell', attrib
={
2520 'table:style-name': 'Table%d.B2' % self
.table_count
,
2521 'office:value-type': 'string',
2523 el1
= SubElement(el
, 'text:p', attrib
={
2524 'text:style-name': 'Table_20_Contents'})
2525 el1
.text
= node
.astext()
2526 raise nodes
.SkipChildren()
2528 def depart_description(self
, node
):
2531 def visit_paragraph(self
, node
):
2532 self
.in_paragraph
= True
2534 el
= self
.append_p('header')
2535 elif self
.in_footer
:
2536 el
= self
.append_p('footer')
2538 style_name
= self
.paragraph_style_stack
[-1]
2539 el
= self
.append_child('text:p',
2540 attrib
={'text:style-name': style_name
})
2541 self
.append_pending_ids(el
)
2542 self
.set_current_element(el
)
2544 def depart_paragraph(self
, node
):
2545 self
.in_paragraph
= False
2546 self
.set_to_parent()
2548 self
.header_content
.append(
2549 self
.current_element
.getchildren()[-1])
2550 self
.current_element
.remove(
2551 self
.current_element
.getchildren()[-1])
2552 elif self
.in_footer
:
2553 self
.footer_content
.append(
2554 self
.current_element
.getchildren()[-1])
2555 self
.current_element
.remove(
2556 self
.current_element
.getchildren()[-1])
2558 def visit_problematic(self
, node
):
2561 def depart_problematic(self
, node
):
2564 def visit_raw(self
, node
):
2565 if 'format' in node
.attributes
:
2566 formats
= node
.attributes
['format']
2567 formatlist
= formats
.split()
2568 if 'odt' in formatlist
:
2569 rawstr
= node
.astext()
2570 attrstr
= ' '.join(['%s="%s"' % (k
, v
, )
2571 for k
,v
in CONTENT_NAMESPACE_ATTRIB
.items()])
2572 contentstr
= '<stuff %s>%s</stuff>' % (attrstr
, rawstr
, )
2573 if WhichElementTree
!= "lxml":
2574 contentstr
= contentstr
.encode("utf-8")
2575 content
= etree
.fromstring(contentstr
)
2576 elements
= content
.getchildren()
2577 if len(elements
) > 0:
2581 elif self
.in_footer
:
2584 self
.current_element
.append(el1
)
2585 raise nodes
.SkipChildren()
2587 def depart_raw(self
, node
):
2590 elif self
.in_footer
:
2595 def visit_reference(self
, node
):
2596 text
= node
.astext()
2597 if self
.settings
.create_links
:
2598 if node
.has_key('refuri'):
2599 href
= node
['refuri']
2600 if ( self
.settings
.cloak_email_addresses
2601 and href
.startswith('mailto:')):
2602 href
= self
.cloak_mailto(href
)
2603 el
= self
.append_child('text:a', attrib
={
2604 'xlink:href': '%s' % href
,
2605 'xlink:type': 'simple',
2607 self
.set_current_element(el
)
2608 elif node
.has_key('refid'):
2609 if self
.settings
.create_links
:
2610 href
= node
['refid']
2611 el
= self
.append_child('text:reference-ref', attrib
={
2612 'text:ref-name': '%s' % href
,
2613 'text:reference-format': 'text',
2616 self
.document
.reporter
.warning(
2617 'References must have "refuri" or "refid" attribute.')
2618 if (self
.in_table_of_contents
and
2619 len(node
.children
) >= 1 and
2620 isinstance(node
.children
[0], docutils
.nodes
.generated
)):
2621 node
.remove(node
.children
[0])
2623 def depart_reference(self
, node
):
2624 if self
.settings
.create_links
:
2625 if node
.has_key('refuri'):
2626 self
.set_to_parent()
2628 def visit_rubric(self
, node
):
2629 style_name
= self
.rststyle('rubric')
2630 classes
= node
.get('classes')
2635 el
= SubElement(self
.current_element
, 'text:h', attrib
= {
2636 #'text:outline-level': '%d' % section_level,
2637 #'text:style-name': 'Heading_20_%d' % section_level,
2638 'text:style-name': style_name
,
2640 text
= node
.astext()
2641 el
.text
= self
.encode(text
)
2643 def depart_rubric(self
, node
):
2646 def visit_section(self
, node
, move_ids
=1):
2647 self
.section_level
+= 1
2648 self
.section_count
+= 1
2649 if self
.settings
.create_sections
:
2650 el
= self
.append_child('text:section', attrib
={
2651 'text:name': 'Section%d' % self
.section_count
,
2652 'text:style-name': 'Sect%d' % self
.section_level
,
2654 self
.set_current_element(el
)
2656 def depart_section(self
, node
):
2657 self
.section_level
-= 1
2658 if self
.settings
.create_sections
:
2659 self
.set_to_parent()
2661 def visit_strong(self
, node
):
2662 el
= SubElement(self
.current_element
, 'text:span',
2663 attrib
={'text:style-name': self
.rststyle('strong')})
2664 self
.set_current_element(el
)
2666 def depart_strong(self
, node
):
2667 self
.set_to_parent()
2669 def visit_substitution_definition(self
, node
):
2670 raise nodes
.SkipChildren()
2672 def depart_substitution_definition(self
, node
):
2675 def visit_system_message(self
, node
):
2678 def depart_system_message(self
, node
):
2681 def get_table_style(self
, node
):
2684 use_predefined_table_style
= False
2685 str_classes
= node
.get('classes')
2686 if str_classes
is not None:
2687 for str_class
in str_classes
:
2688 if str_class
.startswith(TABLESTYLEPREFIX
):
2689 table_name
= str_class
2690 use_predefined_table_style
= True
2692 if table_name
is not None:
2693 table_style
= self
.table_styles
.get(table_name
)
2694 if table_style
is None:
2695 # If we can't find the table style, issue warning
2696 # and use the default table style.
2697 self
.document
.reporter
.warning(
2698 'Can\'t find table style "%s". Using default.' % (
2700 table_name
= TABLENAMEDEFAULT
2701 table_style
= self
.table_styles
.get(table_name
)
2702 if table_style
is None:
2703 # If we can't find the default table style, issue a warning
2704 # and use a built-in default style.
2705 self
.document
.reporter
.warning(
2706 'Can\'t find default table style "%s". Using built-in default.' % (
2708 table_style
= BUILTIN_DEFAULT_TABLE_STYLE
2710 table_name
= TABLENAMEDEFAULT
2711 table_style
= self
.table_styles
.get(table_name
)
2712 if table_style
is None:
2713 # If we can't find the default table style, issue a warning
2714 # and use a built-in default style.
2715 self
.document
.reporter
.warning(
2716 'Can\'t find default table style "%s". Using built-in default.' % (
2718 table_style
= BUILTIN_DEFAULT_TABLE_STYLE
2721 def visit_table(self
, node
):
2722 self
.table_count
+= 1
2723 table_style
= self
.get_table_style(node
)
2724 table_name
= '%s%%d' % TABLESTYLEPREFIX
2725 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2726 'style:name': self
.rststyle(
2727 '%s' % table_name
, ( self
.table_count
, )),
2728 'style:family': 'table',
2730 if table_style
.backgroundcolor
is None:
2731 el1_1
= SubElement(el1
, 'style:table-properties', attrib
={
2732 #'style:width': '17.59cm',
2733 'table:align': 'margins',
2734 'fo:margin-top': '0in',
2735 'fo:margin-bottom': '0.10in',
2738 el1_1
= SubElement(el1
, 'style:table-properties', attrib
={
2739 #'style:width': '17.59cm',
2740 'table:align': 'margins',
2741 'fo:margin-top': '0in',
2742 'fo:margin-bottom': '0.10in',
2743 'fo:background-color': table_style
.backgroundcolor
,
2745 # We use a single cell style for all cells in this table.
2746 # That's probably not correct, but seems to work.
2747 el2
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2748 'style:name': self
.rststyle(
2749 '%s.%%c%%d' % table_name
, ( self
.table_count
, 'A', 1, )),
2750 'style:family': 'table-cell',
2752 thickness
= self
.settings
.table_border_thickness
2753 if thickness
is None:
2754 line_style1
= table_style
.border
2756 line_style1
= '0.%03dcm solid #000000' % (thickness
, )
2757 el2_1
= SubElement(el2
, 'style:table-cell-properties', attrib
={
2758 'fo:padding': '0.049cm',
2759 'fo:border-left': line_style1
,
2760 'fo:border-right': line_style1
,
2761 'fo:border-top': line_style1
,
2762 'fo:border-bottom': line_style1
,
2765 for child
in node
.children
:
2766 if child
.tagname
== 'title':
2767 title
= child
.astext()
2769 if title
is not None:
2770 el3
= self
.append_p('table-title', title
)
2773 el4
= SubElement(self
.current_element
, 'table:table', attrib
={
2774 'table:name': self
.rststyle(
2775 '%s' % table_name
, ( self
.table_count
, )),
2776 'table:style-name': self
.rststyle(
2777 '%s' % table_name
, ( self
.table_count
, )),
2779 self
.set_current_element(el4
)
2780 self
.current_table_style
= el1
2781 self
.table_width
= 0
2783 def depart_table(self
, node
):
2784 attribkey
= add_ns('style:width', nsdict
=SNSD
)
2785 attribval
= '%dcm' % self
.table_width
2786 self
.current_table_style
.attrib
[attribkey
] = attribval
2787 self
.set_to_parent()
2789 def visit_tgroup(self
, node
):
2790 self
.column_count
= ord('A') - 1
2792 def depart_tgroup(self
, node
):
2795 def visit_colspec(self
, node
):
2796 self
.column_count
+= 1
2797 colspec_name
= self
.rststyle(
2798 '%s%%d.%%s' % TABLESTYLEPREFIX
,
2799 (self
.table_count
, chr(self
.column_count
), )
2801 colwidth
= node
['colwidth']
2802 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2803 'style:name': colspec_name
,
2804 'style:family': 'table-column',
2806 el1_1
= SubElement(el1
, 'style:table-column-properties', attrib
={
2807 'style:column-width': '%dcm' % colwidth
}, nsdict
=SNSD
)
2808 el2
= self
.append_child('table:table-column', attrib
={
2809 'table:style-name': colspec_name
,
2811 self
.table_width
+= colwidth
2813 def depart_colspec(self
, node
):
2816 def visit_thead(self
, node
):
2817 el
= self
.append_child('table:table-header-rows')
2818 self
.set_current_element(el
)
2819 self
.in_thead
= True
2820 self
.paragraph_style_stack
.append('Table_20_Heading')
2822 def depart_thead(self
, node
):
2823 self
.set_to_parent()
2824 self
.in_thead
= False
2825 self
.paragraph_style_stack
.pop()
2827 def visit_row(self
, node
):
2828 self
.column_count
= ord('A') - 1
2829 el
= self
.append_child('table:table-row')
2830 self
.set_current_element(el
)
2832 def depart_row(self
, node
):
2833 self
.set_to_parent()
2835 def visit_entry(self
, node
):
2836 self
.column_count
+= 1
2837 cellspec_name
= self
.rststyle(
2838 '%s%%d.%%c%%d' % TABLESTYLEPREFIX
,
2839 (self
.table_count
, 'A', 1, )
2842 'table:style-name': cellspec_name
,
2843 'office:value-type': 'string',
2845 morecols
= node
.get('morecols', 0)
2847 attrib
['table:number-columns-spanned'] = '%d' % (morecols
+ 1,)
2848 self
.column_count
+= morecols
2849 morerows
= node
.get('morerows', 0)
2851 attrib
['table:number-rows-spanned'] = '%d' % (morerows
+ 1,)
2852 el1
= self
.append_child('table:table-cell', attrib
=attrib
)
2853 self
.set_current_element(el1
)
2855 def depart_entry(self
, node
):
2856 self
.set_to_parent()
2858 def visit_tbody(self
, node
):
2861 def depart_tbody(self
, node
):
2864 def visit_target(self
, node
):
2866 # I don't know how to implement targets in ODF.
2867 # How do we create a target in oowriter? A cross-reference?
2868 if not (node
.has_key('refuri') or node
.has_key('refid')
2869 or node
.has_key('refname')):
2874 def depart_target(self
, node
):
2877 def visit_title(self
, node
, move_ids
=1, title_type
='title'):
2878 if isinstance(node
.parent
, docutils
.nodes
.section
):
2879 section_level
= self
.section_level
2880 if section_level
> 7:
2881 self
.document
.reporter
.warning(
2882 'Heading/section levels greater than 7 not supported.')
2883 self
.document
.reporter
.warning(
2884 ' Reducing to heading level 7 for heading: "%s"' % (
2887 el1
= self
.append_child('text:h', attrib
= {
2888 'text:outline-level': '%d' % section_level
,
2889 #'text:style-name': 'Heading_20_%d' % section_level,
2890 'text:style-name': self
.rststyle(
2891 'heading%d', (section_level
, )),
2893 self
.append_pending_ids(el1
)
2894 self
.set_current_element(el1
)
2895 elif isinstance(node
.parent
, docutils
.nodes
.document
):
2896 # text = self.settings.title
2898 # text = node.astext()
2899 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2900 'text:style-name': self
.rststyle(title_type
),
2902 self
.append_pending_ids(el1
)
2903 text
= node
.astext()
2905 self
.found_doc_title
= True
2906 self
.set_current_element(el1
)
2908 def depart_title(self
, node
):
2909 if (isinstance(node
.parent
, docutils
.nodes
.section
) or
2910 isinstance(node
.parent
, docutils
.nodes
.document
)):
2911 self
.set_to_parent()
2913 def visit_subtitle(self
, node
, move_ids
=1):
2914 self
.visit_title(node
, move_ids
, title_type
='subtitle')
2916 def depart_subtitle(self
, node
):
2917 self
.depart_title(node
)
2919 def visit_title_reference(self
, node
):
2920 el
= self
.append_child('text:span', attrib
={
2921 'text:style-name': self
.rststyle('quotation')})
2922 el
.text
= self
.encode(node
.astext())
2923 raise nodes
.SkipChildren()
2925 def depart_title_reference(self
, node
):
2928 def generate_table_of_content_entry_template(self
, el1
):
2929 for idx
in range(1, 11):
2930 el2
= SubElement(el1
,
2931 'text:table-of-content-entry-template',
2933 'text:outline-level': "%d" % (idx
, ),
2934 'text:style-name': self
.rststyle('contents-%d' % (idx
, )),
2936 el3
= SubElement(el2
, 'text:index-entry-chapter')
2937 el3
= SubElement(el2
, 'text:index-entry-text')
2938 el3
= SubElement(el2
, 'text:index-entry-tab-stop', attrib
={
2939 'style:leader-char': ".",
2940 'style:type': "right",
2942 el3
= SubElement(el2
, 'text:index-entry-page-number')
2944 def visit_topic(self
, node
):
2945 if 'classes' in node
.attributes
:
2946 if 'contents' in node
.attributes
['classes']:
2947 if self
.settings
.generate_oowriter_toc
:
2948 el1
= self
.append_child('text:table-of-content', attrib
={
2949 'text:name': 'Table of Contents1',
2950 'text:protected': 'true',
2951 'text:style-name': 'Sect1',
2953 el2
= SubElement(el1
,
2954 'text:table-of-content-source',
2956 'text:outline-level': '10',
2958 el3
=SubElement(el2
, 'text:index-title-template', attrib
={
2959 'text:style-name': 'Contents_20_Heading',
2961 el3
.text
= 'Table of Contents'
2962 self
.generate_table_of_content_entry_template(el2
)
2963 el4
= SubElement(el1
, 'text:index-body')
2964 el5
= SubElement(el4
, 'text:index-title')
2965 el6
= SubElement(el5
, 'text:p', attrib
={
2966 'text:style-name': self
.rststyle('contents-heading'),
2968 el6
.text
= 'Table of Contents'
2969 self
.save_current_element
= self
.current_element
2970 self
.table_of_content_index_body
= el4
2971 self
.set_current_element(el4
)
2973 el
= self
.append_p('horizontalline')
2974 el
= self
.append_p('centeredtextbody')
2975 el1
= SubElement(el
, 'text:span',
2976 attrib
={'text:style-name': self
.rststyle('strong')})
2977 el1
.text
= 'Contents'
2978 self
.in_table_of_contents
= True
2979 elif 'abstract' in node
.attributes
['classes']:
2980 el
= self
.append_p('horizontalline')
2981 el
= self
.append_p('centeredtextbody')
2982 el1
= SubElement(el
, 'text:span',
2983 attrib
={'text:style-name': self
.rststyle('strong')})
2984 el1
.text
= 'Abstract'
2986 def depart_topic(self
, node
):
2987 if 'classes' in node
.attributes
:
2988 if 'contents' in node
.attributes
['classes']:
2989 if self
.settings
.generate_oowriter_toc
:
2990 self
.update_toc_page_numbers(
2991 self
.table_of_content_index_body
)
2992 self
.set_current_element(self
.save_current_element
)
2994 el
= self
.append_p('horizontalline')
2995 self
.in_table_of_contents
= False
2997 def update_toc_page_numbers(self
, el
):
2999 self
.update_toc_collect(el
, 0, collection
)
3000 self
.update_toc_add_numbers(collection
)
3002 def update_toc_collect(self
, el
, level
, collection
):
3003 collection
.append((level
, el
))
3005 for child_el
in el
.getchildren():
3006 if child_el
.tag
!= 'text:index-body':
3007 self
.update_toc_collect(child_el
, level
, collection
)
3009 def update_toc_add_numbers(self
, collection
):
3010 for level
, el1
in collection
:
3011 if (el1
.tag
== 'text:p' and
3012 el1
.text
!= 'Table of Contents'):
3013 el2
= SubElement(el1
, 'text:tab')
3017 def visit_transition(self
, node
):
3018 el
= self
.append_p('horizontalline')
3020 def depart_transition(self
, node
):
3026 def visit_warning(self
, node
):
3027 self
.generate_admonition(node
, 'warning')
3029 def depart_warning(self
, node
):
3030 self
.paragraph_style_stack
.pop()
3032 def visit_attention(self
, node
):
3033 self
.generate_admonition(node
, 'attention')
3035 depart_attention
= depart_warning
3037 def visit_caution(self
, node
):
3038 self
.generate_admonition(node
, 'caution')
3040 depart_caution
= depart_warning
3042 def visit_danger(self
, node
):
3043 self
.generate_admonition(node
, 'danger')
3045 depart_danger
= depart_warning
3047 def visit_error(self
, node
):
3048 self
.generate_admonition(node
, 'error')
3050 depart_error
= depart_warning
3052 def visit_hint(self
, node
):
3053 self
.generate_admonition(node
, 'hint')
3055 depart_hint
= depart_warning
3057 def visit_important(self
, node
):
3058 self
.generate_admonition(node
, 'important')
3060 depart_important
= depart_warning
3062 def visit_note(self
, node
):
3063 self
.generate_admonition(node
, 'note')
3065 depart_note
= depart_warning
3067 def visit_tip(self
, node
):
3068 self
.generate_admonition(node
, 'tip')
3070 depart_tip
= depart_warning
3072 def visit_admonition(self
, node
):
3074 for child
in node
.children
:
3075 if child
.tagname
== 'title':
3076 title
= child
.astext()
3078 classes1
= node
.get('classes')
3081 self
.generate_admonition(node
, 'generic', title
)
3083 depart_admonition
= depart_warning
3085 def generate_admonition(self
, node
, label
, title
=None):
3086 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
3087 'text:style-name': self
.rststyle('admon-%s-hdr', ( label
, )),
3092 el1
.text
= '%s!' % (label
.capitalize(), )
3093 s1
= self
.rststyle('admon-%s-body', ( label
, ))
3094 self
.paragraph_style_stack
.append(s1
)
3097 # Roles (e.g. subscript, superscript, strong, ...
3099 def visit_subscript(self
, node
):
3100 el
= self
.append_child('text:span', attrib
={
3101 'text:style-name': 'rststyle-subscript',
3103 self
.set_current_element(el
)
3105 def depart_subscript(self
, node
):
3106 self
.set_to_parent()
3108 def visit_superscript(self
, node
):
3109 el
= self
.append_child('text:span', attrib
={
3110 'text:style-name': 'rststyle-superscript',
3112 self
.set_current_element(el
)
3114 def depart_superscript(self
, node
):
3115 self
.set_to_parent()
3118 # Use an own reader to modify transformations done.
3119 class Reader(standalone
.Reader
):
3121 def get_transforms(self
):
3122 default
= standalone
.Reader
.get_transforms(self
)
3123 if self
.settings
.create_links
:
3127 if i
is not references
.DanglingReferences
]