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
26 from docutils
.readers
import standalone
27 from docutils
.transforms
import references
33 from lxml
import etree
34 #print '*** using lxml'
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 #print '*** using ElementTree'
41 WhichElementTree
= 'elementtree'
42 except ImportError, e
:
44 # 3. Try to use a version of ElementTree installed as a separate
46 from elementtree
import ElementTree
as etree
47 WhichElementTree
= 'elementtree'
48 except ImportError, e
:
50 print '*** Error: Must install either ElementTree or lxml or'
51 print '*** a version of Python containing ElementTree.'
57 import pygments
.formatter
58 import pygments
.lexers
59 class OdtPygmentsFormatter(pygments
.formatter
.Formatter
):
60 def __init__(self
, rststyle_function
):
61 pygments
.formatter
.Formatter
.__init
__(self
)
62 self
.rststyle_function
= rststyle_function
64 def rststyle(self
, name
, parameters
=( )):
65 return self
.rststyle_function(name
, parameters
)
67 class OdtPygmentsProgFormatter(OdtPygmentsFormatter
):
68 def format(self
, tokensource
, outfile
):
69 tokenclass
= pygments
.token
.Token
70 for ttype
, value
in tokensource
:
71 value
= escape_cdata(value
)
72 if ttype
== tokenclass
.Keyword
:
73 s2
= self
.rststyle('codeblock-keyword')
74 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
76 elif ttype
== tokenclass
.Literal
.String
:
77 s2
= self
.rststyle('codeblock-string')
78 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
81 tokenclass
.Literal
.Number
.Integer
,
82 tokenclass
.Literal
.Number
.Integer
.Long
,
83 tokenclass
.Literal
.Number
.Float
,
84 tokenclass
.Literal
.Number
.Hex
,
85 tokenclass
.Literal
.Number
.Oct
,
86 tokenclass
.Literal
.Number
,
88 s2
= self
.rststyle('codeblock-number')
89 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
91 elif ttype
== tokenclass
.Operator
:
92 s2
= self
.rststyle('codeblock-operator')
93 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
95 elif ttype
== tokenclass
.Comment
:
96 s2
= self
.rststyle('codeblock-comment')
97 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
99 elif ttype
== tokenclass
.Name
.Class
:
100 s2
= self
.rststyle('codeblock-classname')
101 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
103 elif ttype
== tokenclass
.Name
.Function
:
104 s2
= self
.rststyle('codeblock-functionname')
105 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
107 elif ttype
== tokenclass
.Name
:
108 s2
= self
.rststyle('codeblock-name')
109 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
114 class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter
):
115 def format(self
, tokensource
, outfile
):
116 tokenclass
= pygments
.token
.Token
117 for ttype
, value
in tokensource
:
118 value
= escape_cdata(value
)
119 if ttype
== tokenclass
.Keyword
:
120 s2
= self
.rststyle('codeblock-keyword')
121 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
123 elif ttype
in (tokenclass
.Literal
.String
,
124 tokenclass
.Literal
.String
.Backtick
,
126 s2
= self
.rststyle('codeblock-string')
127 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
129 elif ttype
== tokenclass
.Name
.Attribute
:
130 s2
= self
.rststyle('codeblock-operator')
131 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
133 elif ttype
== tokenclass
.Comment
:
134 if value
[-1] == '\n':
135 s2
= self
.rststyle('codeblock-comment')
136 s1
= '<text:span text:style-name="%s">%s</text:span>\n' % \
139 s2
= self
.rststyle('codeblock-comment')
140 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
142 elif ttype
== tokenclass
.Name
.Builtin
:
143 s2
= self
.rststyle('codeblock-name')
144 s1
= '<text:span text:style-name="%s">%s</text:span>' % \
150 except ImportError, e
:
154 # Is the PIL imaging library installed?
157 except ImportError, exp
:
160 ## from IPython.Shell import IPShellEmbed
161 ## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ',
162 ## '-po', 'Out<\\#>: ', '-nosep']
163 ## ipshell = IPShellEmbed(args,
164 ## banner = 'Entering IPython. Press Ctrl-D to exit.',
165 ## exit_msg = 'Leaving Interpreter, back to program.')
169 # ElementTree does not support getparent method (lxml does).
170 # This wrapper class and the following support functions provide
171 # that support for the ability to get the parent of an element.
173 if WhichElementTree
== 'elementtree':
174 class _ElementInterfaceWrapper(etree
._ElementInterface
):
175 def __init__(self
, tag
, attrib
=None):
176 etree
._ElementInterface
.__init
__(self
, tag
, attrib
)
180 def setparent(self
, parent
):
187 # Constants and globals
189 # Turn tracing on/off. See methods trace_visit_node/trace_depart_node.
191 SPACES_PATTERN
= re
.compile(r
'( +)')
192 TABS_PATTERN
= re
.compile(r
'(\t+)')
193 FILL_PAT1
= re
.compile(r
'^ +')
194 FILL_PAT2
= re
.compile(r
' {2,}')
195 # Match a section number followed by 3 \xa0 bytes.
196 # Note that we actually check for the class "auto-toc" instead.
197 #SECTNUM_PAT = re.compile(r'^\d*(\.\d*)*\240\240\240')
199 TableStylePrefix
= 'Table'
201 GENERATOR_DESC
= 'Docutils.org/odtwriter'
203 NAME_SPACE_1
= 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
205 CONTENT_NAMESPACE_DICT
= CNSD
= {
206 # 'office:version': '1.0',
207 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
208 'dc': 'http://purl.org/dc/elements/1.1/',
209 'dom': 'http://www.w3.org/2001/xml-events',
210 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
211 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
212 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
213 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
214 'math': 'http://www.w3.org/1998/Math/MathML',
215 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
216 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
217 'office': NAME_SPACE_1
,
218 'ooo': 'http://openoffice.org/2004/office',
219 'oooc': 'http://openoffice.org/2004/calc',
220 'ooow': 'http://openoffice.org/2004/writer',
221 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
222 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
223 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
224 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
225 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
226 'xforms': 'http://www.w3.org/2002/xforms',
227 'xlink': 'http://www.w3.org/1999/xlink',
228 'xsd': 'http://www.w3.org/2001/XMLSchema',
229 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
232 STYLES_NAMESPACE_DICT
= SNSD
= {
233 # 'office:version': '1.0',
234 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
235 'dc': 'http://purl.org/dc/elements/1.1/',
236 'dom': 'http://www.w3.org/2001/xml-events',
237 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
238 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
239 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
240 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
241 'math': 'http://www.w3.org/1998/Math/MathML',
242 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
243 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
244 'office': NAME_SPACE_1
,
245 'ooo': 'http://openoffice.org/2004/office',
246 'oooc': 'http://openoffice.org/2004/calc',
247 'ooow': 'http://openoffice.org/2004/writer',
248 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
249 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
250 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
251 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
252 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
253 'xlink': 'http://www.w3.org/1999/xlink',
256 MANIFEST_NAMESPACE_DICT
= MANNSD
= {
257 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
260 META_NAMESPACE_DICT
= METNSD
= {
261 # 'office:version': '1.0',
262 'dc': 'http://purl.org/dc/elements/1.1/',
263 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
264 'office': NAME_SPACE_1
,
265 'ooo': 'http://openoffice.org/2004/office',
266 'xlink': 'http://www.w3.org/1999/xlink',
269 MIME_TYPE
= 'application/vnd.oasis.opendocument.text'
273 # Attribute dictionaries for use with ElementTree (not lxml), which
274 # does not support use of nsmap parameter on Element() and SubElement().
276 CONTENT_NAMESPACE_ATTRIB
= {
277 'office:version': '1.0',
278 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
279 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
280 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
281 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
282 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
283 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
284 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
285 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
286 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
287 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
288 'xmlns:office': NAME_SPACE_1
,
289 'xmlns:ooo': 'http://openoffice.org/2004/office',
290 'xmlns:oooc': 'http://openoffice.org/2004/calc',
291 'xmlns:ooow': 'http://openoffice.org/2004/writer',
292 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
293 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
294 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
295 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
296 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
297 'xmlns:xforms': 'http://www.w3.org/2002/xforms',
298 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
299 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
300 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
303 STYLES_NAMESPACE_ATTRIB
= {
304 'office:version': '1.0',
305 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0',
306 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
307 'xmlns:dom': 'http://www.w3.org/2001/xml-events',
308 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0',
309 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
310 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
311 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0',
312 'xmlns:math': 'http://www.w3.org/1998/Math/MathML',
313 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
314 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
315 'xmlns:office': NAME_SPACE_1
,
316 'xmlns:ooo': 'http://openoffice.org/2004/office',
317 'xmlns:oooc': 'http://openoffice.org/2004/calc',
318 'xmlns:ooow': 'http://openoffice.org/2004/writer',
319 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0',
320 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
321 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0',
322 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
323 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
324 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
327 MANIFEST_NAMESPACE_ATTRIB
= {
328 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
331 META_NAMESPACE_ATTRIB
= {
332 'office:version': '1.0',
333 'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
334 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0',
335 'xmlns:office': NAME_SPACE_1
,
336 'xmlns:ooo': 'http://openoffice.org/2004/office',
337 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
346 # ElementTree support functions.
347 # In order to be able to get the parent of elements, must use these
348 # instead of the functions with same name provided by ElementTree.
350 def Element(tag
, attrib
=None, nsmap
=None, nsdict
=CNSD
):
353 tag
, attrib
= fix_ns(tag
, attrib
, nsdict
)
354 if WhichElementTree
== 'lxml':
355 el
= etree
.Element(tag
, attrib
, nsmap
=nsmap
)
357 el
= _ElementInterfaceWrapper(tag
, attrib
)
360 def SubElement(parent
, tag
, attrib
=None, nsmap
=None, nsdict
=CNSD
):
363 tag
, attrib
= fix_ns(tag
, attrib
, nsdict
)
364 if WhichElementTree
== 'lxml':
365 el
= etree
.SubElement(parent
, tag
, attrib
, nsmap
=nsmap
)
367 el
= _ElementInterfaceWrapper(tag
, attrib
)
372 def fix_ns(tag
, attrib
, nsdict
):
373 nstag
= add_ns(tag
, nsdict
)
375 for key
, val
in attrib
.iteritems():
376 nskey
= add_ns(key
, nsdict
)
377 nsattrib
[nskey
] = val
378 return nstag
, nsattrib
380 def add_ns(tag
, nsdict
=CNSD
):
381 if WhichElementTree
== 'lxml':
382 nstag
, name
= tag
.split(':')
383 ns
= nsdict
.get(nstag
)
385 raise RuntimeError, 'Invalid namespace prefix: %s' % nstag
386 tag
= '{%s}%s' % (ns
, name
,)
387 #print '*** tag: "%s"' % tag
391 outstream
= StringIO
.StringIO()
393 s1
= outstream
.getvalue()
397 def escape_cdata(text
):
398 text
= text
.replace("&", "&")
399 text
= text
.replace("<", "<")
400 text
= text
.replace(">", ">")
403 if ord(char
) >= ord("\x7f"):
404 ascii
+= "&#x%X;" % ( ord(char
), )
414 # Does this version of Docutils has Directive support?
415 if hasattr(rst
, 'Directive'):
417 # Class to control syntax highlighting.
418 ## class SyntaxHighlight(rst.Directive):
419 ## required_arguments = 1
420 ## optional_arguments = 0
422 ## # See visit_field for code that processes the node created here.
424 ## arguments = ' '.join(self.arguments)
425 ## paragraph = nodes.paragraph(arguments, arguments)
426 ## field_body = nodes.field_body()
427 ## field_body += paragraph
428 ## paragraph = nodes.paragraph('syntaxhighlight', 'syntaxhighlight')
429 ## field_name = nodes.field_name()
430 ## field_name += paragraph
431 ## field = nodes.field()
432 ## field += field_name
433 ## field += field_body
436 ## rst.directives.register_directive('sourcecode', SyntaxHighlight)
438 class SyntaxHighlightCodeBlock(rst
.Directive
):
439 required_arguments
= 1
440 optional_arguments
= 0
443 # See visit_literal_block for code that processes the node
446 language
= self
.arguments
[0]
447 code_block
= nodes
.literal_block(classes
=["code-block", language
],
450 content
= '\n'.join(lines
)
451 text_node
= nodes
.Text(content
)
452 code_block
.append(text_node
)
453 # Mark this node for high-lighting so that visit_literal_block
454 # will be able to hight-light those produced here and
455 # *not* high-light regular literal blocks (:: in reST).
456 code_block
['hilight'] = True
457 #import pdb; pdb.set_trace()
460 rst
.directives
.register_directive('sourcecode', SyntaxHighlightCodeBlock
)
461 rst
.directives
.register_directive('code', SyntaxHighlightCodeBlock
)
465 # Register directives defined in a module named "odtwriter_plugins".
471 name
= 'odtwriter_plugins'
472 fp
, pathname
, description
= imp
.find_module(name
)
473 plugin_mod
= imp
.load_module(name
, fp
, pathname
, description
)
474 #import odtwriter_plugins
475 #plugin_mod = odtwriter_plugins
476 except ImportError, e
:
478 if plugin_mod
is None:
480 klasses
= inspect
.getmembers(plugin_mod
, inspect
.isclass
)
481 for klass
in klasses
:
482 if register_plugin(*klass
):
486 def register_plugin(name
, klass
):
487 plugin_name
= getattr(klass
, 'plugin_name', None)
488 if plugin_name
is not None:
489 rst
.directives
.register_directive(plugin_name
, klass
)
494 WORD_SPLIT_PAT1
= re
.compile(r
'\b(\w*)\b\W*')
496 def split_words(line
):
497 # We need whitespace at the end of the string for our regexpr.
501 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
502 while mo
is not None:
503 word
= mo
.groups()[0]
506 mo
= WORD_SPLIT_PAT1
.search(line
, pos1
)
511 # Information about the indentation level for lists nested inside
512 # other contexts, e.g. dictionary lists.
513 class ListLevel(object):
514 def __init__(self
, level
, sibling_level
=True, nested_level
=True):
516 self
.sibling_level
= sibling_level
517 self
.nested_level
= nested_level
518 def set_sibling(self
, sibling_level
): self
.sibling_level
= sibling_level
519 def get_sibling(self
): return self
.sibling_level
520 def set_nested(self
, nested_level
): self
.nested_level
= nested_level
521 def get_nested(self
): return self
.nested_level
522 def set_level(self
, level
): self
.level
= level
523 def get_level(self
): return self
.level
526 class Writer(writers
.Writer
):
530 supported
= ('html', 'html4css1', 'xhtml')
531 """Formats this writer supports."""
533 default_stylesheet
= 'styles' + EXTENSION
534 ## default_plugins_name = 'docutils_plugins'
536 default_stylesheet_path
= utils
.relative_path(
537 os
.path
.join(os
.getcwd(), 'dummy'),
538 os
.path
.join(os
.path
.dirname(__file__
), default_stylesheet
))
540 default_template
= 'template.txt'
542 default_template_path
= utils
.relative_path(
543 os
.path
.join(os
.getcwd(), 'dummy'),
544 os
.path
.join(os
.path
.dirname(__file__
), default_template
))
547 ## 'ODF-Specific Options',
549 ## (('Specify the template file (UTF-8 encoded). Default is "%s".'
550 ## % default_template_path,
552 ## {'default': default_template_path, 'metavar': '<file>'}),
553 ## ('Specify a stylesheet URL, used verbatim. Overrides '
554 ## '--stylesheet-path.',
556 ## {'metavar': '<URL>', 'overrides': 'stylesheet_path'}),
557 ## ('Specify a stylesheet file, relative to the current working '
558 ## 'directory. The path is adjusted relative to the output ODF '
559 ## 'file. Overrides --stylesheet. Default: "%s"'
560 ## % default_stylesheet_path,
561 ## ['--stylesheet-path'],
562 ## {'metavar': '<file>', 'overrides': 'stylesheet',
563 ## 'default': default_stylesheet_path}),
564 ## ('Specify the initial header level. Default is 1 for "<h1>". '
565 ## 'Does not affect document title & subtitle (see --no-doc-title).',
566 ## ['--initial-header-level'],
567 ## {'choices': '1 2 3 4 5 6'.split(), 'default': '1',
568 ## 'metavar': '<level>'}),
569 ## ('Specify the maximum width (in characters) for one-column field '
570 ## 'names. Longer field names will span an entire row of the table '
571 ## 'used to render the field list. Default is 14 characters. '
572 ## 'Use 0 for "no limit".',
573 ## ['--field-name-limit'],
574 ## {'default': 14, 'metavar': '<level>',
575 ## 'validator': frontend.validate_nonnegative_int}),
576 ## ('Specify the maximum width (in characters) for options in option '
577 ## 'lists. Longer options will span an entire row of the table used '
578 ## 'to render the option list. Default is 14 characters. '
579 ## 'Use 0 for "no limit".',
580 ## ['--option-limit'],
581 ## {'default': 14, 'metavar': '<level>',
582 ## 'validator': frontend.validate_nonnegative_int}),
583 ## ('Format for footnote references: one of "superscript" or '
584 ## '"brackets". Default is "brackets".',
585 ## ['--footnote-references'],
586 ## {'choices': ['superscript', 'brackets'], 'default': 'brackets',
587 ## 'metavar': '<format>',
588 ## 'overrides': 'trim_footnote_reference_space'}),
589 ## ('Format for block quote attributions: one of "dash" (em-dash '
590 ## 'prefix), "parentheses"/"parens", or "none". Default is "dash".',
591 ## ['--attribution'],
592 ## {'choices': ['dash', 'parentheses', 'parens', 'none'],
593 ## 'default': 'dash', 'metavar': '<format>'}),
594 ## ('Remove extra vertical whitespace between items of "simple" bullet '
595 ## 'lists and enumerated lists. Default: enabled.',
596 ## ['--compact-lists'],
597 ## {'default': 1, 'action': 'store_true',
598 ## 'validator': frontend.validate_boolean}),
599 ## ('Disable compact simple bullet and enumerated lists.',
600 ## ['--no-compact-lists'],
601 ## {'dest': 'compact_lists', 'action': 'store_false'}),
602 ## ('Remove extra vertical whitespace between items of simple field '
603 ## 'lists. Default: enabled.',
604 ## ['--compact-field-lists'],
605 ## {'default': 1, 'action': 'store_true',
606 ## 'validator': frontend.validate_boolean}),
607 ## ('Disable compact simple field lists.',
608 ## ['--no-compact-field-lists'],
609 ## {'dest': 'compact_field_lists', 'action': 'store_false'}),
610 ## ('Omit the XML declaration. Use with caution.',
611 ## ['--no-xml-declaration'],
612 ## {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
613 ## 'validator': frontend.validate_boolean}),
614 ## ('Obfuscate email addresses to confuse harvesters while still '
615 ## 'keeping email links usable with standards-compliant browsers.',
616 ## ['--cloak-email-addresses'],
617 ## {'action': 'store_true', 'validator': frontend.validate_boolean}),
621 'ODF-Specific Options',
624 ('Specify a stylesheet URL, used verbatim. Overrides '
625 '--stylesheet-path.',
627 {'metavar': '<URL>', 'overrides': 'stylesheet_path'}),
628 ('Specify a stylesheet file, relative to the current working '
629 'directory. The path is adjusted relative to the output ODF '
630 'file. Overrides --stylesheet. Default: "%s"'
631 % default_stylesheet_path
,
632 ['--stylesheet-path'],
633 {'metavar': '<file>', 'overrides': 'stylesheet',
634 'default': default_stylesheet_path
}),
635 ('Specify a configuration/mapping file relative to the '
637 'directory for additional ODF options. '
638 'In particular, this file may contain a section named '
639 '"Formats" that maps default style names to '
640 'names to be used in the resulting output file allowing for '
641 'adhering to external standards. '
642 'For more info and the format of the configuration/mapping file, '
643 'see the odtwriter doc.',
644 ['--odf-config-file'],
645 {'metavar': '<file>'}),
646 ('Obfuscate email addresses to confuse harvesters while still '
647 'keeping email links usable with standards-compliant browsers.',
648 ['--cloak-email-addresses'],
649 {'default': False, 'action': 'store_true',
650 'validator': frontend
.validate_boolean
}),
651 ('Specify the thickness of table borders in thousands of a cm. '
653 ['--table-border-thickness'],
655 'validator': frontend
.validate_nonnegative_int
}),
656 ('Add syntax highlighting in literal code blocks.'
657 'Default is No. Requires installation of Pygments.',
658 ['--add-syntax-highlighting'],
659 {'default': False, 'action': 'store_true',
660 'validator': frontend
.validate_boolean
}),
661 ('Create sections for headers. '
663 ['--create-sections'],
664 {'default': True, 'action': 'store_true',
665 'validator': frontend
.validate_boolean
}),
666 ('Create no sections for headers.',
667 ['--no-create-sections'],
668 {'action': 'store_false',
669 'dest': 'create_sections',
670 'validator': frontend
.validate_boolean
}),
674 {'default': False, 'action': 'store_true',
675 'validator': frontend
.validate_boolean
}),
677 ['--no-create-links'],
678 {'action': 'store_false',
679 'dest': 'create_links',
680 'validator': frontend
.validate_boolean
}),
681 ## ('Specify a plugins/directives module (without .py). '
682 ## 'Default: "%s"' % default_plugins_name,
683 ## ['--plugins-module-name'],
684 ## {'default': default_plugins_name}),
687 settings_defaults
= {
688 'output_encoding_error_handler': 'xmlcharrefreplace',
691 relative_path_settings
= (
695 config_section
= 'opendocument odf writer'
696 config_section_dependencies
= (
701 writers
.Writer
.__init
__(self
)
702 self
.translator_class
= ODFTranslator
705 #import pdb; pdb.set_trace()
706 self
.settings
= self
.document
.settings
707 self
.visitor
= self
.translator_class(self
.document
)
708 self
.document
.walkabout(self
.visitor
)
709 self
.visitor
.add_doc_title()
710 self
.assemble_my_parts()
711 self
.output
= self
.parts
['whole']
713 def assemble_my_parts(self
):
714 """Assemble the `self.parts` dictionary. Extend in subclasses.
716 #ipshell('At assemble_parts')
717 writers
.Writer
.assemble_parts(self
)
718 f
= tempfile
.NamedTemporaryFile()
719 zfile
= zipfile
.ZipFile(f
, 'w', zipfile
.ZIP_DEFLATED
)
720 self
.write_zip_str(zfile
, 'content.xml', self
.visitor
.content_astext())
721 self
.write_zip_str(zfile
, 'mimetype', MIME_TYPE
)
722 s1
= self
.create_manifest()
723 self
.write_zip_str(zfile
, 'META-INF/manifest.xml', s1
)
724 s1
= self
.create_meta()
725 self
.write_zip_str(zfile
, 'meta.xml', s1
)
726 s1
= self
.get_stylesheet()
727 self
.write_zip_str(zfile
, 'styles.xml', s1
)
728 self
.store_embedded_files(zfile
)
733 self
.parts
['whole'] = whole
734 self
.parts
['encoding'] = self
.document
.settings
.output_encoding
735 self
.parts
['version'] = docutils
.__version
__
737 def write_zip_str(self
, zfile
, name
, bytes
):
738 localtime
= time
.localtime(time
.time())
739 zinfo
= zipfile
.ZipInfo(name
, localtime
)
740 # Add some standard UNIX file access permissions (-rw-r--r--).
741 zinfo
.external_attr
= (0x81a4 & 0xFFFF) << 16L
742 zinfo
.compress_type
= zipfile
.ZIP_DEFLATED
743 zfile
.writestr(zinfo
, bytes
)
745 def store_embedded_files(self
, zfile
):
746 embedded_files
= self
.visitor
.get_embedded_file_list()
747 for source
, destination
in embedded_files
:
752 destination1
= destination
.decode('latin-1').encode('utf-8')
753 zfile
.write(source
, destination1
, zipfile
.ZIP_STORED
)
755 print "Error: Can't open file %s." % (source
, )
757 def get_stylesheet(self
):
758 """Retrieve the stylesheet from either a .xml file or from
759 a .odt (zip) file. Return the content as a string.
761 stylespath
= utils
.get_stylesheet_reference(self
.settings
,
762 os
.path
.join(os
.getcwd(), 'dummy'))
763 ext
= os
.path
.splitext(stylespath
)[1]
765 stylesfile
= open(stylespath
, 'r')
766 s1
= stylesfile
.read()
768 elif ext
== self
.EXTENSION
:
769 zfile
= zipfile
.ZipFile(stylespath
, 'r')
770 s1
= zfile
.read('styles.xml')
773 raise RuntimeError, 'stylesheet path must be ' + self
.EXTENSION
+ ' or .xml file.'
774 s1
= self
.visitor
.setup_page(s1
)
777 def assemble_parts(self
):
780 def create_manifest(self
):
781 if WhichElementTree
== 'lxml':
782 root
= Element('manifest:manifest',
783 nsmap
=MANIFEST_NAMESPACE_DICT
,
784 nsdict
=MANIFEST_NAMESPACE_DICT
,
787 root
= Element('manifest:manifest',
788 attrib
=MANIFEST_NAMESPACE_ATTRIB
,
789 nsdict
=MANIFEST_NAMESPACE_DICT
,
791 doc
= etree
.ElementTree(root
)
792 SubElement(root
, 'manifest:file-entry', attrib
={
793 'manifest:media-type': MIME_TYPE
,
794 'manifest:full-path': '/',
796 SubElement(root
, 'manifest:file-entry', attrib
={
797 'manifest:media-type': 'text/xml',
798 'manifest:full-path': 'content.xml',
800 SubElement(root
, 'manifest:file-entry', attrib
={
801 'manifest:media-type': 'text/xml',
802 'manifest:full-path': 'styles.xml',
804 SubElement(root
, 'manifest:file-entry', attrib
={
805 'manifest:media-type': 'text/xml',
806 'manifest:full-path': 'meta.xml',
809 doc
= minidom
.parseString(s1
)
810 s1
= doc
.toprettyxml(' ')
813 def create_meta(self
):
814 if WhichElementTree
== 'lxml':
815 root
= Element('office:document-meta',
816 nsmap
=META_NAMESPACE_DICT
,
817 nsdict
=META_NAMESPACE_DICT
,
820 root
= Element('office:document-meta',
821 attrib
=META_NAMESPACE_ATTRIB
,
822 nsdict
=META_NAMESPACE_DICT
,
824 doc
= etree
.ElementTree(root
)
825 root
= SubElement(root
, 'office:meta', nsdict
=METNSD
)
826 el1
= SubElement(root
, 'meta:generator', nsdict
=METNSD
)
827 el1
.text
= 'Docutils/rst2odf.py/%s' % (VERSION
, )
828 s1
= os
.environ
.get('USER', '')
829 el1
= SubElement(root
, 'meta:initial-creator', nsdict
=METNSD
)
831 s2
= time
.strftime('%Y-%m-%dT%H:%M:%S', time
.localtime())
832 el1
= SubElement(root
, 'meta:creation-date', nsdict
=METNSD
)
834 el1
= SubElement(root
, 'dc:creator', nsdict
=METNSD
)
836 el1
= SubElement(root
, 'dc:date', nsdict
=METNSD
)
838 el1
= SubElement(root
, 'dc:language', nsdict
=METNSD
)
840 el1
= SubElement(root
, 'meta:editing-cycles', nsdict
=METNSD
)
842 el1
= SubElement(root
, 'meta:editing-duration', nsdict
=METNSD
)
843 el1
.text
= 'PT00M01S'
844 title
= self
.visitor
.get_title()
845 el1
= SubElement(root
, 'dc:title', nsdict
=METNSD
)
849 el1
.text
= '[no title]'
850 meta_dict
= self
.visitor
.get_meta_dict()
851 keywordstr
= meta_dict
.get('keywords')
852 if keywordstr
is not None:
853 keywords
= split_words(keywordstr
)
854 for keyword
in keywords
:
855 el1
= SubElement(root
, 'meta:keyword', nsdict
=METNSD
)
857 description
= meta_dict
.get('description')
858 if description
is not None:
859 el1
= SubElement(root
, 'dc:description', nsdict
=METNSD
)
860 el1
.text
= description
862 #doc = minidom.parseString(s1)
863 #s1 = doc.toprettyxml(' ')
866 # class ODFTranslator(nodes.SparseNodeVisitor):
868 class ODFTranslator(nodes
.GenericNodeVisitor
):
871 ## 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
872 ## 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
873 ## 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock',
874 ## 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
875 ## 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
876 ## 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
877 ## 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
878 ## 'epigraph-enumitem', 'epigraph-enumlist', 'footer', 'footnote',
879 ## 'header', 'highlights', 'highlights-bulletitem',
880 ## 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
881 ## 'horizontalline', 'inlineliteral', 'lineblock', 'quotation', 'rubric',
882 ## 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
883 ## 'heading%d', 'admon-%s-hdr', 'admon-%s-body', 'tableoption',
884 ## 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
889 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem',
890 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist',
891 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock',
892 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname',
893 'codeblock-keyword', 'codeblock-name', 'codeblock-number',
894 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem',
895 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist',
896 'epigraph-enumitem', 'epigraph-enumlist', 'footer',
897 'footnote', 'citation',
898 'header', 'highlights', 'highlights-bulletitem',
899 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist',
900 'horizontalline', 'inlineliteral', 'lineblock', 'quotation', 'rubric',
901 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist',
907 'admon-attention-hdr',
908 'admon-attention-body',
910 'admon-caution-body',
916 'admon-generic-body',
919 'admon-important-hdr',
920 'admon-important-body',
926 'admon-warning-body',
928 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
932 def __init__(self
, document
):
933 #nodes.SparseNodeVisitor.__init__(self, document)
934 nodes
.GenericNodeVisitor
.__init
__(self
, document
)
935 self
.settings
= document
.settings
936 self
.format_map
= { }
937 if self
.settings
.odf_config_file
:
938 from ConfigParser
import ConfigParser
940 parser
= ConfigParser()
941 parser
.read(self
.settings
.odf_config_file
)
942 for ( rststyle
, format
, ) in parser
.items("Formats"):
943 if rststyle
not in self
.used_styles
:
945 print ('*** Warning: Style "%s" '
946 'is not a style used by odtwriter.' % (
949 #raise RuntimeError, 'Unused style "%s"' % ( rststyle, )
950 self
.format_map
[rststyle
] = format
951 self
.section_level
= 0
952 self
.section_count
= 0
953 # Create ElementTree content and styles documents.
954 if WhichElementTree
== 'lxml':
956 'office:document-content',
957 nsmap
=CONTENT_NAMESPACE_DICT
,
961 'office:document-content',
962 attrib
=CONTENT_NAMESPACE_ATTRIB
,
964 self
.content_tree
= etree
.ElementTree(element
=root
)
965 self
.current_element
= root
966 SubElement(root
, 'office:scripts')
967 SubElement(root
, 'office:font-face-decls')
968 el
= SubElement(root
, 'office:automatic-styles')
969 self
.automatic_styles
= el
970 el
= SubElement(root
, 'office:body')
971 el
= self
.generate_content_element(el
)
972 self
.current_element
= el
973 self
.body_text_element
= el
975 ## if WhichElementTree == 'lxml':
977 ## 'office:document-styles',
978 ## nsmap=STYLES_NAMESPACE_DICT,
979 ## nsdict=STYLES_NAMESPACE_DICT,
982 ## root = Element('office:document-styles',
983 ## attrib=STYLES_NAMESPACE_ATTRIB,
984 ## nsdict=STYLES_NAMESPACE_DICT,
986 ## self.styles_tree = etree.ElementTree(element=root)
987 self
.paragraph_style_stack
= [self
.rststyle('textbody'), ]
988 self
.list_style_stack
= []
990 self
.column_count
= ord('A') - 1
991 self
.trace_level
= -1
992 self
.optiontablestyles_generated
= False
993 self
.field_name
= None
994 self
.field_element
= None
997 self
.image_style_count
= 0
999 self
.embedded_file_list
= []
1000 self
.syntaxhighlighting
= 1
1001 self
.syntaxhighlight_lexer
= 'python'
1002 self
.header_content
= []
1003 self
.footer_content
= []
1004 self
.in_header
= False
1005 self
.in_footer
= False
1006 self
.blockstyle
= ''
1007 self
.in_table_of_contents
= False
1008 self
.footnote_dict
= {}
1009 self
.footnote_found
= False
1010 self
.pending_ids
= [ ]
1011 self
.in_paragraph
= False
1012 self
.found_doc_title
= False
1013 self
.bumped_list_level_stack
= []
1015 self
.in_footnote
= False
1016 self
.in_citation
= None
1018 def add_doc_title(self
):
1019 text
= self
.settings
.title
1022 if not self
.found_doc_title
:
1023 el
= Element('text:h', attrib
= {
1024 'text:outline-level': '1',
1025 'text:style-name': 'rststyle-heading1',
1028 self
.body_text_element
.insert(0, el
)
1030 def rststyle(self
, name
, parameters
=( )):
1032 Returns the style name to use for the given style.
1034 If `parameters` is given `name` must contain a matching number of ``%`` and
1035 is used as a format expression with `parameters` as the value.
1037 ## template = self.format_map.get(name, 'rststyle-%s' % name)
1038 ## return template % parameters
1039 name1
= name
% parameters
1040 stylename
= self
.format_map
.get(name1
, 'rststyle-%s' % name1
)
1043 def generate_content_element(self
, root
):
1044 return SubElement(root
, 'office:text')
1046 def setup_page(self
, content
):
1047 root_el
= etree
.fromstring(content
)
1048 self
.setup_paper(root_el
)
1049 if len(self
.header_content
) > 0 or len(self
.footer_content
) > 0:
1050 self
.add_header_footer(root_el
)
1051 new_content
= etree
.tostring(root_el
)
1054 def setup_paper(self
, root_el
):
1056 fin
= os
.popen("paperconf -s")
1057 w
, h
= map(float, fin
.read().split())
1060 w
, h
= 612, 792 # default to Letter
1062 if el
.tag
== "{%s}page-layout-properties" % SNSD
["style"] and \
1063 not el
.attrib
.has_key("{%s}page-width" % SNSD
["fo"]):
1064 el
.attrib
["{%s}page-width" % SNSD
["fo"]] = "%.3fpt" % w
1065 el
.attrib
["{%s}page-height" % SNSD
["fo"]] = "%.3fpt" % h
1066 el
.attrib
["{%s}margin-left" % SNSD
["fo"]] = \
1067 el
.attrib
["{%s}margin-right" % SNSD
["fo"]] = \
1069 el
.attrib
["{%s}margin-top" % SNSD
["fo"]] = \
1070 el
.attrib
["{%s}margin-bottom" % SNSD
["fo"]] = \
1073 for subel
in el
.getchildren(): walk(subel
)
1076 def add_header_footer(self
, root_el
):
1077 path
= '{%s}master-styles' % (NAME_SPACE_1
, )
1078 master_el
= root_el
.find(path
)
1079 if master_el
is None:
1081 path
= '{%s}master-page' % (SNSD
['style'], )
1082 master_el
= master_el
.find(path
)
1083 if master_el
is None:
1086 if len(self
.header_content
) > 0:
1087 if WhichElementTree
== 'lxml':
1088 el2
= SubElement(el1
, 'style:header', nsdict
=SNSD
)
1090 el2
= SubElement(el1
, 'style:header',
1091 attrib
=STYLES_NAMESPACE_ATTRIB
,
1092 nsdict
=STYLES_NAMESPACE_DICT
,
1094 for el
in self
.header_content
:
1095 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1096 el
.attrib
[attrkey
] = self
.rststyle('header')
1098 if len(self
.footer_content
) > 0:
1099 if WhichElementTree
== 'lxml':
1100 el2
= SubElement(el1
, 'style:footer', nsdict
=SNSD
)
1102 el2
= SubElement(el1
, 'style:footer',
1103 attrib
=STYLES_NAMESPACE_ATTRIB
,
1104 nsdict
=STYLES_NAMESPACE_DICT
,
1106 for el
in self
.footer_content
:
1107 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1108 el
.attrib
[attrkey
] = self
.rststyle('footer')
1110 #new_tree = etree.ElementTree(root_el)
1111 #new_content = ToString(new_tree)
1114 root
= self
.content_tree
.getroot()
1115 et
= etree
.ElementTree(root
)
1119 def content_astext(self
):
1120 return self
.astext()
1123 ## def styles_astext(self):
1124 ## root = self.styles_tree.getroot()
1125 ## et = etree.ElementTree(root)
1126 ## s1 = ToString(et)
1129 def set_title(self
, title
): self
.title
= title
1130 def get_title(self
): return self
.title
1131 def set_embedded_file_list(self
, embedded_file_list
):
1132 self
.embedded_file_list
= embedded_file_list
1133 def get_embedded_file_list(self
): return self
.embedded_file_list
1134 def get_meta_dict(self
): return self
.meta_dict
1139 def append_child(self
, tag
, attrib
=None, parent
=None):
1141 parent
= self
.current_element
1143 el
= SubElement(parent
, tag
)
1145 el
= SubElement(parent
, tag
, attrib
)
1148 def append_p(self
, style
, text
=None):
1149 result
= self
.append_child('text:p', attrib
={
1150 'text:style-name': self
.rststyle(style
)})
1151 self
.append_pending_ids(result
)
1152 if text
is not None:
1156 def append_pending_ids(self
, el
):
1157 if self
.settings
.create_links
:
1158 for id in self
.pending_ids
:
1159 SubElement(el
, 'text:reference-mark', attrib
={
1161 self
.pending_ids
= [ ]
1163 def set_current_element(self
, el
):
1164 self
.current_element
= el
1166 def set_to_parent(self
):
1167 self
.current_element
= self
.current_element
.getparent()
1169 def generate_labeled_block(self
, node
, label
):
1170 el
= self
.append_p('textbody')
1171 el1
= SubElement(el
, 'text:span',
1172 attrib
={'text:style-name': self
.rststyle('strong')})
1174 el
= self
.append_p('blockindent')
1177 def generate_labeled_line(self
, node
, label
):
1178 el
= self
.append_p('textbody')
1179 el1
= SubElement(el
, 'text:span',
1180 attrib
={'text:style-name': self
.rststyle('strong')})
1182 el1
.tail
= node
.astext()
1185 def encode(self
, text
):
1186 text
= text
.replace(u
'\u00a0', " ")
1189 def trace_visit_node(self
, node
):
1191 self
.trace_level
+= 1
1192 self
._trace
_show
_level
(self
.trace_level
)
1194 print '(visit_%s) node: %s' % (node
.tagname
, node
.astext(), )
1196 print '(visit_%s)' % node
.tagname
1198 def trace_depart_node(self
, node
):
1201 self
._trace
_show
_level
(self
.trace_level
)
1202 print '(depart_%s)' % node
.tagname
1203 self
.trace_level
-= 1
1205 def _trace_show_level(self
, level
):
1206 for idx
in range(level
):
1212 # In alphabetic order, more or less.
1213 # See docutils.docutils.nodes.node_class_names.
1216 def dispatch_visit(self
, node
):
1217 """Override to catch basic attributes which many nodes have."""
1218 self
.handle_basic_atts(node
)
1219 nodes
.GenericNodeVisitor
.dispatch_visit(self
, node
)
1221 def handle_basic_atts(self
, node
):
1222 if isinstance(node
, nodes
.Element
) and node
['ids']:
1223 self
.pending_ids
+= node
['ids']
1225 def default_visit(self
, node
):
1226 #ipshell('At default_visit')
1227 print 'missing visit_%s' % (node
.tagname
, )
1229 def default_departure(self
, node
):
1230 print 'missing depart_%s' % (node
.tagname
, )
1232 def visit_Text(self
, node
):
1233 #ipshell('At visit_Text')
1234 # Skip nodes whose text has been processed in parent nodes.
1235 if isinstance(node
.parent
, docutils
.nodes
.literal_block
):
1236 #isinstance(node.parent, docutils.nodes.term) or \
1237 #isinstance(node.parent, docutils.nodes.definition):
1239 text
= node
.astext()
1240 # Are we in mixed content? If so, add the text to the
1241 # etree tail of the previous sibling element.
1242 if len(self
.current_element
.getchildren()) > 0:
1243 #print '*** (visit_Text) 1. text: %s' % text
1244 if self
.current_element
.getchildren()[-1].tail
:
1245 self
.current_element
.getchildren()[-1].tail
+= text
1247 self
.current_element
.getchildren()[-1].tail
= text
1249 if self
.current_element
.text
:
1250 self
.current_element
.text
+= text
1252 self
.current_element
.text
= text
1254 def depart_Text(self
, node
):
1258 # Pre-defined fields
1261 def visit_address(self
, node
):
1262 #ipshell('At visit_address')
1263 el
= self
.generate_labeled_block(node
, 'Address: ')
1264 self
.set_current_element(el
)
1266 def depart_address(self
, node
):
1267 self
.set_to_parent()
1269 def visit_author(self
, node
):
1270 if isinstance(node
.parent
, nodes
.authors
):
1271 el
= self
.append_p('blockindent')
1273 el
= self
.generate_labeled_block(node
, 'Author: ')
1274 self
.set_current_element(el
)
1276 def depart_author(self
, node
):
1277 self
.set_to_parent()
1279 def visit_authors(self
, node
):
1280 #ipshell('At visit_authors')
1281 #self.trace_visit_node(node)
1283 el
= self
.append_p('textbody')
1284 el1
= SubElement(el
, 'text:span',
1285 attrib
={'text:style-name': self
.rststyle('strong')})
1288 def depart_authors(self
, node
):
1289 #self.trace_depart_node(node)
1292 def visit_contact(self
, node
):
1293 el
= self
.generate_labeled_block(node
, 'Contact: ')
1294 self
.set_current_element(el
)
1296 def depart_contact(self
, node
):
1297 self
.set_to_parent()
1299 def visit_copyright(self
, node
):
1300 el
= self
.generate_labeled_block(node
, 'Copyright: ')
1301 self
.set_current_element(el
)
1303 def depart_copyright(self
, node
):
1304 self
.set_to_parent()
1306 def visit_date(self
, node
):
1307 self
.generate_labeled_line(node
, 'Date: ')
1309 def depart_date(self
, node
):
1312 def visit_organization(self
, node
):
1313 el
= self
.generate_labeled_block(node
, 'Organization: ')
1314 self
.set_current_element(el
)
1316 def depart_organization(self
, node
):
1317 self
.set_to_parent()
1319 def visit_status(self
, node
):
1320 el
= self
.generate_labeled_block(node
, 'Status: ')
1321 self
.set_current_element(el
)
1323 def depart_status(self
, node
):
1324 self
.set_to_parent()
1326 def visit_revision(self
, node
):
1327 self
.generate_labeled_line(node
, 'Revision: ')
1329 def depart_revision(self
, node
):
1332 def visit_version(self
, node
):
1333 el
= self
.generate_labeled_line(node
, 'Version: ')
1334 #self.set_current_element(el)
1336 def depart_version(self
, node
):
1337 #self.set_to_parent()
1340 def visit_attribution(self
, node
):
1341 #ipshell('At visit_attribution')
1342 el
= self
.append_p('attribution', node
.astext())
1344 def depart_attribution(self
, node
):
1345 #ipshell('At depart_attribution')
1348 def visit_block_quote(self
, node
):
1349 #ipshell('At visit_block_quote')
1350 if 'epigraph' in node
.attributes
['classes']:
1351 self
.paragraph_style_stack
.append(self
.rststyle('epigraph'))
1352 self
.blockstyle
= self
.rststyle('epigraph')
1353 elif 'highlights' in node
.attributes
['classes']:
1354 self
.paragraph_style_stack
.append(self
.rststyle('highlights'))
1355 self
.blockstyle
= self
.rststyle('highlights')
1357 self
.paragraph_style_stack
.append(self
.rststyle('blockquote'))
1358 self
.blockstyle
= self
.rststyle('blockquote')
1360 def depart_block_quote(self
, node
):
1361 self
.paragraph_style_stack
.pop()
1362 self
.blockstyle
= ''
1364 def visit_bullet_list(self
, node
):
1365 #ipshell('At visit_bullet_list')
1366 if self
.in_table_of_contents
:
1367 if node
.has_key('classes') and \
1368 'auto-toc' in node
.attributes
['classes']:
1369 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1370 'text:style-name': self
.rststyle('tocenumlist'),
1372 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1374 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1375 'text:style-name': self
.rststyle('tocbulletlist'),
1377 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1379 if self
.blockstyle
== self
.rststyle('blockquote'):
1380 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1381 'text:style-name': self
.rststyle('blockquote-bulletlist'),
1383 self
.list_style_stack
.append(self
.rststyle('blockquote-bulletitem'))
1384 elif self
.blockstyle
== self
.rststyle('highlights'):
1385 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1386 'text:style-name': self
.rststyle('highlights-bulletlist'),
1388 self
.list_style_stack
.append(self
.rststyle('highlights-bulletitem'))
1389 elif self
.blockstyle
== self
.rststyle('epigraph'):
1390 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1391 'text:style-name': self
.rststyle('epigraph-bulletlist'),
1393 self
.list_style_stack
.append(self
.rststyle('epigraph-bulletitem'))
1395 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1396 'text:style-name': self
.rststyle('bulletlist'),
1398 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1399 self
.set_current_element(el
)
1401 def depart_bullet_list(self
, node
):
1402 self
.set_to_parent()
1403 self
.list_style_stack
.pop()
1405 def visit_caption(self
, node
):
1406 raise nodes
.SkipChildren()
1409 def depart_caption(self
, node
):
1412 def visit_comment(self
, node
):
1413 #ipshell('At visit_comment')
1414 el
= self
.append_p('textbody')
1415 el1
= SubElement(el
, 'office:annotation', attrib
={})
1416 el2
= SubElement(el1
, 'text:p', attrib
={})
1417 el2
.text
= node
.astext()
1419 def depart_comment(self
, node
):
1422 def visit_compound(self
, node
):
1423 # The compound directive currently receives no special treatment.
1426 def depart_compound(self
, node
):
1429 def visit_container(self
, node
):
1430 styles
= node
.attributes
.get('classes', ())
1432 self
.paragraph_style_stack
.append(styles
[0])
1434 def depart_container(self
, node
):
1435 #ipshell('At depart_container')
1436 styles
= node
.attributes
.get('classes', ())
1438 self
.paragraph_style_stack
.pop()
1440 def visit_decoration(self
, node
):
1442 #ipshell('At visit_decoration')
1444 #self.trace_visit_node(node)
1447 def depart_decoration(self
, node
):
1448 #ipshell('At depart_decoration')
1451 def visit_definition(self
, node
):
1452 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1453 self
.bumped_list_level_stack
.append(ListLevel(1))
1455 def depart_definition(self
, node
):
1456 self
.paragraph_style_stack
.pop()
1457 self
.bumped_list_level_stack
.pop()
1459 def visit_definition_list(self
, node
):
1462 def depart_definition_list(self
, node
):
1465 def visit_definition_list_item(self
, node
):
1468 def depart_definition_list_item(self
, node
):
1471 def visit_term(self
, node
):
1472 #ipshell('At visit_term')
1473 el
= self
.append_p('textbody')
1474 el1
= SubElement(el
, 'text:span',
1475 attrib
={'text:style-name': self
.rststyle('strong')})
1476 #el1.text = node.astext()
1477 self
.set_current_element(el1
)
1479 def depart_term(self
, node
):
1480 #ipshell('At depart_term')
1481 self
.set_to_parent()
1482 self
.set_to_parent()
1484 def visit_classifier(self
, node
):
1485 #ipshell('At visit_classifier')
1486 els
= self
.current_element
.getchildren()
1489 el1
= SubElement(el
, 'text:span',
1490 attrib
={'text:style-name': self
.rststyle('emphasis')
1492 el1
.text
= ' (%s)' % (node
.astext(), )
1494 def depart_classifier(self
, node
):
1497 def visit_document(self
, node
):
1498 #ipshell('At visit_document')
1499 #self.set_current_element(self.content_tree.getroot())
1502 def depart_document(self
, node
):
1505 def visit_docinfo(self
, node
):
1506 #self.document.reporter.debug_flag = 1
1507 self
.trace_visit_node(node
)
1508 self
.section_level
+= 1
1509 self
.section_count
+= 1
1510 if self
.settings
.create_sections
:
1511 el
= self
.append_child('text:section', attrib
={
1512 'text:name': 'Section%d' % self
.section_count
,
1513 'text:style-name': 'Sect%d' % self
.section_level
,
1515 self
.set_current_element(el
)
1517 def depart_docinfo(self
, node
):
1518 #self.document.reporter.debug_flag = 0
1519 self
.trace_depart_node(node
)
1520 self
.section_level
-= 1
1521 if self
.settings
.create_sections
:
1522 self
.set_to_parent()
1524 def visit_emphasis(self
, node
):
1525 el
= SubElement(self
.current_element
, 'text:span',
1526 attrib
={'text:style-name': self
.rststyle('emphasis')})
1527 self
.set_current_element(el
)
1529 def depart_emphasis(self
, node
):
1530 self
.set_to_parent()
1532 def visit_enumerated_list(self
, node
):
1533 el1
= self
.current_element
1534 if self
.blockstyle
== self
.rststyle('blockquote'):
1535 el2
= SubElement(el1
, 'text:list', attrib
={
1536 'text:style-name': self
.rststyle('blockquote-enumlist'),
1538 self
.list_style_stack
.append(self
.rststyle('blockquote-enumitem'))
1539 elif self
.blockstyle
== self
.rststyle('highlights'):
1540 el2
= SubElement(el1
, 'text:list', attrib
={
1541 'text:style-name': self
.rststyle('highlights-enumlist'),
1543 self
.list_style_stack
.append(self
.rststyle('highlights-enumitem'))
1544 elif self
.blockstyle
== self
.rststyle('epigraph'):
1545 el2
= SubElement(el1
, 'text:list', attrib
={
1546 'text:style-name': self
.rststyle('epigraph-enumlist'),
1548 self
.list_style_stack
.append(self
.rststyle('epigraph-enumitem'))
1550 el2
= SubElement(el1
, 'text:list', attrib
={
1551 'text:style-name': self
.rststyle('enumlist'),
1553 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1554 self
.set_current_element(el2
)
1556 def depart_enumerated_list(self
, node
):
1557 self
.set_to_parent()
1558 self
.list_style_stack
.pop()
1560 def visit_list_item(self
, node
):
1561 #ipshell('At visit_list_item')
1562 el1
= self
.append_child('text:list-item')
1563 # If we are in a "bumped" list level, then wrap this
1564 # list in an outer lists in order to increase the
1565 # indentation level.
1567 if len(self
.bumped_list_level_stack
) > 0:
1568 level_obj
= self
.bumped_list_level_stack
[-1]
1569 if level_obj
.get_sibling():
1570 level_obj
.set_nested(False)
1571 for level_obj1
in self
.bumped_list_level_stack
:
1572 for idx
in range(level_obj1
.get_level()):
1573 el2
= self
.append_child('text:list', parent
=el3
)
1574 el3
= self
.append_child('text:list-item', parent
=el2
)
1575 self
.paragraph_style_stack
.append(self
.list_style_stack
[-1])
1576 self
.set_current_element(el3
)
1578 def depart_list_item(self
, node
):
1579 if len(self
.bumped_list_level_stack
) > 0:
1580 level_obj
= self
.bumped_list_level_stack
[-1]
1581 if level_obj
.get_sibling():
1582 level_obj
.set_nested(True)
1583 for level_obj1
in self
.bumped_list_level_stack
:
1584 for idx
in range(level_obj1
.get_level()):
1585 self
.set_to_parent()
1586 self
.set_to_parent()
1587 self
.paragraph_style_stack
.pop()
1588 self
.set_to_parent()
1590 def visit_header(self
, node
):
1591 #ipshell('At visit_header')
1592 self
.in_header
= True
1594 def depart_header(self
, node
):
1595 #ipshell('At depart_header')
1596 self
.in_header
= False
1598 def visit_footer(self
, node
):
1599 #ipshell('At visit_footer')
1600 self
.in_footer
= True
1602 def depart_footer(self
, node
):
1603 #ipshell('At depart_footer')
1604 self
.in_footer
= False
1606 def visit_field(self
, node
):
1608 ## # Note that the syntaxhighlight directive produces this field node.
1609 ## # See class SyntaxHighlight, above.
1610 ## #ipshell('At visit_field')
1611 ## children = node.children
1612 ## if len(children) == 2:
1613 ## name = children[0].astext()
1614 ## if name == 'syntaxhighlight':
1615 ## body = children[1].astext()
1616 ## args = body.split()
1617 ## if args[0] == 'on':
1618 ## self.syntaxhighlighting = 1
1619 ## elif args[0] == 'off':
1620 ## self.syntaxhighlighting = 0
1622 ## self.syntaxhighlight_lexer = args[0]
1623 ## raise nodes.SkipChildren()
1625 def depart_field(self
, node
):
1628 def visit_field_list(self
, node
):
1629 #ipshell('At visit_field_list')
1632 def depart_field_list(self
, node
):
1633 #ipshell('At depart_field_list')
1636 def visit_field_name(self
, node
):
1637 #ipshell('At visit_field_name')
1638 #self.trace_visit_node(node)
1639 el
= self
.append_p('textbody')
1640 el1
= SubElement(el
, 'text:span',
1641 attrib
={'text:style-name': self
.rststyle('strong')})
1642 el1
.text
= node
.astext()
1644 def depart_field_name(self
, node
):
1645 #self.trace_depart_node(node)
1648 def visit_field_body(self
, node
):
1649 #ipshell('At visit_field_body')
1650 #self.trace_visit_node(node)
1651 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1653 def depart_field_body(self
, node
):
1654 #self.trace_depart_node(node)
1655 self
.paragraph_style_stack
.pop()
1657 def visit_figure(self
, node
):
1658 #ipshell('At visit_figure')
1659 #self.trace_visit_node(node)
1662 def depart_figure(self
, node
):
1663 #self.trace_depart_node(node)
1666 def visit_footnote(self
, node
):
1667 #ipshell('At visit_footnote')
1668 self
.in_footnote
= True
1669 ids
= node
.attributes
['ids']
1672 if id in self
.footnote_dict
:
1673 el1
= self
.footnote_dict
[id]
1676 el2
= SubElement(el1
, 'text:note-body')
1677 self
.paragraph_style_stack
.append(self
.rststyle('footnote'))
1678 self
.set_current_element(el2
)
1679 self
.footnote_found
= True
1681 self
.footnote_found
= False
1683 def depart_footnote(self
, node
):
1684 #ipshell('At depart_footnote')
1685 self
.in_footnote
= False
1686 if self
.footnote_found
:
1687 self
.current_element
.text
= ''
1688 self
.set_to_parent()
1689 self
.set_to_parent()
1690 self
.set_to_parent()
1691 self
.paragraph_style_stack
.pop()
1693 def visit_footnote_reference(self
, node
):
1694 #ipshell('At visit_footnote_reference')
1695 if not self
.in_footnote
:
1696 id = node
.attributes
['refid']
1697 children
= self
.current_element
.getchildren()
1698 if len(children
) > 0:
1699 if children
[-1].tail
and children
[-1].tail
[-1] == ' ':
1700 children
[-1].tail
= children
[-1].tail
[:-1]
1701 elif (self
.current_element
.text
and
1702 self
.current_element
.text
[-1] == ' '):
1703 self
.current_element
.text
= self
.current_element
.text
[:-1]
1704 el1
= self
.append_child('text:note', attrib
={
1705 'text:id': '%s' % (id, ),
1706 'text:note-class': 'footnote',
1708 self
.footnote_dict
[id] = el1
1709 raise nodes
.SkipChildren()
1711 def depart_footnote_reference(self
, node
):
1712 #ipshell('At depart_footnote_reference')
1715 def visit_citation(self
, node
):
1716 #ipshell('At visit_citation')
1717 for id in node
.attributes
['ids']:
1718 self
.in_citation
= id
1720 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1721 self
.bumped_list_level_stack
.append(ListLevel(1))
1723 def depart_citation(self
, node
):
1724 #ipshell('At depart_citation')
1725 self
.in_citation
= None
1726 self
.paragraph_style_stack
.pop()
1727 self
.bumped_list_level_stack
.pop()
1729 def visit_citation_reference(self
, node
):
1730 #ipshell('At visit_citation_reference')
1731 if self
.settings
.create_links
:
1732 id = node
.attributes
['refid']
1733 el
= self
.append_child('text:reference-ref', attrib
={
1734 'text:ref-name': '%s' % (id, ),
1735 'text:reference-format': 'text',
1738 self
.set_current_element(el
)
1740 self
.current_element
.text
+= '['
1742 def depart_citation_reference(self
, node
):
1743 #ipshell('At depart_citation_reference')
1744 self
.current_element
.text
+= ']'
1745 if self
.settings
.create_links
:
1746 self
.set_to_parent()
1748 def visit_label(self
, node
):
1749 #ipshell('At visit_label')
1750 if self
.in_citation
is not None:
1751 el
= self
.append_p('textbody')
1752 self
.set_current_element(el
)
1753 if self
.settings
.create_links
:
1754 el1
= self
.append_child('text:reference-mark-start', attrib
={
1755 'text:name': '%s' % (self
.in_citation
, ),
1758 def depart_label(self
, node
):
1759 #ipshell('At depart_label')
1760 if self
.in_citation
is not None:
1761 if self
.settings
.create_links
:
1762 el
= self
.append_child('text:reference-mark-end', attrib
={
1763 'text:name': '%s' % (self
.in_citation
, ),
1765 self
.set_to_parent()
1767 def visit_generated(self
, node
):
1770 def depart_generated(self
, node
):
1773 def check_file_exists(self
, path
):
1774 if os
.path
.exists(path
):
1779 def visit_image(self
, node
):
1780 #ipshell('At visit_image')
1781 #self.trace_visit_node(node)
1782 # Capture the image file.
1783 if 'uri' in node
.attributes
:
1784 source
= node
.attributes
['uri']
1785 if not self
.check_file_exists(source
):
1786 print 'Error: Cannot find image file %s.' % (source
, )
1790 if source
in self
.image_dict
:
1791 filename
, destination
= self
.image_dict
[source
]
1793 self
.image_count
+= 1
1794 filename
= os
.path
.split(source
)[1]
1795 destination
= 'Pictures/1%08x%s' % (self
.image_count
, filename
, )
1796 spec
= (source
, destination
,)
1797 self
.embedded_file_list
.append(spec
)
1798 self
.image_dict
[source
] = (source
, destination
,)
1799 # Is this a figure (containing an image) or just a plain image?
1800 if self
.in_paragraph
:
1801 el1
= self
.current_element
1803 el1
= SubElement(self
.current_element
, 'text:p',
1804 attrib
={'text:style-name': self
.rststyle('textbody')})
1806 if isinstance(node
.parent
, docutils
.nodes
.figure
):
1807 el3
, el4
, caption
= self
.generate_figure(node
, source
,
1811 'draw:color-inversion': 'false',
1812 'draw:color-mode': 'standard',
1813 'draw:contrast': '0%',
1814 'draw:gamma': '100%',
1816 'draw:image-opacity': '100%',
1817 'draw:luminance': '0%',
1819 'fo:border': 'none',
1820 'fo:clip': 'rect(0in 0in 0in 0in)',
1821 'fo:margin-bottom': '0in',
1822 'fo:margin-left': '0in',
1823 'fo:margin-right': '0in',
1824 'fo:margin-top': '0in',
1825 'fo:padding': '0in',
1826 'style:horizontal-pos': 'from-left',
1827 'style:horizontal-rel': 'paragraph-content',
1828 'style:mirror': 'none',
1829 'style:run-through': 'foreground',
1830 'style:shadow': 'none',
1831 'style:vertical-pos': 'from-top',
1832 'style:vertical-rel': 'paragraph-content',
1833 'style:wrap': 'none',
1835 el5
, width
= self
.generate_image(node
, source
, destination
,
1837 if caption
is not None:
1839 else: #if isinstance(node.parent, docutils.nodes.image):
1840 el3
= self
.generate_image(node
, source
, destination
, el2
)
1842 def depart_image(self
, node
):
1845 def get_image_width_height(self
, node
, attr
, scale
):
1847 if attr
in node
.attributes
:
1849 size
= int(node
.attributes
[attr
])
1850 size
*= 35.278 / 1000.0
1852 except ValueError, e
:
1853 print 'Error: Invalid %s for image: "%s"' % (
1854 attr
, node
.attributes
[attr
], )
1857 def get_image_scale(self
, node
):
1858 if 'scale' in node
.attributes
:
1860 scale
= int(node
.attributes
['scale'])
1861 if scale
< 1: # or scale > 100:
1863 scale
= scale
* 0.01
1864 except ValueError, e
:
1865 print 'Error: Invalid scale for image: "%s"' % (
1866 node
.attributes
['scale'], )
1871 def get_image_scale_width_height(self
, node
, source
):
1872 scale
= self
.get_image_scale(node
)
1873 width
= self
.get_image_width_height(node
, 'width', scale
)
1874 height
= self
.get_image_width_height(node
, 'height', scale
)
1875 if ('scale' in node
.attributes
and
1876 ('width' not in node
.attributes
or
1877 'height' not in node
.attributes
)):
1878 if Image
is not None:
1879 if source
in self
.image_dict
:
1880 filename
, destination
= self
.image_dict
[source
]
1881 imageobj
= Image
.open(filename
, 'r')
1882 width
, height
= imageobj
.size
1883 width
= width
* (35.278 / 1000.0)
1885 height
= height
* (35.278 / 1000.0)
1888 raise RuntimeError, 'image has scale and no height/width and PIL not installed'
1889 return scale
, width
, height
1891 def generate_figure(self
, node
, source
, destination
, current_element
):
1892 #ipshell('At generate_figure')
1894 scale
, width
, height
= self
.get_image_scale_width_height(node
, source
)
1895 for node1
in node
.parent
.children
:
1896 if node1
.tagname
== 'caption':
1897 caption
= node1
.astext()
1898 self
.image_style_count
+= 1
1900 # Add the style for the caption.
1901 if caption
is not None:
1903 'style:class': 'extra',
1904 'style:family': 'paragraph',
1905 'style:name': 'Caption',
1906 'style:parent-style-name': 'Standard',
1908 el1
= SubElement(self
.automatic_styles
, 'style:style',
1909 attrib
=attrib
, nsdict
=SNSD
)
1911 'fo:margin-bottom': '0.0835in',
1912 'fo:margin-top': '0.0835in',
1913 'text:line-number': '0',
1914 'text:number-lines': 'false',
1916 el2
= SubElement(el1
, 'style:paragraph-properties',
1917 attrib
=attrib
, nsdict
=SNSD
)
1919 'fo:font-size': '12pt',
1920 'fo:font-style': 'italic',
1921 'style:font-name': 'Times',
1922 'style:font-name-complex': 'Lucidasans1',
1923 'style:font-size-asian': '12pt',
1924 'style:font-size-complex': '12pt',
1925 'style:font-style-asian': 'italic',
1926 'style:font-style-complex': 'italic',
1928 el2
= SubElement(el1
, 'style:text-properties',
1929 attrib
=attrib
, nsdict
=SNSD
)
1930 style_name
= 'rstframestyle%d' % self
.image_style_count
1933 'style:name': style_name
,
1934 'style:family': 'graphic',
1935 'style:parent-style-name': 'Frame',
1937 el1
= SubElement(self
.automatic_styles
,
1938 'style:style', attrib
=attrib
, nsdict
=SNSD
)
1941 if 'align' in node
.attributes
:
1942 align
= node
.attributes
['align'].split()
1944 if val
in ('left', 'center', 'right'):
1946 elif val
in ('top', 'middle', 'bottom'):
1949 'fo:margin-left': '0cm',
1950 'fo:margin-right': '0cm',
1951 'fo:margin-top': '0cm',
1952 'fo:margin-bottom': '0cm',
1953 'style:wrap': 'dynamic',
1954 'style:number-wrapped-paragraphs': 'no-limit',
1955 'style:vertical-pos': valign
,
1956 'style:vertical-rel': 'paragraph',
1957 'style:horizontal-pos': halign
,
1958 'style:horizontal-rel': 'paragraph',
1959 'fo:padding': '0cm',
1960 'fo:border': 'none',
1962 el2
= SubElement(el1
,
1963 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
1964 # Add the content wrapper.
1965 ## attrib = {'text:style-name': self.rststyle('textbody')}
1966 ## el1 = SubElement(self.current_element, 'text:p', attrib=attrib)
1968 'draw:style-name': style_name
,
1969 'draw:name': 'Frame1',
1970 'text:anchor-type': 'paragraph',
1971 'draw:z-index': '1',
1973 if width
is not None:
1974 attrib
['svg:width'] = '%.2fcm' % (width
, )
1975 el3
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
1977 el4
= SubElement(el3
, 'draw:text-box', attrib
=attrib
)
1979 'text:style-name': self
.rststyle('caption'),
1981 el5
= SubElement(el4
, 'text:p', attrib
=attrib
)
1982 ## if caption is not None:
1983 ## el3.tail = caption
1984 return el3
, el5
, caption
1986 def generate_image(self
, node
, source
, destination
, current_element
,
1987 #ipshell('At generate_image')
1989 scale
, width
, height
= self
.get_image_scale_width_height(node
, source
)
1990 self
.image_style_count
+= 1
1991 style_name
= 'rstframestyle%d' % self
.image_style_count
1994 'style:name': style_name
,
1995 'style:family': 'graphic',
1996 'style:parent-style-name': 'Graphics',
1998 el1
= SubElement(self
.automatic_styles
,
1999 'style:style', attrib
=attrib
, nsdict
=SNSD
)
2002 if 'align' in node
.attributes
:
2003 align
= node
.attributes
['align'].split()
2005 if val
in ('left', 'center', 'right'):
2007 elif val
in ('top', 'middle', 'bottom'):
2009 if frame_attrs
is None:
2011 'style:vertical-pos': 'top',
2012 'style:vertical-rel': 'paragraph',
2013 #'style:horizontal-pos': halign,
2014 #'style:vertical-pos': valign,
2015 'style:horizontal-rel': 'paragraph',
2016 'style:mirror': 'none',
2017 'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
2018 'draw:luminance': '0%',
2019 'draw:contrast': '0%',
2023 'draw:gamma': '100%',
2024 'draw:color-inversion': 'false',
2025 'draw:image-opacity': '100%',
2026 'draw:color-mode': 'standard',
2029 attrib
= frame_attrs
2030 if halign
is not None:
2031 attrib
['style:horizontal-pos'] = halign
2032 if valign
is not None:
2033 attrib
['style:vertical-pos'] = valign
2034 #ipshell('At generate_image')
2035 # If we are inside a table, add a no-wrap style.
2036 if self
.is_in_table(node
):
2037 attrib
['style:wrap'] = 'none'
2038 el2
= SubElement(el1
,
2039 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
2041 #el = SubElement(current_element, 'text:p',
2042 # attrib={'text:style-name': self.rststyle('textbody')})
2044 'draw:style-name': style_name
,
2045 'draw:name': 'graphics2',
2046 #'text:anchor-type': 'paragraph',
2047 #'svg:width': '%fcm' % (width, ),
2048 #'svg:height': '%fcm' % (height, ),
2049 'draw:z-index': '1',
2051 if isinstance(node
.parent
, nodes
.TextElement
):
2052 attrib
['text:anchor-type'] = 'char'
2054 attrib
['text:anchor-type'] = 'paragraph'
2055 if width
is not None:
2056 attrib
['svg:width'] = '%.2fcm' % (width
, )
2057 if height
is not None:
2058 attrib
['svg:height'] = '%.2fcm' % (height
, )
2059 el1
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
2060 el2
= SubElement(el1
, 'draw:image', attrib
={
2061 'xlink:href': '%s' % (destination
, ),
2062 'xlink:type': 'simple',
2063 'xlink:show': 'embed',
2064 'xlink:actuate': 'onLoad',
2068 def is_in_table(self
, node
):
2071 if isinstance(node1
, docutils
.nodes
.entry
):
2073 node1
= node1
.parent
2076 def visit_legend(self
, node
):
2077 # Currently, the legend receives *no* special treatment.
2078 #ipshell('At visit_legend')
2081 def depart_legend(self
, node
):
2084 def visit_line(self
, node
):
2085 #ipshell('At visit_line')
2088 def depart_line(self
, node
):
2089 #ipshell('At depart_line')
2092 def visit_line_block(self
, node
):
2093 #ipshell('At visit_line_block')
2095 lines
= s1
.split('\n')
2096 el
= self
.append_p('lineblock', lines
[0])
2099 for line
in lines
[1:]:
2105 el1
= SubElement(el
, 'text:line-break')
2108 def depart_line_block(self
, node
):
2109 #ipshell('At depart_line_block')
2112 def visit_literal(self
, node
):
2113 #ipshell('At visit_literal')
2114 el
= SubElement(self
.current_element
, 'text:span',
2115 attrib
={'text:style-name': self
.rststyle('inlineliteral')})
2116 self
.set_current_element(el
)
2118 def depart_literal(self
, node
):
2119 self
.set_to_parent()
2121 def _calculate_code_block_padding(self
, line
):
2123 matchobj
= SPACES_PATTERN
.match(line
)
2125 pad
= matchobj
.group()
2128 matchobj
= TABS_PATTERN
.match(line
)
2130 pad
= matchobj
.group()
2131 count
= len(pad
) * 8
2134 def _add_syntax_highlighting(self
, insource
, language
):
2135 #print '(_add_syntax_highlighting) using lexer: %s' % (language, )
2136 lexer
= pygments
.lexers
.get_lexer_by_name(language
, stripall
=True)
2137 if language
in ('latex', 'tex'):
2138 fmtr
= OdtPygmentsLaTeXFormatter(lambda name
, parameters
=():
2139 self
.rststyle(name
, parameters
))
2141 fmtr
= OdtPygmentsProgFormatter(lambda name
, parameters
=():
2142 self
.rststyle(name
, parameters
))
2143 outsource
= pygments
.highlight(insource
, lexer
, fmtr
)
2146 def fill_line(self
, line
):
2147 line
= FILL_PAT1
.sub(self
.fill_func1
, line
)
2148 line
= FILL_PAT2
.sub(self
.fill_func2
, line
)
2151 def fill_func1(self
, matchobj
):
2152 spaces
= matchobj
.group(0)
2153 repl
= '<text:s text:c="%d"/>' % (len(spaces
), )
2156 def fill_func2(self
, matchobj
):
2157 spaces
= matchobj
.group(0)
2158 repl
= ' <text:s text:c="%d"/>' % (len(spaces
) - 1, )
2161 def visit_literal_block(self
, node
):
2162 #ipshell('At visit_literal_block')
2163 wrapper1
= '<text:p text:style-name="%s">%%s</text:p>' % (
2164 self
.rststyle('codeblock'), )
2165 source
= node
.astext()
2167 self
.settings
.add_syntax_highlighting
and
2168 node
.get('hilight', False)):
2169 language
= node
.get('language', 'python')
2170 source
= self
._add
_syntax
_highlighting
(source
, language
)
2172 source
= escape_cdata(source
)
2173 lines
= source
.split('\n')
2174 lines1
= ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
2177 for my_line
in lines
:
2178 my_line
= self
.fill_line(my_line
)
2179 my_line
= my_line
.replace(" ", "\n")
2180 my_lines
.append(my_line
)
2181 my_lines_str
= '<text:line-break/>'.join(my_lines
)
2182 my_lines_str2
= wrapper1
% (my_lines_str
, )
2183 lines1
.append(my_lines_str2
)
2184 lines1
.append('</wrappertag1>')
2185 s1
= ''.join(lines1
)
2186 if WhichElementTree
!= "lxml":
2187 s1
= s1
.encode("utf-8")
2188 el1
= etree
.fromstring(s1
)
2189 children
= el1
.getchildren()
2190 for child
in children
:
2191 self
.current_element
.append(child
)
2193 def depart_literal_block(self
, node
):
2196 visit_doctest_block
= visit_literal_block
2197 depart_doctest_block
= depart_literal_block
2199 def visit_meta(self
, node
):
2200 #ipshell('At visit_meta')
2201 name
= node
.attributes
.get('name')
2202 content
= node
.attributes
.get('content')
2203 if name
is not None and content
is not None:
2204 self
.meta_dict
[name
] = content
2206 def depart_meta(self
, node
):
2209 def visit_option_list(self
, node
):
2210 table_name
= 'tableoption'
2212 # Generate automatic styles
2213 if not self
.optiontablestyles_generated
:
2214 self
.optiontablestyles_generated
= True
2215 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2216 'style:name': self
.rststyle(table_name
),
2217 'style:family': 'table'}, nsdict
=SNSD
)
2218 el1
= SubElement(el
, 'style:table-properties', attrib
={
2219 'style:width': '17.59cm',
2220 'table:align': 'left',
2221 'style:shadow': 'none'}, nsdict
=SNSD
)
2222 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2223 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'A', )),
2224 'style:family': 'table-column'}, nsdict
=SNSD
)
2225 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2226 'style:column-width': '4.999cm'}, nsdict
=SNSD
)
2227 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2228 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'B', )),
2229 'style:family': 'table-column'}, nsdict
=SNSD
)
2230 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2231 'style:column-width': '12.587cm'}, nsdict
=SNSD
)
2232 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2233 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2234 'style:family': 'table-cell'}, nsdict
=SNSD
)
2235 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2236 'fo:background-color': 'transparent',
2237 'fo:padding': '0.097cm',
2238 'fo:border-left': '0.035cm solid #000000',
2239 'fo:border-right': 'none',
2240 'fo:border-top': '0.035cm solid #000000',
2241 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2242 el2
= SubElement(el1
, 'style:background-image', nsdict
=SNSD
)
2243 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2244 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2245 'style:family': 'table-cell'}, nsdict
=SNSD
)
2246 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2247 'fo:padding': '0.097cm',
2248 'fo:border': '0.035cm solid #000000'}, nsdict
=SNSD
)
2249 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2250 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 2, )),
2251 'style:family': 'table-cell'}, nsdict
=SNSD
)
2252 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2253 'fo:padding': '0.097cm',
2254 'fo:border-left': '0.035cm solid #000000',
2255 'fo:border-right': 'none',
2256 'fo:border-top': 'none',
2257 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2258 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2259 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 2, )),
2260 'style:family': 'table-cell'}, nsdict
=SNSD
)
2261 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2262 'fo:padding': '0.097cm',
2263 'fo:border-left': '0.035cm solid #000000',
2264 'fo:border-right': '0.035cm solid #000000',
2265 'fo:border-top': 'none',
2266 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2268 # Generate table data
2269 el
= self
.append_child('table:table', attrib
={
2270 'table:name': self
.rststyle(table_name
),
2271 'table:style-name': self
.rststyle(table_name
),
2273 el1
= SubElement(el
, 'table:table-column', attrib
={
2274 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'A', ))})
2275 el1
= SubElement(el
, 'table:table-column', attrib
={
2276 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'B', ))})
2277 el1
= SubElement(el
, 'table:table-header-rows')
2278 el2
= SubElement(el1
, 'table:table-row')
2279 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2280 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2281 'office:value-type': 'string'})
2282 el4
= SubElement(el3
, 'text:p', attrib
={
2283 'text:style-name': 'Table_20_Heading'})
2285 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2286 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2287 'office:value-type': 'string'})
2288 el4
= SubElement(el3
, 'text:p', attrib
={
2289 'text:style-name': 'Table_20_Heading'})
2290 el4
.text
= 'Description'
2291 self
.set_current_element(el
)
2293 def depart_option_list(self
, node
):
2294 #self.document.reporter.debug_flag = 0
2295 self
.set_to_parent()
2297 def visit_option_list_item(self
, node
):
2298 el
= self
.append_child('table:table-row')
2299 self
.set_current_element(el
)
2301 def depart_option_list_item(self
, node
):
2302 self
.set_to_parent()
2304 def visit_option_group(self
, node
):
2305 el
= self
.append_child('table:table-cell', attrib
={
2306 'table:style-name': 'Table%d.A2' % self
.table_count
,
2307 'office:value-type': 'string',
2309 self
.set_current_element(el
)
2311 def depart_option_group(self
, node
):
2312 self
.set_to_parent()
2314 def visit_option(self
, node
):
2315 el
= self
.append_child('text:p', attrib
={
2316 'text:style-name': 'Table_20_Contents'})
2317 el
.text
= node
.astext()
2319 def depart_option(self
, node
):
2322 def visit_option_string(self
, node
):
2325 def depart_option_string(self
, node
):
2328 def visit_option_argument(self
, node
):
2329 #ipshell('At visit_option_argument')
2332 def depart_option_argument(self
, node
):
2335 def visit_description(self
, node
):
2336 el
= self
.append_child('table:table-cell', attrib
={
2337 'table:style-name': 'Table%d.B2' % self
.table_count
,
2338 'office:value-type': 'string',
2340 el1
= SubElement(el
, 'text:p', attrib
={
2341 'text:style-name': 'Table_20_Contents'})
2342 el1
.text
= node
.astext()
2343 raise nodes
.SkipChildren()
2345 def depart_description(self
, node
):
2348 def visit_paragraph(self
, node
):
2349 #ipshell('At visit_paragraph')
2350 #self.trace_visit_node(node)
2351 self
.in_paragraph
= True
2353 el
= self
.append_p('header')
2354 elif self
.in_footer
:
2355 el
= self
.append_p('footer')
2357 style_name
= self
.paragraph_style_stack
[-1]
2358 el
= self
.append_child('text:p',
2359 attrib
={'text:style-name': style_name
})
2360 self
.append_pending_ids(el
)
2361 self
.set_current_element(el
)
2363 def depart_paragraph(self
, node
):
2364 #ipshell('At depart_paragraph')
2365 #self.trace_depart_node(node)
2366 self
.in_paragraph
= False
2367 self
.set_to_parent()
2369 self
.header_content
.append(self
.current_element
.getchildren()[-1])
2370 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2371 elif self
.in_footer
:
2372 self
.footer_content
.append(self
.current_element
.getchildren()[-1])
2373 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2375 def visit_problematic(self
, node
):
2376 #print '(visit_problematic) node: %s' % (node.astext(), )
2379 def depart_problematic(self
, node
):
2382 def visit_raw(self
, node
):
2383 #ipshell('At visit_raw')
2384 if 'format' in node
.attributes
:
2385 formats
= node
.attributes
['format']
2386 formatlist
= formats
.split()
2387 if 'odt' in formatlist
:
2388 rawstr
= node
.astext()
2389 attrstr
= ' '.join(['%s="%s"' % (k
, v
, )
2390 for k
,v
in CONTENT_NAMESPACE_ATTRIB
.items()])
2391 contentstr
= '<stuff %s>%s</stuff>' % (attrstr
, rawstr
, )
2392 if WhichElementTree
!= "lxml":
2393 content
= content
.encode("utf-8")
2394 content
= etree
.fromstring(contentstr
)
2395 elements
= content
.getchildren()
2396 if len(elements
) > 0:
2400 elif self
.in_footer
:
2403 self
.current_element
.append(el1
)
2404 raise nodes
.SkipChildren()
2406 def depart_raw(self
, node
):
2409 elif self
.in_footer
:
2414 def visit_reference(self
, node
):
2415 #self.trace_visit_node(node)
2416 text
= node
.astext()
2417 if self
.settings
.create_links
:
2418 if node
.has_key('refuri'):
2419 href
= node
['refuri']
2420 if ( self
.settings
.cloak_email_addresses
2421 and href
.startswith('mailto:')):
2422 href
= self
.cloak_mailto(href
)
2423 el
= self
.append_child('text:a', attrib
={
2424 'xlink:href': '%s' % href
,
2425 'xlink:type': 'simple',
2427 self
.set_current_element(el
)
2428 elif node
.has_key('refid'):
2429 if self
.settings
.create_links
:
2430 href
= node
['refid']
2431 el
= self
.append_child('text:reference-ref', attrib
={
2432 'text:ref-name': '%s' % href
,
2433 'text:reference-format': 'text',
2436 raise RuntimeError, 'References must have "refuri" or "refid" attribute.'
2437 #print '(visit_reference) href: "%s" text: "%s"' % (href, text, )
2438 if (self
.in_table_of_contents
and
2439 len(node
.children
) >= 1 and
2440 isinstance(node
.children
[0], docutils
.nodes
.generated
)):
2441 node
.remove(node
.children
[0])
2443 def depart_reference(self
, node
):
2444 #self.trace_depart_node(node)
2445 if self
.settings
.create_links
:
2446 if node
.has_key('refuri'):
2447 self
.set_to_parent()
2449 def visit_rubric(self
, node
):
2450 style_name
= self
.rststyle('rubric')
2451 classes
= node
.get('classes')
2456 el
= SubElement(self
.current_element
, 'text:h', attrib
= {
2457 #'text:outline-level': '%d' % section_level,
2458 #'text:style-name': 'Heading_20_%d' % section_level,
2459 'text:style-name': style_name
,
2461 text
= node
.astext()
2462 el
.text
= self
.encode(text
)
2464 def depart_rubric(self
, node
):
2467 def visit_section(self
, node
, move_ids
=1):
2468 #ipshell('At visit_section')
2469 self
.section_level
+= 1
2470 self
.section_count
+= 1
2471 if self
.settings
.create_sections
:
2472 el
= self
.append_child('text:section', attrib
={
2473 'text:name': 'Section%d' % self
.section_count
,
2474 'text:style-name': 'Sect%d' % self
.section_level
,
2476 self
.set_current_element(el
)
2478 def depart_section(self
, node
):
2479 self
.section_level
-= 1
2480 if self
.settings
.create_sections
:
2481 self
.set_to_parent()
2483 def visit_strong(self
, node
):
2484 #ipshell('At visit_strong')
2485 el
= SubElement(self
.current_element
, 'text:span',
2486 attrib
={'text:style-name': self
.rststyle('strong')})
2487 self
.set_current_element(el
)
2489 def depart_strong(self
, node
):
2490 self
.set_to_parent()
2492 def visit_substitution_definition(self
, node
):
2493 #ipshell('At visit_substitution_definition')
2494 raise nodes
.SkipChildren()
2496 def depart_substitution_definition(self
, node
):
2497 #ipshell('At depart_substitution_definition')
2500 def visit_system_message(self
, node
):
2501 #print '(visit_system_message) node: %s' % (node.astext(), )
2504 def depart_system_message(self
, node
):
2507 def visit_table(self
, node
):
2508 #self.trace_visit_node(node)
2509 #ipshell('At visit_table')
2510 self
.table_count
+= 1
2511 table_name
= '%s%%d' % TableStylePrefix
2512 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2513 'style:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2514 'style:family': 'table',
2516 el1_1
= SubElement(el1
, 'style:table-properties', attrib
={
2517 #'style:width': '17.59cm',
2518 'table:align': 'margins',
2520 # We use a single cell style for all cells in this table.
2521 # That's probably not correct, but seems to work.
2522 el2
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2523 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( self
.table_count
, 'A', 1, )),
2524 'style:family': 'table-cell',
2526 line_style1
= '0.%03dcm solid #000000' % self
.settings
.table_border_thickness
2527 el2_1
= SubElement(el2
, 'style:table-cell-properties', attrib
={
2528 'fo:padding': '0.049cm',
2529 'fo:border-left': line_style1
,
2530 'fo:border-right': line_style1
,
2531 'fo:border-top': line_style1
,
2532 'fo:border-bottom': line_style1
,
2535 for child
in node
.children
:
2536 if child
.tagname
== 'title':
2537 title
= child
.astext()
2539 if title
is not None:
2540 el3
= self
.append_p('table-title', title
)
2542 #print 'no table title'
2544 el4
= SubElement(self
.current_element
, 'table:table', attrib
={
2545 'table:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2546 'table:style-name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2548 self
.set_current_element(el4
)
2549 self
.current_table_style
= el1
2550 self
.table_width
= 0
2552 def depart_table(self
, node
):
2553 #self.trace_depart_node(node)
2554 #ipshell('At depart_table')
2555 attribkey
= add_ns('style:width', nsdict
=SNSD
)
2556 attribval
= '%dcm' % self
.table_width
2557 self
.current_table_style
.attrib
[attribkey
] = attribval
2558 self
.set_to_parent()
2560 def visit_tgroup(self
, node
):
2561 #self.trace_visit_node(node)
2562 #ipshell('At visit_tgroup')
2563 self
.column_count
= ord('A') - 1
2565 def depart_tgroup(self
, node
):
2566 #self.trace_depart_node(node)
2569 def visit_colspec(self
, node
):
2570 #self.trace_visit_node(node)
2571 #ipshell('At visit_colspec')
2572 self
.column_count
+= 1
2573 colspec_name
= self
.rststyle('%s%%d.%%s' % TableStylePrefix
, ( self
.table_count
, chr(self
.column_count
), ))
2574 colwidth
= node
['colwidth']
2575 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2576 'style:name': colspec_name
,
2577 'style:family': 'table-column',
2579 el1_1
= SubElement(el1
, 'style:table-column-properties', attrib
={
2580 'style:column-width': '%dcm' % colwidth
}, nsdict
=SNSD
)
2581 el2
= self
.append_child('table:table-column', attrib
={
2582 'table:style-name': colspec_name
,
2584 self
.table_width
+= colwidth
2586 def depart_colspec(self
, node
):
2587 #self.trace_depart_node(node)
2590 def visit_thead(self
, node
):
2591 #self.trace_visit_node(node)
2592 #ipshell('At visit_thead')
2593 el
= self
.append_child('table:table-header-rows')
2594 self
.set_current_element(el
)
2595 self
.in_thead
= True
2596 self
.paragraph_style_stack
.append('Table_20_Heading')
2598 def depart_thead(self
, node
):
2599 #self.trace_depart_node(node)
2600 self
.set_to_parent()
2601 self
.in_thead
= False
2602 self
.paragraph_style_stack
.pop()
2604 def visit_row(self
, node
):
2605 #self.trace_visit_node(node)
2606 #ipshell('At visit_row')
2607 self
.column_count
= ord('A') - 1
2608 el
= self
.append_child('table:table-row')
2609 self
.set_current_element(el
)
2611 def depart_row(self
, node
):
2612 #self.trace_depart_node(node)
2613 self
.set_to_parent()
2615 def visit_entry(self
, node
):
2616 #self.trace_visit_node(node)
2617 #ipshell('At visit_entry')
2618 self
.column_count
+= 1
2619 cellspec_name
= self
.rststyle('%s%%d.%%c%%d' % TableStylePrefix
, ( self
.table_count
, 'A', 1, ))
2621 'table:style-name': cellspec_name
,
2622 'office:value-type': 'string',
2624 morecols
= node
.get('morecols', 0)
2626 attrib
['table:number-columns-spanned'] = '%d' % (morecols
+ 1,)
2627 self
.column_count
+= morecols
2628 el1
= self
.append_child('table:table-cell', attrib
=attrib
)
2629 self
.set_current_element(el1
)
2631 def depart_entry(self
, node
):
2632 #self.trace_depart_node(node)
2633 self
.set_to_parent()
2635 def visit_tbody(self
, node
):
2636 #self.trace_visit_node(node)
2637 #ipshell('At visit_')
2640 def depart_tbody(self
, node
):
2641 #self.trace_depart_node(node)
2644 def visit_target(self
, node
):
2646 # I don't know how to implement targets in ODF.
2647 # How do we create a target in oowriter? A cross-reference?
2648 if not (node
.has_key('refuri') or node
.has_key('refid')
2649 or node
.has_key('refname')):
2654 def depart_target(self
, node
):
2657 def visit_title(self
, node
, move_ids
=1):
2658 #ipshell('At visit_title')
2659 if isinstance(node
.parent
, docutils
.nodes
.section
):
2660 section_level
= self
.section_level
2661 if section_level
> 7:
2662 print 'Warning: Heading/section levels greater than 7 not supported.'
2663 print ' Reducing to heading level 7 for heading:'
2664 print ' "%s"' % node
.astext()
2666 el1
= self
.append_child('text:h', attrib
= {
2667 'text:outline-level': '%d' % section_level
,
2668 #'text:style-name': 'Heading_20_%d' % section_level,
2669 'text:style-name': self
.rststyle('heading%d', (section_level
, )),
2671 self
.append_pending_ids(el1
)
2672 self
.set_current_element(el1
)
2673 elif isinstance(node
.parent
, docutils
.nodes
.document
):
2674 # text = self.settings.title
2676 # text = node.astext()
2677 el1
= SubElement(self
.current_element
, 'text:h', attrib
= {
2678 'text:outline-level': '1',
2679 'text:style-name': self
.rststyle('heading%d', ( 1, )),
2681 self
.append_pending_ids(el1
)
2682 text
= node
.astext()
2684 self
.found_doc_title
= True
2685 self
.set_current_element(el1
)
2687 def depart_title(self
, node
):
2688 if (isinstance(node
.parent
, docutils
.nodes
.section
) or
2689 isinstance(node
.parent
, docutils
.nodes
.document
)):
2690 self
.set_to_parent()
2692 visit_subtitle
= visit_title
2693 depart_subtitle
= depart_title
2695 def visit_title_reference(self
, node
):
2696 #ipshell('At visit_title_reference')
2697 el
= self
.append_child('text:span', attrib
={
2698 'text:style-name': self
.rststyle('quotation')})
2699 el
.text
= self
.encode(node
.astext())
2701 def depart_title_reference(self
, node
):
2704 def visit_topic(self
, node
):
2705 #ipshell('At visit_topic')
2706 if 'classes' in node
.attributes
:
2707 if 'contents' in node
.attributes
['classes']:
2708 el
= self
.append_p('horizontalline')
2709 el
= self
.append_p('centeredtextbody')
2710 el1
= SubElement(el
, 'text:span',
2711 attrib
={'text:style-name': self
.rststyle('strong')})
2712 el1
.text
= 'Contents'
2713 self
.in_table_of_contents
= True
2714 elif 'abstract' in node
.attributes
['classes']:
2715 el
= self
.append_p('horizontalline')
2716 el
= self
.append_p('centeredtextbody')
2717 el1
= SubElement(el
, 'text:span',
2718 attrib
={'text:style-name': self
.rststyle('strong')})
2719 el1
.text
= 'Abstract'
2721 def depart_topic(self
, node
):
2722 #ipshell('At depart_topic')
2723 if 'classes' in node
.attributes
:
2724 if 'contents' in node
.attributes
['classes']:
2725 el
= self
.append_p('horizontalline')
2726 self
.in_table_of_contents
= False
2728 def visit_transition(self
, node
):
2729 el
= self
.append_p('horizontalline')
2731 def depart_transition(self
, node
):
2737 def visit_warning(self
, node
):
2738 self
.generate_admonition(node
, 'warning')
2740 def depart_warning(self
, node
):
2741 self
.paragraph_style_stack
.pop()
2743 def visit_attention(self
, node
):
2744 self
.generate_admonition(node
, 'attention')
2746 depart_attention
= depart_warning
2748 def visit_caution(self
, node
):
2749 self
.generate_admonition(node
, 'caution')
2751 depart_caution
= depart_warning
2753 def visit_danger(self
, node
):
2754 self
.generate_admonition(node
, 'danger')
2756 depart_danger
= depart_warning
2758 def visit_error(self
, node
):
2759 self
.generate_admonition(node
, 'error')
2761 depart_error
= depart_warning
2763 def visit_hint(self
, node
):
2764 self
.generate_admonition(node
, 'hint')
2766 depart_hint
= depart_warning
2768 def visit_important(self
, node
):
2769 self
.generate_admonition(node
, 'important')
2771 depart_important
= depart_warning
2773 def visit_note(self
, node
):
2774 self
.generate_admonition(node
, 'note')
2776 depart_note
= depart_warning
2778 def visit_tip(self
, node
):
2779 self
.generate_admonition(node
, 'tip')
2781 depart_tip
= depart_warning
2783 def visit_admonition(self
, node
):
2784 #import pdb; pdb.set_trace()
2786 for child
in node
.children
:
2787 if child
.tagname
== 'title':
2788 title
= child
.astext()
2790 classes1
= node
.get('classes')
2793 self
.generate_admonition(node
, 'generic', title
)
2795 depart_admonition
= depart_warning
2797 def generate_admonition(self
, node
, label
, title
=None):
2798 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2799 'text:style-name': self
.rststyle('admon-%s-hdr', ( label
, )),
2804 el1
.text
= '%s!' % (label
.capitalize(), )
2805 s1
= self
.rststyle('admon-%s-body', ( label
, ))
2806 self
.paragraph_style_stack
.append(s1
)
2809 # Roles (e.g. subscript, superscript, strong, ...
2811 def visit_subscript(self
, node
):
2812 el
= self
.append_child('text:span', attrib
={
2813 'text:style-name': 'rststyle-subscript',
2815 self
.set_current_element(el
)
2817 def depart_subscript(self
, node
):
2818 self
.set_to_parent()
2820 def visit_superscript(self
, node
):
2821 el
= self
.append_child('text:span', attrib
={
2822 'text:style-name': 'rststyle-superscript',
2824 self
.set_current_element(el
)
2826 def depart_superscript(self
, node
):
2827 self
.set_to_parent()
2830 # Use an own reader to modify transformations done.
2831 class Reader(standalone
.Reader
):
2833 def get_transforms(self
):
2834 default
= standalone
.Reader
.get_transforms(self
)
2835 if self
.settings
.create_links
:
2839 if i
is not references
.DanglingReferences
]