3 Open Document Format (ODF) Writer.
9 __docformat__
= 'reStructuredText'
17 from xml
.dom
import minidom
24 from docutils
import frontend
, nodes
, utils
, writers
, languages
25 from docutils
.parsers
import rst
31 from lxml
import etree
32 #print '*** using lxml'
33 WhichElementTree
= 'lxml'
34 except ImportError, e
:
36 # 2. Try to use ElementTree from the Python standard library.
37 from xml
.etree
import ElementTree
as etree
38 #print '*** using ElementTree'
39 WhichElementTree
= 'elementtree'
40 except ImportError, e
:
42 # 3. Try to use a version of ElementTree installed as a separate
44 from elementtree
import ElementTree
as etree
45 WhichElementTree
= 'elementtree'
46 except ImportError, e
:
48 print '*** Error: Must install either ElementTree or lxml or'
49 print '*** a version of Python containing ElementTree.'
55 import pygments
.formatter
56 import pygments
.lexers
57 class OdtPygmentsFormatter(pygments
.formatter
.Formatter
):
58 def __init__(self
, rststyle_function
):
59 pygments
.formatter
.Formatter
.__init
__(self
)
60 self
.rststyle_function
= rststyle_function
62 def rststyle(self
, name
, parameters
=( )):
63 return self
.rststyle_function(name
, parameters
)
65 class OdtPygmentsProgFormatter(OdtPygmentsFormatter
):
66 def format(self
, tokensource
, outfile
):
67 tokenclass
= pygments
.token
.Token
68 for ttype
, value
in tokensource
:
69 value
= escape_cdata(value
)
70 if ttype
== tokenclass
.Keyword
:
71 s2
= self
.rststyle('codeblock-keyword')
72 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
74 elif ttype
== tokenclass
.Literal
.String
:
75 s2
= self
.rststyle('codeblock-string')
76 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
79 tokenclass
.Literal
.Number
.Integer
,
80 tokenclass
.Literal
.Number
.Integer
.Long
,
81 tokenclass
.Literal
.Number
.Float
,
82 tokenclass
.Literal
.Number
.Hex
,
83 tokenclass
.Literal
.Number
.Oct
,
84 tokenclass
.Literal
.Number
,
86 s2
= self
.rststyle('codeblock-number')
87 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
89 elif ttype
== tokenclass
.Operator
:
90 s2
= self
.rststyle('codeblock-operator')
91 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
93 elif ttype
== tokenclass
.Comment
:
94 s2
= self
.rststyle('codeblock-comment')
95 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
97 elif ttype
== tokenclass
.Name
.Class
:
98 s2
= self
.rststyle('codeblock-classname')
99 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
101 elif ttype
== tokenclass
.Name
.Function
:
102 s2
= self
.rststyle('codeblock-functionname')
103 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
105 elif ttype
== tokenclass
.Name
:
106 s2
= self
.rststyle('codeblock-name')
107 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
112 class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter
):
113 def format(self
, tokensource
, outfile
):
114 tokenclass
= pygments
.token
.Token
115 for ttype
, value
in tokensource
:
116 value
= escape_cdata(value
)
117 if ttype
== tokenclass
.Keyword
:
118 s2
= self
.rststyle('codeblock-keyword')
119 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
121 elif ttype
in (tokenclass
.Literal
.String
,
122 tokenclass
.Literal
.String
.Backtick
,
124 s2
= self
.rststyle('codeblock-string')
125 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
127 elif ttype
== tokenclass
.Name
.Attribute
:
128 s2
= self
.rststyle('codeblock-operator')
129 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
131 elif ttype
== tokenclass
.Comment
:
132 if value
[-1] == '\n':
133 s2
= self
.rststyle('codeblock-comment')
134 s1
= '<text:span text:style-name="%s">%s</text:span>\n' % \
137 s2
= self
.rststyle('codeblock-comment')
138 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
140 elif ttype
== tokenclass
.Name
.Builtin
:
141 s2
= self
.rststyle('codeblock-name')
142 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
148 except ImportError, e
:
152 # Is the PIL imaging library installed?
155 except ImportError, exp
:
158 ## from IPython.Shell import IPShellEmbed
159 ## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ',
160 ## '-po', 'Out<\\#>: ', '-nosep']
161 ## ipshell = IPShellEmbed(args,
162 ## banner = 'Entering IPython. Press Ctrl-D to exit.',
163 ## exit_msg = 'Leaving Interpreter, back to program.')
167 # ElementTree does not support getparent method (lxml does).
168 # This wrapper class and the following support functions provide
169 # that support for the ability to get the parent of an element.
171 if WhichElementTree
== 'elementtree':
172 class _ElementInterfaceWrapper(etree
._ElementInterface
):
173 def __init__(self
, tag
, attrib
=None):
174 etree
._ElementInterface
.__init
__(self
, tag
, attrib
)
178 def setparent(self
, parent
):
185 # Constants and globals
187 # Turn tracing on/off. See methods trace_visit_node/trace_depart_node.
189 SPACES_PATTERN
= re
.compile(r
'( +)')
190 TABS_PATTERN
= re
.compile(r
'(\t+)')
191 FILL_PAT1
= re
.compile(r
'^ +')
192 FILL_PAT2
= re
.compile(r
' {2,}')
193 # Match a section number followed by 3 \xa0 bytes.
194 # Note that we actually check for the class "auto-toc" instead.
195 #SECTNUM_PAT = re.compile(r'^\d*(\.\d*)*\240\240\240')
197 TableStylePrefix
= 'Table'
199 GENERATOR_DESC
= 'Docutils.org/odtwriter'
201 NAME_SPACE_1
= 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
203 CONTENT_NAMESPACE_DICT
= CNSD
= {
204 # 'office:version': '1.0',
205 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
206 'dc': 'http://purl.org/dc/elements/1.1/',
207 'dom': 'http://www.w3.org/2001/xml-events',
208 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
209 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
210 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
211 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
212 'math': 'http://www.w3.org/1998/Math/MathML',
213 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
214 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
215 'office': NAME_SPACE_1
,
216 'ooo': 'http://openoffice.org/2004/office',
217 'oooc': 'http://openoffice.org/2004/calc',
218 'ooow': 'http://openoffice.org/2004/writer',
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 'ooo': 'http://openoffice.org/2004/office',
244 'oooc': 'http://openoffice.org/2004/calc',
245 'ooow': 'http://openoffice.org/2004/writer',
246 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
247 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
248 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
249 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
250 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
251 'xlink': 'http://www.w3.org/1999/xlink',
254 MANIFEST_NAMESPACE_DICT
= MANNSD
= {
255 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
258 META_NAMESPACE_DICT
= METNSD
= {
259 # 'office:version': '1.0',
260 'dc': 'http://purl.org/dc/elements/1.1/',
261 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
262 'office': NAME_SPACE_1
,
263 'ooo': 'http://openoffice.org/2004/office',
264 'xlink': 'http://www.w3.org/1999/xlink',
267 MIME_TYPE
= 'application/vnd.oasis.opendocument.text'
271 # Attribute dictionaries for use with ElementTree (not lxml), which
272 # does not support use of nsmap parameter on Element() and SubElement().
274 CONTENT_NAMESPACE_ATTRIB
= {
275 'office:version': '1.0',
276 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
277 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
278 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
279 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
280 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
281 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
282 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
283 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
284 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
285 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
286 'xmlns:office': NAME_SPACE_1
,
287 'xmlns:ooo': 'http://openoffice.org/2004/office',
288 'xmlns:oooc': 'http://openoffice.org/2004/calc',
289 'xmlns:ooow': 'http://openoffice.org/2004/writer',
290 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
291 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
292 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
293 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
294 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
295 'xmlns:xforms': 'http://www.w3.org/2002/xforms',
296 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
297 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
298 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
301 STYLES_NAMESPACE_ATTRIB
= {
302 'office:version': '1.0',
303 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
304 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
305 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
306 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
307 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
308 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
309 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
310 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
311 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
312 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
313 'xmlns:office': NAME_SPACE_1
,
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
,)
385 #print '*** tag: "%s"' % tag
389 outstream
= StringIO
.StringIO()
391 s1
= outstream
.getvalue()
395 def escape_cdata(text
):
396 text
= text
.replace("&", "&")
397 text
= text
.replace("<", "<")
398 text
= text
.replace(">", ">")
401 if ord(char
) >= ord("\x7f"):
402 ascii
+= "&#x%X;" % ( ord(char
), )
412 # Does this version of Docutils has Directive support?
413 if hasattr(rst
, 'Directive'):
415 # Class to control syntax highlighting.
416 ## class SyntaxHighlight(rst.Directive):
417 ## required_arguments = 1
418 ## optional_arguments = 0
420 ## # See visit_field for code that processes the node created here.
422 ## arguments = ' '.join(self.arguments)
423 ## paragraph = nodes.paragraph(arguments, arguments)
424 ## field_body = nodes.field_body()
425 ## field_body += paragraph
426 ## paragraph = nodes.paragraph('syntaxhighlight', 'syntaxhighlight')
427 ## field_name = nodes.field_name()
428 ## field_name += paragraph
429 ## field = nodes.field()
430 ## field += field_name
431 ## field += field_body
434 ## rst.directives.register_directive('sourcecode', SyntaxHighlight)
436 class SyntaxHighlightCodeBlock(rst
.Directive
):
437 required_arguments
= 1
438 optional_arguments
= 0
441 # See visit_literal_block for code that processes the node
444 language
= self
.arguments
[0]
445 code_block
= nodes
.literal_block(classes
=["code-block", language
],
448 content
= '\n'.join(lines
)
449 text_node
= nodes
.Text(content
)
450 code_block
.append(text_node
)
451 # Mark this node for high-lighting so that visit_literal_block
452 # will be able to hight-light those produced here and
453 # *not* high-light regular literal blocks (:: in reST).
454 code_block
['hilight'] = True
455 #import pdb; pdb.set_trace()
458 rst
.directives
.register_directive('sourcecode', SyntaxHighlightCodeBlock
)
459 rst
.directives
.register_directive('code', SyntaxHighlightCodeBlock
)
463 # Register directives defined in a module named "odtwriter_plugins".
469 name
= 'odtwriter_plugins'
470 fp
, pathname
, description
= imp
.find_module(name
)
471 plugin_mod
= imp
.load_module(name
, fp
, pathname
, description
)
472 #import odtwriter_plugins
473 #plugin_mod = odtwriter_plugins
474 except ImportError, e
:
476 if plugin_mod
is None:
478 klasses
= inspect
.getmembers(plugin_mod
, inspect
.isclass
)
479 for klass
in klasses
:
480 if register_plugin(*klass
):
484 def register_plugin(name
, klass
):
485 plugin_name
= getattr(klass
, 'plugin_name', None)
486 if plugin_name
is not None:
487 rst
.directives
.register_directive(plugin_name
, klass
)
492 WORD_SPLIT_PAT1
= re
.compile(r
'\b(\w*)\b\W*')
494 def split_words(line
):
495 # We need whitespace at the end of the string for our regexpr.
499 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
500 while mo
is not None:
501 word
= mo
.groups()[0]
504 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
509 # Information about the indentation level for lists nested inside
510 # other contexts, e.g. dictionary lists.
511 class ListLevel(object):
512 def __init__(self
, level
, sibling_level
=True, nested_level
=True):
514 self
.sibling_level
= sibling_level
515 self
.nested_level
= nested_level
516 def set_sibling(self
, sibling_level
): self
.sibling_level
= sibling_level
517 def get_sibling(self
): return self
.sibling_level
518 def set_nested(self
, nested_level
): self
.nested_level
= nested_level
519 def get_nested(self
): return self
.nested_level
520 def set_level(self
, level
): self
.level
= level
521 def get_level(self
): return self
.level
524 class Writer(writers
.Writer
):
528 supported
= ('html', 'html4css1', 'xhtml')
529 """Formats this writer supports."""
531 default_stylesheet
= 'styles' + EXTENSION
532 ## default_plugins_name = 'docutils_plugins'
534 default_stylesheet_path
= utils
.relative_path(
535 os
.path
.join(os
.getcwd(), 'dummy'),
536 os
.path
.join(os
.path
.dirname(__file__
), default_stylesheet
))
538 default_template
= 'template.txt'
540 default_template_path
= utils
.relative_path(
541 os
.path
.join(os
.getcwd(), 'dummy'),
542 os
.path
.join(os
.path
.dirname(__file__
), default_template
))
545 ## 'ODF-Specific Options',
547 ## (('Specify the template file (UTF-8 encoded). Default is "%s".'
548 ## % default_template_path,
550 ## {'default': default_template_path, 'metavar': '<file>'}),
551 ## ('Specify a stylesheet URL, used verbatim. Overrides '
552 ## '--stylesheet-path.',
554 ## {'metavar': '<URL>', 'overrides': 'stylesheet_path'}),
555 ## ('Specify a stylesheet file, relative to the current working '
556 ## 'directory. The path is adjusted relative to the output ODF '
557 ## 'file. Overrides --stylesheet. Default: "%s"'
558 ## % default_stylesheet_path,
559 ## ['--stylesheet-path'],
560 ## {'metavar': '<file>', 'overrides': 'stylesheet',
561 ## 'default': default_stylesheet_path}),
562 ## ('Specify the initial header level. Default is 1 for "<h1>". '
563 ## 'Does not affect document title & subtitle (see --no-doc-title).',
564 ## ['--initial-header-level'],
565 ## {'choices': '1 2 3 4 5 6'.split(), 'default': '1',
566 ## 'metavar': '<level>'}),
567 ## ('Specify the maximum width (in characters) for one-column field '
568 ## 'names. Longer field names will span an entire row of the table '
569 ## 'used to render the field list. Default is 14 characters. '
570 ## 'Use 0 for "no limit".',
571 ## ['--field-name-limit'],
572 ## {'default': 14, 'metavar': '<level>',
573 ## 'validator': frontend.validate_nonnegative_int}),
574 ## ('Specify the maximum width (in characters) for options in option '
575 ## 'lists. Longer options will span an entire row of the table used '
576 ## 'to render the option list. Default is 14 characters. '
577 ## 'Use 0 for "no limit".',
578 ## ['--option-limit'],
579 ## {'default': 14, 'metavar': '<level>',
580 ## 'validator': frontend.validate_nonnegative_int}),
581 ## ('Format for footnote references: one of "superscript" or '
582 ## '"brackets". Default is "brackets".',
583 ## ['--footnote-references'],
584 ## {'choices': ['superscript', 'brackets'], 'default': 'brackets',
585 ## 'metavar': '<format>',
586 ## 'overrides': 'trim_footnote_reference_space'}),
587 ## ('Format for block quote attributions: one of "dash" (em-dash '
588 ## 'prefix), "parentheses"/"parens", or "none". Default is "dash".',
589 ## ['--attribution'],
590 ## {'choices': ['dash', 'parentheses', 'parens', 'none'],
591 ## 'default': 'dash', 'metavar': '<format>'}),
592 ## ('Remove extra vertical whitespace between items of "simple" bullet '
593 ## 'lists and enumerated lists. Default: enabled.',
594 ## ['--compact-lists'],
595 ## {'default': 1, 'action': 'store_true',
596 ## 'validator': frontend.validate_boolean}),
597 ## ('Disable compact simple bullet and enumerated lists.',
598 ## ['--no-compact-lists'],
599 ## {'dest': 'compact_lists', 'action': 'store_false'}),
600 ## ('Remove extra vertical whitespace between items of simple field '
601 ## 'lists. Default: enabled.',
602 ## ['--compact-field-lists'],
603 ## {'default': 1, 'action': 'store_true',
604 ## 'validator': frontend.validate_boolean}),
605 ## ('Disable compact simple field lists.',
606 ## ['--no-compact-field-lists'],
607 ## {'dest': 'compact_field_lists', 'action': 'store_false'}),
608 ## ('Omit the XML declaration. Use with caution.',
609 ## ['--no-xml-declaration'],
610 ## {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
611 ## 'validator': frontend.validate_boolean}),
612 ## ('Obfuscate email addresses to confuse harvesters while still '
613 ## 'keeping email links usable with standards-compliant browsers.',
614 ## ['--cloak-email-addresses'],
615 ## {'action': 'store_true', 'validator': frontend.validate_boolean}),
619 'ODF-Specific Options',
622 ('Specify a stylesheet URL, used verbatim. Overrides '
623 '--stylesheet-path.',
625 {'metavar': '<URL>', 'overrides': 'stylesheet_path'}),
626 ('Specify a stylesheet file, relative to the current working '
627 'directory. The path is adjusted relative to the output ODF '
628 'file. Overrides --stylesheet. Default: "%s"'
629 % default_stylesheet_path
,
630 ['--stylesheet-path'],
631 {'metavar': '<file>', 'overrides': 'stylesheet',
632 'default': default_stylesheet_path
}),
633 ('Specify a configuration/mapping file relative to the '
635 'directory for additional ODF options. '
636 'In particular, this file may contain a section named '
637 '"Formats" that maps default style names to '
638 'names to be used in the resulting output file allowing for '
639 'adhering to external standards. '
640 'For more info and the format of the configuration/mapping file, '
641 'see the odtwriter doc.',
642 ['--odf-config-file'],
643 {'metavar': '<file>'}),
644 ('Obfuscate email addresses to confuse harvesters while still '
645 'keeping email links usable with standards-compliant browsers.',
646 ['--cloak-email-addresses'],
647 {'default': False, 'action': 'store_true',
648 'validator': frontend
.validate_boolean
}),
649 ('Specify the thickness of table borders in thousands of a cm. '
651 ['--table-border-thickness'],
653 'validator': frontend
.validate_nonnegative_int
}),
654 ('Add syntax highlighting in literal code blocks.'
655 'Default is No. Requires installation of Pygments.',
656 ['--add-syntax-highlighting'],
657 {'default': False, 'action': 'store_true',
658 'validator': frontend
.validate_boolean
}),
659 ('Create sections for headers. '
661 ['--create-sections'],
662 {'default': True, 'action': 'store_true',
663 'validator': frontend
.validate_boolean
}),
664 ('Create no sections for headers.',
665 ['--no-create-sections'],
666 {'action': 'store_false',
667 'dest': 'create_sections',
668 'validator': frontend
.validate_boolean
}),
669 ## ('Specify a plugins/directives module (without .py). '
670 ## 'Default: "%s"' % default_plugins_name,
671 ## ['--plugins-module-name'],
672 ## {'default': default_plugins_name}),
675 settings_defaults
= {
676 'output_encoding_error_handler': 'xmlcharrefreplace',
679 relative_path_settings
= (
683 config_section
= 'opendocument odf writer'
684 config_section_dependencies
= (
689 writers
.Writer
.__init
__(self
)
690 self
.translator_class
= ODFTranslator
693 #import pdb; pdb.set_trace()
694 self
.settings
= self
.document
.settings
695 self
.visitor
= self
.translator_class(self
.document
)
696 self
.document
.walkabout(self
.visitor
)
697 self
.visitor
.add_doc_title()
698 self
.assemble_my_parts()
699 self
.output
= self
.parts
['whole']
701 def assemble_my_parts(self
):
702 """Assemble the `self.parts` dictionary. Extend in subclasses.
704 #ipshell('At assemble_parts')
705 writers
.Writer
.assemble_parts(self
)
706 f
= tempfile
.NamedTemporaryFile()
707 zfile
= zipfile
.ZipFile(f
, 'w', zipfile
.ZIP_DEFLATED
)
708 self
.write_zip_str(zfile
, 'content.xml', self
.visitor
.content_astext())
709 self
.write_zip_str(zfile
, 'mimetype', MIME_TYPE
)
710 s1
= self
.create_manifest()
711 self
.write_zip_str(zfile
, 'META-INF/manifest.xml', s1
)
712 s1
= self
.create_meta()
713 self
.write_zip_str(zfile
, 'meta.xml', s1
)
714 s1
= self
.get_stylesheet()
715 self
.write_zip_str(zfile
, 'styles.xml', s1
)
716 self
.store_embedded_files(zfile
)
721 self
.parts
['whole'] = whole
722 self
.parts
['encoding'] = self
.document
.settings
.output_encoding
723 self
.parts
['version'] = docutils
.__version
__
725 def write_zip_str(self
, zfile
, name
, bytes
):
726 localtime
= time
.localtime(time
.time())
727 zinfo
= zipfile
.ZipInfo(name
, localtime
)
728 # Add some standard UNIX file access permissions (-rw-r--r--).
729 zinfo
.external_attr
= (0x81a4 & 0xFFFF) << 16L
730 zinfo
.compress_type
= zipfile
.ZIP_DEFLATED
731 zfile
.writestr(zinfo
, bytes
)
733 def store_embedded_files(self
, zfile
):
734 embedded_files
= self
.visitor
.get_embedded_file_list()
735 for source
, destination
in embedded_files
:
740 destination1
= destination
.decode('latin-1').encode('utf-8')
741 zfile
.write(source
, destination1
, zipfile
.ZIP_STORED
)
743 print "Error: Can't open file %s." % (source
, )
745 def get_stylesheet(self
):
746 """Retrieve the stylesheet from either a .xml file or from
747 a .odt (zip) file. Return the content as a string.
749 stylespath
= utils
.get_stylesheet_reference(self
.settings
,
750 os
.path
.join(os
.getcwd(), 'dummy'))
751 ext
= os
.path
.splitext(stylespath
)[1]
753 stylesfile
= open(stylespath
, 'r')
754 s1
= stylesfile
.read()
756 elif ext
== self
.EXTENSION
:
757 zfile
= zipfile
.ZipFile(stylespath
, 'r')
758 s1
= zfile
.read('styles.xml')
761 raise RuntimeError, 'stylesheet path must be ' + self
.EXTENSION
+ ' or .xml file.'
762 s1
= self
.visitor
.setup_page(s1
)
765 def assemble_parts(self
):
768 def create_manifest(self
):
769 if WhichElementTree
== 'lxml':
770 root
= Element('manifest:manifest',
771 nsmap
=MANIFEST_NAMESPACE_DICT
,
772 nsdict
=MANIFEST_NAMESPACE_DICT
,
775 root
= Element('manifest:manifest',
776 attrib
=MANIFEST_NAMESPACE_ATTRIB
,
777 nsdict
=MANIFEST_NAMESPACE_DICT
,
779 doc
= etree
.ElementTree(root
)
780 SubElement(root
, 'manifest:file-entry', attrib
={
781 'manifest:media-type': MIME_TYPE
,
782 'manifest:full-path': '/',
784 SubElement(root
, 'manifest:file-entry', attrib
={
785 'manifest:media-type': 'text/xml',
786 'manifest:full-path': 'content.xml',
788 SubElement(root
, 'manifest:file-entry', attrib
={
789 'manifest:media-type': 'text/xml',
790 'manifest:full-path': 'styles.xml',
792 SubElement(root
, 'manifest:file-entry', attrib
={
793 'manifest:media-type': 'text/xml',
794 'manifest:full-path': 'meta.xml',
797 doc
= minidom
.parseString(s1
)
798 s1
= doc
.toprettyxml(' ')
801 def create_meta(self
):
802 if WhichElementTree
== 'lxml':
803 root
= Element('office:document-meta',
804 nsmap
=META_NAMESPACE_DICT
,
805 nsdict
=META_NAMESPACE_DICT
,
808 root
= Element('office:document-meta',
809 attrib
=META_NAMESPACE_ATTRIB
,
810 nsdict
=META_NAMESPACE_DICT
,
812 doc
= etree
.ElementTree(root
)
813 root
= SubElement(root
, 'office:meta', nsdict
=METNSD
)
814 el1
= SubElement(root
, 'meta:generator', nsdict
=METNSD
)
815 el1
.text
= 'Docutils/rst2odf.py/%s' % (VERSION
, )
816 s1
= os
.environ
.get('USER', '')
817 el1
= SubElement(root
, 'meta:initial-creator', nsdict
=METNSD
)
819 s2
= time
.strftime('%Y-%m-%dT%H:%M:%S', time
.localtime())
820 el1
= SubElement(root
, 'meta:creation-date', nsdict
=METNSD
)
822 el1
= SubElement(root
, 'dc:creator', nsdict
=METNSD
)
824 el1
= SubElement(root
, 'dc:date', nsdict
=METNSD
)
826 el1
= SubElement(root
, 'dc:language', nsdict
=METNSD
)
828 el1
= SubElement(root
, 'meta:editing-cycles', nsdict
=METNSD
)
830 el1
= SubElement(root
, 'meta:editing-duration', nsdict
=METNSD
)
831 el1
.text
= 'PT00M01S'
832 title
= self
.visitor
.get_title()
833 el1
= SubElement(root
, 'dc:title', nsdict
=METNSD
)
837 el1
.text
= '[no title]'
838 meta_dict
= self
.visitor
.get_meta_dict()
839 keywordstr
= meta_dict
.get('keywords')
840 if keywordstr
is not None:
841 keywords
= split_words(keywordstr
)
842 for keyword
in keywords
:
843 el1
= SubElement(root
, 'meta:keyword', nsdict
=METNSD
)
845 description
= meta_dict
.get('description')
846 if description
is not None:
847 el1
= SubElement(root
, 'dc:description', nsdict
=METNSD
)
848 el1
.text
= description
850 #doc = minidom.parseString(s1)
851 #s1 = doc.toprettyxml(' ')
854 # class ODFTranslator(nodes.SparseNodeVisitor):
856 class ODFTranslator(nodes
.GenericNodeVisitor
):
859 ## 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
860 ## 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
861 ## 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock',
862 ## 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
863 ## 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
864 ## 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
865 ## 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
866 ## 'epigraph-enumitem', 'epigraph-enumlist', 'footer', 'footnote',
867 ## 'header', 'highlights', 'highlights-bulletitem',
868 ## 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
869 ## 'horizontalline', 'inlineliteral', 'lineblock', 'quotation', 'rubric',
870 ## 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
871 ## 'heading%d', 'admon-%s-hdr', 'admon-%s-body', 'tableoption',
872 ## 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
877 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
878 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
879 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock',
880 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
881 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
882 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
883 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
884 'epigraph-enumitem', 'epigraph-enumlist', 'footer', 'footnote',
885 'header', 'highlights', 'highlights-bulletitem',
886 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
887 'horizontalline', 'inlineliteral', 'lineblock', 'quotation', 'rubric',
888 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
894 'admon-attention-hdr',
895 'admon-attention-body',
897 'admon-caution-body',
903 'admon-generic-body',
906 'admon-important-hdr',
907 'admon-important-body',
913 'admon-warning-body',
915 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
919 def __init__(self
, document
):
920 #nodes.SparseNodeVisitor.__init__(self, document)
921 nodes
.GenericNodeVisitor
.__init
__(self
, document
)
922 self
.settings
= document
.settings
923 self
.format_map
= { }
924 if self
.settings
.odf_config_file
:
925 from ConfigParser
import ConfigParser
927 parser
= ConfigParser()
928 parser
.read(self
.settings
.odf_config_file
)
929 for ( rststyle
, format
, ) in parser
.items("Formats"):
930 if rststyle
not in self
.used_styles
:
932 print ('*** Warning: Style "%s" '
933 'is not a style used by odtwriter.' % (
936 #raise RuntimeError, 'Unused style "%s"' % ( rststyle, )
937 self
.format_map
[rststyle
] = format
938 self
.section_level
= 0
939 self
.section_count
= 0
940 # Create ElementTree content and styles documents.
941 if WhichElementTree
== 'lxml':
943 'office:document-content',
944 nsmap
=CONTENT_NAMESPACE_DICT
,
948 'office:document-content',
949 attrib
=CONTENT_NAMESPACE_ATTRIB
,
951 self
.content_tree
= etree
.ElementTree(element
=root
)
952 self
.current_element
= root
953 SubElement(root
, 'office:scripts')
954 SubElement(root
, 'office:font-face-decls')
955 el
= SubElement(root
, 'office:automatic-styles')
956 self
.automatic_styles
= el
957 el
= SubElement(root
, 'office:body')
958 el
= self
.generate_content_element(el
)
959 self
.current_element
= el
960 self
.body_text_element
= el
962 ## if WhichElementTree == 'lxml':
964 ## 'office:document-styles',
965 ## nsmap=STYLES_NAMESPACE_DICT,
966 ## nsdict=STYLES_NAMESPACE_DICT,
969 ## root = Element('office:document-styles',
970 ## attrib=STYLES_NAMESPACE_ATTRIB,
971 ## nsdict=STYLES_NAMESPACE_DICT,
973 ## self.styles_tree = etree.ElementTree(element=root)
974 self
.paragraph_style_stack
= [self
.rststyle('textbody'), ]
975 self
.list_style_stack
= []
977 self
.column_count
= ord('A') - 1
978 self
.trace_level
= -1
979 self
.optiontablestyles_generated
= False
980 self
.field_name
= None
981 self
.field_element
= None
984 self
.image_style_count
= 0
986 self
.embedded_file_list
= []
987 self
.syntaxhighlighting
= 1
988 self
.syntaxhighlight_lexer
= 'python'
989 self
.header_content
= []
990 self
.footer_content
= []
991 self
.in_header
= False
992 self
.in_footer
= False
994 self
.in_table_of_contents
= False
995 self
.footnote_dict
= {}
996 self
.footnote_found
= False
997 self
.pending_ids
= [ ]
998 self
.in_paragraph
= False
999 self
.found_doc_title
= False
1000 self
.bumped_list_level_stack
= []
1002 self
.in_footnote
= False
1004 def add_doc_title(self
):
1005 text
= self
.settings
.title
1008 if not self
.found_doc_title
:
1009 el
= Element('text:h', attrib
= {
1010 'text:outline-level': '1',
1011 'text:style-name': 'rststyle-heading1',
1014 self
.body_text_element
.insert(0, el
)
1016 def rststyle(self
, name
, parameters
=( )):
1018 Returns the style name to use for the given style.
1020 If `parameters` is given `name` must contain a matching number of ``%`` and
1021 is used as a format expression with `parameters` as the value.
1023 ## template = self.format_map.get(name, 'rststyle-%s' % name)
1024 ## return template % parameters
1025 name1
= name
% parameters
1026 stylename
= self
.format_map
.get(name1
, 'rststyle-%s' % name1
)
1029 def generate_content_element(self
, root
):
1030 return SubElement(root
, 'office:text')
1032 def setup_page(self
, content
):
1033 root_el
= etree
.fromstring(content
)
1034 self
.setup_paper(root_el
)
1035 if len(self
.header_content
) > 0 or len(self
.footer_content
) > 0:
1036 self
.add_header_footer(root_el
)
1037 new_content
= etree
.tostring(root_el
)
1040 def setup_paper(self
, root_el
):
1042 fin
= os
.popen("paperconf -s")
1043 w
, h
= map(float, fin
.read().split())
1046 w
, h
= 612, 792 # default to Letter
1048 if el
.tag
== "{%s}page-layout-properties" % SNSD
["style"] and \
1049 not el
.attrib
.has_key("{%s}page-width" % SNSD
["fo"]):
1050 el
.attrib
["{%s}page-width" % SNSD
["fo"]] = "%.3fpt" % w
1051 el
.attrib
["{%s}page-height" % SNSD
["fo"]] = "%.3fpt" % h
1052 el
.attrib
["{%s}margin-left" % SNSD
["fo"]] = \
1053 el
.attrib
["{%s}margin-right" % SNSD
["fo"]] = \
1055 el
.attrib
["{%s}margin-top" % SNSD
["fo"]] = \
1056 el
.attrib
["{%s}margin-bottom" % SNSD
["fo"]] = \
1059 for subel
in el
.getchildren(): walk(subel
)
1062 def add_header_footer(self
, root_el
):
1063 path
= '{%s}master-styles' % (NAME_SPACE_1
, )
1064 master_el
= root_el
.find(path
)
1065 if master_el
is None:
1067 path
= '{%s}master-page' % (SNSD
['style'], )
1068 master_el
= master_el
.find(path
)
1069 if master_el
is None:
1072 if len(self
.header_content
) > 0:
1073 if WhichElementTree
== 'lxml':
1074 el2
= SubElement(el1
, 'style:header', nsdict
=SNSD
)
1076 el2
= SubElement(el1
, 'style:header',
1077 attrib
=STYLES_NAMESPACE_ATTRIB
,
1078 nsdict
=STYLES_NAMESPACE_DICT
,
1080 for el
in self
.header_content
:
1081 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1082 el
.attrib
[attrkey
] = self
.rststyle('header')
1084 if len(self
.footer_content
) > 0:
1085 if WhichElementTree
== 'lxml':
1086 el2
= SubElement(el1
, 'style:footer', nsdict
=SNSD
)
1088 el2
= SubElement(el1
, 'style:footer',
1089 attrib
=STYLES_NAMESPACE_ATTRIB
,
1090 nsdict
=STYLES_NAMESPACE_DICT
,
1092 for el
in self
.footer_content
:
1093 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1094 el
.attrib
[attrkey
] = self
.rststyle('footer')
1096 #new_tree = etree.ElementTree(root_el)
1097 #new_content = ToString(new_tree)
1100 root
= self
.content_tree
.getroot()
1101 et
= etree
.ElementTree(root
)
1105 def content_astext(self
):
1106 return self
.astext()
1109 ## def styles_astext(self):
1110 ## root = self.styles_tree.getroot()
1111 ## et = etree.ElementTree(root)
1112 ## s1 = ToString(et)
1115 def set_title(self
, title
): self
.title
= title
1116 def get_title(self
): return self
.title
1117 def set_embedded_file_list(self
, embedded_file_list
):
1118 self
.embedded_file_list
= embedded_file_list
1119 def get_embedded_file_list(self
): return self
.embedded_file_list
1120 def get_meta_dict(self
): return self
.meta_dict
1125 def append_child(self
, tag
, attrib
=None, parent
=None):
1127 parent
= self
.current_element
1129 el
= SubElement(parent
, tag
)
1131 el
= SubElement(parent
, tag
, attrib
)
1134 def append_p(self
, style
, text
=None):
1135 result
= self
.append_child('text:p', attrib
={
1136 'text:style-name': self
.rststyle(style
)})
1137 self
.append_pending_ids(result
)
1138 if text
is not None:
1142 def append_pending_ids(self
, el
):
1143 for id in self
.pending_ids
:
1144 SubElement(el
, 'text:reference-mark', attrib
={
1146 self
.pending_ids
= [ ]
1148 def set_current_element(self
, el
):
1149 self
.current_element
= el
1151 def set_to_parent(self
):
1152 self
.current_element
= self
.current_element
.getparent()
1154 def generate_labeled_block(self
, node
, label
):
1155 el
= self
.append_p('textbody')
1156 el1
= SubElement(el
, 'text:span',
1157 attrib
={'text:style-name': self
.rststyle('strong')})
1159 el
= self
.append_p('blockindent')
1162 def generate_labeled_line(self
, node
, label
):
1163 el
= self
.append_p('textbody')
1164 el1
= SubElement(el
, 'text:span',
1165 attrib
={'text:style-name': self
.rststyle('strong')})
1167 el1
.tail
= node
.astext()
1170 def encode(self
, text
):
1171 text
= text
.replace(u
'\u00a0', " ")
1174 def trace_visit_node(self
, node
):
1176 self
.trace_level
+= 1
1177 self
._trace
_show
_level
(self
.trace_level
)
1179 print '(visit_%s) node: %s' % (node
.tagname
, node
.astext(), )
1181 print '(visit_%s)' % node
.tagname
1183 def trace_depart_node(self
, node
):
1186 self
._trace
_show
_level
(self
.trace_level
)
1187 print '(depart_%s)' % node
.tagname
1188 self
.trace_level
-= 1
1190 def _trace_show_level(self
, level
):
1191 for idx
in range(level
):
1197 # In alphabetic order, more or less.
1198 # See docutils.docutils.nodes.node_class_names.
1201 def dispatch_visit(self
, node
):
1202 """Override to catch basic attributes which many nodes have."""
1203 self
.handle_basic_atts(node
)
1204 nodes
.GenericNodeVisitor
.dispatch_visit(self
, node
)
1206 def handle_basic_atts(self
, node
):
1207 if isinstance(node
, nodes
.Element
) and node
['ids']:
1208 self
.pending_ids
+= node
['ids']
1210 def default_visit(self
, node
):
1211 #ipshell('At default_visit')
1212 print 'missing visit_%s' % (node
.tagname
, )
1214 def default_departure(self
, node
):
1215 print 'missing depart_%s' % (node
.tagname
, )
1217 def visit_Text(self
, node
):
1218 #ipshell('At visit_Text')
1219 # Skip nodes whose text has been processed in parent nodes.
1220 if isinstance(node
.parent
, docutils
.nodes
.literal_block
):
1221 #isinstance(node.parent, docutils.nodes.term) or \
1222 #isinstance(node.parent, docutils.nodes.definition):
1224 text
= node
.astext()
1225 # Are we in mixed content? If so, add the text to the
1226 # etree tail of the previous sibling element.
1227 if len(self
.current_element
.getchildren()) > 0:
1228 #print '*** (visit_Text) 1. text: %s' % text
1229 if self
.current_element
.getchildren()[-1].tail
:
1230 self
.current_element
.getchildren()[-1].tail
+= text
1232 self
.current_element
.getchildren()[-1].tail
= text
1234 if self
.current_element
.text
:
1235 self
.current_element
.text
+= text
1237 self
.current_element
.text
= text
1239 def depart_Text(self
, node
):
1243 # Pre-defined fields
1246 def visit_address(self
, node
):
1247 #ipshell('At visit_address')
1248 el
= self
.generate_labeled_block(node
, 'Address: ')
1249 self
.set_current_element(el
)
1251 def depart_address(self
, node
):
1252 self
.set_to_parent()
1254 def visit_author(self
, node
):
1255 if isinstance(node
.parent
, nodes
.authors
):
1256 el
= self
.append_p('blockindent')
1258 el
= self
.generate_labeled_block(node
, 'Author: ')
1259 self
.set_current_element(el
)
1261 def depart_author(self
, node
):
1262 self
.set_to_parent()
1264 def visit_authors(self
, node
):
1265 #ipshell('At visit_authors')
1266 #self.trace_visit_node(node)
1268 el
= self
.append_p('textbody')
1269 el1
= SubElement(el
, 'text:span',
1270 attrib
={'text:style-name': self
.rststyle('strong')})
1273 def depart_authors(self
, node
):
1274 #self.trace_depart_node(node)
1277 def visit_contact(self
, node
):
1278 el
= self
.generate_labeled_block(node
, 'Contact: ')
1279 self
.set_current_element(el
)
1281 def depart_contact(self
, node
):
1282 self
.set_to_parent()
1284 def visit_copyright(self
, node
):
1285 el
= self
.generate_labeled_block(node
, 'Copyright: ')
1286 self
.set_current_element(el
)
1288 def depart_copyright(self
, node
):
1289 self
.set_to_parent()
1291 def visit_date(self
, node
):
1292 self
.generate_labeled_line(node
, 'Date: ')
1294 def depart_date(self
, node
):
1297 def visit_organization(self
, node
):
1298 el
= self
.generate_labeled_block(node
, 'Organization: ')
1299 self
.set_current_element(el
)
1301 def depart_organization(self
, node
):
1302 self
.set_to_parent()
1304 def visit_status(self
, node
):
1305 el
= self
.generate_labeled_block(node
, 'Status: ')
1306 self
.set_current_element(el
)
1308 def depart_status(self
, node
):
1309 self
.set_to_parent()
1311 def visit_revision(self
, node
):
1312 self
.generate_labeled_line(node
, 'Revision: ')
1314 def depart_revision(self
, node
):
1317 def visit_version(self
, node
):
1318 el
= self
.generate_labeled_line(node
, 'Version: ')
1319 #self.set_current_element(el)
1321 def depart_version(self
, node
):
1322 #self.set_to_parent()
1325 def visit_attribution(self
, node
):
1326 #ipshell('At visit_attribution')
1327 el
= self
.append_p('attribution', node
.astext())
1329 def depart_attribution(self
, node
):
1330 #ipshell('At depart_attribution')
1333 def visit_block_quote(self
, node
):
1334 #ipshell('At visit_block_quote')
1335 if 'epigraph' in node
.attributes
['classes']:
1336 self
.paragraph_style_stack
.append(self
.rststyle('epigraph'))
1337 self
.blockstyle
= self
.rststyle('epigraph')
1338 elif 'highlights' in node
.attributes
['classes']:
1339 self
.paragraph_style_stack
.append(self
.rststyle('highlights'))
1340 self
.blockstyle
= self
.rststyle('highlights')
1342 self
.paragraph_style_stack
.append(self
.rststyle('blockquote'))
1343 self
.blockstyle
= self
.rststyle('blockquote')
1345 def depart_block_quote(self
, node
):
1346 self
.paragraph_style_stack
.pop()
1347 self
.blockstyle
= ''
1349 def visit_bullet_list(self
, node
):
1350 #ipshell('At visit_bullet_list')
1351 if self
.in_table_of_contents
:
1352 if node
.has_key('classes') and \
1353 'auto-toc' in node
.attributes
['classes']:
1354 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1355 'text:style-name': self
.rststyle('tocenumlist'),
1357 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1359 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1360 'text:style-name': self
.rststyle('tocbulletlist'),
1362 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1364 if self
.blockstyle
== self
.rststyle('blockquote'):
1365 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1366 'text:style-name': self
.rststyle('blockquote-bulletlist'),
1368 self
.list_style_stack
.append(self
.rststyle('blockquote-bulletitem'))
1369 elif self
.blockstyle
== self
.rststyle('highlights'):
1370 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1371 'text:style-name': self
.rststyle('highlights-bulletlist'),
1373 self
.list_style_stack
.append(self
.rststyle('highlights-bulletitem'))
1374 elif self
.blockstyle
== self
.rststyle('epigraph'):
1375 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1376 'text:style-name': self
.rststyle('epigraph-bulletlist'),
1378 self
.list_style_stack
.append(self
.rststyle('epigraph-bulletitem'))
1380 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1381 'text:style-name': self
.rststyle('bulletlist'),
1383 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1384 self
.set_current_element(el
)
1386 def depart_bullet_list(self
, node
):
1387 self
.set_to_parent()
1388 self
.list_style_stack
.pop()
1390 def visit_caption(self
, node
):
1391 raise nodes
.SkipChildren()
1394 def depart_caption(self
, node
):
1397 def visit_comment(self
, node
):
1398 #ipshell('At visit_comment')
1399 el
= self
.append_p('textbody')
1400 el1
= SubElement(el
, 'office:annotation', attrib
={})
1401 el2
= SubElement(el1
, 'text:p', attrib
={})
1402 el2
.text
= node
.astext()
1404 def depart_comment(self
, node
):
1407 def visit_compound(self
, node
):
1408 # The compound directive currently receives no special treatment.
1411 def depart_compound(self
, node
):
1414 def visit_container(self
, node
):
1415 styles
= node
.attributes
.get('classes', ())
1417 self
.paragraph_style_stack
.append(styles
[0])
1419 def depart_container(self
, node
):
1420 #ipshell('At depart_container')
1421 styles
= node
.attributes
.get('classes', ())
1423 self
.paragraph_style_stack
.pop()
1425 def visit_decoration(self
, node
):
1427 #ipshell('At visit_decoration')
1429 #self.trace_visit_node(node)
1432 def depart_decoration(self
, node
):
1433 #ipshell('At depart_decoration')
1436 def visit_definition(self
, node
):
1437 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1438 self
.bumped_list_level_stack
.append(ListLevel(1))
1440 def depart_definition(self
, node
):
1441 self
.paragraph_style_stack
.pop()
1442 self
.bumped_list_level_stack
.pop()
1444 def visit_definition_list(self
, node
):
1447 def depart_definition_list(self
, node
):
1450 def visit_definition_list_item(self
, node
):
1453 def depart_definition_list_item(self
, node
):
1456 def visit_term(self
, node
):
1457 #ipshell('At visit_term')
1458 el
= self
.append_p('textbody')
1459 el1
= SubElement(el
, 'text:span',
1460 attrib
={'text:style-name': self
.rststyle('strong')})
1461 #el1.text = node.astext()
1462 self
.set_current_element(el1
)
1464 def depart_term(self
, node
):
1465 #ipshell('At depart_term')
1466 self
.set_to_parent()
1467 self
.set_to_parent()
1469 def visit_classifier(self
, node
):
1470 #ipshell('At visit_classifier')
1471 els
= self
.current_element
.getchildren()
1474 el1
= SubElement(el
, 'text:span',
1475 attrib
={'text:style-name': self
.rststyle('emphasis')
1477 el1
.text
= ' (%s)' % (node
.astext(), )
1479 def depart_classifier(self
, node
):
1482 def visit_document(self
, node
):
1483 #ipshell('At visit_document')
1484 #self.set_current_element(self.content_tree.getroot())
1487 def depart_document(self
, node
):
1490 def visit_docinfo(self
, node
):
1491 #self.document.reporter.debug_flag = 1
1492 self
.trace_visit_node(node
)
1493 self
.section_level
+= 1
1494 self
.section_count
+= 1
1495 if self
.settings
.create_sections
:
1496 el
= self
.append_child('text:section', attrib
={
1497 'text:name': 'Section%d' % self
.section_count
,
1498 'text:style-name': 'Sect%d' % self
.section_level
,
1500 self
.set_current_element(el
)
1502 def depart_docinfo(self
, node
):
1503 #self.document.reporter.debug_flag = 0
1504 self
.trace_depart_node(node
)
1505 self
.section_level
-= 1
1506 if self
.settings
.create_sections
:
1507 self
.set_to_parent()
1509 def visit_emphasis(self
, node
):
1510 el
= SubElement(self
.current_element
, 'text:span',
1511 attrib
={'text:style-name': self
.rststyle('emphasis')})
1512 self
.set_current_element(el
)
1514 def depart_emphasis(self
, node
):
1515 self
.set_to_parent()
1517 def visit_enumerated_list(self
, node
):
1518 el1
= self
.current_element
1519 if self
.blockstyle
== self
.rststyle('blockquote'):
1520 el2
= SubElement(el1
, 'text:list', attrib
={
1521 'text:style-name': self
.rststyle('blockquote-enumlist'),
1523 self
.list_style_stack
.append(self
.rststyle('blockquote-enumitem'))
1524 elif self
.blockstyle
== self
.rststyle('highlights'):
1525 el2
= SubElement(el1
, 'text:list', attrib
={
1526 'text:style-name': self
.rststyle('highlights-enumlist'),
1528 self
.list_style_stack
.append(self
.rststyle('highlights-enumitem'))
1529 elif self
.blockstyle
== self
.rststyle('epigraph'):
1530 el2
= SubElement(el1
, 'text:list', attrib
={
1531 'text:style-name': self
.rststyle('epigraph-enumlist'),
1533 self
.list_style_stack
.append(self
.rststyle('epigraph-enumitem'))
1535 el2
= SubElement(el1
, 'text:list', attrib
={
1536 'text:style-name': self
.rststyle('enumlist'),
1538 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1539 self
.set_current_element(el2
)
1541 def depart_enumerated_list(self
, node
):
1542 self
.set_to_parent()
1543 self
.list_style_stack
.pop()
1545 def visit_list_item(self
, node
):
1546 #ipshell('At visit_list_item')
1547 el1
= self
.append_child('text:list-item')
1548 # If we are in a "bumped" list level, then wrap this
1549 # list in an outer lists in order to increase the
1550 # indentation level.
1552 if len(self
.bumped_list_level_stack
) > 0:
1553 level_obj
= self
.bumped_list_level_stack
[-1]
1554 if level_obj
.get_sibling():
1555 level_obj
.set_nested(False)
1556 for level_obj1
in self
.bumped_list_level_stack
:
1557 for idx
in range(level_obj1
.get_level()):
1558 el2
= self
.append_child('text:list', parent
=el3
)
1559 el3
= self
.append_child('text:list-item', parent
=el2
)
1560 self
.paragraph_style_stack
.append(self
.list_style_stack
[-1])
1561 self
.set_current_element(el3
)
1563 def depart_list_item(self
, node
):
1564 if len(self
.bumped_list_level_stack
) > 0:
1565 level_obj
= self
.bumped_list_level_stack
[-1]
1566 if level_obj
.get_sibling():
1567 level_obj
.set_nested(True)
1568 for level_obj1
in self
.bumped_list_level_stack
:
1569 for idx
in range(level_obj1
.get_level()):
1570 self
.set_to_parent()
1571 self
.set_to_parent()
1572 self
.paragraph_style_stack
.pop()
1573 self
.set_to_parent()
1575 def visit_header(self
, node
):
1576 #ipshell('At visit_header')
1577 self
.in_header
= True
1579 def depart_header(self
, node
):
1580 #ipshell('At depart_header')
1581 self
.in_header
= False
1583 def visit_footer(self
, node
):
1584 #ipshell('At visit_footer')
1585 self
.in_footer
= True
1587 def depart_footer(self
, node
):
1588 #ipshell('At depart_footer')
1589 self
.in_footer
= False
1591 def visit_field(self
, node
):
1593 ## # Note that the syntaxhighlight directive produces this field node.
1594 ## # See class SyntaxHighlight, above.
1595 ## #ipshell('At visit_field')
1596 ## children = node.children
1597 ## if len(children) == 2:
1598 ## name = children[0].astext()
1599 ## if name == 'syntaxhighlight':
1600 ## body = children[1].astext()
1601 ## args = body.split()
1602 ## if args[0] == 'on':
1603 ## self.syntaxhighlighting = 1
1604 ## elif args[0] == 'off':
1605 ## self.syntaxhighlighting = 0
1607 ## self.syntaxhighlight_lexer = args[0]
1608 ## raise nodes.SkipChildren()
1610 def depart_field(self
, node
):
1613 def visit_field_list(self
, node
):
1614 #ipshell('At visit_field_list')
1617 def depart_field_list(self
, node
):
1618 #ipshell('At depart_field_list')
1621 def visit_field_name(self
, node
):
1622 #ipshell('At visit_field_name')
1623 #self.trace_visit_node(node)
1624 el
= self
.append_p('textbody')
1625 el1
= SubElement(el
, 'text:span',
1626 attrib
={'text:style-name': self
.rststyle('strong')})
1627 el1
.text
= node
.astext()
1629 def depart_field_name(self
, node
):
1630 #self.trace_depart_node(node)
1633 def visit_field_body(self
, node
):
1634 #ipshell('At visit_field_body')
1635 #self.trace_visit_node(node)
1636 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1638 def depart_field_body(self
, node
):
1639 #self.trace_depart_node(node)
1640 self
.paragraph_style_stack
.pop()
1642 def visit_figure(self
, node
):
1643 #ipshell('At visit_figure')
1644 #self.trace_visit_node(node)
1647 def depart_figure(self
, node
):
1648 #self.trace_depart_node(node)
1651 def visit_footnote(self
, node
):
1652 #ipshell('At visit_footnote')
1653 self
.in_footnote
= True
1654 ids
= node
.attributes
['ids']
1657 if id in self
.footnote_dict
:
1658 el1
= self
.footnote_dict
[id]
1661 el2
= SubElement(el1
, 'text:note-body')
1662 self
.paragraph_style_stack
.append(self
.rststyle('footnote'))
1663 self
.set_current_element(el2
)
1664 self
.footnote_found
= True
1666 self
.footnote_found
= False
1668 def depart_footnote(self
, node
):
1669 #ipshell('At depart_footnote')
1670 self
.in_footnote
= False
1671 if self
.footnote_found
:
1672 self
.current_element
.text
= ''
1673 self
.set_to_parent()
1674 self
.set_to_parent()
1675 self
.set_to_parent()
1676 self
.paragraph_style_stack
.pop()
1678 def visit_footnote_reference(self
, node
):
1679 #ipshell('At visit_footnote_reference')
1680 if not self
.in_footnote
:
1681 id = node
.attributes
['refid']
1682 children
= self
.current_element
.getchildren()
1683 if len(children
) > 0:
1684 if children
[-1].tail
and children
[-1].tail
[-1] == ' ':
1685 children
[-1].tail
= children
[-1].tail
[:-1]
1686 elif (self
.current_element
.text
and
1687 self
.current_element
.text
[-1] == ' '):
1688 self
.current_element
.text
= self
.current_element
.text
[:-1]
1689 el1
= self
.append_child('text:note', attrib
={
1690 'text:id': '%s' % (id, ),
1691 'text:note-class': 'footnote',
1693 self
.footnote_dict
[id] = el1
1694 raise nodes
.SkipChildren()
1696 def depart_footnote_reference(self
, node
):
1697 #ipshell('At depart_footnote_reference')
1700 def visit_label(self
, node
):
1701 #ipshell('At visit_label')
1704 def depart_label(self
, node
):
1705 #ipshell('At depart_label')
1708 def visit_generated(self
, node
):
1711 def depart_generated(self
, node
):
1714 def check_file_exists(self
, path
):
1715 if os
.path
.exists(path
):
1720 def visit_image(self
, node
):
1721 #ipshell('At visit_image')
1722 #self.trace_visit_node(node)
1723 # Capture the image file.
1724 if 'uri' in node
.attributes
:
1725 source
= node
.attributes
['uri']
1726 if not self
.check_file_exists(source
):
1727 print 'Error: Cannot find image file %s.' % (source
, )
1731 if source
in self
.image_dict
:
1732 filename
, destination
= self
.image_dict
[source
]
1734 self
.image_count
+= 1
1735 filename
= os
.path
.split(source
)[1]
1736 destination
= 'Pictures/1%08x%s' % (self
.image_count
, filename
, )
1737 spec
= (source
, destination
,)
1738 self
.embedded_file_list
.append(spec
)
1739 self
.image_dict
[source
] = (source
, destination
,)
1740 # Is this a figure (containing an image) or just a plain image?
1741 if self
.in_paragraph
:
1742 el1
= self
.current_element
1744 el1
= SubElement(self
.current_element
, 'text:p',
1745 attrib
={'text:style-name': self
.rststyle('textbody')})
1747 if isinstance(node
.parent
, docutils
.nodes
.figure
):
1748 el3
, el4
, caption
= self
.generate_figure(node
, source
,
1752 'draw:color-inversion': 'false',
1753 'draw:color-mode': 'standard',
1754 'draw:contrast': '0%',
1755 'draw:gamma': '100%',
1757 'draw:image-opacity': '100%',
1758 'draw:luminance': '0%',
1760 'fo:border': 'none',
1761 'fo:clip': 'rect(0in 0in 0in 0in)',
1762 'fo:margin-bottom': '0in',
1763 'fo:margin-left': '0in',
1764 'fo:margin-right': '0in',
1765 'fo:margin-top': '0in',
1766 'fo:padding': '0in',
1767 'style:horizontal-pos': 'from-left',
1768 'style:horizontal-rel': 'paragraph-content',
1769 'style:mirror': 'none',
1770 'style:run-through': 'foreground',
1771 'style:shadow': 'none',
1772 'style:vertical-pos': 'from-top',
1773 'style:vertical-rel': 'paragraph-content',
1774 'style:wrap': 'none',
1776 el5
, width
= self
.generate_image(node
, source
, destination
,
1778 if caption
is not None:
1780 else: #if isinstance(node.parent, docutils.nodes.image):
1781 el3
= self
.generate_image(node
, source
, destination
, el2
)
1783 def depart_image(self
, node
):
1786 def get_image_width_height(self
, node
, attr
, scale
):
1788 if attr
in node
.attributes
:
1790 size
= int(node
.attributes
[attr
])
1791 size
*= 35.278 / 1000.0
1793 except ValueError, e
:
1794 print 'Error: Invalid %s for image: "%s"' % (
1795 attr
, node
.attributes
[attr
], )
1798 def get_image_scale(self
, node
):
1799 if 'scale' in node
.attributes
:
1801 scale
= int(node
.attributes
['scale'])
1802 if scale
< 1: # or scale > 100:
1804 scale
= scale
* 0.01
1805 except ValueError, e
:
1806 print 'Error: Invalid scale for image: "%s"' % (
1807 node
.attributes
['scale'], )
1812 def get_image_scale_width_height(self
, node
, source
):
1813 scale
= self
.get_image_scale(node
)
1814 width
= self
.get_image_width_height(node
, 'width', scale
)
1815 height
= self
.get_image_width_height(node
, 'height', scale
)
1816 if ('scale' in node
.attributes
and
1817 ('width' not in node
.attributes
or
1818 'height' not in node
.attributes
)):
1819 if Image
is not None:
1820 if source
in self
.image_dict
:
1821 filename
, destination
= self
.image_dict
[source
]
1822 imageobj
= Image
.open(filename
, 'r')
1823 width
, height
= imageobj
.size
1824 width
= width
* (35.278 / 1000.0)
1826 height
= height
* (35.278 / 1000.0)
1829 raise RuntimeError, 'image has scale and no height/width and PIL not installed'
1830 return scale
, width
, height
1832 def generate_figure(self
, node
, source
, destination
, current_element
):
1833 #ipshell('At generate_figure')
1835 scale
, width
, height
= self
.get_image_scale_width_height(node
, source
)
1836 for node1
in node
.parent
.children
:
1837 if node1
.tagname
== 'caption':
1838 caption
= node1
.astext()
1839 self
.image_style_count
+= 1
1841 # Add the style for the caption.
1842 if caption
is not None:
1844 'style:class': 'extra',
1845 'style:family': 'paragraph',
1846 'style:name': 'Caption',
1847 'style:parent-style-name': 'Standard',
1849 el1
= SubElement(self
.automatic_styles
, 'style:style',
1850 attrib
=attrib
, nsdict
=SNSD
)
1852 'fo:margin-bottom': '0.0835in',
1853 'fo:margin-top': '0.0835in',
1854 'text:line-number': '0',
1855 'text:number-lines': 'false',
1857 el2
= SubElement(el1
, 'style:paragraph-properties',
1858 attrib
=attrib
, nsdict
=SNSD
)
1860 'fo:font-size': '12pt',
1861 'fo:font-style': 'italic',
1862 'style:font-name': 'Times',
1863 'style:font-name-complex': 'Lucidasans1',
1864 'style:font-size-asian': '12pt',
1865 'style:font-size-complex': '12pt',
1866 'style:font-style-asian': 'italic',
1867 'style:font-style-complex': 'italic',
1869 el2
= SubElement(el1
, 'style:text-properties',
1870 attrib
=attrib
, nsdict
=SNSD
)
1871 style_name
= 'rstframestyle%d' % self
.image_style_count
1874 'style:name': style_name
,
1875 'style:family': 'graphic',
1876 'style:parent-style-name': 'Frame',
1878 el1
= SubElement(self
.automatic_styles
,
1879 'style:style', attrib
=attrib
, nsdict
=SNSD
)
1882 if 'align' in node
.attributes
:
1883 align
= node
.attributes
['align'].split()
1885 if val
in ('left', 'center', 'right'):
1887 elif val
in ('top', 'middle', 'bottom'):
1890 'fo:margin-left': '0cm',
1891 'fo:margin-right': '0cm',
1892 'fo:margin-top': '0cm',
1893 'fo:margin-bottom': '0cm',
1894 'style:wrap': 'dynamic',
1895 'style:number-wrapped-paragraphs': 'no-limit',
1896 'style:vertical-pos': valign
,
1897 'style:vertical-rel': 'paragraph',
1898 'style:horizontal-pos': halign
,
1899 'style:horizontal-rel': 'paragraph',
1900 'fo:padding': '0cm',
1901 'fo:border': 'none',
1903 el2
= SubElement(el1
,
1904 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
1905 # Add the content wrapper.
1906 ## attrib = {'text:style-name': self.rststyle('textbody')}
1907 ## el1 = SubElement(self.current_element, 'text:p', attrib=attrib)
1909 'draw:style-name': style_name
,
1910 'draw:name': 'Frame1',
1911 'text:anchor-type': 'paragraph',
1912 'draw:z-index': '1',
1914 if width
is not None:
1915 attrib
['svg:width'] = '%.2fcm' % (width
, )
1916 el3
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
1918 el4
= SubElement(el3
, 'draw:text-box', attrib
=attrib
)
1920 'text:style-name': self
.rststyle('caption'),
1922 el5
= SubElement(el4
, 'text:p', attrib
=attrib
)
1923 ## if caption is not None:
1924 ## el3.tail = caption
1925 return el3
, el5
, caption
1927 def generate_image(self
, node
, source
, destination
, current_element
,
1928 #ipshell('At generate_image')
1930 scale
, width
, height
= self
.get_image_scale_width_height(node
, source
)
1931 self
.image_style_count
+= 1
1932 style_name
= 'rstframestyle%d' % self
.image_style_count
1935 'style:name': style_name
,
1936 'style:family': 'graphic',
1937 'style:parent-style-name': 'Graphics',
1939 el1
= SubElement(self
.automatic_styles
,
1940 'style:style', attrib
=attrib
, nsdict
=SNSD
)
1943 if 'align' in node
.attributes
:
1944 align
= node
.attributes
['align'].split()
1946 if val
in ('left', 'center', 'right'):
1948 elif val
in ('top', 'middle', 'bottom'):
1950 if frame_attrs
is None:
1952 'style:vertical-pos': 'top',
1953 'style:vertical-rel': 'paragraph',
1954 #'style:horizontal-pos': halign,
1955 #'style:vertical-pos': valign,
1956 'style:horizontal-rel': 'paragraph',
1957 'style:mirror': 'none',
1958 'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
1959 'draw:luminance': '0%',
1960 'draw:contrast': '0%',
1964 'draw:gamma': '100%',
1965 'draw:color-inversion': 'false',
1966 'draw:image-opacity': '100%',
1967 'draw:color-mode': 'standard',
1970 attrib
= frame_attrs
1971 if halign
is not None:
1972 attrib
['style:horizontal-pos'] = halign
1973 if valign
is not None:
1974 attrib
['style:vertical-pos'] = valign
1975 #ipshell('At generate_image')
1976 # If we are inside a table, add a no-wrap style.
1977 if self
.is_in_table(node
):
1978 attrib
['style:wrap'] = 'none'
1979 el2
= SubElement(el1
,
1980 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
1982 #el = SubElement(current_element, 'text:p',
1983 # attrib={'text:style-name': self.rststyle('textbody')})
1985 'draw:style-name': style_name
,
1986 'draw:name': 'graphics2',
1987 #'text:anchor-type': 'paragraph',
1988 #'svg:width': '%fcm' % (width, ),
1989 #'svg:height': '%fcm' % (height, ),
1990 'draw:z-index': '1',
1992 if isinstance(node
.parent
, nodes
.TextElement
):
1993 attrib
['text:anchor-type'] = 'char'
1995 attrib
['text:anchor-type'] = 'paragraph'
1996 if width
is not None:
1997 attrib
['svg:width'] = '%.2fcm' % (width
, )
1998 if height
is not None:
1999 attrib
['svg:height'] = '%.2fcm' % (height
, )
2000 el1
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
2001 el2
= SubElement(el1
, 'draw:image', attrib
={
2002 'xlink:href': '%s' % (destination
, ),
2003 'xlink:type': 'simple',
2004 'xlink:show': 'embed',
2005 'xlink:actuate': 'onLoad',
2009 def is_in_table(self
, node
):
2012 if isinstance(node1
, docutils
.nodes
.entry
):
2014 node1
= node1
.parent
2017 def visit_legend(self
, node
):
2018 # Currently, the legend receives *no* special treatment.
2019 #ipshell('At visit_legend')
2022 def depart_legend(self
, node
):
2025 def visit_line(self
, node
):
2026 #ipshell('At visit_line')
2029 def depart_line(self
, node
):
2030 #ipshell('At depart_line')
2033 def visit_line_block(self
, node
):
2034 #ipshell('At visit_line_block')
2036 lines
= s1
.split('\n')
2037 el
= self
.append_p('lineblock', lines
[0])
2040 for line
in lines
[1:]:
2046 el1
= SubElement(el
, 'text:line-break')
2049 def depart_line_block(self
, node
):
2050 #ipshell('At depart_line_block')
2053 def visit_literal(self
, node
):
2054 #ipshell('At visit_literal')
2055 el
= SubElement(self
.current_element
, 'text:span',
2056 attrib
={'text:style-name': self
.rststyle('inlineliteral')})
2057 self
.set_current_element(el
)
2059 def depart_literal(self
, node
):
2060 self
.set_to_parent()
2062 def _calculate_code_block_padding(self
, line
):
2064 matchobj
= SPACES_PATTERN
.match(line
)
2066 pad
= matchobj
.group()
2069 matchobj
= TABS_PATTERN
.match(line
)
2071 pad
= matchobj
.group()
2072 count
= len(pad
) * 8
2075 def _add_syntax_highlighting(self
, insource
, language
):
2076 #print '(_add_syntax_highlighting) using lexer: %s' % (language, )
2077 lexer
= pygments
.lexers
.get_lexer_by_name(language
, stripall
=True)
2078 if language
in ('latex', 'tex'):
2079 fmtr
= OdtPygmentsLaTeXFormatter(lambda name
, parameters
=():
2080 self
.rststyle(name
, parameters
))
2082 fmtr
= OdtPygmentsProgFormatter(lambda name
, parameters
=():
2083 self
.rststyle(name
, parameters
))
2084 outsource
= pygments
.highlight(insource
, lexer
, fmtr
)
2087 def fill_line(self
, line
):
2088 line
= FILL_PAT1
.sub(self
.fill_func1
, line
)
2089 line
= FILL_PAT2
.sub(self
.fill_func2
, line
)
2092 def fill_func1(self
, matchobj
):
2093 spaces
= matchobj
.group(0)
2094 repl
= '<text:s text:c="%d"/>' % (len(spaces
), )
2097 def fill_func2(self
, matchobj
):
2098 spaces
= matchobj
.group(0)
2099 repl
= ' <text:s text:c="%d"/>' % (len(spaces
) - 1, )
2102 def visit_literal_block(self
, node
):
2103 #ipshell('At visit_literal_block')
2104 wrapper1
= '<text:p text:style-name="%s">%%s</text:p>' % (
2105 self
.rststyle('codeblock'), )
2106 source
= node
.astext()
2108 self
.settings
.add_syntax_highlighting
and
2109 node
.get('hilight', False)):
2110 language
= node
.get('language', 'python')
2111 source
= self
._add
_syntax
_highlighting
(source
, language
)
2113 source
= escape_cdata(source
)
2114 lines
= source
.split('\n')
2115 lines1
= ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
2118 for my_line
in lines
:
2119 my_line
= self
.fill_line(my_line
)
2120 my_line
= my_line
.replace(" ", "\n")
2121 my_lines
.append(my_line
)
2122 my_lines_str
= '<text:line-break/>'.join(my_lines
)
2123 my_lines_str2
= wrapper1
% (my_lines_str
, )
2124 lines1
.append(my_lines_str2
)
2125 lines1
.append('</wrappertag1>')
2126 s1
= ''.join(lines1
)
2127 if WhichElementTree
!= "lxml":
2128 s1
= s1
.encode("utf-8")
2129 el1
= etree
.fromstring(s1
)
2130 children
= el1
.getchildren()
2131 for child
in children
:
2132 self
.current_element
.append(child
)
2134 def depart_literal_block(self
, node
):
2137 visit_doctest_block
= visit_literal_block
2138 depart_doctest_block
= depart_literal_block
2140 def visit_meta(self
, node
):
2141 #ipshell('At visit_meta')
2142 name
= node
.attributes
.get('name')
2143 content
= node
.attributes
.get('content')
2144 if name
is not None and content
is not None:
2145 self
.meta_dict
[name
] = content
2147 def depart_meta(self
, node
):
2150 def visit_option_list(self
, node
):
2151 table_name
= 'tableoption'
2153 # Generate automatic styles
2154 if not self
.optiontablestyles_generated
:
2155 self
.optiontablestyles_generated
= True
2156 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2157 'style:name': self
.rststyle(table_name
),
2158 'style:family': 'table'}, nsdict
=SNSD
)
2159 el1
= SubElement(el
, 'style:table-properties', attrib
={
2160 'style:width': '17.59cm',
2161 'table:align': 'left',
2162 'style:shadow': 'none'}, nsdict
=SNSD
)
2163 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2164 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'A', )),
2165 'style:family': 'table-column'}, nsdict
=SNSD
)
2166 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2167 'style:column-width': '4.999cm'}, nsdict
=SNSD
)
2168 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2169 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'B', )),
2170 'style:family': 'table-column'}, nsdict
=SNSD
)
2171 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2172 'style:column-width': '12.587cm'}, nsdict
=SNSD
)
2173 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2174 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2175 'style:family': 'table-cell'}, nsdict
=SNSD
)
2176 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2177 'fo:background-color': 'transparent',
2178 'fo:padding': '0.097cm',
2179 'fo:border-left': '0.035cm solid #000000',
2180 'fo:border-right': 'none',
2181 'fo:border-top': '0.035cm solid #000000',
2182 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2183 el2
= SubElement(el1
, 'style:background-image', nsdict
=SNSD
)
2184 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2185 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2186 'style:family': 'table-cell'}, nsdict
=SNSD
)
2187 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2188 'fo:padding': '0.097cm',
2189 'fo:border': '0.035cm solid #000000'}, nsdict
=SNSD
)
2190 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2191 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 2, )),
2192 'style:family': 'table-cell'}, nsdict
=SNSD
)
2193 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2194 'fo:padding': '0.097cm',
2195 'fo:border-left': '0.035cm solid #000000',
2196 'fo:border-right': 'none',
2197 'fo:border-top': 'none',
2198 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2199 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2200 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 2, )),
2201 'style:family': 'table-cell'}, nsdict
=SNSD
)
2202 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2203 'fo:padding': '0.097cm',
2204 'fo:border-left': '0.035cm solid #000000',
2205 'fo:border-right': '0.035cm solid #000000',
2206 'fo:border-top': 'none',
2207 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2209 # Generate table data
2210 el
= self
.append_child('table:table', attrib
={
2211 'table:name': self
.rststyle(table_name
),
2212 'table:style-name': self
.rststyle(table_name
),
2214 el1
= SubElement(el
, 'table:table-column', attrib
={
2215 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'A', ))})
2216 el1
= SubElement(el
, 'table:table-column', attrib
={
2217 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'B', ))})
2218 el1
= SubElement(el
, 'table:table-header-rows')
2219 el2
= SubElement(el1
, 'table:table-row')
2220 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2221 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2222 'office:value-type': 'string'})
2223 el4
= SubElement(el3
, 'text:p', attrib
={
2224 'text:style-name': 'Table_20_Heading'})
2226 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2227 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2228 'office:value-type': 'string'})
2229 el4
= SubElement(el3
, 'text:p', attrib
={
2230 'text:style-name': 'Table_20_Heading'})
2231 el4
.text
= 'Description'
2232 self
.set_current_element(el
)
2234 def depart_option_list(self
, node
):
2235 #self.document.reporter.debug_flag = 0
2236 self
.set_to_parent()
2238 def visit_option_list_item(self
, node
):
2239 el
= self
.append_child('table:table-row')
2240 self
.set_current_element(el
)
2242 def depart_option_list_item(self
, node
):
2243 self
.set_to_parent()
2245 def visit_option_group(self
, node
):
2246 el
= self
.append_child('table:table-cell', attrib
={
2247 'table:style-name': 'Table%d.A2' % self
.table_count
,
2248 'office:value-type': 'string',
2250 self
.set_current_element(el
)
2252 def depart_option_group(self
, node
):
2253 self
.set_to_parent()
2255 def visit_option(self
, node
):
2256 el
= self
.append_child('text:p', attrib
={
2257 'text:style-name': 'Table_20_Contents'})
2258 el
.text
= node
.astext()
2260 def depart_option(self
, node
):
2263 def visit_option_string(self
, node
):
2266 def depart_option_string(self
, node
):
2269 def visit_option_argument(self
, node
):
2270 #ipshell('At visit_option_argument')
2273 def depart_option_argument(self
, node
):
2276 def visit_description(self
, node
):
2277 el
= self
.append_child('table:table-cell', attrib
={
2278 'table:style-name': 'Table%d.B2' % self
.table_count
,
2279 'office:value-type': 'string',
2281 el1
= SubElement(el
, 'text:p', attrib
={
2282 'text:style-name': 'Table_20_Contents'})
2283 el1
.text
= node
.astext()
2284 raise nodes
.SkipChildren()
2286 def depart_description(self
, node
):
2289 def visit_paragraph(self
, node
):
2290 #ipshell('At visit_paragraph')
2291 #self.trace_visit_node(node)
2292 self
.in_paragraph
= True
2294 el
= self
.append_p('header')
2295 elif self
.in_footer
:
2296 el
= self
.append_p('footer')
2298 style_name
= self
.paragraph_style_stack
[-1]
2299 el
= self
.append_child('text:p',
2300 attrib
={'text:style-name': style_name
})
2301 self
.append_pending_ids(el
)
2302 self
.set_current_element(el
)
2304 def depart_paragraph(self
, node
):
2305 #ipshell('At depart_paragraph')
2306 #self.trace_depart_node(node)
2307 self
.in_paragraph
= False
2308 self
.set_to_parent()
2310 self
.header_content
.append(self
.current_element
.getchildren()[-1])
2311 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2312 elif self
.in_footer
:
2313 self
.footer_content
.append(self
.current_element
.getchildren()[-1])
2314 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2316 def visit_problematic(self
, node
):
2317 #print '(visit_problematic) node: %s' % (node.astext(), )
2320 def depart_problematic(self
, node
):
2323 def visit_raw(self
, node
):
2324 #ipshell('At visit_raw')
2325 if 'format' in node
.attributes
:
2326 formats
= node
.attributes
['format']
2327 formatlist
= formats
.split()
2328 if 'odt' in formatlist
:
2329 rawstr
= node
.astext()
2330 attrstr
= ' '.join(['%s="%s"' % (k
, v
, )
2331 for k
,v
in CONTENT_NAMESPACE_ATTRIB
.items()])
2332 contentstr
= '<stuff %s>%s</stuff>' % (attrstr
, rawstr
, )
2333 if WhichElementTree
!= "lxml":
2334 content
= content
.encode("utf-8")
2335 content
= etree
.fromstring(contentstr
)
2336 elements
= content
.getchildren()
2337 if len(elements
) > 0:
2341 elif self
.in_footer
:
2344 self
.current_element
.append(el1
)
2345 raise nodes
.SkipChildren()
2347 def depart_raw(self
, node
):
2350 elif self
.in_footer
:
2355 def visit_reference(self
, node
):
2356 #self.trace_visit_node(node)
2357 text
= node
.astext()
2358 if node
.has_key('refuri'):
2359 href
= node
['refuri']
2360 if ( self
.settings
.cloak_email_addresses
2361 and href
.startswith('mailto:')):
2362 href
= self
.cloak_mailto(href
)
2363 el
= self
.append_child('text:a', attrib
={
2364 'xlink:href': '%s' % href
,
2365 'xlink:type': 'simple',
2367 self
.set_current_element(el
)
2368 elif node
.has_key('refid'):
2369 href
= node
['refid']
2370 el
= self
.append_child('text:reference-ref', attrib
={
2371 'text:ref-name': '%s' % href
,
2372 'text:reference-format': 'text',
2375 raise RuntimeError, 'References must have "refuri" or "refid" attribute.'
2376 #print '(visit_reference) href: "%s" text: "%s"' % (href, text, )
2377 if (self
.in_table_of_contents
and
2378 len(node
.children
) >= 1 and
2379 isinstance(node
.children
[0], docutils
.nodes
.generated
)):
2380 node
.remove(node
.children
[0])
2382 def depart_reference(self
, node
):
2383 #self.trace_depart_node(node)
2384 if node
.has_key('refuri'):
2385 self
.set_to_parent()
2387 def visit_rubric(self
, node
):
2388 style_name
= self
.rststyle('rubric')
2389 classes
= node
.get('classes')
2394 el
= SubElement(self
.current_element
, 'text:h', attrib
= {
2395 #'text:outline-level': '%d' % section_level,
2396 #'text:style-name': 'Heading_20_%d' % section_level,
2397 'text:style-name': style_name
,
2399 text
= node
.astext()
2400 el
.text
= self
.encode(text
)
2402 def depart_rubric(self
, node
):
2405 def visit_section(self
, node
, move_ids
=1):
2406 #ipshell('At visit_section')
2407 self
.section_level
+= 1
2408 self
.section_count
+= 1
2409 if self
.settings
.create_sections
:
2410 el
= self
.append_child('text:section', attrib
={
2411 'text:name': 'Section%d' % self
.section_count
,
2412 'text:style-name': 'Sect%d' % self
.section_level
,
2414 self
.set_current_element(el
)
2416 def depart_section(self
, node
):
2417 self
.section_level
-= 1
2418 if self
.settings
.create_sections
:
2419 self
.set_to_parent()
2421 def visit_strong(self
, node
):
2422 #ipshell('At visit_strong')
2423 el
= SubElement(self
.current_element
, 'text:span',
2424 attrib
={'text:style-name': self
.rststyle('strong')})
2425 self
.set_current_element(el
)
2427 def depart_strong(self
, node
):
2428 self
.set_to_parent()
2430 def visit_substitution_definition(self
, node
):
2431 #ipshell('At visit_substitution_definition')
2432 raise nodes
.SkipChildren()
2434 def depart_substitution_definition(self
, node
):
2435 #ipshell('At depart_substitution_definition')
2438 def visit_system_message(self
, node
):
2439 #print '(visit_system_message) node: %s' % (node.astext(), )
2442 def depart_system_message(self
, node
):
2445 def visit_table(self
, node
):
2446 #self.trace_visit_node(node)
2447 #ipshell('At visit_table')
2448 self
.table_count
+= 1
2449 table_name
= '%s%%d' % TableStylePrefix
2450 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2451 'style:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2452 'style:family': 'table',
2454 el1_1
= SubElement(el1
, 'style:table-properties', attrib
={
2455 #'style:width': '17.59cm',
2456 'table:align': 'margins',
2458 # We use a single cell style for all cells in this table.
2459 # That's probably not correct, but seems to work.
2460 el2
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2461 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( self
.table_count
, 'A', 1, )),
2462 'style:family': 'table-cell',
2464 line_style1
= '0.%03dcm solid #000000' % self
.settings
.table_border_thickness
2465 el2_1
= SubElement(el2
, 'style:table-cell-properties', attrib
={
2466 'fo:padding': '0.049cm',
2467 'fo:border-left': line_style1
,
2468 'fo:border-right': line_style1
,
2469 'fo:border-top': line_style1
,
2470 'fo:border-bottom': line_style1
,
2473 for child
in node
.children
:
2474 if child
.tagname
== 'title':
2475 title
= child
.astext()
2477 if title
is not None:
2478 el3
= self
.append_p('table-title', title
)
2480 #print 'no table title'
2482 el4
= SubElement(self
.current_element
, 'table:table', attrib
={
2483 'table:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2484 'table:style-name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2486 self
.set_current_element(el4
)
2487 self
.current_table_style
= el1
2488 self
.table_width
= 0
2490 def depart_table(self
, node
):
2491 #self.trace_depart_node(node)
2492 #ipshell('At depart_table')
2493 attribkey
= add_ns('style:width', nsdict
=SNSD
)
2494 attribval
= '%dcm' % self
.table_width
2495 self
.current_table_style
.attrib
[attribkey
] = attribval
2496 self
.set_to_parent()
2498 def visit_tgroup(self
, node
):
2499 #self.trace_visit_node(node)
2500 #ipshell('At visit_tgroup')
2501 self
.column_count
= ord('A') - 1
2503 def depart_tgroup(self
, node
):
2504 #self.trace_depart_node(node)
2507 def visit_colspec(self
, node
):
2508 #self.trace_visit_node(node)
2509 #ipshell('At visit_colspec')
2510 self
.column_count
+= 1
2511 colspec_name
= self
.rststyle('%s%%d.%%s' % TableStylePrefix
, ( self
.table_count
, chr(self
.column_count
), ))
2512 colwidth
= node
['colwidth']
2513 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2514 'style:name': colspec_name
,
2515 'style:family': 'table-column',
2517 el1_1
= SubElement(el1
, 'style:table-column-properties', attrib
={
2518 'style:column-width': '%dcm' % colwidth
}, nsdict
=SNSD
)
2519 el2
= self
.append_child('table:table-column', attrib
={
2520 'table:style-name': colspec_name
,
2522 self
.table_width
+= colwidth
2524 def depart_colspec(self
, node
):
2525 #self.trace_depart_node(node)
2528 def visit_thead(self
, node
):
2529 #self.trace_visit_node(node)
2530 #ipshell('At visit_thead')
2531 el
= self
.append_child('table:table-header-rows')
2532 self
.set_current_element(el
)
2533 self
.in_thead
= True
2534 self
.paragraph_style_stack
.append('Table_20_Heading')
2536 def depart_thead(self
, node
):
2537 #self.trace_depart_node(node)
2538 self
.set_to_parent()
2539 self
.in_thead
= False
2540 self
.paragraph_style_stack
.pop()
2542 def visit_row(self
, node
):
2543 #self.trace_visit_node(node)
2544 #ipshell('At visit_row')
2545 self
.column_count
= ord('A') - 1
2546 el
= self
.append_child('table:table-row')
2547 self
.set_current_element(el
)
2549 def depart_row(self
, node
):
2550 #self.trace_depart_node(node)
2551 self
.set_to_parent()
2553 def visit_entry(self
, node
):
2554 #self.trace_visit_node(node)
2555 #ipshell('At visit_entry')
2556 self
.column_count
+= 1
2557 cellspec_name
= self
.rststyle('%s%%d.%%c%%d' % TableStylePrefix
, ( self
.table_count
, 'A', 1, ))
2559 'table:style-name': cellspec_name
,
2560 'office:value-type': 'string',
2562 morecols
= node
.get('morecols', 0)
2564 attrib
['table:number-columns-spanned'] = '%d' % (morecols
+ 1,)
2565 self
.column_count
+= morecols
2566 el1
= self
.append_child('table:table-cell', attrib
=attrib
)
2567 self
.set_current_element(el1
)
2569 def depart_entry(self
, node
):
2570 #self.trace_depart_node(node)
2571 self
.set_to_parent()
2573 def visit_tbody(self
, node
):
2574 #self.trace_visit_node(node)
2575 #ipshell('At visit_')
2578 def depart_tbody(self
, node
):
2579 #self.trace_depart_node(node)
2582 def visit_target(self
, node
):
2584 # I don't know how to implement targets in ODF.
2585 # How do we create a target in oowriter? A cross-reference?
2586 if not (node
.has_key('refuri') or node
.has_key('refid')
2587 or node
.has_key('refname')):
2592 def depart_target(self
, node
):
2595 def visit_title(self
, node
, move_ids
=1):
2596 #ipshell('At visit_title')
2597 if isinstance(node
.parent
, docutils
.nodes
.section
):
2598 section_level
= self
.section_level
2599 if section_level
> 7:
2600 print 'Warning: Heading/section levels greater than 7 not supported.'
2601 print ' Reducing to heading level 7 for heading:'
2602 print ' "%s"' % node
.astext()
2604 el1
= self
.append_child('text:h', attrib
= {
2605 'text:outline-level': '%d' % section_level
,
2606 #'text:style-name': 'Heading_20_%d' % section_level,
2607 'text:style-name': self
.rststyle('heading%d', (section_level
, )),
2609 self
.append_pending_ids(el1
)
2610 self
.set_current_element(el1
)
2611 elif isinstance(node
.parent
, docutils
.nodes
.document
):
2612 # text = self.settings.title
2614 # text = node.astext()
2615 el1
= SubElement(self
.current_element
, 'text:h', attrib
= {
2616 'text:outline-level': '1',
2617 'text:style-name': self
.rststyle('heading%d', ( 1, )),
2619 self
.append_pending_ids(el1
)
2620 text
= node
.astext()
2622 self
.found_doc_title
= True
2623 self
.set_current_element(el1
)
2625 def depart_title(self
, node
):
2626 if (isinstance(node
.parent
, docutils
.nodes
.section
) or
2627 isinstance(node
.parent
, docutils
.nodes
.document
)):
2628 self
.set_to_parent()
2630 visit_subtitle
= visit_title
2631 depart_subtitle
= depart_title
2633 def visit_title_reference(self
, node
):
2634 #ipshell('At visit_title_reference')
2635 el
= self
.append_child('text:span', attrib
={
2636 'text:style-name': self
.rststyle('quotation')})
2637 el
.text
= self
.encode(node
.astext())
2639 def depart_title_reference(self
, node
):
2642 def visit_topic(self
, node
):
2643 #ipshell('At visit_topic')
2644 if 'classes' in node
.attributes
:
2645 if 'contents' in node
.attributes
['classes']:
2646 el
= self
.append_p('horizontalline')
2647 el
= self
.append_p('centeredtextbody')
2648 el1
= SubElement(el
, 'text:span',
2649 attrib
={'text:style-name': self
.rststyle('strong')})
2650 el1
.text
= 'Contents'
2651 self
.in_table_of_contents
= True
2652 elif 'abstract' in node
.attributes
['classes']:
2653 el
= self
.append_p('horizontalline')
2654 el
= self
.append_p('centeredtextbody')
2655 el1
= SubElement(el
, 'text:span',
2656 attrib
={'text:style-name': self
.rststyle('strong')})
2657 el1
.text
= 'Abstract'
2659 def depart_topic(self
, node
):
2660 #ipshell('At depart_topic')
2661 if 'classes' in node
.attributes
:
2662 if 'contents' in node
.attributes
['classes']:
2663 el
= self
.append_p('horizontalline')
2664 self
.in_table_of_contents
= False
2666 def visit_transition(self
, node
):
2667 el
= self
.append_p('horizontalline')
2669 def depart_transition(self
, node
):
2675 def visit_warning(self
, node
):
2676 self
.generate_admonition(node
, 'warning')
2678 def depart_warning(self
, node
):
2679 self
.paragraph_style_stack
.pop()
2681 def visit_attention(self
, node
):
2682 self
.generate_admonition(node
, 'attention')
2684 depart_attention
= depart_warning
2686 def visit_caution(self
, node
):
2687 self
.generate_admonition(node
, 'caution')
2689 depart_caution
= depart_warning
2691 def visit_danger(self
, node
):
2692 self
.generate_admonition(node
, 'danger')
2694 depart_danger
= depart_warning
2696 def visit_error(self
, node
):
2697 self
.generate_admonition(node
, 'error')
2699 depart_error
= depart_warning
2701 def visit_hint(self
, node
):
2702 self
.generate_admonition(node
, 'hint')
2704 depart_hint
= depart_warning
2706 def visit_important(self
, node
):
2707 self
.generate_admonition(node
, 'important')
2709 depart_important
= depart_warning
2711 def visit_note(self
, node
):
2712 self
.generate_admonition(node
, 'note')
2714 depart_note
= depart_warning
2716 def visit_tip(self
, node
):
2717 self
.generate_admonition(node
, 'tip')
2719 depart_tip
= depart_warning
2721 def visit_admonition(self
, node
):
2722 #import pdb; pdb.set_trace()
2724 for child
in node
.children
:
2725 if child
.tagname
== 'title':
2726 title
= child
.astext()
2728 classes1
= node
.get('classes')
2731 self
.generate_admonition(node
, 'generic', title
)
2733 depart_admonition
= depart_warning
2735 def generate_admonition(self
, node
, label
, title
=None):
2736 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2737 'text:style-name': self
.rststyle('admon-%s-hdr', ( label
, )),
2742 el1
.text
= '%s!' % (label
.capitalize(), )
2743 s1
= self
.rststyle('admon-%s-body', ( label
, ))
2744 self
.paragraph_style_stack
.append(s1
)
2747 # Roles (e.g. subscript, superscript, strong, ...
2749 def visit_subscript(self
, node
):
2750 el
= self
.append_child('text:span', attrib
={
2751 'text:style-name': 'rststyle-subscript',
2753 self
.set_current_element(el
)
2755 def depart_subscript(self
, node
):
2756 self
.set_to_parent()
2758 def visit_superscript(self
, node
):
2759 el
= self
.append_child('text:span', attrib
={
2760 'text:style-name': 'rststyle-superscript',
2762 self
.set_current_element(el
)
2764 def depart_superscript(self
, node
):
2765 self
.set_to_parent()