2 # -*- coding: utf-8 -*-
3 # Copyright 2008-2009 Matt Harrison
4 # Licensed under Apache License, Version 2.0 (current)
9 from docutils
import io
, writers
, nodes
10 from docutils
.readers
import standalone
11 from docutils
.core
import Publisher
, default_description
, \
13 from docutils
.parsers
import rst
15 import odplib
.preso
as preso
26 fuchsia
='#ff00ff', # FIX
49 class SyntaxHighlightCodeBlock(rst
.Directive
):
50 required_arguments
= 1
51 optional_arguments
= 0
54 # See visit_literal_block for code that processes the node
57 language
= self
.arguments
[0]
58 code_block
= nodes
.literal_block(classes
=["code-block", language
],
61 content
= '\n'.join(lines
)
62 text_node
= nodes
.Text(content
)
63 code_block
.append(text_node
)
64 # Mark this node for high-lighting so that visit_literal_block
65 # will be able to hight-light those produced here and
66 # *not* high-light regular literal blocks (:: in reST).
67 code_block
['hilight'] = True
70 rst
.directives
.register_directive('code-block', SyntaxHighlightCodeBlock
)
72 class ImportNode(nodes
.General
, nodes
.Inline
, nodes
.Element
): pass
74 class ImportSlideBlock(rst
.Directive
):
75 required_arguments
= 2
76 optional_arguments
= 0
78 node_class
= ImportNode
80 # See visit_literal_block for code that processes the node
83 odp_path
= self
.arguments
[0]
84 page_num
= self
.arguments
[1]
85 node
= ImportNode(odp_path
=odp_path
, page_num
=page_num
)
88 rst
.directives
.register_directive('importslide', ImportSlideBlock
)
90 class Writer(writers
.Writer
):
92 'ODP Specific Options', # option group title
94 ( # options (help string, list of options, dictions of OptionParser.add_option dicts)
95 ('Specify a template (.otp) to use for styling',
98 'dest': 'template_file'}),
99 ('Specify a monospace font to use ("Courier New" default)',
102 'dest': 'mono_font'}),
103 ('Specify a normal font to use ("Arial" default)',
107 ('Specify pages to export (2,3,9-10)',
108 ['--pages-to-output'],
110 'dest': 'pages_to_output'})
114 writers
.Writer
.__init
__(self
)
115 self
.translator_class
= ODPTranslator
118 self
.visitor
= self
.translator_class(self
.document
)
119 self
.document
.walkabout(self
.visitor
)
120 self
.parts
['whole'] = self
.visitor
.get_whole()
121 self
.output
= self
.parts
['whole']
122 self
.parts
['encoding'] = self
.document
.settings
.output_encoding
123 self
.parts
['version'] = docutils
.__version
__
125 class ODPTranslator(nodes
.GenericNodeVisitor
):
126 def __init__(self
, document
):
127 nodes
.GenericNodeVisitor
.__init
__(self
, document
)
128 self
.settings
= document
.settings
129 self
.preso
= preso
.Preso()
131 if self
.settings
.pages_to_output
:
132 self
.preso
.limit_pages
= num_string_to_list(self
.settings
.pages_to_output
)
134 if self
.settings
.mono_font
:
135 preso
.MONO_FONT
= self
.settings
.mono_font
137 if self
.settings
.font
:
138 preso
.NORMAL_FONT
= self
.settings
.font
140 self
.in_node
= {} # map of tagname to True if we are in/under this
144 # state we keep track of
145 self
.cur_slide
= None
146 self
.bullet_list
= None
147 self
.bullet_depth
= 0
150 def _init_slide(self
, force
=False):
151 if force
or self
.cur_slide
is None:
153 self
.cur_slide
= self
.preso
.add_slide()
155 def at(self
, nodename
):
157 shortcut for at/under this node
159 return self
.in_node
.get(nodename
, False)
162 return self
.preso
.get_data(self
.settings
.template_file
)
164 def dispatch_visit(self
, node
):
165 # Easier just to throw nodes I'm in in a dict, than keeping
167 count
= self
.in_node
.setdefault(node
.tagname
, 0)
168 self
.in_node
[node
.tagname
] += 1
169 nodes
.GenericNodeVisitor
.dispatch_visit(self
, node
)
171 def dispatch_departure(self
, node
):
172 self
.in_node
[node
.tagname
] -= 1
173 nodes
.GenericNodeVisitor
.dispatch_departure(self
, node
)
175 def default_visit(self
, node
):
176 if self
.settings
.report_level
>= 3:
177 print "ERR! NODE", node
, node
.tagname
178 raise NotImplementedError('node is %r, tag is %s' % (node
, node
.tagname
))
181 def default_departure(self
, node
):
182 if self
.settings
.report_level
>= 3:
183 print "NODE", node
, node
.tagname
184 raise NotImplementedError
187 def _dumb_visit(self
, node
):
189 _dumb_depart
= _dumb_visit
192 def visit_document(self
, node
):
193 if self
.settings
.report_level
>= 3:
196 depart_document
= _dumb_depart
198 def visit_title(self
, node
):
199 if self
.at('section') < 2:
203 elif self
.at('sidebar'):
205 elif self
.at('section') < 2:
206 self
.cur_slide
.add_title_frame()
208 def depart_title(self
, node
):
211 elif self
.at('sidebar'):
213 elif self
.at('section') < 2:
214 # not in a title element anymore
215 self
.cur_slide
.cur_element
= None
220 def visit_Text(self
, node
):
221 if self
.bullet_list
and not self
.at('handout'): # !!!need to deal w/ bullets in handout
222 self
.bullet_list
.write(node
.astext())
223 elif self
.at('footer'):
224 self
.footer
.write(node
.astext())
225 elif self
.at('comment'):
227 elif self
.at('topic'):
229 elif self
.at('literal_block'):
231 elif self
.at('reference'):
232 # FirstClown - if we have link text, we need to make sure the text
233 # doesn't get any styles applied or it sometimes doesn't show.
234 self
.cur_slide
.write(node
.astext(), add_p_style
=False, add_t_style
=False)
235 elif self
.at('doctest_block'):
237 elif self
.at('field_name'):
240 self
.cur_slide
.write(node
.astext())
241 depart_Text
= _dumb_depart
243 def _push_handout(self
, classes
):
244 if 'handout' in classes
:
245 self
.in_node
['handout'] = True
246 self
.cur_slide
.push_element()
247 if not self
.cur_slide
.notes_frame
:
248 self
.cur_slide
.add_notes_frame()
250 self
.cur_slide
.cur_element
= self
.cur_slide
.notes_frame
251 self
.cur_slide
.insert_line_break
+= 1
252 self
.cur_slide
.insert_line_breaks()
255 def visit_paragraph(self
, node
):
256 classes
= node
.attributes
.get('classes', [])
257 self
._push
_handout
(classes
)
259 ## if 'center' in classes:
260 ## attribs = {'fo:text-align':'center',
261 ## 'fo:margin-left':'1.2cm',
262 ## 'fo:margin-right':'-.9cm',
264 ## style = preso.ParagraphStyle(**attribs)
265 ## self.cur_slide.push_style(style)
267 ## elif 'left' in classes:
269 ## elif 'right' in classes:
270 ## attribs = {'fo:text-align':'end',
272 ## style = preso.ParagraphStyle(**attribs)
273 ## self.cur_slide.push_style(style)
274 p_attribs
= self
._get
_para
_attribs
(node
)
276 self
.cur_slide
.push_style(preso
.ParagraphStyle(**p_attribs
))
279 attribs
= self
._get
_text
_attribs
(node
)
281 style
= preso
.TextStyle(**attribs
)
282 self
.cur_slide
.push_style(style
)
286 elif self
.at('topic'):
288 elif self
.at('block_quote'):
289 return # block quote adds paragraph style
290 elif self
.at('doctest_block'):
294 def depart_paragraph(self
, node
):
296 if not self
.at('list_item'):
297 self
.depart_line(node
)
299 classes
= node
.attributes
.get('classes', [])
300 if 'center' in classes
or 'right' in classes
:
301 self
.cur_slide
.pop_node()
302 if 'handout' in classes
:
303 self
.in_node
['handout'] = False
304 self
.cur_slide
.pop_element()
306 if self
._get
_text
_attribs
(node
):
308 self
.cur_slide
.pop_node()
309 self
.cur_slide
.pop_style()
311 elif self
.at('topic'):
313 elif self
.at('block_quote'):
314 return # block quote adds paragraph style
316 self
.cur_slide
.parent_of('text:p')
318 visit_definition
= _dumb_visit
319 depart_definition
= _dumb_depart
321 def visit_bullet_list(self
, node
):
324 p_attribs
= self
._get
_para
_attribs
(node
)
326 self
.cur_slide
.push_style(preso
.ParagraphStyle(**p_attribs
))
328 attribs
= self
._get
_text
_attribs
(node
)
330 style
= preso
.TextStyle(**attribs
)
331 self
.cur_slide
.push_style(style
)
333 classes
= node
.attributes
.get('classes', [])
334 if 'handout' in classes
:
335 self
._push
_handout
(classes
)
337 self
.bullet_depth
+= 1
338 if not self
.bullet_list
:
340 self
.bullet_list
= preso
.OutlineList(self
.cur_slide
)
341 self
.cur_slide
.add_list(self
.bullet_list
)
344 self
.bullet_list
.indent()
345 if 'incremental' in node
.attributes
.get('classes', []):
346 self
.in_node
['incremental'] = True
348 def depart_bullet_list(self
, node
):
352 classes
= node
.attributes
.get('classes', [])
353 if 'handout' in classes
:
354 self
.in_node
['handout'] = False
355 self
.cur_slide
.pop_element()
357 self
.bullet_depth
-= 1
358 if self
.bullet_depth
== 0:
360 self
.bullet_list
= None
361 self
.cur_slide
.pop_element()
362 self
.cur_slide
.insert_line_break
+= 1
364 self
.bullet_list
.dedent()
365 if 'incremental' in node
.attributes
.get('classes', []):
366 self
.in_node
['incremental'] = False
368 visit_definition_list
= visit_bullet_list
369 depart_definition_list
= depart_bullet_list
372 def visit_list_item(self
, node
):
375 if self
.at('incremental'):
376 self
.cur_slide
.start_animation(preso
.Animation())
377 self
.bullet_list
.new_item()
379 def depart_list_item(self
, node
):
382 if self
.at('incremental'):
383 self
.cur_slide
.end_animation()
385 visit_definition_list_item
= visit_list_item
386 depart_definition_list_item
= depart_list_item
389 visit_decoration
= _dumb_visit
390 depart_decoration
= _dumb_depart
392 def visit_footer(self
, node
):
393 self
.footer
= preso
.Footer(self
.cur_slide
)
395 def depart_footer(self
, node
):
396 self
.preso
.add_footer(self
.footer
)
399 visit_docinfo
= _dumb_visit
400 depart_docinfo
= _dumb_depart
402 # bibliographic elements
403 def visit_author(self
, node
):
404 self
.visit_attribution(node
)
406 def depart_author(self
, node
):
407 self
.depart_line(node
) # add new-line
408 self
.depart_attribution(node
)
410 visit_copyright
= visit_author
411 depart_copyright
= depart_author
413 def visit_date(self
, node
):
414 self
.visit_attribution(node
)
416 def depart_date(self
, node
):
417 self
.depart_line(node
) # add new-line
418 self
.depart_attribution(node
)
420 def visit_field(self
, node
):
423 def depart_field(self
, node
):
426 def visit_field_name(self
, node
):
427 pass # maybe put this somewhere
429 def depart_field_name(self
, node
):
432 def visit_field_body(self
, node
):
433 self
.visit_attribution(node
)
435 def depart_field_body(self
, node
):
436 self
.depart_attribution(node
)
438 def visit_comment(self
, node
):
440 def depart_comment(self
, node
):
443 visit_topic
= _dumb_visit
444 depart_topic
= _dumb_depart
446 def visit_reference(self
, node
):
449 <text:p text:style-name="P4">
450 <text:a xlink:href="http://www.yahoo.com/">Yahoo corp</text:a>
454 if node
.has_key('refid'):
456 elif self
.at('topic'):
458 elif self
.at('field_body'):
459 self
.visit_attribution(node
)
460 self
.cur_slide
.push_pending_node('text:a', {'xlink:href': '%s' % node
['refuri'],
461 'xlink:type': 'simple'})
462 self
.cur_slide
.write(node
.astext())
465 # need to hack a bit, since .write usually inserts text:p and text:span
466 if not self
.cur_slide
.cur_element
or not self
.cur_slide
.cur_element
._in
_p
():
467 #self.cur_slide.add_node('text:p', {})
469 # we write an empty string since write() creates the paragraph
470 # we need, with the style needed to reset from a possible Title
471 # P0 style. This was most apparent when a link was first word
472 # in a section after a title.
473 self
.cur_slide
.write("")
475 self
.cur_slide
.add_node('text:a', attrib
={'xlink:href': '%s' % node
['refuri'],
476 'xlink:type': 'simple'})
478 def depart_reference(self
, node
):
479 if node
.has_key('refid'):
481 elif self
.at('topic'):
483 elif self
.at('field_body'):
484 self
.depart_attribution(node
)
485 self
.cur_slide
.parent_of('text:a')
487 self
.cur_slide
.parent_of('text:a')
489 def visit_target(self
,node
):
490 # Skip the target element since the <reference> of the target is
491 # responsible for writing out the content
494 def depart_target(self
, node
):
497 def visit_container(self
, node
):
498 classes
= node
.attributes
.get('classes', [])
499 self
._push
_handout
(classes
)
501 def depart_container(self
, node
):
502 if self
.in_node
.get('handout', False):
503 self
.cur_slide
.pop_element()
504 self
.in_node
['handout'] = False
506 visit_substitution_definition
= _dumb_visit
507 depart_substitution_definition
= _dumb_depart
509 def visit_section(self
, node
):
511 # first page has no section
512 if self
.at('section') < 2:
513 # don't create slide for subsections
514 self
._init
_slide
(force
=True)
516 def depart_section(self
, node
):
517 if self
.at('section') < 1:
520 def visit_transition(self
, node
):
521 # hack to have titleless slides (transitions come in between sections)
523 self
._init
_slide
(force
=True)
525 depart_transition
= _dumb_depart
527 def visit_literal(self
, node
):
528 style
= preso
.TextStyle(**{
529 'fo:font-family':preso
.MONO_FONT
,
530 'style:font-family-generic':"swiss",
531 'style:font-pitch':"fixed"})
532 self
.cur_slide
.push_style(style
)
534 def depart_literal(self
, node
):
535 # pop off the text:span
536 self
.cur_slide
.pop_style()
537 self
.cur_slide
.pop_node()
539 def visit_inline(self
,node
):
540 attribs
= self
._get
_text
_attribs
(node
)
542 style
= preso
.TextStyle(**attribs
)
544 self
.cur_slide
.push_style(style
)
545 if 'incremental' in node
.attributes
.get('classes', []):
546 self
.cur_slide
.start_animation(preso
.Animation())
548 def depart_inline(self
, node
):
549 # pop off the text:span
550 attribs
= self
._get
_text
_attribs
(node
)
552 self
.cur_slide
.pop_style()
553 self
.cur_slide
.pop_node()
554 if 'incremental' in node
.attributes
.get('classes', []):
555 self
.cur_slide
.end_animation()
557 def visit_emphasis(self
, node
):
558 attribs
= {'fo:font-style':'italic'}
559 style
= preso
.TextStyle(**attribs
)
560 self
.cur_slide
.push_style(style
)
562 def depart_emphasis(self
, node
):
563 # pop off the text:span
564 self
.cur_slide
.pop_style()
565 self
.cur_slide
.pop_node()
567 visit_title_reference
= visit_emphasis
568 depart_title_reference
= depart_emphasis
571 def visit_strong(self
, node
):
572 attribs
= {'fo:font-weight':'bold'}
573 style
= preso
.TextStyle(**attribs
)
574 self
.cur_slide
.push_style(style
)
576 def depart_strong(self
, node
):
577 # pop off the text:span
578 self
.cur_slide
.pop_style()
579 self
.cur_slide
.pop_node()
581 visit_term
= visit_strong
582 depart_term
= depart_strong
584 def visit_superscript(self
, node
):
585 attribs
= {'style:text-position':'super 58%'}
586 style
= preso
.TextStyle(**attribs
)
587 self
.cur_slide
.push_style(style
)
589 def depart_superscript(self
, node
):
590 # pop off the text:span
591 self
.cur_slide
.pop_style()
592 self
.cur_slide
.pop_node()
594 def visit_subscript(self
, node
):
595 attribs
= {'style:text-position':'sub 58%'}
596 style
= preso
.TextStyle(**attribs
)
597 self
.cur_slide
.push_style(style
)
599 def depart_subscript(self
, node
):
600 # pop off the text:span
601 self
.cur_slide
.pop_style()
602 self
.cur_slide
.pop_node()
604 def visit_block_quote(self
, node
):
605 attribs
= {'fo:text-align':'start',
606 'fo:margin-left':'1.2cm',
607 'fo:margin-right':'-.9cm',
609 style
= preso
.ParagraphStyle(**attribs
)
610 self
.cur_slide
.push_style(style
)
612 def depart_block_quote(self
, node
):
614 self
.cur_slide
.pop_style()
615 self
.cur_slide
.pop_node()
617 def visit_attribution(self
, node
):
619 attribs
= {'fo:text-align':'end',
620 'fo:margin-right':'.9cm'
622 style
= preso
.ParagraphStyle(**attribs
)
623 self
.cur_slide
.push_style(style
)
626 style
= preso
.TextStyle(**{'fo:font-size':S5_SIZES
['small'],
627 'fo:font-style':'italic'})
628 self
.cur_slide
.push_style(style
)
630 def depart_attribution(self
, node
):
631 # pop off the text:p and text:span
632 self
.cur_slide
.pop_style()
633 self
.cur_slide
.pop_node()
634 self
.cur_slide
.pop_style()
635 self
.cur_slide
.pop_node()
637 def visit_line_block(self
, node
):
638 #jump out of current paragraph
639 self
.cur_slide
.parent_of('text:p')
641 p_attribs
= self
._get
_para
_attribs
(node
)
643 self
.cur_slide
.push_style(preso
.ParagraphStyle(**p_attribs
))
646 P_attribs
= {'fo:text-align':'end',
647 'fo:margin-right':'.9cm'
649 style
= preso
.ParagraphStyle(**p_attribs
)
650 self
.cur_slide
.push_style(preso
.ParagraphStyle(**p_attribs
))
651 #self.cur_slide.push_style(style)
652 attribs
= self
._get
_text
_attribs
(node
)
654 style
= preso
.TextStyle(**attribs
)
655 self
.cur_slide
.push_style(style
)
657 def depart_line_block(self
, node
):
658 attribs
= self
._get
_text
_attribs
(node
)
660 #self.cur_slide.pop_style()
661 self
.cur_slide
.pop_node()
663 self
.cur_slide
.pop_style()
664 self
.cur_slide
.pop_node()
667 def visit_line(self
, node
):
670 def depart_line(self
, node
):
671 self
.cur_slide
.insert_line_break
+= 1
672 self
.cur_slide
.insert_line_breaks()
675 def visit_image(self
, node
):
676 classes
= node
.attributes
.get('classes', [])
678 source
= node
.attributes
['uri']
679 p
= preso
.Picture(os
.path
.abspath(source
), **node
.attributes
)
680 self
.cur_slide
.add_picture(p
)
682 def depart_image(self
, node
):
685 visit_figure
= _dumb_visit
686 depart_figure
= _dumb_depart
688 def visit_caption(self
, node
):
692 def depart_caption(self
, node
):
695 def visit_literal_block(self
, node
):
696 attributes
= node
.attributes
698 style
= preso
.ParagraphStyle(**{'fo:text-align':'start'})
699 self
.cur_slide
.push_style(style
)
701 attribs
= self
._get
_text
_attribs
(node
)
703 style
= preso
.TextStyle(**attribs
)
704 self
.cur_slide
.push_style(style
)
706 if attributes
['classes'] and 'code-block' in attributes
['classes']:
707 node_input
= node
.astext()
708 language
= node
.attributes
['language']
709 self
.cur_slide
.add_code(node_input
, language
)
710 # insert a new line after
711 self
.cur_slide
.insert_line_break
+= 1
714 style
= preso
.TextStyle(**{
715 'fo:font-family':preso
.MONO_FONT
,
716 'style:font-family-generic':"swiss",
717 'style:font-pitch':"fixed"})
718 self
.cur_slide
.push_style(style
)
719 node_input
= node
.astext()
720 chunks
= node_input
.split('\n')
722 self
.cur_slide
.write(chunk
)
723 self
.cur_slide
.insert_line_break
+= 1
724 self
.cur_slide
.insert_line_breaks()
728 def depart_literal_block(self
, node
):
730 self
.cur_slide
.pop_style()
731 self
.cur_slide
.pop_node()
732 attributes
= node
.attributes
733 if attributes
['classes'] and 'code-block' in attributes
['classes']:
736 self
.cur_slide
.pop_style()
737 self
.cur_slide
.pop_node()
739 def visit_footnote(self
, node
):
740 # shift to bottom of page?
743 def depart_footnote(self
, node
):
746 def visit_footnote_reference(self
, node
):
747 self
.visit_superscript(node
)
748 self
.cur_slide
.write('[')
750 def depart_footnote_reference(self
, node
):
751 self
.cur_slide
.write(']')
752 self
.depart_superscript(node
)
755 def visit_label(self
, node
):
757 if self
.at('footnote'):
758 self
.cur_slide
.write('[')
760 def depart_label(self
, node
):
761 if self
.at('footnote'):
762 self
.cur_slide
.write('] ')
765 def visit_enumerated_list(self
, node
):
768 self
.bullet_depth
+= 1
769 if not self
.bullet_list
:
770 self
.bullet_list
= preso
.NumberList(self
.cur_slide
)
771 self
.cur_slide
.add_list(self
.bullet_list
)
774 self
.bullet_list
.indent()
775 if 'incremental' in node
.attributes
.get('classes', []):
776 self
.in_node
['incremental'] = True
779 def depart_enumerated_list(self
, node
):
780 self
.depart_bullet_list(node
)
782 def visit_doctest_block(self
, node
):
783 node_input
= node
.astext()
785 self
.cur_slide
.add_code(node_input
, language
)
786 # insert a new line after
787 self
.cur_slide
.insert_line_break
+= 1
790 def depart_doctest_block(self
, node
):
793 def visit_importslide(self
, node
):
797 def visit_table(self
, node
):
798 table
= preso
.TableFrame(self
.cur_slide
)
799 self
.cur_slide
.add_table(table
)
801 def depart_table(self
, node
):
802 self
.cur_slide
.pop_element()
804 def visit_row(self
, node
):
805 self
.cur_slide
.cur_element
.add_row()
807 def depart_row(self
, node
):
810 def visit_entry(self
, node
):
811 self
.cur_slide
.cur_element
.add_cell()
813 def depart_entry(self
, node
):
816 visit_tgroup
= _dumb_visit
817 depart_tgroup
= _dumb_depart
819 visit_colspec
= _dumb_visit
820 depart_colspec
= _dumb_depart
822 visit_thead
= _dumb_visit
823 depart_thead
= _dumb_depart
825 visit_tbody
= _dumb_visit
826 depart_tbody
= _dumb_depart
828 def visit_hint(self
, node
):
829 return self
._visit
_hint
(node
, 'Hint')
832 def _visit_hint(self
, node
, name
):
833 if self
.cur_slide
.text_frames
:
834 # should adjust width of other frame
835 node
= self
.cur_slide
.text_frames
[-1].get_node()
836 node
.attrib
['svg:width'] = '12.296cm'
838 self
.cur_slide
.add_text_frame()
840 self
.cur_slide
.push_element()
841 #put the hint on the right side
843 'presentation:style-name':'pr2',
844 'draw:layer':'layout',
845 'svg:width':'12.296cm',
846 'svg:height':'13.86cm',
849 'presentation:class':'subtitle'
851 self
.cur_slide
.add_text_frame(attrib
)
852 self
.cur_slide
.write(name
)
853 self
.cur_slide
.insert_line_break
= 2
854 self
.cur_slide
.insert_line_breaks()
856 def depart_hint(self
, node
):
857 self
.cur_slide
.pop_element()
859 def visit_sidebar(self
, node
):
860 return self
._visit
_hint
(node
, 'Sidebar')
862 depart_sidebar
= depart_hint
865 def _get_para_attribs(self
, node
):
866 classes
= node
.attributes
.get('classes', [])
870 attribs
.update({'fo:text-align':'center',
871 'fo:margin-left':'1.2cm',
872 'fo:margin-right':'.9cm',
875 attribs
.update({'fo:text-align':'end',
876 'fo:margin-right':'.9cm'
879 attribs
.update({'fo:text-align':'start',
880 'fo:margin-left':'1.2cm',
881 'fo:margin-right':'5.9cm',
887 def _get_text_attribs(self
, node
):
888 classes
= node
.attributes
.get('classes', [])
892 attribs
['fo:color'] = S5_COLORS
[c
]
894 attribs
['fo:font-size'] = S5_SIZES
[c
]
897 def num_string_to_list(numstr
):
899 >>> num_string_to_list('2,5-7')
901 >>> num_string_to_list('1')
907 comma_delim
= numstr
.split(',')
908 for part
in comma_delim
:
910 start
, end
= [int(x
) for x
in part
.split('-')]
911 for num
in range(start
, end
+1):
914 nums
.append(int(part
))
916 start
, end
= [int(x
) for x
in numstr
.split('-')]
917 for num
in range(start
, end
+1):
920 nums
.append(int(numstr
))
924 class BinaryFileOutput(io
.FileOutput
):
926 A version of docutils.io.FileOutput which writes to a binary file.
930 self
.destination
= open(self
.destination_path
, 'wb')
932 except IOError, error
:
933 if not self
.handle_io_errors
:
935 print >>sys
.stderr
, '%s: %s' % (error
.__class
__.__name
__,
937 print >>sys
.stderr
, ('Unable to open destination file for writing '
938 '(%r). Exiting.' % self
.destination_path
)
945 reader
= standalone
.Reader()
946 reader_name
= 'standalone'
948 writer_name
= 'pseudoxml'
950 parser_name
= 'restructuredtext'
953 settings_overrides
= None
954 config_section
= None
955 enable_exit_status
= 1
956 usage
= default_usage
957 publisher
= Publisher(reader
, parser
, writer
, settings
,
958 destination_class
=BinaryFileOutput
)
959 publisher
.set_components(reader_name
, parser_name
, writer_name
)
960 description
= ('Generates OpenDocument/OpenOffice/ODF slides from '
961 'standalone reStructuredText sources. ' + default_description
)
963 output
= publisher
.publish(argv
, usage
, description
,
964 settings_spec
, settings_overrides
,
965 config_section
=config_section
,
966 enable_exit_status
=enable_exit_status
)
973 if __name__
== "__main__":
974 if '--doctest' in sys
.argv
:
977 sys
.exit(main(sys
.argv
) or 0)