3 Open Document Format (ODF) Writer.
9 __docformat__
= 'reStructuredText'
17 from xml
.dom
import minidom
25 from docutils
import frontend
, nodes
, utils
, writers
, languages
26 from docutils
.parsers
import rst
27 from docutils
.readers
import standalone
28 from docutils
.transforms
import references
34 from lxml
import etree
35 WhichElementTree
= 'lxml'
36 except ImportError, e
:
38 # 2. Try to use ElementTree from the Python standard library.
39 from xml
.etree
import ElementTree
as etree
40 WhichElementTree
= 'elementtree'
41 except ImportError, e
:
43 # 3. Try to use a version of ElementTree installed as a separate
45 from elementtree
import ElementTree
as etree
46 WhichElementTree
= 'elementtree'
47 except ImportError, e
:
49 print '*** Error: Must install either ElementTree or lxml or'
50 print '*** a version of Python containing ElementTree.'
56 import pygments
.formatter
57 import pygments
.lexers
58 class OdtPygmentsFormatter(pygments
.formatter
.Formatter
):
59 def __init__(self
, rststyle_function
):
60 pygments
.formatter
.Formatter
.__init
__(self
)
61 self
.rststyle_function
= rststyle_function
63 def rststyle(self
, name
, parameters
=( )):
64 return self
.rststyle_function(name
, parameters
)
66 class OdtPygmentsProgFormatter(OdtPygmentsFormatter
):
67 def format(self
, tokensource
, outfile
):
68 tokenclass
= pygments
.token
.Token
69 for ttype
, value
in tokensource
:
70 value
= escape_cdata(value
)
71 if ttype
== tokenclass
.Keyword
:
72 s2
= self
.rststyle('codeblock-keyword')
73 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
75 elif ttype
== tokenclass
.Literal
.String
:
76 s2
= self
.rststyle('codeblock-string')
77 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
80 tokenclass
.Literal
.Number
.Integer
,
81 tokenclass
.Literal
.Number
.Integer
.Long
,
82 tokenclass
.Literal
.Number
.Float
,
83 tokenclass
.Literal
.Number
.Hex
,
84 tokenclass
.Literal
.Number
.Oct
,
85 tokenclass
.Literal
.Number
,
87 s2
= self
.rststyle('codeblock-number')
88 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
90 elif ttype
== tokenclass
.Operator
:
91 s2
= self
.rststyle('codeblock-operator')
92 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
94 elif ttype
== tokenclass
.Comment
:
95 s2
= self
.rststyle('codeblock-comment')
96 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
98 elif ttype
== tokenclass
.Name
.Class
:
99 s2
= self
.rststyle('codeblock-classname')
100 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
102 elif ttype
== tokenclass
.Name
.Function
:
103 s2
= self
.rststyle('codeblock-functionname')
104 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
106 elif ttype
== tokenclass
.Name
:
107 s2
= self
.rststyle('codeblock-name')
108 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
113 class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter
):
114 def format(self
, tokensource
, outfile
):
115 tokenclass
= pygments
.token
.Token
116 for ttype
, value
in tokensource
:
117 value
= escape_cdata(value
)
118 if ttype
== tokenclass
.Keyword
:
119 s2
= self
.rststyle('codeblock-keyword')
120 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
122 elif ttype
in (tokenclass
.Literal
.String
,
123 tokenclass
.Literal
.String
.Backtick
,
125 s2
= self
.rststyle('codeblock-string')
126 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
128 elif ttype
== tokenclass
.Name
.Attribute
:
129 s2
= self
.rststyle('codeblock-operator')
130 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
132 elif ttype
== tokenclass
.Comment
:
133 if value
[-1] == '\n':
134 s2
= self
.rststyle('codeblock-comment')
135 s1
= '<text:span text:style-name="%s">%s</text:span>\n' % \
138 s2
= self
.rststyle('codeblock-comment')
139 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
141 elif ttype
== tokenclass
.Name
.Builtin
:
142 s2
= self
.rststyle('codeblock-name')
143 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
149 except ImportError, e
:
153 # Is the PIL imaging library installed?
156 except ImportError, exp
:
159 ## from IPython.Shell import IPShellEmbed
160 ## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ',
161 ## '-po', 'Out<\\#>: ', '-nosep']
162 ## ipshell = IPShellEmbed(args,
163 ## banner = 'Entering IPython. Press Ctrl-D to exit.',
164 ## exit_msg = 'Leaving Interpreter, back to program.')
168 # ElementTree does not support getparent method (lxml does).
169 # This wrapper class and the following support functions provide
170 # that support for the ability to get the parent of an element.
172 if WhichElementTree
== 'elementtree':
173 class _ElementInterfaceWrapper(etree
._ElementInterface
):
174 def __init__(self
, tag
, attrib
=None):
175 etree
._ElementInterface
.__init
__(self
, tag
, attrib
)
179 def setparent(self
, parent
):
186 # Constants and globals
188 # Turn tracing on/off. See methods trace_visit_node/trace_depart_node.
190 SPACES_PATTERN
= re
.compile(r
'( +)')
191 TABS_PATTERN
= re
.compile(r
'(\t+)')
192 FILL_PAT1
= re
.compile(r
'^ +')
193 FILL_PAT2
= re
.compile(r
' {2,}')
195 TableStylePrefix
= 'Table'
197 GENERATOR_DESC
= 'Docutils.org/odtwriter'
199 NAME_SPACE_1
= 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
201 CONTENT_NAMESPACE_DICT
= CNSD
= {
202 # 'office:version': '1.0',
203 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
204 'dc': 'http://purl.org/dc/elements/1.1/',
205 'dom': 'http://www.w3.org/2001/xml-events',
206 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
207 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
208 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
209 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
210 'math': 'http://www.w3.org/1998/Math/MathML',
211 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
212 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
213 'office': NAME_SPACE_1
,
214 'ooo': 'http://openoffice.org/2004/office',
215 'oooc': 'http://openoffice.org/2004/calc',
216 'ooow': 'http://openoffice.org/2004/writer',
217 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
219 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
220 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
221 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
222 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
223 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
224 'xforms': 'http://www.w3.org/2002/xforms',
225 'xlink': 'http://www.w3.org/1999/xlink',
226 'xsd': 'http://www.w3.org/2001/XMLSchema',
227 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
230 STYLES_NAMESPACE_DICT
= SNSD
= {
231 # 'office:version': '1.0',
232 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
233 'dc': 'http://purl.org/dc/elements/1.1/',
234 'dom': 'http://www.w3.org/2001/xml-events',
235 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
236 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
237 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
238 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
239 'math': 'http://www.w3.org/1998/Math/MathML',
240 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
241 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
242 'office': NAME_SPACE_1
,
243 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
244 'ooo': 'http://openoffice.org/2004/office',
245 'oooc': 'http://openoffice.org/2004/calc',
246 'ooow': 'http://openoffice.org/2004/writer',
247 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
248 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
249 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
250 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
251 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
252 'xlink': 'http://www.w3.org/1999/xlink',
255 MANIFEST_NAMESPACE_DICT
= MANNSD
= {
256 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
259 META_NAMESPACE_DICT
= METNSD
= {
260 # 'office:version': '1.0',
261 'dc': 'http://purl.org/dc/elements/1.1/',
262 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
263 'office': NAME_SPACE_1
,
264 'ooo': 'http://openoffice.org/2004/office',
265 'xlink': 'http://www.w3.org/1999/xlink',
269 # Attribute dictionaries for use with ElementTree (not lxml), which
270 # does not support use of nsmap parameter on Element() and SubElement().
272 CONTENT_NAMESPACE_ATTRIB
= {
273 'office:version': '1.0',
274 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
275 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
276 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
277 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
278 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
279 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
280 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
281 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
282 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
283 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
284 'xmlns:office': NAME_SPACE_1
,
285 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
286 'xmlns:ooo': 'http://openoffice.org/2004/office',
287 'xmlns:oooc': 'http://openoffice.org/2004/calc',
288 'xmlns:ooow': 'http://openoffice.org/2004/writer',
289 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
290 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
291 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
292 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
293 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
294 'xmlns:xforms': 'http://www.w3.org/2002/xforms',
295 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
296 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
297 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
300 STYLES_NAMESPACE_ATTRIB
= {
301 'office:version': '1.0',
302 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
303 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
304 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
305 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
306 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
307 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
308 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
309 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
310 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
311 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
312 'xmlns:office': NAME_SPACE_1
,
313 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0',
314 'xmlns:ooo': 'http://openoffice.org/2004/office',
315 'xmlns:oooc': 'http://openoffice.org/2004/calc',
316 'xmlns:ooow': 'http://openoffice.org/2004/writer',
317 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
318 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
319 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
320 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
321 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
322 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
325 MANIFEST_NAMESPACE_ATTRIB
= {
326 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
329 META_NAMESPACE_ATTRIB
= {
330 'office:version': '1.0',
331 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
332 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
333 'xmlns:office': NAME_SPACE_1
,
334 'xmlns:ooo': 'http://openoffice.org/2004/office',
335 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
344 # ElementTree support functions.
345 # In order to be able to get the parent of elements, must use these
346 # instead of the functions with same name provided by ElementTree.
348 def Element(tag
, attrib
=None, nsmap
=None, nsdict
=CNSD
):
351 tag
, attrib
= fix_ns(tag
, attrib
, nsdict
)
352 if WhichElementTree
== 'lxml':
353 el
= etree
.Element(tag
, attrib
, nsmap
=nsmap
)
355 el
= _ElementInterfaceWrapper(tag
, attrib
)
358 def SubElement(parent
, tag
, attrib
=None, nsmap
=None, nsdict
=CNSD
):
361 tag
, attrib
= fix_ns(tag
, attrib
, nsdict
)
362 if WhichElementTree
== 'lxml':
363 el
= etree
.SubElement(parent
, tag
, attrib
, nsmap
=nsmap
)
365 el
= _ElementInterfaceWrapper(tag
, attrib
)
370 def fix_ns(tag
, attrib
, nsdict
):
371 nstag
= add_ns(tag
, nsdict
)
373 for key
, val
in attrib
.iteritems():
374 nskey
= add_ns(key
, nsdict
)
375 nsattrib
[nskey
] = val
376 return nstag
, nsattrib
378 def add_ns(tag
, nsdict
=CNSD
):
379 if WhichElementTree
== 'lxml':
380 nstag
, name
= tag
.split(':')
381 ns
= nsdict
.get(nstag
)
383 raise RuntimeError, 'Invalid namespace prefix: %s' % nstag
384 tag
= '{%s}%s' % (ns
, name
,)
388 outstream
= StringIO
.StringIO()
390 s1
= outstream
.getvalue()
394 def escape_cdata(text
):
395 text
= text
.replace("&", "&")
396 text
= text
.replace("<", "<")
397 text
= text
.replace(">", ">")
400 if ord(char
) >= ord("\x7f"):
401 ascii
+= "&#x%X;" % ( ord(char
), )
411 # Does this version of Docutils has Directive support?
412 if hasattr(rst
, 'Directive'):
414 # Class to control syntax highlighting.
415 class SyntaxHighlightCodeBlock(rst
.Directive
):
416 required_arguments
= 1
417 optional_arguments
= 0
420 # See visit_literal_block for code that processes the node
423 language
= self
.arguments
[0]
424 code_block
= nodes
.literal_block(classes
=["code-block", language
],
427 content
= '\n'.join(lines
)
428 text_node
= nodes
.Text(content
)
429 code_block
.append(text_node
)
430 # Mark this node for high-lighting so that visit_literal_block
431 # will be able to hight-light those produced here and
432 # *not* high-light regular literal blocks (:: in reST).
433 code_block
['hilight'] = True
434 #import pdb; pdb.set_trace()
437 rst
.directives
.register_directive('sourcecode', SyntaxHighlightCodeBlock
)
438 rst
.directives
.register_directive('code', SyntaxHighlightCodeBlock
)
440 rst
.directives
.register_directive('code-block', SyntaxHighlightCodeBlock
)
443 # Register directives defined in a module named "odtwriter_plugins".
449 name
= 'odtwriter_plugins'
450 fp
, pathname
, description
= imp
.find_module(name
)
451 plugin_mod
= imp
.load_module(name
, fp
, pathname
, description
)
452 #import odtwriter_plugins
453 #plugin_mod = odtwriter_plugins
454 except ImportError, e
:
456 if plugin_mod
is None:
458 klasses
= inspect
.getmembers(plugin_mod
, inspect
.isclass
)
459 for klass
in klasses
:
460 if register_plugin(*klass
):
464 def register_plugin(name
, klass
):
465 plugin_name
= getattr(klass
, 'plugin_name', None)
466 if plugin_name
is not None:
467 rst
.directives
.register_directive(plugin_name
, klass
)
472 WORD_SPLIT_PAT1
= re
.compile(r
'\b(\w*)\b\W*')
474 def split_words(line
):
475 # We need whitespace at the end of the string for our regexpr.
479 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
480 while mo
is not None:
481 word
= mo
.groups()[0]
484 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
489 # Information about the indentation level for lists nested inside
490 # other contexts, e.g. dictionary lists.
491 class ListLevel(object):
492 def __init__(self
, level
, sibling_level
=True, nested_level
=True):
494 self
.sibling_level
= sibling_level
495 self
.nested_level
= nested_level
496 def set_sibling(self
, sibling_level
): self
.sibling_level
= sibling_level
497 def get_sibling(self
): return self
.sibling_level
498 def set_nested(self
, nested_level
): self
.nested_level
= nested_level
499 def get_nested(self
): return self
.nested_level
500 def set_level(self
, level
): self
.level
= level
501 def get_level(self
): return self
.level
504 class Writer(writers
.Writer
):
506 MIME_TYPE
= 'application/vnd.oasis.opendocument.text'
509 supported
= ('html', 'html4css1', 'xhtml')
510 """Formats this writer supports."""
512 default_stylesheet
= 'styles' + EXTENSION
514 default_stylesheet_path
= utils
.relative_path(
515 os
.path
.join(os
.getcwd(), 'dummy'),
516 os
.path
.join(os
.path
.dirname(__file__
), default_stylesheet
))
518 default_template
= 'template.txt'
520 default_template_path
= utils
.relative_path(
521 os
.path
.join(os
.getcwd(), 'dummy'),
522 os
.path
.join(os
.path
.dirname(__file__
), default_template
))
525 'ODF-Specific Options',
528 ('Specify a stylesheet URL, used verbatim. Overrides '
529 '--stylesheet-path.',
531 {'metavar': '<URL>', 'overrides': 'stylesheet_path'}),
532 ('Specify a stylesheet file, relative to the current working '
533 'directory. The path is adjusted relative to the output ODF '
534 'file. Overrides --stylesheet. Default: "%s"'
535 % default_stylesheet_path
,
536 ['--stylesheet-path'],
537 {'metavar': '<file>', 'overrides': 'stylesheet',
538 'default': default_stylesheet_path
}),
539 ('Specify a configuration/mapping file relative to the '
541 'directory for additional ODF options. '
542 'In particular, this file may contain a section named '
543 '"Formats" that maps default style names to '
544 'names to be used in the resulting output file allowing for '
545 'adhering to external standards. '
546 'For more info and the format of the configuration/mapping file, '
547 'see the odtwriter doc.',
548 ['--odf-config-file'],
549 {'metavar': '<file>'}),
550 ('Obfuscate email addresses to confuse harvesters while still '
551 'keeping email links usable with standards-compliant browsers.',
552 ['--cloak-email-addresses'],
554 'action': 'store_true',
555 'dest': 'cloak_email_addresses',
556 'validator': frontend
.validate_boolean
}),
557 ('Do not obfuscate email addresses.',
558 ['--no-cloak-email-addresses'],
560 'action': 'store_false',
561 'dest': 'cloak_email_addresses',
562 'validator': frontend
.validate_boolean
}),
563 ('Specify the thickness of table borders in thousands of a cm. '
565 ['--table-border-thickness'],
567 'validator': frontend
.validate_nonnegative_int
}),
568 ('Add syntax highlighting in literal code blocks.',
569 ['--add-syntax-highlighting'],
571 'action': 'store_true',
572 'dest': 'add_syntax_highlighting',
573 'validator': frontend
.validate_boolean
}),
574 ('Do not add syntax highlighting in literal code blocks. (default)',
575 ['--no-add-syntax-highlighting'],
577 'action': 'store_false',
578 'dest': 'add_syntax_highlighting',
579 'validator': frontend
.validate_boolean
}),
580 ('Create sections for headers. (default)',
581 ['--create-sections'],
583 'action': 'store_true',
584 'dest': 'create_sections',
585 'validator': frontend
.validate_boolean
}),
586 ('Do not create sections for headers.',
587 ['--no-create-sections'],
589 'action': 'store_false',
590 'dest': 'create_sections',
591 'validator': frontend
.validate_boolean
}),
595 'action': 'store_true',
596 'dest': 'create_links',
597 'validator': frontend
.validate_boolean
}),
598 ('Do not create links. (default)',
599 ['--no-create-links'],
601 'action': 'store_false',
602 'dest': 'create_links',
603 'validator': frontend
.validate_boolean
}),
604 ('Generate endnotes at end of document, not footnotes '
605 'at bottom of page.',
606 ['--endnotes-end-doc'],
608 'action': 'store_true',
609 'dest': 'endnotes_end_doc',
610 'validator': frontend
.validate_boolean
}),
611 ('Generate footnotes at bottom of page, not endnotes '
612 'at end of document. (default)',
613 ['--no-endnotes-end-doc'],
615 'action': 'store_false',
616 'dest': 'endnotes_end_doc',
617 'validator': frontend
.validate_boolean
}),
620 settings_defaults
= {
621 'output_encoding_error_handler': 'xmlcharrefreplace',
624 relative_path_settings
= (
628 config_section
= 'opendocument odf writer'
629 config_section_dependencies
= (
634 writers
.Writer
.__init
__(self
)
635 self
.translator_class
= ODFTranslator
638 self
.settings
= self
.document
.settings
639 self
.visitor
= self
.translator_class(self
.document
)
640 self
.document
.walkabout(self
.visitor
)
641 self
.visitor
.add_doc_title()
642 self
.assemble_my_parts()
643 self
.output
= self
.parts
['whole']
645 def assemble_my_parts(self
):
646 """Assemble the `self.parts` dictionary. Extend in subclasses.
648 #ipshell('At assemble_parts')
649 writers
.Writer
.assemble_parts(self
)
650 f
= tempfile
.NamedTemporaryFile()
651 zfile
= zipfile
.ZipFile(f
, 'w', zipfile
.ZIP_DEFLATED
)
652 content
= self
.visitor
.content_astext()
653 self
.write_zip_str(zfile
, 'content.xml', content
)
654 self
.write_zip_str(zfile
, 'mimetype', self
.MIME_TYPE
)
655 s1
= self
.create_manifest()
656 self
.write_zip_str(zfile
, 'META-INF/manifest.xml', s1
)
657 s1
= self
.create_meta()
658 self
.write_zip_str(zfile
, 'meta.xml', s1
)
659 s1
= self
.get_stylesheet()
660 self
.write_zip_str(zfile
, 'styles.xml', s1
)
661 s1
= self
.get_settings()
662 self
.write_zip_str(zfile
, 'settings.xml', s1
)
663 self
.store_embedded_files(zfile
)
668 self
.parts
['whole'] = whole
669 self
.parts
['encoding'] = self
.document
.settings
.output_encoding
670 self
.parts
['version'] = docutils
.__version
__
672 def write_zip_str(self
, zfile
, name
, bytes
):
673 localtime
= time
.localtime(time
.time())
674 zinfo
= zipfile
.ZipInfo(name
, localtime
)
675 # Add some standard UNIX file access permissions (-rw-r--r--).
676 zinfo
.external_attr
= (0x81a4 & 0xFFFF) << 16L
677 zinfo
.compress_type
= zipfile
.ZIP_DEFLATED
678 zfile
.writestr(zinfo
, bytes
)
680 def store_embedded_files(self
, zfile
):
681 embedded_files
= self
.visitor
.get_embedded_file_list()
682 for source
, destination
in embedded_files
:
687 destination1
= destination
.decode('latin-1').encode('utf-8')
688 zfile
.write(source
, destination1
, zipfile
.ZIP_STORED
)
690 print "Error: Can't open file %s." % (source
, )
692 def get_settings(self
):
694 modeled after get_stylesheet
696 stylespath
= utils
.get_stylesheet_reference(self
.settings
,
697 os
.path
.join(os
.getcwd(), 'dummy'))
698 zfile
= zipfile
.ZipFile(stylespath
, 'r')
699 s1
= zfile
.read('settings.xml')
703 def get_stylesheet(self
):
704 """Retrieve the stylesheet from either a .xml file or from
705 a .odt (zip) file. Return the content as a string.
707 stylespath
= utils
.get_stylesheet_reference(self
.settings
,
708 os
.path
.join(os
.getcwd(), 'dummy'))
709 ext
= os
.path
.splitext(stylespath
)[1]
711 stylesfile
= open(stylespath
, 'r')
712 s1
= stylesfile
.read()
714 elif ext
== self
.EXTENSION
:
715 zfile
= zipfile
.ZipFile(stylespath
, 'r')
716 s1
= zfile
.read('styles.xml')
719 raise RuntimeError, 'stylesheet path (%s) must be %s or .xml file' %(stylespath
, self
.EXTENSION
)
720 s1
= self
.visitor
.setup_page(s1
)
723 def assemble_parts(self
):
726 def create_manifest(self
):
727 if WhichElementTree
== 'lxml':
728 root
= Element('manifest:manifest',
729 nsmap
=MANIFEST_NAMESPACE_DICT
,
730 nsdict
=MANIFEST_NAMESPACE_DICT
,
733 root
= Element('manifest:manifest',
734 attrib
=MANIFEST_NAMESPACE_ATTRIB
,
735 nsdict
=MANIFEST_NAMESPACE_DICT
,
737 doc
= etree
.ElementTree(root
)
738 SubElement(root
, 'manifest:file-entry', attrib
={
739 'manifest:media-type': self
.MIME_TYPE
,
740 'manifest:full-path': '/',
742 SubElement(root
, 'manifest:file-entry', attrib
={
743 'manifest:media-type': 'text/xml',
744 'manifest:full-path': 'content.xml',
746 SubElement(root
, 'manifest:file-entry', attrib
={
747 'manifest:media-type': 'text/xml',
748 'manifest:full-path': 'styles.xml',
750 SubElement(root
, 'manifest:file-entry', attrib
={
751 'manifest:media-type': 'text/xml',
752 'manifest:full-path': 'meta.xml',
755 doc
= minidom
.parseString(s1
)
756 s1
= doc
.toprettyxml(' ')
759 def create_meta(self
):
760 if WhichElementTree
== 'lxml':
761 root
= Element('office:document-meta',
762 nsmap
=META_NAMESPACE_DICT
,
763 nsdict
=META_NAMESPACE_DICT
,
766 root
= Element('office:document-meta',
767 attrib
=META_NAMESPACE_ATTRIB
,
768 nsdict
=META_NAMESPACE_DICT
,
770 doc
= etree
.ElementTree(root
)
771 root
= SubElement(root
, 'office:meta', nsdict
=METNSD
)
772 el1
= SubElement(root
, 'meta:generator', nsdict
=METNSD
)
773 el1
.text
= 'Docutils/rst2odf.py/%s' % (VERSION
, )
774 s1
= os
.environ
.get('USER', '')
775 el1
= SubElement(root
, 'meta:initial-creator', nsdict
=METNSD
)
777 s2
= time
.strftime('%Y-%m-%dT%H:%M:%S', time
.localtime())
778 el1
= SubElement(root
, 'meta:creation-date', nsdict
=METNSD
)
780 el1
= SubElement(root
, 'dc:creator', nsdict
=METNSD
)
782 el1
= SubElement(root
, 'dc:date', nsdict
=METNSD
)
784 el1
= SubElement(root
, 'dc:language', nsdict
=METNSD
)
786 el1
= SubElement(root
, 'meta:editing-cycles', nsdict
=METNSD
)
788 el1
= SubElement(root
, 'meta:editing-duration', nsdict
=METNSD
)
789 el1
.text
= 'PT00M01S'
790 title
= self
.visitor
.get_title()
791 el1
= SubElement(root
, 'dc:title', nsdict
=METNSD
)
795 el1
.text
= '[no title]'
796 meta_dict
= self
.visitor
.get_meta_dict()
797 keywordstr
= meta_dict
.get('keywords')
798 if keywordstr
is not None:
799 keywords
= split_words(keywordstr
)
800 for keyword
in keywords
:
801 el1
= SubElement(root
, 'meta:keyword', nsdict
=METNSD
)
803 description
= meta_dict
.get('description')
804 if description
is not None:
805 el1
= SubElement(root
, 'dc:description', nsdict
=METNSD
)
806 el1
.text
= description
808 #doc = minidom.parseString(s1)
809 #s1 = doc.toprettyxml(' ')
812 # class ODFTranslator(nodes.SparseNodeVisitor):
814 class ODFTranslator(nodes
.GenericNodeVisitor
):
817 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
818 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
819 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock',
820 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
821 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
822 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
823 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
824 'epigraph-enumitem', 'epigraph-enumlist', 'footer',
825 'footnote', 'citation',
826 'header', 'highlights', 'highlights-bulletitem',
827 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
828 'horizontalline', 'inlineliteral', 'quotation', 'rubric',
829 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
839 'admon-attention-hdr',
840 'admon-attention-body',
842 'admon-caution-body',
848 'admon-generic-body',
851 'admon-important-hdr',
852 'admon-important-body',
858 'admon-warning-body',
860 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
870 def __init__(self
, document
):
871 #nodes.SparseNodeVisitor.__init__(self, document)
872 nodes
.GenericNodeVisitor
.__init
__(self
, document
)
873 self
.settings
= document
.settings
874 self
.format_map
= { }
875 if self
.settings
.odf_config_file
:
876 from ConfigParser
import ConfigParser
878 parser
= ConfigParser()
879 parser
.read(self
.settings
.odf_config_file
)
880 for rststyle
, format
in parser
.items("Formats"):
881 if rststyle
not in self
.used_styles
:
883 print ('*** Warning: Style "%s" '
884 'is not a style used by odtwriter.' % (
887 #raise RuntimeError, 'Unused style "%s"' % ( rststyle, )
888 self
.format_map
[rststyle
] = format
889 self
.section_level
= 0
890 self
.section_count
= 0
891 # Create ElementTree content and styles documents.
892 if WhichElementTree
== 'lxml':
894 'office:document-content',
895 nsmap
=CONTENT_NAMESPACE_DICT
,
899 'office:document-content',
900 attrib
=CONTENT_NAMESPACE_ATTRIB
,
902 self
.content_tree
= etree
.ElementTree(element
=root
)
903 self
.current_element
= root
904 SubElement(root
, 'office:scripts')
905 SubElement(root
, 'office:font-face-decls')
906 el
= SubElement(root
, 'office:automatic-styles')
907 self
.automatic_styles
= el
908 el
= SubElement(root
, 'office:body')
909 el
= self
.generate_content_element(el
)
910 self
.current_element
= el
911 self
.body_text_element
= el
912 self
.paragraph_style_stack
= [self
.rststyle('textbody'), ]
913 self
.list_style_stack
= []
915 self
.column_count
= ord('A') - 1
916 self
.trace_level
= -1
917 self
.optiontablestyles_generated
= False
918 self
.field_name
= None
919 self
.field_element
= None
922 self
.image_style_count
= 0
924 self
.embedded_file_list
= []
925 self
.syntaxhighlighting
= 1
926 self
.syntaxhighlight_lexer
= 'python'
927 self
.header_content
= []
928 self
.footer_content
= []
929 self
.in_header
= False
930 self
.in_footer
= False
932 self
.in_table_of_contents
= False
933 self
.footnote_ref_dict
= {}
934 self
.footnote_list
= []
935 self
.footnote_chars_idx
= 0
936 self
.footnote_level
= 0
937 self
.pending_ids
= [ ]
938 self
.in_paragraph
= False
939 self
.found_doc_title
= False
940 self
.bumped_list_level_stack
= []
942 self
.line_block_level
= 0
943 self
.line_indent_level
= 0
944 self
.citation_id
= None
946 def add_doc_title(self
):
947 text
= self
.settings
.title
950 if not self
.found_doc_title
:
951 el
= Element('text:p', attrib
= {
952 'text:style-name': self
.rststyle('title'),
955 self
.body_text_element
.insert(0, el
)
957 def rststyle(self
, name
, parameters
=( )):
959 Returns the style name to use for the given style.
961 If `parameters` is given `name` must contain a matching number of ``%`` and
962 is used as a format expression with `parameters` as the value.
964 name1
= name
% parameters
965 stylename
= self
.format_map
.get(name1
, 'rststyle-%s' % name1
)
968 def generate_content_element(self
, root
):
969 return SubElement(root
, 'office:text')
971 def setup_page(self
, content
):
972 root_el
= etree
.fromstring(content
)
973 self
.setup_paper(root_el
)
974 if len(self
.header_content
) > 0 or len(self
.footer_content
) > 0:
975 self
.add_header_footer(root_el
)
976 new_content
= etree
.tostring(root_el
)
979 def setup_paper(self
, root_el
):
981 fin
= os
.popen("paperconf -s 2> /dev/null")
982 w
, h
= map(float, fin
.read().split())
985 w
, h
= 612, 792 # default to Letter
987 if el
.tag
== "{%s}page-layout-properties" % SNSD
["style"] and \
988 not el
.attrib
.has_key("{%s}page-width" % SNSD
["fo"]):
989 el
.attrib
["{%s}page-width" % SNSD
["fo"]] = "%.3fpt" % w
990 el
.attrib
["{%s}page-height" % SNSD
["fo"]] = "%.3fpt" % h
991 el
.attrib
["{%s}margin-left" % SNSD
["fo"]] = \
992 el
.attrib
["{%s}margin-right" % SNSD
["fo"]] = \
994 el
.attrib
["{%s}margin-top" % SNSD
["fo"]] = \
995 el
.attrib
["{%s}margin-bottom" % SNSD
["fo"]] = \
998 for subel
in el
.getchildren(): walk(subel
)
1001 def add_header_footer(self
, root_el
):
1002 path
= '{%s}master-styles' % (NAME_SPACE_1
, )
1003 master_el
= root_el
.find(path
)
1004 if master_el
is None:
1006 path
= '{%s}master-page' % (SNSD
['style'], )
1007 master_el
= master_el
.find(path
)
1008 if master_el
is None:
1011 if len(self
.header_content
) > 0:
1012 if WhichElementTree
== 'lxml':
1013 el2
= SubElement(el1
, 'style:header', nsdict
=SNSD
)
1015 el2
= SubElement(el1
, 'style:header',
1016 attrib
=STYLES_NAMESPACE_ATTRIB
,
1017 nsdict
=STYLES_NAMESPACE_DICT
,
1019 for el
in self
.header_content
:
1020 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1021 el
.attrib
[attrkey
] = self
.rststyle('header')
1023 if len(self
.footer_content
) > 0:
1024 if WhichElementTree
== 'lxml':
1025 el2
= SubElement(el1
, 'style:footer', nsdict
=SNSD
)
1027 el2
= SubElement(el1
, 'style:footer',
1028 attrib
=STYLES_NAMESPACE_ATTRIB
,
1029 nsdict
=STYLES_NAMESPACE_DICT
,
1031 for el
in self
.footer_content
:
1032 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1033 el
.attrib
[attrkey
] = self
.rststyle('footer')
1035 #new_tree = etree.ElementTree(root_el)
1036 #new_content = ToString(new_tree)
1039 root
= self
.content_tree
.getroot()
1040 et
= etree
.ElementTree(root
)
1044 def content_astext(self
):
1045 return self
.astext()
1047 def set_title(self
, title
): self
.title
= title
1048 def get_title(self
): return self
.title
1049 def set_embedded_file_list(self
, embedded_file_list
):
1050 self
.embedded_file_list
= embedded_file_list
1051 def get_embedded_file_list(self
): return self
.embedded_file_list
1052 def get_meta_dict(self
): return self
.meta_dict
1054 def process_footnotes(self
):
1055 for node
, el1
in self
.footnote_list
:
1056 backrefs
= node
.attributes
.get('backrefs', [])
1058 for ref
in backrefs
:
1059 el2
= self
.footnote_ref_dict
.get(ref
)
1063 el3
= copy
.deepcopy(el1
)
1066 children
= el2
.getchildren()
1067 if len(children
) > 0: # and 'id' in el2.attrib:
1070 attribkey
= add_ns('text:id', nsdict
=SNSD
)
1071 id1
= el2
.get(attribkey
, 'footnote-error')
1074 tag
= add_ns('text:note-ref', nsdict
=SNSD
)
1076 if self
.settings
.endnotes_end_doc
:
1077 note_class
= 'endnote'
1079 note_class
= 'footnote'
1081 attribkey
= add_ns('text:note-class', nsdict
=SNSD
)
1082 el2
.attrib
[attribkey
] = note_class
1083 attribkey
= add_ns('text:ref-name', nsdict
=SNSD
)
1084 el2
.attrib
[attribkey
] = id1
1085 attribkey
= add_ns('text:reference-format', nsdict
=SNSD
)
1086 el2
.attrib
[attribkey
] = 'page'
1092 def append_child(self
, tag
, attrib
=None, parent
=None):
1094 parent
= self
.current_element
1096 el
= SubElement(parent
, tag
)
1098 el
= SubElement(parent
, tag
, attrib
)
1101 def append_p(self
, style
, text
=None):
1102 result
= self
.append_child('text:p', attrib
={
1103 'text:style-name': self
.rststyle(style
)})
1104 self
.append_pending_ids(result
)
1105 if text
is not None:
1109 def append_pending_ids(self
, el
):
1110 if self
.settings
.create_links
:
1111 for id in self
.pending_ids
:
1112 SubElement(el
, 'text:reference-mark', attrib
={
1114 self
.pending_ids
= [ ]
1116 def set_current_element(self
, el
):
1117 self
.current_element
= el
1119 def set_to_parent(self
):
1120 self
.current_element
= self
.current_element
.getparent()
1122 def generate_labeled_block(self
, node
, label
):
1123 el
= self
.append_p('textbody')
1124 el1
= SubElement(el
, 'text:span',
1125 attrib
={'text:style-name': self
.rststyle('strong')})
1127 el
= self
.append_p('blockindent')
1130 def generate_labeled_line(self
, node
, label
):
1131 el
= self
.append_p('textbody')
1132 el1
= SubElement(el
, 'text:span',
1133 attrib
={'text:style-name': self
.rststyle('strong')})
1135 el1
.tail
= node
.astext()
1138 def encode(self
, text
):
1139 text
= text
.replace(u
'\u00a0', " ")
1142 def trace_visit_node(self
, node
):
1144 self
.trace_level
+= 1
1145 self
._trace
_show
_level
(self
.trace_level
)
1147 print '(visit_%s) node: %s' % (node
.tagname
, node
.astext(), )
1149 print '(visit_%s)' % node
.tagname
1151 def trace_depart_node(self
, node
):
1154 self
._trace
_show
_level
(self
.trace_level
)
1155 print '(depart_%s)' % node
.tagname
1156 self
.trace_level
-= 1
1158 def _trace_show_level(self
, level
):
1159 for idx
in range(level
):
1165 # In alphabetic order, more or less.
1166 # See docutils.docutils.nodes.node_class_names.
1169 def dispatch_visit(self
, node
):
1170 """Override to catch basic attributes which many nodes have."""
1171 self
.handle_basic_atts(node
)
1172 nodes
.GenericNodeVisitor
.dispatch_visit(self
, node
)
1174 def handle_basic_atts(self
, node
):
1175 if isinstance(node
, nodes
.Element
) and node
['ids']:
1176 self
.pending_ids
+= node
['ids']
1178 def default_visit(self
, node
):
1179 #ipshell('At default_visit')
1180 print 'missing visit_%s' % (node
.tagname
, )
1182 def default_departure(self
, node
):
1183 print 'missing depart_%s' % (node
.tagname
, )
1185 def visit_Text(self
, node
):
1186 #ipshell('At visit_Text')
1187 # Skip nodes whose text has been processed in parent nodes.
1188 if isinstance(node
.parent
, docutils
.nodes
.literal_block
):
1189 #isinstance(node.parent, docutils.nodes.term) or \
1190 #isinstance(node.parent, docutils.nodes.definition):
1192 text
= node
.astext()
1193 # Are we in mixed content? If so, add the text to the
1194 # etree tail of the previous sibling element.
1195 if len(self
.current_element
.getchildren()) > 0:
1196 if self
.current_element
.getchildren()[-1].tail
:
1197 self
.current_element
.getchildren()[-1].tail
+= text
1199 self
.current_element
.getchildren()[-1].tail
= text
1201 if self
.current_element
.text
:
1202 self
.current_element
.text
+= text
1204 self
.current_element
.text
= text
1206 def depart_Text(self
, node
):
1210 # Pre-defined fields
1213 def visit_address(self
, node
):
1214 #ipshell('At visit_address')
1215 el
= self
.generate_labeled_block(node
, 'Address: ')
1216 self
.set_current_element(el
)
1218 def depart_address(self
, node
):
1219 self
.set_to_parent()
1221 def visit_author(self
, node
):
1222 if isinstance(node
.parent
, nodes
.authors
):
1223 el
= self
.append_p('blockindent')
1225 el
= self
.generate_labeled_block(node
, 'Author: ')
1226 self
.set_current_element(el
)
1228 def depart_author(self
, node
):
1229 self
.set_to_parent()
1231 def visit_authors(self
, node
):
1232 #ipshell('At visit_authors')
1233 #self.trace_visit_node(node)
1235 el
= self
.append_p('textbody')
1236 el1
= SubElement(el
, 'text:span',
1237 attrib
={'text:style-name': self
.rststyle('strong')})
1240 def depart_authors(self
, node
):
1241 #self.trace_depart_node(node)
1244 def visit_contact(self
, node
):
1245 el
= self
.generate_labeled_block(node
, 'Contact: ')
1246 self
.set_current_element(el
)
1248 def depart_contact(self
, node
):
1249 self
.set_to_parent()
1251 def visit_copyright(self
, node
):
1252 el
= self
.generate_labeled_block(node
, 'Copyright: ')
1253 self
.set_current_element(el
)
1255 def depart_copyright(self
, node
):
1256 self
.set_to_parent()
1258 def visit_date(self
, node
):
1259 self
.generate_labeled_line(node
, 'Date: ')
1261 def depart_date(self
, node
):
1264 def visit_organization(self
, node
):
1265 el
= self
.generate_labeled_block(node
, 'Organization: ')
1266 self
.set_current_element(el
)
1268 def depart_organization(self
, node
):
1269 self
.set_to_parent()
1271 def visit_status(self
, node
):
1272 el
= self
.generate_labeled_block(node
, 'Status: ')
1273 self
.set_current_element(el
)
1275 def depart_status(self
, node
):
1276 self
.set_to_parent()
1278 def visit_revision(self
, node
):
1279 self
.generate_labeled_line(node
, 'Revision: ')
1281 def depart_revision(self
, node
):
1284 def visit_version(self
, node
):
1285 el
= self
.generate_labeled_line(node
, 'Version: ')
1286 #self.set_current_element(el)
1288 def depart_version(self
, node
):
1289 #self.set_to_parent()
1292 def visit_attribution(self
, node
):
1293 #ipshell('At visit_attribution')
1294 el
= self
.append_p('attribution', node
.astext())
1296 def depart_attribution(self
, node
):
1297 #ipshell('At depart_attribution')
1300 def visit_block_quote(self
, node
):
1301 #ipshell('At visit_block_quote')
1302 if 'epigraph' in node
.attributes
['classes']:
1303 self
.paragraph_style_stack
.append(self
.rststyle('epigraph'))
1304 self
.blockstyle
= self
.rststyle('epigraph')
1305 elif 'highlights' in node
.attributes
['classes']:
1306 self
.paragraph_style_stack
.append(self
.rststyle('highlights'))
1307 self
.blockstyle
= self
.rststyle('highlights')
1309 self
.paragraph_style_stack
.append(self
.rststyle('blockquote'))
1310 self
.blockstyle
= self
.rststyle('blockquote')
1311 self
.line_indent_level
+= 1
1313 def depart_block_quote(self
, node
):
1314 self
.paragraph_style_stack
.pop()
1315 self
.blockstyle
= ''
1316 self
.line_indent_level
-= 1
1318 def visit_bullet_list(self
, node
):
1319 #ipshell('At visit_bullet_list')
1320 if self
.in_table_of_contents
:
1321 if node
.has_key('classes') and \
1322 'auto-toc' in node
.attributes
['classes']:
1323 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1324 'text:style-name': self
.rststyle('tocenumlist'),
1326 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1328 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1329 'text:style-name': self
.rststyle('tocbulletlist'),
1331 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1333 if self
.blockstyle
== self
.rststyle('blockquote'):
1334 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1335 'text:style-name': self
.rststyle('blockquote-bulletlist'),
1337 self
.list_style_stack
.append(self
.rststyle('blockquote-bulletitem'))
1338 elif self
.blockstyle
== self
.rststyle('highlights'):
1339 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1340 'text:style-name': self
.rststyle('highlights-bulletlist'),
1342 self
.list_style_stack
.append(self
.rststyle('highlights-bulletitem'))
1343 elif self
.blockstyle
== self
.rststyle('epigraph'):
1344 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1345 'text:style-name': self
.rststyle('epigraph-bulletlist'),
1347 self
.list_style_stack
.append(self
.rststyle('epigraph-bulletitem'))
1349 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1350 'text:style-name': self
.rststyle('bulletlist'),
1352 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1353 self
.set_current_element(el
)
1355 def depart_bullet_list(self
, node
):
1356 self
.set_to_parent()
1357 self
.list_style_stack
.pop()
1359 def visit_caption(self
, node
):
1360 raise nodes
.SkipChildren()
1363 def depart_caption(self
, node
):
1366 def visit_comment(self
, node
):
1367 #ipshell('At visit_comment')
1368 el
= self
.append_p('textbody')
1369 el1
= SubElement(el
, 'office:annotation', attrib
={})
1370 el2
= SubElement(el1
, 'text:p', attrib
={})
1371 el2
.text
= node
.astext()
1373 def depart_comment(self
, node
):
1376 def visit_compound(self
, node
):
1377 # The compound directive currently receives no special treatment.
1380 def depart_compound(self
, node
):
1383 def visit_container(self
, node
):
1384 styles
= node
.attributes
.get('classes', ())
1386 self
.paragraph_style_stack
.append(self
.rststyle(styles
[0]))
1388 def depart_container(self
, node
):
1389 #ipshell('At depart_container')
1390 styles
= node
.attributes
.get('classes', ())
1392 self
.paragraph_style_stack
.pop()
1394 def visit_decoration(self
, node
):
1396 #ipshell('At visit_decoration')
1398 #self.trace_visit_node(node)
1401 def depart_decoration(self
, node
):
1402 #ipshell('At depart_decoration')
1405 def visit_definition(self
, node
):
1406 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1407 self
.bumped_list_level_stack
.append(ListLevel(1))
1409 def depart_definition(self
, node
):
1410 self
.paragraph_style_stack
.pop()
1411 self
.bumped_list_level_stack
.pop()
1413 def visit_definition_list(self
, node
):
1416 def depart_definition_list(self
, node
):
1419 def visit_definition_list_item(self
, node
):
1422 def depart_definition_list_item(self
, node
):
1425 def visit_term(self
, node
):
1426 #ipshell('At visit_term')
1427 el
= self
.append_p('textbody')
1428 el1
= SubElement(el
, 'text:span',
1429 attrib
={'text:style-name': self
.rststyle('strong')})
1430 #el1.text = node.astext()
1431 self
.set_current_element(el1
)
1433 def depart_term(self
, node
):
1434 #ipshell('At depart_term')
1435 self
.set_to_parent()
1436 self
.set_to_parent()
1438 def visit_classifier(self
, node
):
1439 #ipshell('At visit_classifier')
1440 els
= self
.current_element
.getchildren()
1443 el1
= SubElement(el
, 'text:span',
1444 attrib
={'text:style-name': self
.rststyle('emphasis')
1446 el1
.text
= ' (%s)' % (node
.astext(), )
1448 def depart_classifier(self
, node
):
1451 def visit_document(self
, node
):
1452 #ipshell('At visit_document')
1455 def depart_document(self
, node
):
1456 self
.process_footnotes()
1458 def visit_docinfo(self
, node
):
1459 #self.trace_visit_node(node)
1460 self
.section_level
+= 1
1461 self
.section_count
+= 1
1462 if self
.settings
.create_sections
:
1463 el
= self
.append_child('text:section', attrib
={
1464 'text:name': 'Section%d' % self
.section_count
,
1465 'text:style-name': 'Sect%d' % self
.section_level
,
1467 self
.set_current_element(el
)
1469 def depart_docinfo(self
, node
):
1470 #self.trace_depart_node(node)
1471 self
.section_level
-= 1
1472 if self
.settings
.create_sections
:
1473 self
.set_to_parent()
1475 def visit_emphasis(self
, node
):
1476 el
= SubElement(self
.current_element
, 'text:span',
1477 attrib
={'text:style-name': self
.rststyle('emphasis')})
1478 self
.set_current_element(el
)
1480 def depart_emphasis(self
, node
):
1481 self
.set_to_parent()
1483 def visit_enumerated_list(self
, node
):
1484 el1
= self
.current_element
1485 if self
.blockstyle
== self
.rststyle('blockquote'):
1486 el2
= SubElement(el1
, 'text:list', attrib
={
1487 'text:style-name': self
.rststyle('blockquote-enumlist'),
1489 self
.list_style_stack
.append(self
.rststyle('blockquote-enumitem'))
1490 elif self
.blockstyle
== self
.rststyle('highlights'):
1491 el2
= SubElement(el1
, 'text:list', attrib
={
1492 'text:style-name': self
.rststyle('highlights-enumlist'),
1494 self
.list_style_stack
.append(self
.rststyle('highlights-enumitem'))
1495 elif self
.blockstyle
== self
.rststyle('epigraph'):
1496 el2
= SubElement(el1
, 'text:list', attrib
={
1497 'text:style-name': self
.rststyle('epigraph-enumlist'),
1499 self
.list_style_stack
.append(self
.rststyle('epigraph-enumitem'))
1501 el2
= SubElement(el1
, 'text:list', attrib
={
1502 'text:style-name': self
.rststyle('enumlist'),
1504 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1505 self
.set_current_element(el2
)
1507 def depart_enumerated_list(self
, node
):
1508 self
.set_to_parent()
1509 self
.list_style_stack
.pop()
1511 def visit_list_item(self
, node
):
1512 #ipshell('At visit_list_item')
1513 el1
= self
.append_child('text:list-item')
1514 # If we are in a "bumped" list level, then wrap this
1515 # list in an outer lists in order to increase the
1516 # indentation level.
1518 if len(self
.bumped_list_level_stack
) > 0:
1519 level_obj
= self
.bumped_list_level_stack
[-1]
1520 if level_obj
.get_sibling():
1521 level_obj
.set_nested(False)
1522 for level_obj1
in self
.bumped_list_level_stack
:
1523 for idx
in range(level_obj1
.get_level()):
1524 el2
= self
.append_child('text:list', parent
=el3
)
1525 el3
= self
.append_child('text:list-item', parent
=el2
)
1526 self
.paragraph_style_stack
.append(self
.list_style_stack
[-1])
1527 self
.set_current_element(el3
)
1529 def depart_list_item(self
, node
):
1530 if len(self
.bumped_list_level_stack
) > 0:
1531 level_obj
= self
.bumped_list_level_stack
[-1]
1532 if level_obj
.get_sibling():
1533 level_obj
.set_nested(True)
1534 for level_obj1
in self
.bumped_list_level_stack
:
1535 for idx
in range(level_obj1
.get_level()):
1536 self
.set_to_parent()
1537 self
.set_to_parent()
1538 self
.paragraph_style_stack
.pop()
1539 self
.set_to_parent()
1541 def visit_header(self
, node
):
1542 #ipshell('At visit_header')
1543 self
.in_header
= True
1545 def depart_header(self
, node
):
1546 #ipshell('At depart_header')
1547 self
.in_header
= False
1549 def visit_footer(self
, node
):
1550 #ipshell('At visit_footer')
1551 self
.in_footer
= True
1553 def depart_footer(self
, node
):
1554 #ipshell('At depart_footer')
1555 self
.in_footer
= False
1557 def visit_field(self
, node
):
1560 def depart_field(self
, node
):
1563 def visit_field_list(self
, node
):
1564 #ipshell('At visit_field_list')
1567 def depart_field_list(self
, node
):
1568 #ipshell('At depart_field_list')
1571 def visit_field_name(self
, node
):
1572 #ipshell('At visit_field_name')
1573 #self.trace_visit_node(node)
1574 el
= self
.append_p('textbody')
1575 el1
= SubElement(el
, 'text:span',
1576 attrib
={'text:style-name': self
.rststyle('strong')})
1577 el1
.text
= node
.astext()
1579 def depart_field_name(self
, node
):
1580 #self.trace_depart_node(node)
1583 def visit_field_body(self
, node
):
1584 #ipshell('At visit_field_body')
1585 #self.trace_visit_node(node)
1586 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1588 def depart_field_body(self
, node
):
1589 #self.trace_depart_node(node)
1590 self
.paragraph_style_stack
.pop()
1592 def visit_figure(self
, node
):
1593 #ipshell('At visit_figure')
1594 #self.trace_visit_node(node)
1597 def depart_figure(self
, node
):
1598 #self.trace_depart_node(node)
1601 def visit_footnote(self
, node
):
1602 #ipshell('At visit_footnote')
1603 self
.footnote_level
+= 1
1604 self
.save_footnote_current
= self
.current_element
1605 el1
= Element('text:note-body')
1606 self
.current_element
= el1
1607 self
.footnote_list
.append((node
, el1
))
1608 if isinstance(node
, docutils
.nodes
.citation
):
1609 self
.paragraph_style_stack
.append(self
.rststyle('citation'))
1611 self
.paragraph_style_stack
.append(self
.rststyle('footnote'))
1613 def depart_footnote(self
, node
):
1614 #ipshell('At depart_footnote')
1615 self
.paragraph_style_stack
.pop()
1616 self
.current_element
= self
.save_footnote_current
1617 self
.footnote_level
-= 1
1626 def visit_footnote_reference(self
, node
):
1627 #ipshell('At visit_footnote_reference')
1628 if self
.footnote_level
<= 0:
1629 id = node
.attributes
['ids'][0]
1630 refid
= node
.attributes
.get('refid')
1633 if self
.settings
.endnotes_end_doc
:
1634 note_class
= 'endnote'
1636 note_class
= 'footnote'
1637 el1
= self
.append_child('text:note', attrib
={
1638 'text:id': '%s' % (refid
, ),
1639 'text:note-class': note_class
,
1641 note_auto
= str(node
.attributes
.get('auto', 1))
1642 if isinstance(node
, docutils
.nodes
.citation_reference
):
1643 citation
= '[%s]' % node
.astext()
1644 el2
= SubElement(el1
, 'text:note-citation', attrib
={
1645 'text:label': citation
,
1648 elif note_auto
== '1':
1649 el2
= SubElement(el1
, 'text:note-citation')
1650 el2
.text
= node
.astext()
1651 elif note_auto
== '*':
1652 if self
.footnote_chars_idx
>= len(
1653 ODFTranslator
.footnote_chars
):
1654 self
.footnote_chars_idx
= 0
1655 footnote_char
= ODFTranslator
.footnote_chars
[
1656 self
.footnote_chars_idx
]
1657 self
.footnote_chars_idx
+= 1
1658 el2
= SubElement(el1
, 'text:note-citation', attrib
={
1659 'text:label': footnote_char
,
1661 el2
.text
= footnote_char
1662 self
.footnote_ref_dict
[id] = el1
1663 raise nodes
.SkipChildren()
1665 def depart_footnote_reference(self
, node
):
1666 #ipshell('At depart_footnote_reference')
1669 def visit_citation(self
, node
):
1670 #ipshell('At visit_citation')
1671 for id in node
.attributes
['ids']:
1672 self
.citation_id
= id
1674 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1675 self
.bumped_list_level_stack
.append(ListLevel(1))
1677 def depart_citation(self
, node
):
1678 #ipshell('At depart_citation')
1679 self
.citation_id
= None
1680 self
.paragraph_style_stack
.pop()
1681 self
.bumped_list_level_stack
.pop()
1683 def visit_citation_reference(self
, node
):
1684 #ipshell('At visit_citation_reference')
1685 if self
.settings
.create_links
:
1686 id = node
.attributes
['refid']
1687 el
= self
.append_child('text:reference-ref', attrib
={
1688 'text:ref-name': '%s' % (id, ),
1689 'text:reference-format': 'text',
1692 self
.set_current_element(el
)
1693 elif self
.current_element
.text
is None:
1694 self
.current_element
.text
= '['
1696 self
.current_element
.text
+= '['
1698 def depart_citation_reference(self
, node
):
1699 #ipshell('At depart_citation_reference')
1700 self
.current_element
.text
+= ']'
1701 if self
.settings
.create_links
:
1702 self
.set_to_parent()
1704 # visit_citation = visit_footnote
1705 # depart_citation = depart_footnote
1706 # visit_citation_reference = visit_footnote_reference
1707 # depart_citation_reference = depart_footnote_reference
1709 def visit_label(self
, node
):
1710 #ipshell('At visit_label')
1711 if isinstance(node
.parent
, docutils
.nodes
.footnote
):
1712 raise nodes
.SkipChildren()
1713 elif self
.citation_id
is not None:
1714 el
= self
.append_p('textbody')
1715 self
.set_current_element(el
)
1717 if self
.settings
.create_links
:
1718 el1
= self
.append_child('text:reference-mark-start', attrib
={
1719 'text:name': '%s' % (self
.citation_id
, ),
1722 def depart_label(self
, node
):
1723 #ipshell('At depart_label')
1724 if isinstance(node
.parent
, docutils
.nodes
.footnote
):
1726 elif self
.citation_id
is not None:
1727 self
.current_element
.text
+= ']'
1728 if self
.settings
.create_links
:
1729 el
= self
.append_child('text:reference-mark-end', attrib
={
1730 'text:name': '%s' % (self
.citation_id
, ),
1732 self
.set_to_parent()
1734 def visit_generated(self
, node
):
1737 def depart_generated(self
, node
):
1740 def check_file_exists(self
, path
):
1741 if os
.path
.exists(path
):
1746 def visit_image(self
, node
):
1747 #ipshell('At visit_image')
1748 #self.trace_visit_node(node)
1749 # Capture the image file.
1750 if 'uri' in node
.attributes
:
1751 source
= node
.attributes
['uri']
1752 if not self
.check_file_exists(source
):
1753 print 'Error: Cannot find image file %s.' % (source
, )
1757 if source
in self
.image_dict
:
1758 filename
, destination
= self
.image_dict
[source
]
1760 self
.image_count
+= 1
1761 filename
= os
.path
.split(source
)[1]
1762 destination
= 'Pictures/1%08x%s' % (self
.image_count
, filename
, )
1763 spec
= (os
.path
.abspath(source
), destination
,)
1765 self
.embedded_file_list
.append(spec
)
1766 self
.image_dict
[source
] = (source
, destination
,)
1767 # Is this a figure (containing an image) or just a plain image?
1768 if self
.in_paragraph
:
1769 el1
= self
.current_element
1771 el1
= SubElement(self
.current_element
, 'text:p',
1772 attrib
={'text:style-name': self
.rststyle('textbody')})
1774 if isinstance(node
.parent
, docutils
.nodes
.figure
):
1775 el3
, el4
, caption
= self
.generate_figure(node
, source
,
1779 'draw:color-inversion': 'false',
1780 'draw:color-mode': 'standard',
1781 'draw:contrast': '0%',
1782 'draw:gamma': '100%',
1784 'draw:image-opacity': '100%',
1785 'draw:luminance': '0%',
1787 'fo:border': 'none',
1788 'fo:clip': 'rect(0in 0in 0in 0in)',
1789 'fo:margin-bottom': '0in',
1790 'fo:margin-left': '0in',
1791 'fo:margin-right': '0in',
1792 'fo:margin-top': '0in',
1793 'fo:padding': '0in',
1794 'style:horizontal-pos': 'from-left',
1795 'style:horizontal-rel': 'paragraph-content',
1796 'style:mirror': 'none',
1797 'style:run-through': 'foreground',
1798 'style:shadow': 'none',
1799 'style:vertical-pos': 'from-top',
1800 'style:vertical-rel': 'paragraph-content',
1801 'style:wrap': 'none',
1803 el5
, width
= self
.generate_image(node
, source
, destination
,
1805 if caption
is not None:
1807 else: #if isinstance(node.parent, docutils.nodes.image):
1808 el3
= self
.generate_image(node
, source
, destination
, el2
)
1810 def depart_image(self
, node
):
1813 def get_image_width_height(self
, node
, attr
):
1815 if attr
in node
.attributes
:
1816 size
= node
.attributes
[attr
]
1824 except ValueError, e
:
1825 print 'Error: Invalid %s for image: "%s"' % (
1826 attr
, node
.attributes
[attr
])
1830 def get_image_scale(self
, node
):
1831 if 'scale' in node
.attributes
:
1833 scale
= int(node
.attributes
['scale'])
1834 if scale
< 1: # or scale > 100:
1836 scale
= scale
* 0.01
1837 except ValueError, e
:
1838 print 'Error: Invalid scale for image: "%s"' % (
1839 node
.attributes
['scale'], )
1844 def get_image_scaled_width_height(self
, node
, source
):
1845 scale
= self
.get_image_scale(node
)
1846 width
= self
.get_image_width_height(node
, 'width')
1847 height
= self
.get_image_width_height(node
, 'height')
1850 if Image
is not None and source
in self
.image_dict
:
1851 filename
, destination
= self
.image_dict
[source
]
1852 imageobj
= Image
.open(filename
, 'r')
1853 dpi
= imageobj
.info
.get('dpi', dpi
)
1854 # dpi information can be (xdpi, ydpi) or xydpi
1856 except: dpi
= (dpi
, dpi
)
1860 if width
is None or height
is None:
1861 if imageobj
is None:
1862 raise RuntimeError, 'image size not fully specified and PIL not installed'
1863 if width
is None: width
= [imageobj
.size
[0], 'px']
1864 if height
is None: height
= [imageobj
.size
[1], 'px']
1868 if width
[1] == 'px': width
= [width
[0] / dpi
[0], 'in']
1869 if height
[1] == 'px': height
= [height
[0] / dpi
[1], 'in']
1871 width
[0] = str(width
[0])
1872 height
[0] = str(height
[0])
1873 return ''.join(width
), ''.join(height
)
1875 def generate_figure(self
, node
, source
, destination
, current_element
):
1876 #ipshell('At generate_figure')
1878 width
, height
= self
.get_image_scaled_width_height(node
, source
)
1879 for node1
in node
.parent
.children
:
1880 if node1
.tagname
== 'caption':
1881 caption
= node1
.astext()
1882 self
.image_style_count
+= 1
1884 # Add the style for the caption.
1885 if caption
is not None:
1887 'style:class': 'extra',
1888 'style:family': 'paragraph',
1889 'style:name': 'Caption',
1890 'style:parent-style-name': 'Standard',
1892 el1
= SubElement(self
.automatic_styles
, 'style:style',
1893 attrib
=attrib
, nsdict
=SNSD
)
1895 'fo:margin-bottom': '0.0835in',
1896 'fo:margin-top': '0.0835in',
1897 'text:line-number': '0',
1898 'text:number-lines': 'false',
1900 el2
= SubElement(el1
, 'style:paragraph-properties',
1901 attrib
=attrib
, nsdict
=SNSD
)
1903 'fo:font-size': '12pt',
1904 'fo:font-style': 'italic',
1905 'style:font-name': 'Times',
1906 'style:font-name-complex': 'Lucidasans1',
1907 'style:font-size-asian': '12pt',
1908 'style:font-size-complex': '12pt',
1909 'style:font-style-asian': 'italic',
1910 'style:font-style-complex': 'italic',
1912 el2
= SubElement(el1
, 'style:text-properties',
1913 attrib
=attrib
, nsdict
=SNSD
)
1914 style_name
= 'rstframestyle%d' % self
.image_style_count
1917 'style:name': style_name
,
1918 'style:family': 'graphic',
1919 'style:parent-style-name': 'Frame',
1921 el1
= SubElement(self
.automatic_styles
,
1922 'style:style', attrib
=attrib
, nsdict
=SNSD
)
1925 if 'align' in node
.attributes
:
1926 align
= node
.attributes
['align'].split()
1928 if val
in ('left', 'center', 'right'):
1930 elif val
in ('top', 'middle', 'bottom'):
1933 'fo:margin-left': '0cm',
1934 'fo:margin-right': '0cm',
1935 'fo:margin-top': '0cm',
1936 'fo:margin-bottom': '0cm',
1937 'style:wrap': 'dynamic',
1938 'style:number-wrapped-paragraphs': 'no-limit',
1939 'style:vertical-pos': valign
,
1940 'style:vertical-rel': 'paragraph',
1941 'style:horizontal-pos': halign
,
1942 'style:horizontal-rel': 'paragraph',
1943 'fo:padding': '0cm',
1944 'fo:border': 'none',
1946 el2
= SubElement(el1
,
1947 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
1949 'draw:style-name': style_name
,
1950 'draw:name': 'Frame1',
1951 'text:anchor-type': 'paragraph',
1952 'draw:z-index': '1',
1954 attrib
['svg:width'] = width
1956 #attrib['svg:height'] = height
1957 el3
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
1959 el4
= SubElement(el3
, 'draw:text-box', attrib
=attrib
)
1961 'text:style-name': self
.rststyle('caption'),
1963 el5
= SubElement(el4
, 'text:p', attrib
=attrib
)
1964 return el3
, el5
, caption
1966 def generate_image(self
, node
, source
, destination
, current_element
,
1967 #ipshell('At generate_image')
1969 width
, height
= self
.get_image_scaled_width_height(node
, source
)
1970 self
.image_style_count
+= 1
1971 style_name
= 'rstframestyle%d' % self
.image_style_count
1974 'style:name': style_name
,
1975 'style:family': 'graphic',
1976 'style:parent-style-name': 'Graphics',
1978 el1
= SubElement(self
.automatic_styles
,
1979 'style:style', attrib
=attrib
, nsdict
=SNSD
)
1982 if 'align' in node
.attributes
:
1983 align
= node
.attributes
['align'].split()
1985 if val
in ('left', 'center', 'right'):
1987 elif val
in ('top', 'middle', 'bottom'):
1989 if frame_attrs
is None:
1991 'style:vertical-pos': 'top',
1992 'style:vertical-rel': 'paragraph',
1993 #'style:horizontal-pos': halign,
1994 #'style:vertical-pos': valign,
1995 'style:horizontal-rel': 'paragraph',
1996 'style:mirror': 'none',
1997 'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
1998 'draw:luminance': '0%',
1999 'draw:contrast': '0%',
2003 'draw:gamma': '100%',
2004 'draw:color-inversion': 'false',
2005 'draw:image-opacity': '100%',
2006 'draw:color-mode': 'standard',
2009 attrib
= frame_attrs
2010 if halign
is not None:
2011 attrib
['style:horizontal-pos'] = halign
2012 if valign
is not None:
2013 attrib
['style:vertical-pos'] = valign
2014 #ipshell('At generate_image')
2015 # If we are inside a table, add a no-wrap style.
2016 if self
.is_in_table(node
):
2017 attrib
['style:wrap'] = 'none'
2018 el2
= SubElement(el1
,
2019 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
2021 #el = SubElement(current_element, 'text:p',
2022 # attrib={'text:style-name': self.rststyle('textbody')})
2024 'draw:style-name': style_name
,
2025 'draw:name': 'graphics2',
2026 #'text:anchor-type': 'paragraph',
2027 #'svg:width': '%fcm' % (width, ),
2028 #'svg:height': '%fcm' % (height, ),
2029 'draw:z-index': '1',
2031 if isinstance(node
.parent
, nodes
.TextElement
):
2032 attrib
['text:anchor-type'] = 'char'
2034 attrib
['text:anchor-type'] = 'paragraph'
2035 attrib
['svg:width'] = width
2036 attrib
['svg:height'] = height
2037 el1
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
2038 el2
= SubElement(el1
, 'draw:image', attrib
={
2039 'xlink:href': '%s' % (destination
, ),
2040 'xlink:type': 'simple',
2041 'xlink:show': 'embed',
2042 'xlink:actuate': 'onLoad',
2046 def is_in_table(self
, node
):
2049 if isinstance(node1
, docutils
.nodes
.entry
):
2051 node1
= node1
.parent
2054 def visit_legend(self
, node
):
2055 # Currently, the legend receives *no* special treatment.
2056 #ipshell('At visit_legend')
2059 def depart_legend(self
, node
):
2062 def visit_line_block(self
, node
):
2063 #ipshell('At visit_line_block')
2064 self
.line_indent_level
+= 1
2065 self
.line_block_level
+= 1
2067 def depart_line_block(self
, node
):
2068 #ipshell('At depart_line_block')
2069 if self
.line_block_level
<= 1:
2070 el1
= SubElement(self
.current_element
, 'text:p', attrib
={
2071 'text:style-name': self
.rststyle('lineblock1'),
2073 self
.line_indent_level
-= 1
2074 self
.line_block_level
-= 1
2076 def visit_line(self
, node
):
2077 #ipshell('At visit_line')
2078 style
= 'lineblock%d' % self
.line_indent_level
2079 el1
= SubElement(self
.current_element
, 'text:p', attrib
={
2080 'text:style-name': self
.rststyle(style
),
2082 self
.current_element
= el1
2084 def depart_line(self
, node
):
2085 #ipshell('At depart_line')
2086 self
.set_to_parent()
2088 def visit_literal(self
, node
):
2089 #ipshell('At visit_literal')
2090 el
= SubElement(self
.current_element
, 'text:span',
2091 attrib
={'text:style-name': self
.rststyle('inlineliteral')})
2092 self
.set_current_element(el
)
2094 def depart_literal(self
, node
):
2095 self
.set_to_parent()
2097 def _calculate_code_block_padding(self
, line
):
2099 matchobj
= SPACES_PATTERN
.match(line
)
2101 pad
= matchobj
.group()
2104 matchobj
= TABS_PATTERN
.match(line
)
2106 pad
= matchobj
.group()
2107 count
= len(pad
) * 8
2110 def _add_syntax_highlighting(self
, insource
, language
):
2111 lexer
= pygments
.lexers
.get_lexer_by_name(language
, stripall
=True)
2112 if language
in ('latex', 'tex'):
2113 fmtr
= OdtPygmentsLaTeXFormatter(lambda name
, parameters
=():
2114 self
.rststyle(name
, parameters
))
2116 fmtr
= OdtPygmentsProgFormatter(lambda name
, parameters
=():
2117 self
.rststyle(name
, parameters
))
2118 outsource
= pygments
.highlight(insource
, lexer
, fmtr
)
2121 def fill_line(self
, line
):
2122 line
= FILL_PAT1
.sub(self
.fill_func1
, line
)
2123 line
= FILL_PAT2
.sub(self
.fill_func2
, line
)
2126 def fill_func1(self
, matchobj
):
2127 spaces
= matchobj
.group(0)
2128 repl
= '<text:s text:c="%d"/>' % (len(spaces
), )
2131 def fill_func2(self
, matchobj
):
2132 spaces
= matchobj
.group(0)
2133 repl
= ' <text:s text:c="%d"/>' % (len(spaces
) - 1, )
2136 def visit_literal_block(self
, node
):
2137 #ipshell('At visit_literal_block')
2138 wrapper1
= '<text:p text:style-name="%s">%%s</text:p>' % (
2139 self
.rststyle('codeblock'), )
2140 source
= node
.astext()
2142 self
.settings
.add_syntax_highlighting
and
2143 node
.get('hilight', False)):
2144 language
= node
.get('language', 'python')
2145 source
= self
._add
_syntax
_highlighting
(source
, language
)
2147 source
= escape_cdata(source
)
2148 lines
= source
.split('\n')
2149 lines1
= ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
2152 for my_line
in lines
:
2153 my_line
= self
.fill_line(my_line
)
2154 my_line
= my_line
.replace(" ", "\n")
2155 my_lines
.append(my_line
)
2156 my_lines_str
= '<text:line-break/>'.join(my_lines
)
2157 my_lines_str2
= wrapper1
% (my_lines_str
, )
2158 lines1
.append(my_lines_str2
)
2159 lines1
.append('</wrappertag1>')
2160 s1
= ''.join(lines1
)
2161 if WhichElementTree
!= "lxml":
2162 s1
= s1
.encode("utf-8")
2163 el1
= etree
.fromstring(s1
)
2164 children
= el1
.getchildren()
2165 for child
in children
:
2166 self
.current_element
.append(child
)
2168 def depart_literal_block(self
, node
):
2171 visit_doctest_block
= visit_literal_block
2172 depart_doctest_block
= depart_literal_block
2174 def visit_meta(self
, node
):
2175 #ipshell('At visit_meta')
2176 name
= node
.attributes
.get('name')
2177 content
= node
.attributes
.get('content')
2178 if name
is not None and content
is not None:
2179 self
.meta_dict
[name
] = content
2181 def depart_meta(self
, node
):
2184 def visit_option_list(self
, node
):
2185 table_name
= 'tableoption'
2187 # Generate automatic styles
2188 if not self
.optiontablestyles_generated
:
2189 self
.optiontablestyles_generated
= True
2190 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2191 'style:name': self
.rststyle(table_name
),
2192 'style:family': 'table'}, nsdict
=SNSD
)
2193 el1
= SubElement(el
, 'style:table-properties', attrib
={
2194 'style:width': '17.59cm',
2195 'table:align': 'left',
2196 'style:shadow': 'none'}, nsdict
=SNSD
)
2197 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2198 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'A', )),
2199 'style:family': 'table-column'}, nsdict
=SNSD
)
2200 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2201 'style:column-width': '4.999cm'}, nsdict
=SNSD
)
2202 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2203 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'B', )),
2204 'style:family': 'table-column'}, nsdict
=SNSD
)
2205 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2206 'style:column-width': '12.587cm'}, nsdict
=SNSD
)
2207 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2208 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2209 'style:family': 'table-cell'}, nsdict
=SNSD
)
2210 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2211 'fo:background-color': 'transparent',
2212 'fo:padding': '0.097cm',
2213 'fo:border-left': '0.035cm solid #000000',
2214 'fo:border-right': 'none',
2215 'fo:border-top': '0.035cm solid #000000',
2216 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2217 el2
= SubElement(el1
, 'style:background-image', nsdict
=SNSD
)
2218 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2219 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2220 'style:family': 'table-cell'}, nsdict
=SNSD
)
2221 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2222 'fo:padding': '0.097cm',
2223 'fo:border': '0.035cm solid #000000'}, nsdict
=SNSD
)
2224 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2225 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 2, )),
2226 'style:family': 'table-cell'}, nsdict
=SNSD
)
2227 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2228 'fo:padding': '0.097cm',
2229 'fo:border-left': '0.035cm solid #000000',
2230 'fo:border-right': 'none',
2231 'fo:border-top': 'none',
2232 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2233 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2234 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 2, )),
2235 'style:family': 'table-cell'}, nsdict
=SNSD
)
2236 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2237 'fo:padding': '0.097cm',
2238 'fo:border-left': '0.035cm solid #000000',
2239 'fo:border-right': '0.035cm solid #000000',
2240 'fo:border-top': 'none',
2241 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2243 # Generate table data
2244 el
= self
.append_child('table:table', attrib
={
2245 'table:name': self
.rststyle(table_name
),
2246 'table:style-name': self
.rststyle(table_name
),
2248 el1
= SubElement(el
, 'table:table-column', attrib
={
2249 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'A', ))})
2250 el1
= SubElement(el
, 'table:table-column', attrib
={
2251 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'B', ))})
2252 el1
= SubElement(el
, 'table:table-header-rows')
2253 el2
= SubElement(el1
, 'table:table-row')
2254 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2255 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2256 'office:value-type': 'string'})
2257 el4
= SubElement(el3
, 'text:p', attrib
={
2258 'text:style-name': 'Table_20_Heading'})
2260 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2261 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2262 'office:value-type': 'string'})
2263 el4
= SubElement(el3
, 'text:p', attrib
={
2264 'text:style-name': 'Table_20_Heading'})
2265 el4
.text
= 'Description'
2266 self
.set_current_element(el
)
2268 def depart_option_list(self
, node
):
2269 self
.set_to_parent()
2271 def visit_option_list_item(self
, node
):
2272 el
= self
.append_child('table:table-row')
2273 self
.set_current_element(el
)
2275 def depart_option_list_item(self
, node
):
2276 self
.set_to_parent()
2278 def visit_option_group(self
, node
):
2279 el
= self
.append_child('table:table-cell', attrib
={
2280 'table:style-name': 'Table%d.A2' % self
.table_count
,
2281 'office:value-type': 'string',
2283 self
.set_current_element(el
)
2285 def depart_option_group(self
, node
):
2286 self
.set_to_parent()
2288 def visit_option(self
, node
):
2289 el
= self
.append_child('text:p', attrib
={
2290 'text:style-name': 'Table_20_Contents'})
2291 el
.text
= node
.astext()
2293 def depart_option(self
, node
):
2296 def visit_option_string(self
, node
):
2299 def depart_option_string(self
, node
):
2302 def visit_option_argument(self
, node
):
2303 #ipshell('At visit_option_argument')
2306 def depart_option_argument(self
, node
):
2309 def visit_description(self
, node
):
2310 el
= self
.append_child('table:table-cell', attrib
={
2311 'table:style-name': 'Table%d.B2' % self
.table_count
,
2312 'office:value-type': 'string',
2314 el1
= SubElement(el
, 'text:p', attrib
={
2315 'text:style-name': 'Table_20_Contents'})
2316 el1
.text
= node
.astext()
2317 raise nodes
.SkipChildren()
2319 def depart_description(self
, node
):
2322 def visit_paragraph(self
, node
):
2323 #ipshell('At visit_paragraph')
2324 #self.trace_visit_node(node)
2325 self
.in_paragraph
= True
2327 el
= self
.append_p('header')
2328 elif self
.in_footer
:
2329 el
= self
.append_p('footer')
2331 style_name
= self
.paragraph_style_stack
[-1]
2332 el
= self
.append_child('text:p',
2333 attrib
={'text:style-name': style_name
})
2334 self
.append_pending_ids(el
)
2335 self
.set_current_element(el
)
2337 def depart_paragraph(self
, node
):
2338 #ipshell('At depart_paragraph')
2339 #self.trace_depart_node(node)
2340 self
.in_paragraph
= False
2341 self
.set_to_parent()
2343 self
.header_content
.append(self
.current_element
.getchildren()[-1])
2344 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2345 elif self
.in_footer
:
2346 self
.footer_content
.append(self
.current_element
.getchildren()[-1])
2347 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2349 def visit_problematic(self
, node
):
2350 #print '(visit_problematic) node: %s' % (node.astext(), )
2353 def depart_problematic(self
, node
):
2356 def visit_raw(self
, node
):
2357 #ipshell('At visit_raw')
2358 if 'format' in node
.attributes
:
2359 formats
= node
.attributes
['format']
2360 formatlist
= formats
.split()
2361 if 'odt' in formatlist
:
2362 rawstr
= node
.astext()
2363 attrstr
= ' '.join(['%s="%s"' % (k
, v
, )
2364 for k
,v
in CONTENT_NAMESPACE_ATTRIB
.items()])
2365 contentstr
= '<stuff %s>%s</stuff>' % (attrstr
, rawstr
, )
2366 if WhichElementTree
!= "lxml":
2367 contentstr
= contentstr
.encode("utf-8")
2368 content
= etree
.fromstring(contentstr
)
2369 elements
= content
.getchildren()
2370 if len(elements
) > 0:
2374 elif self
.in_footer
:
2377 self
.current_element
.append(el1
)
2378 raise nodes
.SkipChildren()
2380 def depart_raw(self
, node
):
2383 elif self
.in_footer
:
2388 def visit_reference(self
, node
):
2389 #self.trace_visit_node(node)
2390 text
= node
.astext()
2391 if self
.settings
.create_links
:
2392 if node
.has_key('refuri'):
2393 href
= node
['refuri']
2394 if ( self
.settings
.cloak_email_addresses
2395 and href
.startswith('mailto:')):
2396 href
= self
.cloak_mailto(href
)
2397 el
= self
.append_child('text:a', attrib
={
2398 'xlink:href': '%s' % href
,
2399 'xlink:type': 'simple',
2401 self
.set_current_element(el
)
2402 elif node
.has_key('refid'):
2403 if self
.settings
.create_links
:
2404 href
= node
['refid']
2405 el
= self
.append_child('text:reference-ref', attrib
={
2406 'text:ref-name': '%s' % href
,
2407 'text:reference-format': 'text',
2410 raise RuntimeError, 'References must have "refuri" or "refid" attribute.'
2411 if (self
.in_table_of_contents
and
2412 len(node
.children
) >= 1 and
2413 isinstance(node
.children
[0], docutils
.nodes
.generated
)):
2414 node
.remove(node
.children
[0])
2416 def depart_reference(self
, node
):
2417 #self.trace_depart_node(node)
2418 if self
.settings
.create_links
:
2419 if node
.has_key('refuri'):
2420 self
.set_to_parent()
2422 def visit_rubric(self
, node
):
2423 style_name
= self
.rststyle('rubric')
2424 classes
= node
.get('classes')
2429 el
= SubElement(self
.current_element
, 'text:h', attrib
= {
2430 #'text:outline-level': '%d' % section_level,
2431 #'text:style-name': 'Heading_20_%d' % section_level,
2432 'text:style-name': style_name
,
2434 text
= node
.astext()
2435 el
.text
= self
.encode(text
)
2437 def depart_rubric(self
, node
):
2440 def visit_section(self
, node
, move_ids
=1):
2441 #ipshell('At visit_section')
2442 self
.section_level
+= 1
2443 self
.section_count
+= 1
2444 if self
.settings
.create_sections
:
2445 el
= self
.append_child('text:section', attrib
={
2446 'text:name': 'Section%d' % self
.section_count
,
2447 'text:style-name': 'Sect%d' % self
.section_level
,
2449 self
.set_current_element(el
)
2451 def depart_section(self
, node
):
2452 self
.section_level
-= 1
2453 if self
.settings
.create_sections
:
2454 self
.set_to_parent()
2456 def visit_strong(self
, node
):
2457 #ipshell('At visit_strong')
2458 el
= SubElement(self
.current_element
, 'text:span',
2459 attrib
={'text:style-name': self
.rststyle('strong')})
2460 self
.set_current_element(el
)
2462 def depart_strong(self
, node
):
2463 self
.set_to_parent()
2465 def visit_substitution_definition(self
, node
):
2466 #ipshell('At visit_substitution_definition')
2467 raise nodes
.SkipChildren()
2469 def depart_substitution_definition(self
, node
):
2470 #ipshell('At depart_substitution_definition')
2473 def visit_system_message(self
, node
):
2474 #print '(visit_system_message) node: %s' % (node.astext(), )
2477 def depart_system_message(self
, node
):
2480 def visit_table(self
, node
):
2481 #self.trace_visit_node(node)
2482 #ipshell('At visit_table')
2483 self
.table_count
+= 1
2484 table_name
= '%s%%d' % TableStylePrefix
2485 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2486 'style:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2487 'style:family': 'table',
2489 el1_1
= SubElement(el1
, 'style:table-properties', attrib
={
2490 #'style:width': '17.59cm',
2491 'table:align': 'margins',
2492 'fo:margin-top': '0in',
2493 'fo:margin-bottom': '0.10in',
2495 # We use a single cell style for all cells in this table.
2496 # That's probably not correct, but seems to work.
2497 el2
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2498 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( self
.table_count
, 'A', 1, )),
2499 'style:family': 'table-cell',
2501 line_style1
= '0.%03dcm solid #000000' % self
.settings
.table_border_thickness
2502 el2_1
= SubElement(el2
, 'style:table-cell-properties', attrib
={
2503 'fo:padding': '0.049cm',
2504 'fo:border-left': line_style1
,
2505 'fo:border-right': line_style1
,
2506 'fo:border-top': line_style1
,
2507 'fo:border-bottom': line_style1
,
2510 for child
in node
.children
:
2511 if child
.tagname
== 'title':
2512 title
= child
.astext()
2514 if title
is not None:
2515 el3
= self
.append_p('table-title', title
)
2517 #print 'no table title'
2519 el4
= SubElement(self
.current_element
, 'table:table', attrib
={
2520 'table:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2521 'table:style-name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2523 self
.set_current_element(el4
)
2524 self
.current_table_style
= el1
2525 self
.table_width
= 0
2527 def depart_table(self
, node
):
2528 #self.trace_depart_node(node)
2529 #ipshell('At depart_table')
2530 attribkey
= add_ns('style:width', nsdict
=SNSD
)
2531 attribval
= '%dcm' % self
.table_width
2532 self
.current_table_style
.attrib
[attribkey
] = attribval
2533 self
.set_to_parent()
2535 def visit_tgroup(self
, node
):
2536 #self.trace_visit_node(node)
2537 #ipshell('At visit_tgroup')
2538 self
.column_count
= ord('A') - 1
2540 def depart_tgroup(self
, node
):
2541 #self.trace_depart_node(node)
2544 def visit_colspec(self
, node
):
2545 #self.trace_visit_node(node)
2546 #ipshell('At visit_colspec')
2547 self
.column_count
+= 1
2548 colspec_name
= self
.rststyle('%s%%d.%%s' % TableStylePrefix
, ( self
.table_count
, chr(self
.column_count
), ))
2549 colwidth
= node
['colwidth']
2550 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2551 'style:name': colspec_name
,
2552 'style:family': 'table-column',
2554 el1_1
= SubElement(el1
, 'style:table-column-properties', attrib
={
2555 'style:column-width': '%dcm' % colwidth
}, nsdict
=SNSD
)
2556 el2
= self
.append_child('table:table-column', attrib
={
2557 'table:style-name': colspec_name
,
2559 self
.table_width
+= colwidth
2561 def depart_colspec(self
, node
):
2562 #self.trace_depart_node(node)
2565 def visit_thead(self
, node
):
2566 #self.trace_visit_node(node)
2567 #ipshell('At visit_thead')
2568 el
= self
.append_child('table:table-header-rows')
2569 self
.set_current_element(el
)
2570 self
.in_thead
= True
2571 self
.paragraph_style_stack
.append('Table_20_Heading')
2573 def depart_thead(self
, node
):
2574 #self.trace_depart_node(node)
2575 self
.set_to_parent()
2576 self
.in_thead
= False
2577 self
.paragraph_style_stack
.pop()
2579 def visit_row(self
, node
):
2580 #self.trace_visit_node(node)
2581 #ipshell('At visit_row')
2582 self
.column_count
= ord('A') - 1
2583 el
= self
.append_child('table:table-row')
2584 self
.set_current_element(el
)
2586 def depart_row(self
, node
):
2587 #self.trace_depart_node(node)
2588 self
.set_to_parent()
2590 def visit_entry(self
, node
):
2591 #self.trace_visit_node(node)
2592 #ipshell('At visit_entry')
2593 self
.column_count
+= 1
2594 cellspec_name
= self
.rststyle('%s%%d.%%c%%d' % TableStylePrefix
, ( self
.table_count
, 'A', 1, ))
2596 'table:style-name': cellspec_name
,
2597 'office:value-type': 'string',
2599 morecols
= node
.get('morecols', 0)
2601 attrib
['table:number-columns-spanned'] = '%d' % (morecols
+ 1,)
2602 self
.column_count
+= morecols
2603 el1
= self
.append_child('table:table-cell', attrib
=attrib
)
2604 self
.set_current_element(el1
)
2606 def depart_entry(self
, node
):
2607 #self.trace_depart_node(node)
2608 self
.set_to_parent()
2610 def visit_tbody(self
, node
):
2611 #self.trace_visit_node(node)
2612 #ipshell('At visit_')
2615 def depart_tbody(self
, node
):
2616 #self.trace_depart_node(node)
2619 def visit_target(self
, node
):
2621 # I don't know how to implement targets in ODF.
2622 # How do we create a target in oowriter? A cross-reference?
2623 if not (node
.has_key('refuri') or node
.has_key('refid')
2624 or node
.has_key('refname')):
2629 def depart_target(self
, node
):
2632 def visit_title(self
, node
, move_ids
=1, title_type
='title'):
2633 #ipshell('At visit_title')
2634 if isinstance(node
.parent
, docutils
.nodes
.section
):
2635 section_level
= self
.section_level
2636 if section_level
> 7:
2637 print 'Warning: Heading/section levels greater than 7 not supported.'
2638 print ' Reducing to heading level 7 for heading:'
2639 print ' "%s"' % node
.astext()
2641 el1
= self
.append_child('text:h', attrib
= {
2642 'text:outline-level': '%d' % section_level
,
2643 #'text:style-name': 'Heading_20_%d' % section_level,
2644 'text:style-name': self
.rststyle('heading%d', (section_level
, )),
2646 self
.append_pending_ids(el1
)
2647 self
.set_current_element(el1
)
2648 elif isinstance(node
.parent
, docutils
.nodes
.document
):
2649 # text = self.settings.title
2651 # text = node.astext()
2652 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2653 'text:style-name': self
.rststyle(title_type
),
2655 self
.append_pending_ids(el1
)
2656 text
= node
.astext()
2658 self
.found_doc_title
= True
2659 self
.set_current_element(el1
)
2661 def depart_title(self
, node
):
2662 if (isinstance(node
.parent
, docutils
.nodes
.section
) or
2663 isinstance(node
.parent
, docutils
.nodes
.document
)):
2664 self
.set_to_parent()
2666 def visit_subtitle(self
, node
, move_ids
=1):
2667 self
.visit_title(node
, move_ids
, title_type
='subtitle')
2669 def depart_subtitle(self
, node
):
2670 self
.depart_title(node
)
2672 def visit_title_reference(self
, node
):
2673 #ipshell('At visit_title_reference')
2674 el
= self
.append_child('text:span', attrib
={
2675 'text:style-name': self
.rststyle('quotation')})
2676 el
.text
= self
.encode(node
.astext())
2678 def depart_title_reference(self
, node
):
2681 def visit_topic(self
, node
):
2682 #ipshell('At visit_topic')
2683 if 'classes' in node
.attributes
:
2684 if 'contents' in node
.attributes
['classes']:
2685 el
= self
.append_p('horizontalline')
2686 el
= self
.append_p('centeredtextbody')
2687 el1
= SubElement(el
, 'text:span',
2688 attrib
={'text:style-name': self
.rststyle('strong')})
2689 el1
.text
= 'Contents'
2690 self
.in_table_of_contents
= True
2691 elif 'abstract' in node
.attributes
['classes']:
2692 el
= self
.append_p('horizontalline')
2693 el
= self
.append_p('centeredtextbody')
2694 el1
= SubElement(el
, 'text:span',
2695 attrib
={'text:style-name': self
.rststyle('strong')})
2696 el1
.text
= 'Abstract'
2698 def depart_topic(self
, node
):
2699 #ipshell('At depart_topic')
2700 if 'classes' in node
.attributes
:
2701 if 'contents' in node
.attributes
['classes']:
2702 el
= self
.append_p('horizontalline')
2703 self
.in_table_of_contents
= False
2705 def visit_transition(self
, node
):
2706 el
= self
.append_p('horizontalline')
2708 def depart_transition(self
, node
):
2714 def visit_warning(self
, node
):
2715 self
.generate_admonition(node
, 'warning')
2717 def depart_warning(self
, node
):
2718 self
.paragraph_style_stack
.pop()
2720 def visit_attention(self
, node
):
2721 self
.generate_admonition(node
, 'attention')
2723 depart_attention
= depart_warning
2725 def visit_caution(self
, node
):
2726 self
.generate_admonition(node
, 'caution')
2728 depart_caution
= depart_warning
2730 def visit_danger(self
, node
):
2731 self
.generate_admonition(node
, 'danger')
2733 depart_danger
= depart_warning
2735 def visit_error(self
, node
):
2736 self
.generate_admonition(node
, 'error')
2738 depart_error
= depart_warning
2740 def visit_hint(self
, node
):
2741 self
.generate_admonition(node
, 'hint')
2743 depart_hint
= depart_warning
2745 def visit_important(self
, node
):
2746 self
.generate_admonition(node
, 'important')
2748 depart_important
= depart_warning
2750 def visit_note(self
, node
):
2751 self
.generate_admonition(node
, 'note')
2753 depart_note
= depart_warning
2755 def visit_tip(self
, node
):
2756 self
.generate_admonition(node
, 'tip')
2758 depart_tip
= depart_warning
2760 def visit_admonition(self
, node
):
2761 #import pdb; pdb.set_trace()
2763 for child
in node
.children
:
2764 if child
.tagname
== 'title':
2765 title
= child
.astext()
2767 classes1
= node
.get('classes')
2770 self
.generate_admonition(node
, 'generic', title
)
2772 depart_admonition
= depart_warning
2774 def generate_admonition(self
, node
, label
, title
=None):
2775 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2776 'text:style-name': self
.rststyle('admon-%s-hdr', ( label
, )),
2781 el1
.text
= '%s!' % (label
.capitalize(), )
2782 s1
= self
.rststyle('admon-%s-body', ( label
, ))
2783 self
.paragraph_style_stack
.append(s1
)
2786 # Roles (e.g. subscript, superscript, strong, ...
2788 def visit_subscript(self
, node
):
2789 el
= self
.append_child('text:span', attrib
={
2790 'text:style-name': 'rststyle-subscript',
2792 self
.set_current_element(el
)
2794 def depart_subscript(self
, node
):
2795 self
.set_to_parent()
2797 def visit_superscript(self
, node
):
2798 el
= self
.append_child('text:span', attrib
={
2799 'text:style-name': 'rststyle-superscript',
2801 self
.set_current_element(el
)
2803 def depart_superscript(self
, node
):
2804 self
.set_to_parent()
2807 # Use an own reader to modify transformations done.
2808 class Reader(standalone
.Reader
):
2810 def get_transforms(self
):
2811 default
= standalone
.Reader
.get_transforms(self
)
2812 if self
.settings
.create_links
:
2816 if i
is not references
.DanglingReferences
]