buildframework/helium/external/python/lib/2.5/docutils-0.5-py2.5.egg/docutils/writers/s5_html/__init__.py
changeset 179 d8ac696cc51f
parent 1 be27ed110b50
child 180 e02a83d4c571
child 592 3215c239276a
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
     1 # $Id: __init__.py 4883 2007-01-16 01:51:28Z wiemann $
       
     2 # Authors: Chris Liechti <cliechti@gmx.net>;
       
     3 #          David Goodger <goodger@python.org>
       
     4 # Copyright: This module has been placed in the public domain.
       
     5 
       
     6 """
       
     7 S5/HTML Slideshow Writer.
       
     8 """
       
     9 
       
    10 __docformat__ = 'reStructuredText'
       
    11 
       
    12 
       
    13 import sys
       
    14 import os
       
    15 import re
       
    16 import docutils
       
    17 from docutils import frontend, nodes, utils
       
    18 from docutils.writers import html4css1
       
    19 from docutils.parsers.rst import directives
       
    20 
       
    21 themes_dir_path = utils.relative_path(
       
    22     os.path.join(os.getcwd(), 'dummy'),
       
    23     os.path.join(os.path.dirname(__file__), 'themes'))
       
    24 
       
    25 def find_theme(name):
       
    26     # Where else to look for a theme?
       
    27     # Check working dir?  Destination dir?  Config dir?  Plugins dir?
       
    28     path = os.path.join(themes_dir_path, name)
       
    29     if not os.path.isdir(path):
       
    30         raise docutils.ApplicationError(
       
    31             'Theme directory not found: %r (path: %r)' % (name, path))
       
    32     return path
       
    33 
       
    34 
       
    35 class Writer(html4css1.Writer):
       
    36 
       
    37     settings_spec = html4css1.Writer.settings_spec + (
       
    38         'S5 Slideshow Specific Options',
       
    39         'For the S5/HTML writer, the --no-toc-backlinks option '
       
    40         '(defined in General Docutils Options above) is the default, '
       
    41         'and should not be changed.',
       
    42         (('Specify an installed S5 theme by name.  Overrides --theme-url.  '
       
    43           'The default theme name is "default".  The theme files will be '
       
    44           'copied into a "ui/<theme>" directory, in the same directory as the '
       
    45           'destination file (output HTML).  Note that existing theme files '
       
    46           'will not be overwritten (unless --overwrite-theme-files is used).',
       
    47           ['--theme'],
       
    48           {'default': 'default', 'metavar': '<name>',
       
    49            'overrides': 'theme_url'}),
       
    50          ('Specify an S5 theme URL.  The destination file (output HTML) will '
       
    51           'link to this theme; nothing will be copied.  Overrides --theme.',
       
    52           ['--theme-url'],
       
    53           {'metavar': '<URL>', 'overrides': 'theme'}),
       
    54          ('Allow existing theme files in the ``ui/<theme>`` directory to be '
       
    55           'overwritten.  The default is not to overwrite theme files.',
       
    56           ['--overwrite-theme-files'],
       
    57           {'action': 'store_true', 'validator': frontend.validate_boolean}),
       
    58          ('Keep existing theme files in the ``ui/<theme>`` directory; do not '
       
    59           'overwrite any.  This is the default.',
       
    60           ['--keep-theme-files'],
       
    61           {'dest': 'overwrite_theme_files', 'action': 'store_false'}),
       
    62          ('Set the initial view mode to "slideshow" [default] or "outline".',
       
    63           ['--view-mode'],
       
    64           {'choices': ['slideshow', 'outline'], 'default': 'slideshow',
       
    65            'metavar': '<mode>'}),
       
    66          ('Normally hide the presentation controls in slideshow mode. '
       
    67           'This is the default.',
       
    68           ['--hidden-controls'],
       
    69           {'action': 'store_true', 'default': True,
       
    70            'validator': frontend.validate_boolean}),
       
    71          ('Always show the presentation controls in slideshow mode.  '
       
    72           'The default is to hide the controls.',
       
    73           ['--visible-controls'],
       
    74           {'dest': 'hidden_controls', 'action': 'store_false'}),
       
    75          ('Enable the current slide indicator ("1 / 15").  '
       
    76           'The default is to disable it.',
       
    77           ['--current-slide'],
       
    78           {'action': 'store_true', 'validator': frontend.validate_boolean}),
       
    79          ('Disable the current slide indicator.  This is the default.',
       
    80           ['--no-current-slide'],
       
    81           {'dest': 'current_slide', 'action': 'store_false'}),))
       
    82 
       
    83     settings_default_overrides = {'toc_backlinks': 0}
       
    84 
       
    85     config_section = 's5_html writer'
       
    86     config_section_dependencies = ('writers', 'html4css1 writer')
       
    87 
       
    88     def __init__(self):
       
    89         html4css1.Writer.__init__(self)
       
    90         self.translator_class = S5HTMLTranslator
       
    91 
       
    92 
       
    93 class S5HTMLTranslator(html4css1.HTMLTranslator):
       
    94 
       
    95     s5_stylesheet_template = """\
       
    96 <!-- configuration parameters -->
       
    97 <meta name="defaultView" content="%(view_mode)s" />
       
    98 <meta name="controlVis" content="%(control_visibility)s" />
       
    99 <!-- style sheet links -->
       
   100 <script src="%(path)s/slides.js" type="text/javascript"></script>
       
   101 <link rel="stylesheet" href="%(path)s/slides.css"
       
   102       type="text/css" media="projection" id="slideProj" />
       
   103 <link rel="stylesheet" href="%(path)s/outline.css"
       
   104       type="text/css" media="screen" id="outlineStyle" />
       
   105 <link rel="stylesheet" href="%(path)s/print.css"
       
   106       type="text/css" media="print" id="slidePrint" />
       
   107 <link rel="stylesheet" href="%(path)s/opera.css"
       
   108       type="text/css" media="projection" id="operaFix" />\n"""
       
   109     # The script element must go in front of the link elements to
       
   110     # avoid a flash of unstyled content (FOUC), reproducible with
       
   111     # Firefox.
       
   112 
       
   113     disable_current_slide = """
       
   114 <style type="text/css">
       
   115 #currentSlide {display: none;}
       
   116 </style>\n"""
       
   117 
       
   118     layout_template = """\
       
   119 <div class="layout">
       
   120 <div id="controls"></div>
       
   121 <div id="currentSlide"></div>
       
   122 <div id="header">
       
   123 %(header)s
       
   124 </div>
       
   125 <div id="footer">
       
   126 %(title)s%(footer)s
       
   127 </div>
       
   128 </div>\n"""
       
   129 # <div class="topleft"></div>
       
   130 # <div class="topright"></div>
       
   131 # <div class="bottomleft"></div>
       
   132 # <div class="bottomright"></div>
       
   133 
       
   134     default_theme = 'default'
       
   135     """Name of the default theme."""
       
   136 
       
   137     base_theme_file = '__base__'
       
   138     """Name of the file containing the name of the base theme."""
       
   139 
       
   140     direct_theme_files = (
       
   141         'slides.css', 'outline.css', 'print.css', 'opera.css', 'slides.js')
       
   142     """Names of theme files directly linked to in the output HTML"""
       
   143 
       
   144     indirect_theme_files = (
       
   145         's5-core.css', 'framing.css', 'pretty.css', 'blank.gif', 'iepngfix.htc')
       
   146     """Names of files used indirectly; imported or used by files in
       
   147     `direct_theme_files`."""
       
   148 
       
   149     required_theme_files = indirect_theme_files + direct_theme_files
       
   150     """Names of mandatory theme files."""
       
   151 
       
   152     def __init__(self, *args):
       
   153         html4css1.HTMLTranslator.__init__(self, *args)
       
   154         #insert S5-specific stylesheet and script stuff:
       
   155         self.theme_file_path = None
       
   156         self.setup_theme()
       
   157         view_mode = self.document.settings.view_mode
       
   158         control_visibility = ('visible', 'hidden')[self.document.settings
       
   159                                                    .hidden_controls]
       
   160         self.stylesheet.append(self.s5_stylesheet_template
       
   161                                % {'path': self.theme_file_path,
       
   162                                   'view_mode': view_mode,
       
   163                                   'control_visibility': control_visibility})
       
   164         if not self.document.settings.current_slide:
       
   165             self.stylesheet.append(self.disable_current_slide)
       
   166         self.add_meta('<meta name="version" content="S5 1.1" />\n')
       
   167         self.s5_footer = []
       
   168         self.s5_header = []
       
   169         self.section_count = 0
       
   170         self.theme_files_copied = None
       
   171 
       
   172     def setup_theme(self):
       
   173         if self.document.settings.theme:
       
   174             self.copy_theme()
       
   175         elif self.document.settings.theme_url:
       
   176             self.theme_file_path = self.document.settings.theme_url
       
   177         else:
       
   178             raise docutils.ApplicationError(
       
   179                 'No theme specified for S5/HTML writer.')
       
   180 
       
   181     def copy_theme(self):
       
   182         """
       
   183         Locate & copy theme files.
       
   184 
       
   185         A theme may be explicitly based on another theme via a '__base__'
       
   186         file.  The default base theme is 'default'.  Files are accumulated
       
   187         from the specified theme, any base themes, and 'default'.
       
   188         """
       
   189         settings = self.document.settings
       
   190         path = find_theme(settings.theme)
       
   191         theme_paths = [path]
       
   192         self.theme_files_copied = {}
       
   193         required_files_copied = {}
       
   194         # This is a link (URL) in HTML, so we use "/", not os.sep:
       
   195         self.theme_file_path = '%s/%s' % ('ui', settings.theme)
       
   196         if settings._destination:
       
   197             dest = os.path.join(
       
   198                 os.path.dirname(settings._destination), 'ui', settings.theme)
       
   199             if not os.path.isdir(dest):
       
   200                 os.makedirs(dest)
       
   201         else:
       
   202             # no destination, so we can't copy the theme
       
   203             return
       
   204         default = 0
       
   205         while path:
       
   206             for f in os.listdir(path):  # copy all files from each theme
       
   207                 if f == self.base_theme_file:
       
   208                     continue            # ... except the "__base__" file
       
   209                 if ( self.copy_file(f, path, dest)
       
   210                      and f in self.required_theme_files):
       
   211                     required_files_copied[f] = 1
       
   212             if default:
       
   213                 break                   # "default" theme has no base theme
       
   214             # Find the "__base__" file in theme directory:
       
   215             base_theme_file = os.path.join(path, self.base_theme_file)
       
   216             # If it exists, read it and record the theme path:
       
   217             if os.path.isfile(base_theme_file):
       
   218                 lines = open(base_theme_file).readlines()
       
   219                 for line in lines:
       
   220                     line = line.strip()
       
   221                     if line and not line.startswith('#'):
       
   222                         path = find_theme(line)
       
   223                         if path in theme_paths: # check for duplicates (cycles)
       
   224                             path = None         # if found, use default base
       
   225                         else:
       
   226                             theme_paths.append(path)
       
   227                         break
       
   228                 else:                   # no theme name found
       
   229                     path = None         # use default base
       
   230             else:                       # no base theme file found
       
   231                 path = None             # use default base
       
   232             if not path:
       
   233                 path = find_theme(self.default_theme)
       
   234                 theme_paths.append(path)
       
   235                 default = 1
       
   236         if len(required_files_copied) != len(self.required_theme_files):
       
   237             # Some required files weren't found & couldn't be copied.
       
   238             required = list(self.required_theme_files)
       
   239             for f in required_files_copied.keys():
       
   240                 required.remove(f)
       
   241             raise docutils.ApplicationError(
       
   242                 'Theme files not found: %s'
       
   243                 % ', '.join(['%r' % f for f in required]))
       
   244 
       
   245     files_to_skip_pattern = re.compile(r'~$|\.bak$|#$|\.cvsignore$')
       
   246 
       
   247     def copy_file(self, name, source_dir, dest_dir):
       
   248         """
       
   249         Copy file `name` from `source_dir` to `dest_dir`.
       
   250         Return 1 if the file exists in either `source_dir` or `dest_dir`.
       
   251         """
       
   252         source = os.path.join(source_dir, name)
       
   253         dest = os.path.join(dest_dir, name)
       
   254         if self.theme_files_copied.has_key(dest):
       
   255             return 1
       
   256         else:
       
   257             self.theme_files_copied[dest] = 1
       
   258         if os.path.isfile(source):
       
   259             if self.files_to_skip_pattern.search(source):
       
   260                 return None
       
   261             settings = self.document.settings
       
   262             if os.path.exists(dest) and not settings.overwrite_theme_files:
       
   263                 settings.record_dependencies.add(dest)
       
   264             else:
       
   265                 src_file = open(source, 'rb')
       
   266                 src_data = src_file.read()
       
   267                 src_file.close()
       
   268                 dest_file = open(dest, 'wb')
       
   269                 dest_dir = dest_dir.replace(os.sep, '/')
       
   270                 dest_file.write(src_data.replace(
       
   271                     'ui/default', dest_dir[dest_dir.rfind('ui/'):]))
       
   272                 dest_file.close()
       
   273                 settings.record_dependencies.add(source)
       
   274             return 1
       
   275         if os.path.isfile(dest):
       
   276             return 1
       
   277 
       
   278     def depart_document(self, node):
       
   279         header = ''.join(self.s5_header)
       
   280         footer = ''.join(self.s5_footer)
       
   281         title = ''.join(self.html_title).replace('<h1 class="title">', '<h1>')
       
   282         layout = self.layout_template % {'header': header,
       
   283                                          'title': title,
       
   284                                          'footer': footer}
       
   285         self.fragment.extend(self.body)
       
   286         self.body_prefix.extend(layout)
       
   287         self.body_prefix.append('<div class="presentation">\n')
       
   288         self.body_prefix.append(
       
   289             self.starttag({'classes': ['slide'], 'ids': ['slide0']}, 'div'))
       
   290         if not self.section_count:
       
   291             self.body.append('</div>\n')
       
   292         self.body_suffix.insert(0, '</div>\n')
       
   293         # skip content-type meta tag with interpolated charset value:
       
   294         self.html_head.extend(self.head[1:])
       
   295         self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
       
   296                               + self.docinfo + self.body
       
   297                               + self.body_suffix[:-1])
       
   298 
       
   299     def depart_footer(self, node):
       
   300         start = self.context.pop()
       
   301         self.s5_footer.append('<h2>')
       
   302         self.s5_footer.extend(self.body[start:])
       
   303         self.s5_footer.append('</h2>')
       
   304         del self.body[start:]
       
   305 
       
   306     def depart_header(self, node):
       
   307         start = self.context.pop()
       
   308         header = ['<div id="header">\n']
       
   309         header.extend(self.body[start:])
       
   310         header.append('\n</div>\n')
       
   311         del self.body[start:]
       
   312         self.s5_header.extend(header)
       
   313 
       
   314     def visit_section(self, node):
       
   315         if not self.section_count:
       
   316             self.body.append('\n</div>\n')
       
   317         self.section_count += 1
       
   318         self.section_level += 1
       
   319         if self.section_level > 1:
       
   320             # dummy for matching div's
       
   321             self.body.append(self.starttag(node, 'div', CLASS='section'))
       
   322         else:
       
   323             self.body.append(self.starttag(node, 'div', CLASS='slide'))
       
   324 
       
   325     def visit_subtitle(self, node):
       
   326         if isinstance(node.parent, nodes.section):
       
   327             level = self.section_level + self.initial_header_level - 1
       
   328             if level == 1:
       
   329                 level = 2
       
   330             tag = 'h%s' % level
       
   331             self.body.append(self.starttag(node, tag, ''))
       
   332             self.context.append('</%s>\n' % tag)
       
   333         else:
       
   334             html4css1.HTMLTranslator.visit_subtitle(self, node)
       
   335 
       
   336     def visit_title(self, node):
       
   337         html4css1.HTMLTranslator.visit_title(self, node)