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',
911 'admon-attention-hdr',
912 'admon-attention-body',
914 'admon-caution-body',
920 'admon-generic-body',
923 'admon-important-hdr',
924 'admon-important-body',
930 'admon-warning-body',
932 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c',
936 def __init__(self
, document
):
937 #nodes.SparseNodeVisitor.__init__(self, document)
938 nodes
.GenericNodeVisitor
.__init
__(self
, document
)
939 self
.settings
= document
.settings
940 self
.format_map
= { }
941 if self
.settings
.odf_config_file
:
942 from ConfigParser
import ConfigParser
944 parser
= ConfigParser()
945 parser
.read(self
.settings
.odf_config_file
)
946 for ( rststyle
, format
, ) in parser
.items("Formats"):
947 if rststyle
not in self
.used_styles
:
949 print ('*** Warning: Style "%s" '
950 'is not a style used by odtwriter.' % (
953 #raise RuntimeError, 'Unused style "%s"' % ( rststyle, )
954 self
.format_map
[rststyle
] = format
955 self
.section_level
= 0
956 self
.section_count
= 0
957 # Create ElementTree content and styles documents.
958 if WhichElementTree
== 'lxml':
960 'office:document-content',
961 nsmap
=CONTENT_NAMESPACE_DICT
,
965 'office:document-content',
966 attrib
=CONTENT_NAMESPACE_ATTRIB
,
968 self
.content_tree
= etree
.ElementTree(element
=root
)
969 self
.current_element
= root
970 SubElement(root
, 'office:scripts')
971 SubElement(root
, 'office:font-face-decls')
972 el
= SubElement(root
, 'office:automatic-styles')
973 self
.automatic_styles
= el
974 el
= SubElement(root
, 'office:body')
975 el
= self
.generate_content_element(el
)
976 self
.current_element
= el
977 self
.body_text_element
= el
979 ## if WhichElementTree == 'lxml':
981 ## 'office:document-styles',
982 ## nsmap=STYLES_NAMESPACE_DICT,
983 ## nsdict=STYLES_NAMESPACE_DICT,
986 ## root = Element('office:document-styles',
987 ## attrib=STYLES_NAMESPACE_ATTRIB,
988 ## nsdict=STYLES_NAMESPACE_DICT,
990 ## self.styles_tree = etree.ElementTree(element=root)
991 self
.paragraph_style_stack
= [self
.rststyle('textbody'), ]
992 self
.list_style_stack
= []
994 self
.column_count
= ord('A') - 1
995 self
.trace_level
= -1
996 self
.optiontablestyles_generated
= False
997 self
.field_name
= None
998 self
.field_element
= None
1000 self
.image_count
= 0
1001 self
.image_style_count
= 0
1002 self
.image_dict
= {}
1003 self
.embedded_file_list
= []
1004 self
.syntaxhighlighting
= 1
1005 self
.syntaxhighlight_lexer
= 'python'
1006 self
.header_content
= []
1007 self
.footer_content
= []
1008 self
.in_header
= False
1009 self
.in_footer
= False
1010 self
.blockstyle
= ''
1011 self
.in_table_of_contents
= False
1012 self
.footnote_dict
= {}
1013 self
.footnote_found
= False
1014 self
.pending_ids
= [ ]
1015 self
.in_paragraph
= False
1016 self
.found_doc_title
= False
1017 self
.bumped_list_level_stack
= []
1019 self
.in_footnote
= False
1020 self
.in_citation
= None
1022 def add_doc_title(self
):
1023 text
= self
.settings
.title
1026 if not self
.found_doc_title
:
1027 el
= Element('text:p', attrib
= {
1028 'text:style-name': self
.rststyle('title'),
1031 self
.body_text_element
.insert(0, el
)
1033 def rststyle(self
, name
, parameters
=( )):
1035 Returns the style name to use for the given style.
1037 If `parameters` is given `name` must contain a matching number of ``%`` and
1038 is used as a format expression with `parameters` as the value.
1040 ## template = self.format_map.get(name, 'rststyle-%s' % name)
1041 ## return template % parameters
1042 name1
= name
% parameters
1043 stylename
= self
.format_map
.get(name1
, 'rststyle-%s' % name1
)
1046 def generate_content_element(self
, root
):
1047 return SubElement(root
, 'office:text')
1049 def setup_page(self
, content
):
1050 root_el
= etree
.fromstring(content
)
1051 self
.setup_paper(root_el
)
1052 if len(self
.header_content
) > 0 or len(self
.footer_content
) > 0:
1053 self
.add_header_footer(root_el
)
1054 new_content
= etree
.tostring(root_el
)
1057 def setup_paper(self
, root_el
):
1059 fin
= os
.popen("paperconf -s")
1060 w
, h
= map(float, fin
.read().split())
1063 w
, h
= 612, 792 # default to Letter
1065 if el
.tag
== "{%s}page-layout-properties" % SNSD
["style"] and \
1066 not el
.attrib
.has_key("{%s}page-width" % SNSD
["fo"]):
1067 el
.attrib
["{%s}page-width" % SNSD
["fo"]] = "%.3fpt" % w
1068 el
.attrib
["{%s}page-height" % SNSD
["fo"]] = "%.3fpt" % h
1069 el
.attrib
["{%s}margin-left" % SNSD
["fo"]] = \
1070 el
.attrib
["{%s}margin-right" % SNSD
["fo"]] = \
1072 el
.attrib
["{%s}margin-top" % SNSD
["fo"]] = \
1073 el
.attrib
["{%s}margin-bottom" % SNSD
["fo"]] = \
1076 for subel
in el
.getchildren(): walk(subel
)
1079 def add_header_footer(self
, root_el
):
1080 path
= '{%s}master-styles' % (NAME_SPACE_1
, )
1081 master_el
= root_el
.find(path
)
1082 if master_el
is None:
1084 path
= '{%s}master-page' % (SNSD
['style'], )
1085 master_el
= master_el
.find(path
)
1086 if master_el
is None:
1089 if len(self
.header_content
) > 0:
1090 if WhichElementTree
== 'lxml':
1091 el2
= SubElement(el1
, 'style:header', nsdict
=SNSD
)
1093 el2
= SubElement(el1
, 'style:header',
1094 attrib
=STYLES_NAMESPACE_ATTRIB
,
1095 nsdict
=STYLES_NAMESPACE_DICT
,
1097 for el
in self
.header_content
:
1098 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1099 el
.attrib
[attrkey
] = self
.rststyle('header')
1101 if len(self
.footer_content
) > 0:
1102 if WhichElementTree
== 'lxml':
1103 el2
= SubElement(el1
, 'style:footer', nsdict
=SNSD
)
1105 el2
= SubElement(el1
, 'style:footer',
1106 attrib
=STYLES_NAMESPACE_ATTRIB
,
1107 nsdict
=STYLES_NAMESPACE_DICT
,
1109 for el
in self
.footer_content
:
1110 attrkey
= add_ns('text:style-name', nsdict
=SNSD
)
1111 el
.attrib
[attrkey
] = self
.rststyle('footer')
1113 #new_tree = etree.ElementTree(root_el)
1114 #new_content = ToString(new_tree)
1117 root
= self
.content_tree
.getroot()
1118 et
= etree
.ElementTree(root
)
1122 def content_astext(self
):
1123 return self
.astext()
1126 ## def styles_astext(self):
1127 ## root = self.styles_tree.getroot()
1128 ## et = etree.ElementTree(root)
1129 ## s1 = ToString(et)
1132 def set_title(self
, title
): self
.title
= title
1133 def get_title(self
): return self
.title
1134 def set_embedded_file_list(self
, embedded_file_list
):
1135 self
.embedded_file_list
= embedded_file_list
1136 def get_embedded_file_list(self
): return self
.embedded_file_list
1137 def get_meta_dict(self
): return self
.meta_dict
1142 def append_child(self
, tag
, attrib
=None, parent
=None):
1144 parent
= self
.current_element
1146 el
= SubElement(parent
, tag
)
1148 el
= SubElement(parent
, tag
, attrib
)
1151 def append_p(self
, style
, text
=None):
1152 result
= self
.append_child('text:p', attrib
={
1153 'text:style-name': self
.rststyle(style
)})
1154 self
.append_pending_ids(result
)
1155 if text
is not None:
1159 def append_pending_ids(self
, el
):
1160 if self
.settings
.create_links
:
1161 for id in self
.pending_ids
:
1162 SubElement(el
, 'text:reference-mark', attrib
={
1164 self
.pending_ids
= [ ]
1166 def set_current_element(self
, el
):
1167 self
.current_element
= el
1169 def set_to_parent(self
):
1170 self
.current_element
= self
.current_element
.getparent()
1172 def generate_labeled_block(self
, node
, label
):
1173 el
= self
.append_p('textbody')
1174 el1
= SubElement(el
, 'text:span',
1175 attrib
={'text:style-name': self
.rststyle('strong')})
1177 el
= self
.append_p('blockindent')
1180 def generate_labeled_line(self
, node
, label
):
1181 el
= self
.append_p('textbody')
1182 el1
= SubElement(el
, 'text:span',
1183 attrib
={'text:style-name': self
.rststyle('strong')})
1185 el1
.tail
= node
.astext()
1188 def encode(self
, text
):
1189 text
= text
.replace(u
'\u00a0', " ")
1192 def trace_visit_node(self
, node
):
1194 self
.trace_level
+= 1
1195 self
._trace
_show
_level
(self
.trace_level
)
1197 print '(visit_%s) node: %s' % (node
.tagname
, node
.astext(), )
1199 print '(visit_%s)' % node
.tagname
1201 def trace_depart_node(self
, node
):
1204 self
._trace
_show
_level
(self
.trace_level
)
1205 print '(depart_%s)' % node
.tagname
1206 self
.trace_level
-= 1
1208 def _trace_show_level(self
, level
):
1209 for idx
in range(level
):
1215 # In alphabetic order, more or less.
1216 # See docutils.docutils.nodes.node_class_names.
1219 def dispatch_visit(self
, node
):
1220 """Override to catch basic attributes which many nodes have."""
1221 self
.handle_basic_atts(node
)
1222 nodes
.GenericNodeVisitor
.dispatch_visit(self
, node
)
1224 def handle_basic_atts(self
, node
):
1225 if isinstance(node
, nodes
.Element
) and node
['ids']:
1226 self
.pending_ids
+= node
['ids']
1228 def default_visit(self
, node
):
1229 #ipshell('At default_visit')
1230 print 'missing visit_%s' % (node
.tagname
, )
1232 def default_departure(self
, node
):
1233 print 'missing depart_%s' % (node
.tagname
, )
1235 def visit_Text(self
, node
):
1236 #ipshell('At visit_Text')
1237 # Skip nodes whose text has been processed in parent nodes.
1238 if isinstance(node
.parent
, docutils
.nodes
.literal_block
):
1239 #isinstance(node.parent, docutils.nodes.term) or \
1240 #isinstance(node.parent, docutils.nodes.definition):
1242 text
= node
.astext()
1243 # Are we in mixed content? If so, add the text to the
1244 # etree tail of the previous sibling element.
1245 if len(self
.current_element
.getchildren()) > 0:
1246 #print '*** (visit_Text) 1. text: %s' % text
1247 if self
.current_element
.getchildren()[-1].tail
:
1248 self
.current_element
.getchildren()[-1].tail
+= text
1250 self
.current_element
.getchildren()[-1].tail
= text
1252 if self
.current_element
.text
:
1253 self
.current_element
.text
+= text
1255 self
.current_element
.text
= text
1257 def depart_Text(self
, node
):
1261 # Pre-defined fields
1264 def visit_address(self
, node
):
1265 #ipshell('At visit_address')
1266 el
= self
.generate_labeled_block(node
, 'Address: ')
1267 self
.set_current_element(el
)
1269 def depart_address(self
, node
):
1270 self
.set_to_parent()
1272 def visit_author(self
, node
):
1273 if isinstance(node
.parent
, nodes
.authors
):
1274 el
= self
.append_p('blockindent')
1276 el
= self
.generate_labeled_block(node
, 'Author: ')
1277 self
.set_current_element(el
)
1279 def depart_author(self
, node
):
1280 self
.set_to_parent()
1282 def visit_authors(self
, node
):
1283 #ipshell('At visit_authors')
1284 #self.trace_visit_node(node)
1286 el
= self
.append_p('textbody')
1287 el1
= SubElement(el
, 'text:span',
1288 attrib
={'text:style-name': self
.rststyle('strong')})
1291 def depart_authors(self
, node
):
1292 #self.trace_depart_node(node)
1295 def visit_contact(self
, node
):
1296 el
= self
.generate_labeled_block(node
, 'Contact: ')
1297 self
.set_current_element(el
)
1299 def depart_contact(self
, node
):
1300 self
.set_to_parent()
1302 def visit_copyright(self
, node
):
1303 el
= self
.generate_labeled_block(node
, 'Copyright: ')
1304 self
.set_current_element(el
)
1306 def depart_copyright(self
, node
):
1307 self
.set_to_parent()
1309 def visit_date(self
, node
):
1310 self
.generate_labeled_line(node
, 'Date: ')
1312 def depart_date(self
, node
):
1315 def visit_organization(self
, node
):
1316 el
= self
.generate_labeled_block(node
, 'Organization: ')
1317 self
.set_current_element(el
)
1319 def depart_organization(self
, node
):
1320 self
.set_to_parent()
1322 def visit_status(self
, node
):
1323 el
= self
.generate_labeled_block(node
, 'Status: ')
1324 self
.set_current_element(el
)
1326 def depart_status(self
, node
):
1327 self
.set_to_parent()
1329 def visit_revision(self
, node
):
1330 self
.generate_labeled_line(node
, 'Revision: ')
1332 def depart_revision(self
, node
):
1335 def visit_version(self
, node
):
1336 el
= self
.generate_labeled_line(node
, 'Version: ')
1337 #self.set_current_element(el)
1339 def depart_version(self
, node
):
1340 #self.set_to_parent()
1343 def visit_attribution(self
, node
):
1344 #ipshell('At visit_attribution')
1345 el
= self
.append_p('attribution', node
.astext())
1347 def depart_attribution(self
, node
):
1348 #ipshell('At depart_attribution')
1351 def visit_block_quote(self
, node
):
1352 #ipshell('At visit_block_quote')
1353 if 'epigraph' in node
.attributes
['classes']:
1354 self
.paragraph_style_stack
.append(self
.rststyle('epigraph'))
1355 self
.blockstyle
= self
.rststyle('epigraph')
1356 elif 'highlights' in node
.attributes
['classes']:
1357 self
.paragraph_style_stack
.append(self
.rststyle('highlights'))
1358 self
.blockstyle
= self
.rststyle('highlights')
1360 self
.paragraph_style_stack
.append(self
.rststyle('blockquote'))
1361 self
.blockstyle
= self
.rststyle('blockquote')
1363 def depart_block_quote(self
, node
):
1364 self
.paragraph_style_stack
.pop()
1365 self
.blockstyle
= ''
1367 def visit_bullet_list(self
, node
):
1368 #ipshell('At visit_bullet_list')
1369 if self
.in_table_of_contents
:
1370 if node
.has_key('classes') and \
1371 'auto-toc' in node
.attributes
['classes']:
1372 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1373 'text:style-name': self
.rststyle('tocenumlist'),
1375 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1377 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1378 'text:style-name': self
.rststyle('tocbulletlist'),
1380 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1382 if self
.blockstyle
== self
.rststyle('blockquote'):
1383 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1384 'text:style-name': self
.rststyle('blockquote-bulletlist'),
1386 self
.list_style_stack
.append(self
.rststyle('blockquote-bulletitem'))
1387 elif self
.blockstyle
== self
.rststyle('highlights'):
1388 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1389 'text:style-name': self
.rststyle('highlights-bulletlist'),
1391 self
.list_style_stack
.append(self
.rststyle('highlights-bulletitem'))
1392 elif self
.blockstyle
== self
.rststyle('epigraph'):
1393 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1394 'text:style-name': self
.rststyle('epigraph-bulletlist'),
1396 self
.list_style_stack
.append(self
.rststyle('epigraph-bulletitem'))
1398 el
= SubElement(self
.current_element
, 'text:list', attrib
={
1399 'text:style-name': self
.rststyle('bulletlist'),
1401 self
.list_style_stack
.append(self
.rststyle('bulletitem'))
1402 self
.set_current_element(el
)
1404 def depart_bullet_list(self
, node
):
1405 self
.set_to_parent()
1406 self
.list_style_stack
.pop()
1408 def visit_caption(self
, node
):
1409 raise nodes
.SkipChildren()
1412 def depart_caption(self
, node
):
1415 def visit_comment(self
, node
):
1416 #ipshell('At visit_comment')
1417 el
= self
.append_p('textbody')
1418 el1
= SubElement(el
, 'office:annotation', attrib
={})
1419 el2
= SubElement(el1
, 'text:p', attrib
={})
1420 el2
.text
= node
.astext()
1422 def depart_comment(self
, node
):
1425 def visit_compound(self
, node
):
1426 # The compound directive currently receives no special treatment.
1429 def depart_compound(self
, node
):
1432 def visit_container(self
, node
):
1433 styles
= node
.attributes
.get('classes', ())
1435 self
.paragraph_style_stack
.append(styles
[0])
1437 def depart_container(self
, node
):
1438 #ipshell('At depart_container')
1439 styles
= node
.attributes
.get('classes', ())
1441 self
.paragraph_style_stack
.pop()
1443 def visit_decoration(self
, node
):
1445 #ipshell('At visit_decoration')
1447 #self.trace_visit_node(node)
1450 def depart_decoration(self
, node
):
1451 #ipshell('At depart_decoration')
1454 def visit_definition(self
, node
):
1455 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1456 self
.bumped_list_level_stack
.append(ListLevel(1))
1458 def depart_definition(self
, node
):
1459 self
.paragraph_style_stack
.pop()
1460 self
.bumped_list_level_stack
.pop()
1462 def visit_definition_list(self
, node
):
1465 def depart_definition_list(self
, node
):
1468 def visit_definition_list_item(self
, node
):
1471 def depart_definition_list_item(self
, node
):
1474 def visit_term(self
, node
):
1475 #ipshell('At visit_term')
1476 el
= self
.append_p('textbody')
1477 el1
= SubElement(el
, 'text:span',
1478 attrib
={'text:style-name': self
.rststyle('strong')})
1479 #el1.text = node.astext()
1480 self
.set_current_element(el1
)
1482 def depart_term(self
, node
):
1483 #ipshell('At depart_term')
1484 self
.set_to_parent()
1485 self
.set_to_parent()
1487 def visit_classifier(self
, node
):
1488 #ipshell('At visit_classifier')
1489 els
= self
.current_element
.getchildren()
1492 el1
= SubElement(el
, 'text:span',
1493 attrib
={'text:style-name': self
.rststyle('emphasis')
1495 el1
.text
= ' (%s)' % (node
.astext(), )
1497 def depart_classifier(self
, node
):
1500 def visit_document(self
, node
):
1501 #ipshell('At visit_document')
1502 #self.set_current_element(self.content_tree.getroot())
1505 def depart_document(self
, node
):
1508 def visit_docinfo(self
, node
):
1509 #self.document.reporter.debug_flag = 1
1510 self
.trace_visit_node(node
)
1511 self
.section_level
+= 1
1512 self
.section_count
+= 1
1513 if self
.settings
.create_sections
:
1514 el
= self
.append_child('text:section', attrib
={
1515 'text:name': 'Section%d' % self
.section_count
,
1516 'text:style-name': 'Sect%d' % self
.section_level
,
1518 self
.set_current_element(el
)
1520 def depart_docinfo(self
, node
):
1521 #self.document.reporter.debug_flag = 0
1522 self
.trace_depart_node(node
)
1523 self
.section_level
-= 1
1524 if self
.settings
.create_sections
:
1525 self
.set_to_parent()
1527 def visit_emphasis(self
, node
):
1528 el
= SubElement(self
.current_element
, 'text:span',
1529 attrib
={'text:style-name': self
.rststyle('emphasis')})
1530 self
.set_current_element(el
)
1532 def depart_emphasis(self
, node
):
1533 self
.set_to_parent()
1535 def visit_enumerated_list(self
, node
):
1536 el1
= self
.current_element
1537 if self
.blockstyle
== self
.rststyle('blockquote'):
1538 el2
= SubElement(el1
, 'text:list', attrib
={
1539 'text:style-name': self
.rststyle('blockquote-enumlist'),
1541 self
.list_style_stack
.append(self
.rststyle('blockquote-enumitem'))
1542 elif self
.blockstyle
== self
.rststyle('highlights'):
1543 el2
= SubElement(el1
, 'text:list', attrib
={
1544 'text:style-name': self
.rststyle('highlights-enumlist'),
1546 self
.list_style_stack
.append(self
.rststyle('highlights-enumitem'))
1547 elif self
.blockstyle
== self
.rststyle('epigraph'):
1548 el2
= SubElement(el1
, 'text:list', attrib
={
1549 'text:style-name': self
.rststyle('epigraph-enumlist'),
1551 self
.list_style_stack
.append(self
.rststyle('epigraph-enumitem'))
1553 el2
= SubElement(el1
, 'text:list', attrib
={
1554 'text:style-name': self
.rststyle('enumlist'),
1556 self
.list_style_stack
.append(self
.rststyle('enumitem'))
1557 self
.set_current_element(el2
)
1559 def depart_enumerated_list(self
, node
):
1560 self
.set_to_parent()
1561 self
.list_style_stack
.pop()
1563 def visit_list_item(self
, node
):
1564 #ipshell('At visit_list_item')
1565 el1
= self
.append_child('text:list-item')
1566 # If we are in a "bumped" list level, then wrap this
1567 # list in an outer lists in order to increase the
1568 # indentation level.
1570 if len(self
.bumped_list_level_stack
) > 0:
1571 level_obj
= self
.bumped_list_level_stack
[-1]
1572 if level_obj
.get_sibling():
1573 level_obj
.set_nested(False)
1574 for level_obj1
in self
.bumped_list_level_stack
:
1575 for idx
in range(level_obj1
.get_level()):
1576 el2
= self
.append_child('text:list', parent
=el3
)
1577 el3
= self
.append_child('text:list-item', parent
=el2
)
1578 self
.paragraph_style_stack
.append(self
.list_style_stack
[-1])
1579 self
.set_current_element(el3
)
1581 def depart_list_item(self
, node
):
1582 if len(self
.bumped_list_level_stack
) > 0:
1583 level_obj
= self
.bumped_list_level_stack
[-1]
1584 if level_obj
.get_sibling():
1585 level_obj
.set_nested(True)
1586 for level_obj1
in self
.bumped_list_level_stack
:
1587 for idx
in range(level_obj1
.get_level()):
1588 self
.set_to_parent()
1589 self
.set_to_parent()
1590 self
.paragraph_style_stack
.pop()
1591 self
.set_to_parent()
1593 def visit_header(self
, node
):
1594 #ipshell('At visit_header')
1595 self
.in_header
= True
1597 def depart_header(self
, node
):
1598 #ipshell('At depart_header')
1599 self
.in_header
= False
1601 def visit_footer(self
, node
):
1602 #ipshell('At visit_footer')
1603 self
.in_footer
= True
1605 def depart_footer(self
, node
):
1606 #ipshell('At depart_footer')
1607 self
.in_footer
= False
1609 def visit_field(self
, node
):
1611 ## # Note that the syntaxhighlight directive produces this field node.
1612 ## # See class SyntaxHighlight, above.
1613 ## #ipshell('At visit_field')
1614 ## children = node.children
1615 ## if len(children) == 2:
1616 ## name = children[0].astext()
1617 ## if name == 'syntaxhighlight':
1618 ## body = children[1].astext()
1619 ## args = body.split()
1620 ## if args[0] == 'on':
1621 ## self.syntaxhighlighting = 1
1622 ## elif args[0] == 'off':
1623 ## self.syntaxhighlighting = 0
1625 ## self.syntaxhighlight_lexer = args[0]
1626 ## raise nodes.SkipChildren()
1628 def depart_field(self
, node
):
1631 def visit_field_list(self
, node
):
1632 #ipshell('At visit_field_list')
1635 def depart_field_list(self
, node
):
1636 #ipshell('At depart_field_list')
1639 def visit_field_name(self
, node
):
1640 #ipshell('At visit_field_name')
1641 #self.trace_visit_node(node)
1642 el
= self
.append_p('textbody')
1643 el1
= SubElement(el
, 'text:span',
1644 attrib
={'text:style-name': self
.rststyle('strong')})
1645 el1
.text
= node
.astext()
1647 def depart_field_name(self
, node
):
1648 #self.trace_depart_node(node)
1651 def visit_field_body(self
, node
):
1652 #ipshell('At visit_field_body')
1653 #self.trace_visit_node(node)
1654 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1656 def depart_field_body(self
, node
):
1657 #self.trace_depart_node(node)
1658 self
.paragraph_style_stack
.pop()
1660 def visit_figure(self
, node
):
1661 #ipshell('At visit_figure')
1662 #self.trace_visit_node(node)
1665 def depart_figure(self
, node
):
1666 #self.trace_depart_node(node)
1669 def visit_footnote(self
, node
):
1670 #ipshell('At visit_footnote')
1671 self
.in_footnote
= True
1672 ids
= node
.attributes
['ids']
1675 if id in self
.footnote_dict
:
1676 el1
= self
.footnote_dict
[id]
1679 el2
= SubElement(el1
, 'text:note-body')
1680 self
.paragraph_style_stack
.append(self
.rststyle('footnote'))
1681 self
.set_current_element(el2
)
1682 self
.footnote_found
= True
1684 self
.footnote_found
= False
1686 def depart_footnote(self
, node
):
1687 #ipshell('At depart_footnote')
1688 self
.in_footnote
= False
1689 if self
.footnote_found
:
1690 self
.current_element
.text
= ''
1691 self
.set_to_parent()
1692 self
.set_to_parent()
1693 self
.set_to_parent()
1694 self
.paragraph_style_stack
.pop()
1696 def visit_footnote_reference(self
, node
):
1697 #ipshell('At visit_footnote_reference')
1698 if not self
.in_footnote
:
1699 id = node
.attributes
['refid']
1700 children
= self
.current_element
.getchildren()
1701 if len(children
) > 0:
1702 if children
[-1].tail
and children
[-1].tail
[-1] == ' ':
1703 children
[-1].tail
= children
[-1].tail
[:-1]
1704 elif (self
.current_element
.text
and
1705 self
.current_element
.text
[-1] == ' '):
1706 self
.current_element
.text
= self
.current_element
.text
[:-1]
1707 el1
= self
.append_child('text:note', attrib
={
1708 'text:id': '%s' % (id, ),
1709 'text:note-class': 'footnote',
1711 self
.footnote_dict
[id] = el1
1712 raise nodes
.SkipChildren()
1714 def depart_footnote_reference(self
, node
):
1715 #ipshell('At depart_footnote_reference')
1718 def visit_citation(self
, node
):
1719 #ipshell('At visit_citation')
1720 for id in node
.attributes
['ids']:
1721 self
.in_citation
= id
1723 self
.paragraph_style_stack
.append(self
.rststyle('blockindent'))
1724 self
.bumped_list_level_stack
.append(ListLevel(1))
1726 def depart_citation(self
, node
):
1727 #ipshell('At depart_citation')
1728 self
.in_citation
= None
1729 self
.paragraph_style_stack
.pop()
1730 self
.bumped_list_level_stack
.pop()
1732 def visit_citation_reference(self
, node
):
1733 #ipshell('At visit_citation_reference')
1734 if self
.settings
.create_links
:
1735 id = node
.attributes
['refid']
1736 el
= self
.append_child('text:reference-ref', attrib
={
1737 'text:ref-name': '%s' % (id, ),
1738 'text:reference-format': 'text',
1741 self
.set_current_element(el
)
1743 self
.current_element
.text
+= '['
1745 def depart_citation_reference(self
, node
):
1746 #ipshell('At depart_citation_reference')
1747 self
.current_element
.text
+= ']'
1748 if self
.settings
.create_links
:
1749 self
.set_to_parent()
1751 def visit_label(self
, node
):
1752 #ipshell('At visit_label')
1753 if self
.in_citation
is not None:
1754 el
= self
.append_p('textbody')
1755 self
.set_current_element(el
)
1756 if self
.settings
.create_links
:
1757 el1
= self
.append_child('text:reference-mark-start', attrib
={
1758 'text:name': '%s' % (self
.in_citation
, ),
1761 def depart_label(self
, node
):
1762 #ipshell('At depart_label')
1763 if self
.in_citation
is not None:
1764 if self
.settings
.create_links
:
1765 el
= self
.append_child('text:reference-mark-end', attrib
={
1766 'text:name': '%s' % (self
.in_citation
, ),
1768 self
.set_to_parent()
1770 def visit_generated(self
, node
):
1773 def depart_generated(self
, node
):
1776 def check_file_exists(self
, path
):
1777 if os
.path
.exists(path
):
1782 def visit_image(self
, node
):
1783 #ipshell('At visit_image')
1784 #self.trace_visit_node(node)
1785 # Capture the image file.
1786 if 'uri' in node
.attributes
:
1787 source
= node
.attributes
['uri']
1788 if not self
.check_file_exists(source
):
1789 print 'Error: Cannot find image file %s.' % (source
, )
1793 if source
in self
.image_dict
:
1794 filename
, destination
= self
.image_dict
[source
]
1796 self
.image_count
+= 1
1797 filename
= os
.path
.split(source
)[1]
1798 destination
= 'Pictures/1%08x%s' % (self
.image_count
, filename
, )
1799 spec
= (source
, destination
,)
1800 self
.embedded_file_list
.append(spec
)
1801 self
.image_dict
[source
] = (source
, destination
,)
1802 # Is this a figure (containing an image) or just a plain image?
1803 if self
.in_paragraph
:
1804 el1
= self
.current_element
1806 el1
= SubElement(self
.current_element
, 'text:p',
1807 attrib
={'text:style-name': self
.rststyle('textbody')})
1809 if isinstance(node
.parent
, docutils
.nodes
.figure
):
1810 el3
, el4
, caption
= self
.generate_figure(node
, source
,
1814 'draw:color-inversion': 'false',
1815 'draw:color-mode': 'standard',
1816 'draw:contrast': '0%',
1817 'draw:gamma': '100%',
1819 'draw:image-opacity': '100%',
1820 'draw:luminance': '0%',
1822 'fo:border': 'none',
1823 'fo:clip': 'rect(0in 0in 0in 0in)',
1824 'fo:margin-bottom': '0in',
1825 'fo:margin-left': '0in',
1826 'fo:margin-right': '0in',
1827 'fo:margin-top': '0in',
1828 'fo:padding': '0in',
1829 'style:horizontal-pos': 'from-left',
1830 'style:horizontal-rel': 'paragraph-content',
1831 'style:mirror': 'none',
1832 'style:run-through': 'foreground',
1833 'style:shadow': 'none',
1834 'style:vertical-pos': 'from-top',
1835 'style:vertical-rel': 'paragraph-content',
1836 'style:wrap': 'none',
1838 el5
, width
= self
.generate_image(node
, source
, destination
,
1840 if caption
is not None:
1842 else: #if isinstance(node.parent, docutils.nodes.image):
1843 el3
= self
.generate_image(node
, source
, destination
, el2
)
1845 def depart_image(self
, node
):
1848 def get_image_width_height(self
, node
, attr
, scale
):
1850 if attr
in node
.attributes
:
1852 size
= int(node
.attributes
[attr
])
1853 size
*= 35.278 / 1000.0
1855 except ValueError, e
:
1856 print 'Error: Invalid %s for image: "%s"' % (
1857 attr
, node
.attributes
[attr
], )
1860 def get_image_scale(self
, node
):
1861 if 'scale' in node
.attributes
:
1863 scale
= int(node
.attributes
['scale'])
1864 if scale
< 1: # or scale > 100:
1866 scale
= scale
* 0.01
1867 except ValueError, e
:
1868 print 'Error: Invalid scale for image: "%s"' % (
1869 node
.attributes
['scale'], )
1874 def get_image_scale_width_height(self
, node
, source
):
1875 scale
= self
.get_image_scale(node
)
1876 width
= self
.get_image_width_height(node
, 'width', scale
)
1877 height
= self
.get_image_width_height(node
, 'height', scale
)
1878 if ('scale' in node
.attributes
and
1879 ('width' not in node
.attributes
or
1880 'height' not in node
.attributes
)):
1881 if Image
is not None:
1882 if source
in self
.image_dict
:
1883 filename
, destination
= self
.image_dict
[source
]
1884 imageobj
= Image
.open(filename
, 'r')
1885 width
, height
= imageobj
.size
1886 width
= width
* (35.278 / 1000.0)
1888 height
= height
* (35.278 / 1000.0)
1891 raise RuntimeError, 'image has scale and no height/width and PIL not installed'
1892 return scale
, width
, height
1894 def generate_figure(self
, node
, source
, destination
, current_element
):
1895 #ipshell('At generate_figure')
1897 scale
, width
, height
= self
.get_image_scale_width_height(node
, source
)
1898 for node1
in node
.parent
.children
:
1899 if node1
.tagname
== 'caption':
1900 caption
= node1
.astext()
1901 self
.image_style_count
+= 1
1903 # Add the style for the caption.
1904 if caption
is not None:
1906 'style:class': 'extra',
1907 'style:family': 'paragraph',
1908 'style:name': 'Caption',
1909 'style:parent-style-name': 'Standard',
1911 el1
= SubElement(self
.automatic_styles
, 'style:style',
1912 attrib
=attrib
, nsdict
=SNSD
)
1914 'fo:margin-bottom': '0.0835in',
1915 'fo:margin-top': '0.0835in',
1916 'text:line-number': '0',
1917 'text:number-lines': 'false',
1919 el2
= SubElement(el1
, 'style:paragraph-properties',
1920 attrib
=attrib
, nsdict
=SNSD
)
1922 'fo:font-size': '12pt',
1923 'fo:font-style': 'italic',
1924 'style:font-name': 'Times',
1925 'style:font-name-complex': 'Lucidasans1',
1926 'style:font-size-asian': '12pt',
1927 'style:font-size-complex': '12pt',
1928 'style:font-style-asian': 'italic',
1929 'style:font-style-complex': 'italic',
1931 el2
= SubElement(el1
, 'style:text-properties',
1932 attrib
=attrib
, nsdict
=SNSD
)
1933 style_name
= 'rstframestyle%d' % self
.image_style_count
1936 'style:name': style_name
,
1937 'style:family': 'graphic',
1938 'style:parent-style-name': 'Frame',
1940 el1
= SubElement(self
.automatic_styles
,
1941 'style:style', attrib
=attrib
, nsdict
=SNSD
)
1944 if 'align' in node
.attributes
:
1945 align
= node
.attributes
['align'].split()
1947 if val
in ('left', 'center', 'right'):
1949 elif val
in ('top', 'middle', 'bottom'):
1952 'fo:margin-left': '0cm',
1953 'fo:margin-right': '0cm',
1954 'fo:margin-top': '0cm',
1955 'fo:margin-bottom': '0cm',
1956 'style:wrap': 'dynamic',
1957 'style:number-wrapped-paragraphs': 'no-limit',
1958 'style:vertical-pos': valign
,
1959 'style:vertical-rel': 'paragraph',
1960 'style:horizontal-pos': halign
,
1961 'style:horizontal-rel': 'paragraph',
1962 'fo:padding': '0cm',
1963 'fo:border': 'none',
1965 el2
= SubElement(el1
,
1966 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
1967 # Add the content wrapper.
1968 ## attrib = {'text:style-name': self.rststyle('textbody')}
1969 ## el1 = SubElement(self.current_element, 'text:p', attrib=attrib)
1971 'draw:style-name': style_name
,
1972 'draw:name': 'Frame1',
1973 'text:anchor-type': 'paragraph',
1974 'draw:z-index': '1',
1976 if width
is not None:
1977 attrib
['svg:width'] = '%.2fcm' % (width
, )
1978 el3
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
1980 el4
= SubElement(el3
, 'draw:text-box', attrib
=attrib
)
1982 'text:style-name': self
.rststyle('caption'),
1984 el5
= SubElement(el4
, 'text:p', attrib
=attrib
)
1985 ## if caption is not None:
1986 ## el3.tail = caption
1987 return el3
, el5
, caption
1989 def generate_image(self
, node
, source
, destination
, current_element
,
1990 #ipshell('At generate_image')
1992 scale
, width
, height
= self
.get_image_scale_width_height(node
, source
)
1993 self
.image_style_count
+= 1
1994 style_name
= 'rstframestyle%d' % self
.image_style_count
1997 'style:name': style_name
,
1998 'style:family': 'graphic',
1999 'style:parent-style-name': 'Graphics',
2001 el1
= SubElement(self
.automatic_styles
,
2002 'style:style', attrib
=attrib
, nsdict
=SNSD
)
2005 if 'align' in node
.attributes
:
2006 align
= node
.attributes
['align'].split()
2008 if val
in ('left', 'center', 'right'):
2010 elif val
in ('top', 'middle', 'bottom'):
2012 if frame_attrs
is None:
2014 'style:vertical-pos': 'top',
2015 'style:vertical-rel': 'paragraph',
2016 #'style:horizontal-pos': halign,
2017 #'style:vertical-pos': valign,
2018 'style:horizontal-rel': 'paragraph',
2019 'style:mirror': 'none',
2020 'fo:clip': 'rect(0cm 0cm 0cm 0cm)',
2021 'draw:luminance': '0%',
2022 'draw:contrast': '0%',
2026 'draw:gamma': '100%',
2027 'draw:color-inversion': 'false',
2028 'draw:image-opacity': '100%',
2029 'draw:color-mode': 'standard',
2032 attrib
= frame_attrs
2033 if halign
is not None:
2034 attrib
['style:horizontal-pos'] = halign
2035 if valign
is not None:
2036 attrib
['style:vertical-pos'] = valign
2037 #ipshell('At generate_image')
2038 # If we are inside a table, add a no-wrap style.
2039 if self
.is_in_table(node
):
2040 attrib
['style:wrap'] = 'none'
2041 el2
= SubElement(el1
,
2042 'style:graphic-properties', attrib
=attrib
, nsdict
=SNSD
)
2044 #el = SubElement(current_element, 'text:p',
2045 # attrib={'text:style-name': self.rststyle('textbody')})
2047 'draw:style-name': style_name
,
2048 'draw:name': 'graphics2',
2049 #'text:anchor-type': 'paragraph',
2050 #'svg:width': '%fcm' % (width, ),
2051 #'svg:height': '%fcm' % (height, ),
2052 'draw:z-index': '1',
2054 if isinstance(node
.parent
, nodes
.TextElement
):
2055 attrib
['text:anchor-type'] = 'char'
2057 attrib
['text:anchor-type'] = 'paragraph'
2058 if width
is not None:
2059 attrib
['svg:width'] = '%.2fcm' % (width
, )
2060 if height
is not None:
2061 attrib
['svg:height'] = '%.2fcm' % (height
, )
2062 el1
= SubElement(current_element
, 'draw:frame', attrib
=attrib
)
2063 el2
= SubElement(el1
, 'draw:image', attrib
={
2064 'xlink:href': '%s' % (destination
, ),
2065 'xlink:type': 'simple',
2066 'xlink:show': 'embed',
2067 'xlink:actuate': 'onLoad',
2071 def is_in_table(self
, node
):
2074 if isinstance(node1
, docutils
.nodes
.entry
):
2076 node1
= node1
.parent
2079 def visit_legend(self
, node
):
2080 # Currently, the legend receives *no* special treatment.
2081 #ipshell('At visit_legend')
2084 def depart_legend(self
, node
):
2087 def visit_line(self
, node
):
2088 #ipshell('At visit_line')
2091 def depart_line(self
, node
):
2092 #ipshell('At depart_line')
2095 def visit_line_block(self
, node
):
2096 #ipshell('At visit_line_block')
2098 lines
= s1
.split('\n')
2099 el
= self
.append_p('lineblock', lines
[0])
2102 for line
in lines
[1:]:
2108 el1
= SubElement(el
, 'text:line-break')
2111 def depart_line_block(self
, node
):
2112 #ipshell('At depart_line_block')
2115 def visit_literal(self
, node
):
2116 #ipshell('At visit_literal')
2117 el
= SubElement(self
.current_element
, 'text:span',
2118 attrib
={'text:style-name': self
.rststyle('inlineliteral')})
2119 self
.set_current_element(el
)
2121 def depart_literal(self
, node
):
2122 self
.set_to_parent()
2124 def _calculate_code_block_padding(self
, line
):
2126 matchobj
= SPACES_PATTERN
.match(line
)
2128 pad
= matchobj
.group()
2131 matchobj
= TABS_PATTERN
.match(line
)
2133 pad
= matchobj
.group()
2134 count
= len(pad
) * 8
2137 def _add_syntax_highlighting(self
, insource
, language
):
2138 #print '(_add_syntax_highlighting) using lexer: %s' % (language, )
2139 lexer
= pygments
.lexers
.get_lexer_by_name(language
, stripall
=True)
2140 if language
in ('latex', 'tex'):
2141 fmtr
= OdtPygmentsLaTeXFormatter(lambda name
, parameters
=():
2142 self
.rststyle(name
, parameters
))
2144 fmtr
= OdtPygmentsProgFormatter(lambda name
, parameters
=():
2145 self
.rststyle(name
, parameters
))
2146 outsource
= pygments
.highlight(insource
, lexer
, fmtr
)
2149 def fill_line(self
, line
):
2150 line
= FILL_PAT1
.sub(self
.fill_func1
, line
)
2151 line
= FILL_PAT2
.sub(self
.fill_func2
, line
)
2154 def fill_func1(self
, matchobj
):
2155 spaces
= matchobj
.group(0)
2156 repl
= '<text:s text:c="%d"/>' % (len(spaces
), )
2159 def fill_func2(self
, matchobj
):
2160 spaces
= matchobj
.group(0)
2161 repl
= ' <text:s text:c="%d"/>' % (len(spaces
) - 1, )
2164 def visit_literal_block(self
, node
):
2165 #ipshell('At visit_literal_block')
2166 wrapper1
= '<text:p text:style-name="%s">%%s</text:p>' % (
2167 self
.rststyle('codeblock'), )
2168 source
= node
.astext()
2170 self
.settings
.add_syntax_highlighting
and
2171 node
.get('hilight', False)):
2172 language
= node
.get('language', 'python')
2173 source
= self
._add
_syntax
_highlighting
(source
, language
)
2175 source
= escape_cdata(source
)
2176 lines
= source
.split('\n')
2177 lines1
= ['<wrappertag1 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">']
2180 for my_line
in lines
:
2181 my_line
= self
.fill_line(my_line
)
2182 my_line
= my_line
.replace(" ", "\n")
2183 my_lines
.append(my_line
)
2184 my_lines_str
= '<text:line-break/>'.join(my_lines
)
2185 my_lines_str2
= wrapper1
% (my_lines_str
, )
2186 lines1
.append(my_lines_str2
)
2187 lines1
.append('</wrappertag1>')
2188 s1
= ''.join(lines1
)
2189 if WhichElementTree
!= "lxml":
2190 s1
= s1
.encode("utf-8")
2191 el1
= etree
.fromstring(s1
)
2192 children
= el1
.getchildren()
2193 for child
in children
:
2194 self
.current_element
.append(child
)
2196 def depart_literal_block(self
, node
):
2199 visit_doctest_block
= visit_literal_block
2200 depart_doctest_block
= depart_literal_block
2202 def visit_meta(self
, node
):
2203 #ipshell('At visit_meta')
2204 name
= node
.attributes
.get('name')
2205 content
= node
.attributes
.get('content')
2206 if name
is not None and content
is not None:
2207 self
.meta_dict
[name
] = content
2209 def depart_meta(self
, node
):
2212 def visit_option_list(self
, node
):
2213 table_name
= 'tableoption'
2215 # Generate automatic styles
2216 if not self
.optiontablestyles_generated
:
2217 self
.optiontablestyles_generated
= True
2218 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2219 'style:name': self
.rststyle(table_name
),
2220 'style:family': 'table'}, nsdict
=SNSD
)
2221 el1
= SubElement(el
, 'style:table-properties', attrib
={
2222 'style:width': '17.59cm',
2223 'table:align': 'left',
2224 'style:shadow': 'none'}, nsdict
=SNSD
)
2225 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2226 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'A', )),
2227 'style:family': 'table-column'}, nsdict
=SNSD
)
2228 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2229 'style:column-width': '4.999cm'}, nsdict
=SNSD
)
2230 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2231 'style:name': self
.rststyle('%s.%%c' % table_name
, ( 'B', )),
2232 'style:family': 'table-column'}, nsdict
=SNSD
)
2233 el1
= SubElement(el
, 'style:table-column-properties', attrib
={
2234 'style:column-width': '12.587cm'}, nsdict
=SNSD
)
2235 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2236 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2237 'style:family': 'table-cell'}, nsdict
=SNSD
)
2238 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2239 'fo:background-color': 'transparent',
2240 'fo:padding': '0.097cm',
2241 'fo:border-left': '0.035cm solid #000000',
2242 'fo:border-right': 'none',
2243 'fo:border-top': '0.035cm solid #000000',
2244 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2245 el2
= SubElement(el1
, 'style:background-image', nsdict
=SNSD
)
2246 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2247 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2248 'style:family': 'table-cell'}, nsdict
=SNSD
)
2249 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2250 'fo:padding': '0.097cm',
2251 'fo:border': '0.035cm solid #000000'}, nsdict
=SNSD
)
2252 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2253 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 2, )),
2254 'style:family': 'table-cell'}, nsdict
=SNSD
)
2255 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2256 'fo:padding': '0.097cm',
2257 'fo:border-left': '0.035cm solid #000000',
2258 'fo:border-right': 'none',
2259 'fo:border-top': 'none',
2260 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2261 el
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2262 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 2, )),
2263 'style:family': 'table-cell'}, nsdict
=SNSD
)
2264 el1
= SubElement(el
, 'style:table-cell-properties', attrib
={
2265 'fo:padding': '0.097cm',
2266 'fo:border-left': '0.035cm solid #000000',
2267 'fo:border-right': '0.035cm solid #000000',
2268 'fo:border-top': 'none',
2269 'fo:border-bottom': '0.035cm solid #000000'}, nsdict
=SNSD
)
2271 # Generate table data
2272 el
= self
.append_child('table:table', attrib
={
2273 'table:name': self
.rststyle(table_name
),
2274 'table:style-name': self
.rststyle(table_name
),
2276 el1
= SubElement(el
, 'table:table-column', attrib
={
2277 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'A', ))})
2278 el1
= SubElement(el
, 'table:table-column', attrib
={
2279 'table:style-name': self
.rststyle('%s.%%c' % table_name
, ( 'B', ))})
2280 el1
= SubElement(el
, 'table:table-header-rows')
2281 el2
= SubElement(el1
, 'table:table-row')
2282 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2283 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'A', 1, )),
2284 'office:value-type': 'string'})
2285 el4
= SubElement(el3
, 'text:p', attrib
={
2286 'text:style-name': 'Table_20_Heading'})
2288 el3
= SubElement(el2
, 'table:table-cell', attrib
={
2289 'table:style-name': self
.rststyle('%s.%%c%%d' % table_name
, ( 'B', 1, )),
2290 'office:value-type': 'string'})
2291 el4
= SubElement(el3
, 'text:p', attrib
={
2292 'text:style-name': 'Table_20_Heading'})
2293 el4
.text
= 'Description'
2294 self
.set_current_element(el
)
2296 def depart_option_list(self
, node
):
2297 #self.document.reporter.debug_flag = 0
2298 self
.set_to_parent()
2300 def visit_option_list_item(self
, node
):
2301 el
= self
.append_child('table:table-row')
2302 self
.set_current_element(el
)
2304 def depart_option_list_item(self
, node
):
2305 self
.set_to_parent()
2307 def visit_option_group(self
, node
):
2308 el
= self
.append_child('table:table-cell', attrib
={
2309 'table:style-name': 'Table%d.A2' % self
.table_count
,
2310 'office:value-type': 'string',
2312 self
.set_current_element(el
)
2314 def depart_option_group(self
, node
):
2315 self
.set_to_parent()
2317 def visit_option(self
, node
):
2318 el
= self
.append_child('text:p', attrib
={
2319 'text:style-name': 'Table_20_Contents'})
2320 el
.text
= node
.astext()
2322 def depart_option(self
, node
):
2325 def visit_option_string(self
, node
):
2328 def depart_option_string(self
, node
):
2331 def visit_option_argument(self
, node
):
2332 #ipshell('At visit_option_argument')
2335 def depart_option_argument(self
, node
):
2338 def visit_description(self
, node
):
2339 el
= self
.append_child('table:table-cell', attrib
={
2340 'table:style-name': 'Table%d.B2' % self
.table_count
,
2341 'office:value-type': 'string',
2343 el1
= SubElement(el
, 'text:p', attrib
={
2344 'text:style-name': 'Table_20_Contents'})
2345 el1
.text
= node
.astext()
2346 raise nodes
.SkipChildren()
2348 def depart_description(self
, node
):
2351 def visit_paragraph(self
, node
):
2352 #ipshell('At visit_paragraph')
2353 #self.trace_visit_node(node)
2354 self
.in_paragraph
= True
2356 el
= self
.append_p('header')
2357 elif self
.in_footer
:
2358 el
= self
.append_p('footer')
2360 style_name
= self
.paragraph_style_stack
[-1]
2361 el
= self
.append_child('text:p',
2362 attrib
={'text:style-name': style_name
})
2363 self
.append_pending_ids(el
)
2364 self
.set_current_element(el
)
2366 def depart_paragraph(self
, node
):
2367 #ipshell('At depart_paragraph')
2368 #self.trace_depart_node(node)
2369 self
.in_paragraph
= False
2370 self
.set_to_parent()
2372 self
.header_content
.append(self
.current_element
.getchildren()[-1])
2373 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2374 elif self
.in_footer
:
2375 self
.footer_content
.append(self
.current_element
.getchildren()[-1])
2376 self
.current_element
.remove(self
.current_element
.getchildren()[-1])
2378 def visit_problematic(self
, node
):
2379 #print '(visit_problematic) node: %s' % (node.astext(), )
2382 def depart_problematic(self
, node
):
2385 def visit_raw(self
, node
):
2386 #ipshell('At visit_raw')
2387 if 'format' in node
.attributes
:
2388 formats
= node
.attributes
['format']
2389 formatlist
= formats
.split()
2390 if 'odt' in formatlist
:
2391 rawstr
= node
.astext()
2392 attrstr
= ' '.join(['%s="%s"' % (k
, v
, )
2393 for k
,v
in CONTENT_NAMESPACE_ATTRIB
.items()])
2394 contentstr
= '<stuff %s>%s</stuff>' % (attrstr
, rawstr
, )
2395 if WhichElementTree
!= "lxml":
2396 content
= content
.encode("utf-8")
2397 content
= etree
.fromstring(contentstr
)
2398 elements
= content
.getchildren()
2399 if len(elements
) > 0:
2403 elif self
.in_footer
:
2406 self
.current_element
.append(el1
)
2407 raise nodes
.SkipChildren()
2409 def depart_raw(self
, node
):
2412 elif self
.in_footer
:
2417 def visit_reference(self
, node
):
2418 #self.trace_visit_node(node)
2419 text
= node
.astext()
2420 if self
.settings
.create_links
:
2421 if node
.has_key('refuri'):
2422 href
= node
['refuri']
2423 if ( self
.settings
.cloak_email_addresses
2424 and href
.startswith('mailto:')):
2425 href
= self
.cloak_mailto(href
)
2426 el
= self
.append_child('text:a', attrib
={
2427 'xlink:href': '%s' % href
,
2428 'xlink:type': 'simple',
2430 self
.set_current_element(el
)
2431 elif node
.has_key('refid'):
2432 if self
.settings
.create_links
:
2433 href
= node
['refid']
2434 el
= self
.append_child('text:reference-ref', attrib
={
2435 'text:ref-name': '%s' % href
,
2436 'text:reference-format': 'text',
2439 raise RuntimeError, 'References must have "refuri" or "refid" attribute.'
2440 #print '(visit_reference) href: "%s" text: "%s"' % (href, text, )
2441 if (self
.in_table_of_contents
and
2442 len(node
.children
) >= 1 and
2443 isinstance(node
.children
[0], docutils
.nodes
.generated
)):
2444 node
.remove(node
.children
[0])
2446 def depart_reference(self
, node
):
2447 #self.trace_depart_node(node)
2448 if self
.settings
.create_links
:
2449 if node
.has_key('refuri'):
2450 self
.set_to_parent()
2452 def visit_rubric(self
, node
):
2453 style_name
= self
.rststyle('rubric')
2454 classes
= node
.get('classes')
2459 el
= SubElement(self
.current_element
, 'text:h', attrib
= {
2460 #'text:outline-level': '%d' % section_level,
2461 #'text:style-name': 'Heading_20_%d' % section_level,
2462 'text:style-name': style_name
,
2464 text
= node
.astext()
2465 el
.text
= self
.encode(text
)
2467 def depart_rubric(self
, node
):
2470 def visit_section(self
, node
, move_ids
=1):
2471 #ipshell('At visit_section')
2472 self
.section_level
+= 1
2473 self
.section_count
+= 1
2474 if self
.settings
.create_sections
:
2475 el
= self
.append_child('text:section', attrib
={
2476 'text:name': 'Section%d' % self
.section_count
,
2477 'text:style-name': 'Sect%d' % self
.section_level
,
2479 self
.set_current_element(el
)
2481 def depart_section(self
, node
):
2482 self
.section_level
-= 1
2483 if self
.settings
.create_sections
:
2484 self
.set_to_parent()
2486 def visit_strong(self
, node
):
2487 #ipshell('At visit_strong')
2488 el
= SubElement(self
.current_element
, 'text:span',
2489 attrib
={'text:style-name': self
.rststyle('strong')})
2490 self
.set_current_element(el
)
2492 def depart_strong(self
, node
):
2493 self
.set_to_parent()
2495 def visit_substitution_definition(self
, node
):
2496 #ipshell('At visit_substitution_definition')
2497 raise nodes
.SkipChildren()
2499 def depart_substitution_definition(self
, node
):
2500 #ipshell('At depart_substitution_definition')
2503 def visit_system_message(self
, node
):
2504 #print '(visit_system_message) node: %s' % (node.astext(), )
2507 def depart_system_message(self
, node
):
2510 def visit_table(self
, node
):
2511 #self.trace_visit_node(node)
2512 #ipshell('At visit_table')
2513 self
.table_count
+= 1
2514 table_name
= '%s%%d' % TableStylePrefix
2515 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2516 'style:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2517 'style:family': 'table',
2519 el1_1
= SubElement(el1
, 'style:table-properties', attrib
={
2520 #'style:width': '17.59cm',
2521 'table:align': 'margins',
2523 # We use a single cell style for all cells in this table.
2524 # That's probably not correct, but seems to work.
2525 el2
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2526 'style:name': self
.rststyle('%s.%%c%%d' % table_name
, ( self
.table_count
, 'A', 1, )),
2527 'style:family': 'table-cell',
2529 line_style1
= '0.%03dcm solid #000000' % self
.settings
.table_border_thickness
2530 el2_1
= SubElement(el2
, 'style:table-cell-properties', attrib
={
2531 'fo:padding': '0.049cm',
2532 'fo:border-left': line_style1
,
2533 'fo:border-right': line_style1
,
2534 'fo:border-top': line_style1
,
2535 'fo:border-bottom': line_style1
,
2538 for child
in node
.children
:
2539 if child
.tagname
== 'title':
2540 title
= child
.astext()
2542 if title
is not None:
2543 el3
= self
.append_p('table-title', title
)
2545 #print 'no table title'
2547 el4
= SubElement(self
.current_element
, 'table:table', attrib
={
2548 'table:name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2549 'table:style-name': self
.rststyle('%s' % table_name
, ( self
.table_count
, )),
2551 self
.set_current_element(el4
)
2552 self
.current_table_style
= el1
2553 self
.table_width
= 0
2555 def depart_table(self
, node
):
2556 #self.trace_depart_node(node)
2557 #ipshell('At depart_table')
2558 attribkey
= add_ns('style:width', nsdict
=SNSD
)
2559 attribval
= '%dcm' % self
.table_width
2560 self
.current_table_style
.attrib
[attribkey
] = attribval
2561 self
.set_to_parent()
2563 def visit_tgroup(self
, node
):
2564 #self.trace_visit_node(node)
2565 #ipshell('At visit_tgroup')
2566 self
.column_count
= ord('A') - 1
2568 def depart_tgroup(self
, node
):
2569 #self.trace_depart_node(node)
2572 def visit_colspec(self
, node
):
2573 #self.trace_visit_node(node)
2574 #ipshell('At visit_colspec')
2575 self
.column_count
+= 1
2576 colspec_name
= self
.rststyle('%s%%d.%%s' % TableStylePrefix
, ( self
.table_count
, chr(self
.column_count
), ))
2577 colwidth
= node
['colwidth']
2578 el1
= SubElement(self
.automatic_styles
, 'style:style', attrib
={
2579 'style:name': colspec_name
,
2580 'style:family': 'table-column',
2582 el1_1
= SubElement(el1
, 'style:table-column-properties', attrib
={
2583 'style:column-width': '%dcm' % colwidth
}, nsdict
=SNSD
)
2584 el2
= self
.append_child('table:table-column', attrib
={
2585 'table:style-name': colspec_name
,
2587 self
.table_width
+= colwidth
2589 def depart_colspec(self
, node
):
2590 #self.trace_depart_node(node)
2593 def visit_thead(self
, node
):
2594 #self.trace_visit_node(node)
2595 #ipshell('At visit_thead')
2596 el
= self
.append_child('table:table-header-rows')
2597 self
.set_current_element(el
)
2598 self
.in_thead
= True
2599 self
.paragraph_style_stack
.append('Table_20_Heading')
2601 def depart_thead(self
, node
):
2602 #self.trace_depart_node(node)
2603 self
.set_to_parent()
2604 self
.in_thead
= False
2605 self
.paragraph_style_stack
.pop()
2607 def visit_row(self
, node
):
2608 #self.trace_visit_node(node)
2609 #ipshell('At visit_row')
2610 self
.column_count
= ord('A') - 1
2611 el
= self
.append_child('table:table-row')
2612 self
.set_current_element(el
)
2614 def depart_row(self
, node
):
2615 #self.trace_depart_node(node)
2616 self
.set_to_parent()
2618 def visit_entry(self
, node
):
2619 #self.trace_visit_node(node)
2620 #ipshell('At visit_entry')
2621 self
.column_count
+= 1
2622 cellspec_name
= self
.rststyle('%s%%d.%%c%%d' % TableStylePrefix
, ( self
.table_count
, 'A', 1, ))
2624 'table:style-name': cellspec_name
,
2625 'office:value-type': 'string',
2627 morecols
= node
.get('morecols', 0)
2629 attrib
['table:number-columns-spanned'] = '%d' % (morecols
+ 1,)
2630 self
.column_count
+= morecols
2631 el1
= self
.append_child('table:table-cell', attrib
=attrib
)
2632 self
.set_current_element(el1
)
2634 def depart_entry(self
, node
):
2635 #self.trace_depart_node(node)
2636 self
.set_to_parent()
2638 def visit_tbody(self
, node
):
2639 #self.trace_visit_node(node)
2640 #ipshell('At visit_')
2643 def depart_tbody(self
, node
):
2644 #self.trace_depart_node(node)
2647 def visit_target(self
, node
):
2649 # I don't know how to implement targets in ODF.
2650 # How do we create a target in oowriter? A cross-reference?
2651 if not (node
.has_key('refuri') or node
.has_key('refid')
2652 or node
.has_key('refname')):
2657 def depart_target(self
, node
):
2660 def visit_title(self
, node
, move_ids
=1, title_type
='title'):
2661 #ipshell('At visit_title')
2662 if isinstance(node
.parent
, docutils
.nodes
.section
):
2663 section_level
= self
.section_level
2664 if section_level
> 7:
2665 print 'Warning: Heading/section levels greater than 7 not supported.'
2666 print ' Reducing to heading level 7 for heading:'
2667 print ' "%s"' % node
.astext()
2669 el1
= self
.append_child('text:h', attrib
= {
2670 'text:outline-level': '%d' % section_level
,
2671 #'text:style-name': 'Heading_20_%d' % section_level,
2672 'text:style-name': self
.rststyle('heading%d', (section_level
, )),
2674 self
.append_pending_ids(el1
)
2675 self
.set_current_element(el1
)
2676 elif isinstance(node
.parent
, docutils
.nodes
.document
):
2677 # text = self.settings.title
2679 # text = node.astext()
2680 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2681 'text:style-name': self
.rststyle(title_type
),
2683 self
.append_pending_ids(el1
)
2684 text
= node
.astext()
2686 self
.found_doc_title
= True
2687 self
.set_current_element(el1
)
2689 def depart_title(self
, node
):
2690 if (isinstance(node
.parent
, docutils
.nodes
.section
) or
2691 isinstance(node
.parent
, docutils
.nodes
.document
)):
2692 self
.set_to_parent()
2694 def visit_subtitle(self
, node
, move_ids
=1):
2695 self
.visit_title(node
, move_ids
, title_type
='subtitle')
2697 def depart_subtitle(self
, node
):
2698 self
.depart_title(node
)
2700 def visit_title_reference(self
, node
):
2701 #ipshell('At visit_title_reference')
2702 el
= self
.append_child('text:span', attrib
={
2703 'text:style-name': self
.rststyle('quotation')})
2704 el
.text
= self
.encode(node
.astext())
2706 def depart_title_reference(self
, node
):
2709 def visit_topic(self
, node
):
2710 #ipshell('At visit_topic')
2711 if 'classes' in node
.attributes
:
2712 if 'contents' in node
.attributes
['classes']:
2713 el
= self
.append_p('horizontalline')
2714 el
= self
.append_p('centeredtextbody')
2715 el1
= SubElement(el
, 'text:span',
2716 attrib
={'text:style-name': self
.rststyle('strong')})
2717 el1
.text
= 'Contents'
2718 self
.in_table_of_contents
= True
2719 elif 'abstract' in node
.attributes
['classes']:
2720 el
= self
.append_p('horizontalline')
2721 el
= self
.append_p('centeredtextbody')
2722 el1
= SubElement(el
, 'text:span',
2723 attrib
={'text:style-name': self
.rststyle('strong')})
2724 el1
.text
= 'Abstract'
2726 def depart_topic(self
, node
):
2727 #ipshell('At depart_topic')
2728 if 'classes' in node
.attributes
:
2729 if 'contents' in node
.attributes
['classes']:
2730 el
= self
.append_p('horizontalline')
2731 self
.in_table_of_contents
= False
2733 def visit_transition(self
, node
):
2734 el
= self
.append_p('horizontalline')
2736 def depart_transition(self
, node
):
2742 def visit_warning(self
, node
):
2743 self
.generate_admonition(node
, 'warning')
2745 def depart_warning(self
, node
):
2746 self
.paragraph_style_stack
.pop()
2748 def visit_attention(self
, node
):
2749 self
.generate_admonition(node
, 'attention')
2751 depart_attention
= depart_warning
2753 def visit_caution(self
, node
):
2754 self
.generate_admonition(node
, 'caution')
2756 depart_caution
= depart_warning
2758 def visit_danger(self
, node
):
2759 self
.generate_admonition(node
, 'danger')
2761 depart_danger
= depart_warning
2763 def visit_error(self
, node
):
2764 self
.generate_admonition(node
, 'error')
2766 depart_error
= depart_warning
2768 def visit_hint(self
, node
):
2769 self
.generate_admonition(node
, 'hint')
2771 depart_hint
= depart_warning
2773 def visit_important(self
, node
):
2774 self
.generate_admonition(node
, 'important')
2776 depart_important
= depart_warning
2778 def visit_note(self
, node
):
2779 self
.generate_admonition(node
, 'note')
2781 depart_note
= depart_warning
2783 def visit_tip(self
, node
):
2784 self
.generate_admonition(node
, 'tip')
2786 depart_tip
= depart_warning
2788 def visit_admonition(self
, node
):
2789 #import pdb; pdb.set_trace()
2791 for child
in node
.children
:
2792 if child
.tagname
== 'title':
2793 title
= child
.astext()
2795 classes1
= node
.get('classes')
2798 self
.generate_admonition(node
, 'generic', title
)
2800 depart_admonition
= depart_warning
2802 def generate_admonition(self
, node
, label
, title
=None):
2803 el1
= SubElement(self
.current_element
, 'text:p', attrib
= {
2804 'text:style-name': self
.rststyle('admon-%s-hdr', ( label
, )),
2809 el1
.text
= '%s!' % (label
.capitalize(), )
2810 s1
= self
.rststyle('admon-%s-body', ( label
, ))
2811 self
.paragraph_style_stack
.append(s1
)
2814 # Roles (e.g. subscript, superscript, strong, ...
2816 def visit_subscript(self
, node
):
2817 el
= self
.append_child('text:span', attrib
={
2818 'text:style-name': 'rststyle-subscript',
2820 self
.set_current_element(el
)
2822 def depart_subscript(self
, node
):
2823 self
.set_to_parent()
2825 def visit_superscript(self
, node
):
2826 el
= self
.append_child('text:span', attrib
={
2827 'text:style-name': 'rststyle-superscript',
2829 self
.set_current_element(el
)
2831 def depart_superscript(self
, node
):
2832 self
.set_to_parent()
2835 # Use an own reader to modify transformations done.
2836 class Reader(standalone
.Reader
):
2838 def get_transforms(self
):
2839 default
= standalone
.Reader
.get_transforms(self
)
2840 if self
.settings
.create_links
:
2844 if i
is not references
.DanglingReferences
]