diff --git a/python/helpers/docutils/__init__.py b/python/helpers/docutils/__init__.py new file mode 100644 index 000000000000..ec0f725dd0f6 --- /dev/null +++ b/python/helpers/docutils/__init__.py @@ -0,0 +1,204 @@ +# $Id: __init__.py 6364 2010-07-07 14:56:53Z grubert $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This is the Docutils (Python Documentation Utilities) package. + +Package Structure +================= + +Modules: + +- __init__.py: Contains component base classes, exception classes, and + Docutils version information. + +- core.py: Contains the ``Publisher`` class and ``publish_*()`` convenience + functions. + +- frontend.py: Runtime settings (command-line interface, configuration files) + processing, for Docutils front-ends. + +- io.py: Provides a uniform API for low-level input and output. + +- nodes.py: Docutils document tree (doctree) node class library. + +- statemachine.py: A finite state machine specialized for + regular-expression-based text filters. + +- urischemes.py: Contains a complete mapping of known URI addressing + scheme names to descriptions. + +- utils.py: Contains the ``Reporter`` system warning class and miscellaneous + utilities. + +Subpackages: + +- languages: Language-specific mappings of terms. + +- parsers: Syntax-specific input parser modules or packages. + +- readers: Context-specific input handlers which understand the data + source and manage a parser. + +- transforms: Modules used by readers and writers to modify DPS + doctrees. + +- writers: Format-specific output translators. +""" + +__docformat__ = 'reStructuredText' + +__version__ = '0.8' +"""``major.minor.micro`` version number. The micro number is bumped for API +changes, for new functionality, and for interim project releases. The minor +number is bumped whenever there is a significant project release. The major +number will be bumped when the project is feature-complete, and perhaps if +there is a major change in the design.""" + +__version_details__ = 'snapshot 2010-09-01, r6395' +"""Extra version details (e.g. 'snapshot 2005-05-29, r3410', 'repository', +'release'), modified automatically & manually.""" + +class ApplicationError(StandardError): pass +class DataError(ApplicationError): pass + + +class SettingsSpec: + + """ + Runtime setting specification base class. + + SettingsSpec subclass objects used by `docutils.frontend.OptionParser`. + """ + + settings_spec = () + """Runtime settings specification. Override in subclasses. + + Defines runtime settings and associated command-line options, as used by + `docutils.frontend.OptionParser`. This is a tuple of: + + - Option group title (string or `None` which implies no group, just a list + of single options). + + - Description (string or `None`). + + - A sequence of option tuples. Each consists of: + + - Help text (string) + + - List of option strings (e.g. ``['-Q', '--quux']``). + + - Dictionary of keyword arguments sent to the OptionParser/OptionGroup + ``add_option`` method. + + Runtime setting names are derived implicitly from long option names + ('--a-setting' becomes ``settings.a_setting``) or explicitly from the + 'dest' keyword argument. + + Most settings will also have a 'validator' keyword & function. The + validator function validates setting values (from configuration files + and command-line option arguments) and converts them to appropriate + types. For example, the ``docutils.frontend.validate_boolean`` + function, **required by all boolean settings**, converts true values + ('1', 'on', 'yes', and 'true') to 1 and false values ('0', 'off', + 'no', 'false', and '') to 0. Validators need only be set once per + setting. See the `docutils.frontend.validate_*` functions. + + See the optparse docs for more details. + + - More triples of group title, description, options, as many times as + needed. Thus, `settings_spec` tuples can be simply concatenated. + """ + + settings_defaults = None + """A dictionary of defaults for settings not in `settings_spec` (internal + settings, intended to be inaccessible by command-line and config file). + Override in subclasses.""" + + settings_default_overrides = None + """A dictionary of auxiliary defaults, to override defaults for settings + defined in other components. Override in subclasses.""" + + relative_path_settings = () + """Settings containing filesystem paths. Override in subclasses. + Settings listed here are to be interpreted relative to the current working + directory.""" + + config_section = None + """The name of the config file section specific to this component + (lowercase, no brackets). Override in subclasses.""" + + config_section_dependencies = None + """A list of names of config file sections that are to be applied before + `config_section`, in order (from general to specific). In other words, + the settings in `config_section` are to be overlaid on top of the settings + from these sections. The "general" section is assumed implicitly. + Override in subclasses.""" + + +class TransformSpec: + + """ + Runtime transform specification base class. + + TransformSpec subclass objects used by `docutils.transforms.Transformer`. + """ + + def get_transforms(self): + """Transforms required by this class. Override in subclasses.""" + if self.default_transforms != (): + import warnings + warnings.warn('default_transforms attribute deprecated.\n' + 'Use get_transforms() method instead.', + DeprecationWarning) + return list(self.default_transforms) + return [] + + # Deprecated; for compatibility. + default_transforms = () + + unknown_reference_resolvers = () + """List of functions to try to resolve unknown references. Unknown + references have a 'refname' attribute which doesn't correspond to any + target in the document. Called when the transforms in + `docutils.tranforms.references` are unable to find a correct target. The + list should contain functions which will try to resolve unknown + references, with the following signature:: + + def reference_resolver(node): + '''Returns boolean: true if resolved, false if not.''' + + If the function is able to resolve the reference, it should also remove + the 'refname' attribute and mark the node as resolved:: + + del node['refname'] + node.resolved = 1 + + Each function must have a "priority" attribute which will affect the order + the unknown_reference_resolvers are run:: + + reference_resolver.priority = 100 + + Override in subclasses.""" + + +class Component(SettingsSpec, TransformSpec): + + """Base class for Docutils components.""" + + component_type = None + """Name of the component type ('reader', 'parser', 'writer'). Override in + subclasses.""" + + supported = () + """Names for this component. Override in subclasses.""" + + def supports(self, format): + """ + Is `format` supported by this component? + + To be used by transforms to ask the dependent component if it supports + a certain input context or output format. + """ + return format in self.supported diff --git a/python/helpers/docutils/_compat.py b/python/helpers/docutils/_compat.py new file mode 100644 index 000000000000..87f46da972ce --- /dev/null +++ b/python/helpers/docutils/_compat.py @@ -0,0 +1,36 @@ +# $Id: _compat.py 5908 2009-04-21 13:43:23Z goodger $ +# Author: Georg Brandl +# Copyright: This module has been placed in the public domain. + +""" +Python 2/3 compatibility definitions. + +This module currently provides the following helper symbols: + +* bytes (name of byte string type; str in 2.x, bytes in 3.x) +* b (function converting a string literal to an ASCII byte string; + can be also used to convert a Unicode string into a byte string) +* u_prefix (unicode repr prefix, 'u' in 2.x, nothing in 3.x) +* BytesIO (a StringIO class that works with bytestrings) +""" + +import sys + +if sys.version_info < (3,0): + b = bytes = str + u_prefix = 'u' + from StringIO import StringIO as BytesIO +else: + import builtins + bytes = builtins.bytes + u_prefix = '' + def b(s): + if isinstance(s, str): + return s.encode('latin1') + elif isinstance(s, bytes): + return s + else: + raise TypeError("Invalid argument %r for b()" % (s,)) + # using this hack since 2to3 "fixes" the relative import + # when using ``from io import BytesIO`` + BytesIO = __import__('io').BytesIO diff --git a/python/helpers/docutils/_string_template_compat.py b/python/helpers/docutils/_string_template_compat.py new file mode 100644 index 000000000000..38929c296291 --- /dev/null +++ b/python/helpers/docutils/_string_template_compat.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# string_template_compat.py: string.Template for Python <= 2.4 +# ===================================================== + +# This is just an excerpt of the standard string module to provide backwards +# compatibility. + +import re as _re + +class _multimap: + """Helper class for combining multiple mappings. + + Used by .{safe_,}substitute() to combine the mapping and keyword + arguments. + """ + def __init__(self, primary, secondary): + self._primary = primary + self._secondary = secondary + + def __getitem__(self, key): + try: + return self._primary[key] + except KeyError: + return self._secondary[key] + + +class _TemplateMetaclass(type): + pattern = r""" + %(delim)s(?: + (?P%(delim)s) | # Escape sequence of two delimiters + (?P%(id)s) | # delimiter and a Python identifier + {(?P%(id)s)} | # delimiter and a braced identifier + (?P) # Other ill-formed delimiter exprs + ) + """ + + def __init__(cls, name, bases, dct): + super(_TemplateMetaclass, cls).__init__(name, bases, dct) + if 'pattern' in dct: + pattern = cls.pattern + else: + pattern = _TemplateMetaclass.pattern % { + 'delim' : _re.escape(cls.delimiter), + 'id' : cls.idpattern, + } + cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE) + + +class Template: + """A string class for supporting $-substitutions.""" + __metaclass__ = _TemplateMetaclass + + delimiter = '$' + idpattern = r'[_a-z][_a-z0-9]*' + + def __init__(self, template): + self.template = template + + # Search for $$, $identifier, ${identifier}, and any bare $'s + + def _invalid(self, mo): + i = mo.start('invalid') + lines = self.template[:i].splitlines(True) + if not lines: + colno = 1 + lineno = 1 + else: + colno = i - len(''.join(lines[:-1])) + lineno = len(lines) + raise ValueError('Invalid placeholder in string: line %d, col %d' % + (lineno, colno)) + + def substitute(self, *args, **kws): + if len(args) > 1: + raise TypeError('Too many positional arguments') + if not args: + mapping = kws + elif kws: + mapping = _multimap(kws, args[0]) + else: + mapping = args[0] + # Helper function for .sub() + def convert(mo): + # Check the most common path first. + named = mo.group('named') or mo.group('braced') + if named is not None: + val = mapping[named] + # We use this idiom instead of str() because the latter will + # fail if val is a Unicode containing non-ASCII characters. + return '%s' % (val,) + if mo.group('escaped') is not None: + return self.delimiter + if mo.group('invalid') is not None: + self._invalid(mo) + raise ValueError('Unrecognized named group in pattern', + self.pattern) + return self.pattern.sub(convert, self.template) + + def safe_substitute(self, *args, **kws): + if len(args) > 1: + raise TypeError('Too many positional arguments') + if not args: + mapping = kws + elif kws: + mapping = _multimap(kws, args[0]) + else: + mapping = args[0] + # Helper function for .sub() + def convert(mo): + named = mo.group('named') + if named is not None: + try: + # We use this idiom instead of str() because the latter + # will fail if val is a Unicode containing non-ASCII + return '%s' % (mapping[named],) + except KeyError: + return self.delimiter + named + braced = mo.group('braced') + if braced is not None: + try: + return '%s' % (mapping[braced],) + except KeyError: + return self.delimiter + '{' + braced + '}' + if mo.group('escaped') is not None: + return self.delimiter + if mo.group('invalid') is not None: + return self.delimiter + raise ValueError('Unrecognized named group in pattern', + self.pattern) + return self.pattern.sub(convert, self.template) + diff --git a/python/helpers/docutils/core.py b/python/helpers/docutils/core.py new file mode 100644 index 000000000000..68928fb20076 --- /dev/null +++ b/python/helpers/docutils/core.py @@ -0,0 +1,642 @@ +# $Id: core.py 6294 2010-03-26 10:47:00Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Calling the ``publish_*`` convenience functions (or instantiating a +`Publisher` object) with component names will result in default +behavior. For custom behavior (setting component options), create +custom component objects first, and pass *them* to +``publish_*``/`Publisher`. See `The Docutils Publisher`_. + +.. _The Docutils Publisher: http://docutils.sf.net/docs/api/publisher.html +""" + +__docformat__ = 'reStructuredText' + +import sys +import pprint +from docutils import __version__, __version_details__, SettingsSpec +from docutils import frontend, io, utils, readers, writers +from docutils.frontend import OptionParser +from docutils.transforms import Transformer +import docutils.readers.doctree + + +class Publisher: + + """ + A facade encapsulating the high-level logic of a Docutils system. + """ + + def __init__(self, reader=None, parser=None, writer=None, + source=None, source_class=io.FileInput, + destination=None, destination_class=io.FileOutput, + settings=None): + """ + Initial setup. If any of `reader`, `parser`, or `writer` are not + specified, the corresponding ``set_...`` method should be called with + a component name (`set_reader` sets the parser as well). + """ + + self.document = None + """The document tree (`docutils.nodes` objects).""" + + self.reader = reader + """A `docutils.readers.Reader` instance.""" + + self.parser = parser + """A `docutils.parsers.Parser` instance.""" + + self.writer = writer + """A `docutils.writers.Writer` instance.""" + + for component in 'reader', 'parser', 'writer': + assert not isinstance(getattr(self, component), str), ( + 'passed string "%s" as "%s" parameter; pass an instance, ' + 'or use the "%s_name" parameter instead (in ' + 'docutils.core.publish_* convenience functions).' + % (getattr(self, component), component, component)) + + self.source = source + """The source of input data, a `docutils.io.Input` instance.""" + + self.source_class = source_class + """The class for dynamically created source objects.""" + + self.destination = destination + """The destination for docutils output, a `docutils.io.Output` + instance.""" + + self.destination_class = destination_class + """The class for dynamically created destination objects.""" + + self.settings = settings + """An object containing Docutils settings as instance attributes. + Set by `self.process_command_line()` or `self.get_settings()`.""" + + def set_reader(self, reader_name, parser, parser_name): + """Set `self.reader` by name.""" + reader_class = readers.get_reader_class(reader_name) + self.reader = reader_class(parser, parser_name) + self.parser = self.reader.parser + + def set_writer(self, writer_name): + """Set `self.writer` by name.""" + writer_class = writers.get_writer_class(writer_name) + self.writer = writer_class() + + def set_components(self, reader_name, parser_name, writer_name): + if self.reader is None: + self.set_reader(reader_name, self.parser, parser_name) + if self.parser is None: + if self.reader.parser is None: + self.reader.set_parser(parser_name) + self.parser = self.reader.parser + if self.writer is None: + self.set_writer(writer_name) + + def setup_option_parser(self, usage=None, description=None, + settings_spec=None, config_section=None, + **defaults): + if config_section: + if not settings_spec: + settings_spec = SettingsSpec() + settings_spec.config_section = config_section + parts = config_section.split() + if len(parts) > 1 and parts[-1] == 'application': + settings_spec.config_section_dependencies = ['applications'] + #@@@ Add self.source & self.destination to components in future? + option_parser = OptionParser( + components=(self.parser, self.reader, self.writer, settings_spec), + defaults=defaults, read_config_files=1, + usage=usage, description=description) + return option_parser + + def get_settings(self, usage=None, description=None, + settings_spec=None, config_section=None, **defaults): + """ + Set and return default settings (overrides in `defaults` dict). + + Set components first (`self.set_reader` & `self.set_writer`). + Explicitly setting `self.settings` disables command line option + processing from `self.publish()`. + """ + option_parser = self.setup_option_parser( + usage, description, settings_spec, config_section, **defaults) + self.settings = option_parser.get_default_values() + return self.settings + + def process_programmatic_settings(self, settings_spec, + settings_overrides, + config_section): + if self.settings is None: + defaults = (settings_overrides or {}).copy() + # Propagate exceptions by default when used programmatically: + defaults.setdefault('traceback', 1) + self.get_settings(settings_spec=settings_spec, + config_section=config_section, + **defaults) + + def process_command_line(self, argv=None, usage=None, description=None, + settings_spec=None, config_section=None, + **defaults): + """ + Pass an empty list to `argv` to avoid reading `sys.argv` (the + default). + + Set components first (`self.set_reader` & `self.set_writer`). + """ + option_parser = self.setup_option_parser( + usage, description, settings_spec, config_section, **defaults) + if argv is None: + argv = sys.argv[1:] + self.settings = option_parser.parse_args(argv) + + def set_io(self, source_path=None, destination_path=None): + if self.source is None: + self.set_source(source_path=source_path) + if self.destination is None: + self.set_destination(destination_path=destination_path) + + def set_source(self, source=None, source_path=None): + if source_path is None: + source_path = self.settings._source + else: + self.settings._source = source_path + self.source = self.source_class( + source=source, source_path=source_path, + encoding=self.settings.input_encoding) + + def set_destination(self, destination=None, destination_path=None): + if destination_path is None: + destination_path = self.settings._destination + else: + self.settings._destination = destination_path + self.destination = self.destination_class( + destination=destination, destination_path=destination_path, + encoding=self.settings.output_encoding, + error_handler=self.settings.output_encoding_error_handler) + + def apply_transforms(self): + self.document.transformer.populate_from_components( + (self.source, self.reader, self.reader.parser, self.writer, + self.destination)) + self.document.transformer.apply_transforms() + + def publish(self, argv=None, usage=None, description=None, + settings_spec=None, settings_overrides=None, + config_section=None, enable_exit_status=None): + """ + Process command line options and arguments (if `self.settings` not + already set), run `self.reader` and then `self.writer`. Return + `self.writer`'s output. + """ + exit = None + try: + if self.settings is None: + self.process_command_line( + argv, usage, description, settings_spec, config_section, + **(settings_overrides or {})) + self.set_io() + self.document = self.reader.read(self.source, self.parser, + self.settings) + self.apply_transforms() + output = self.writer.write(self.document, self.destination) + self.writer.assemble_parts() + except SystemExit, error: + exit = 1 + exit_status = error.code + except Exception, error: + if not self.settings: # exception too early to report nicely + raise + if self.settings.traceback: # Propagate exceptions? + self.debugging_dumps() + raise + self.report_Exception(error) + exit = 1 + exit_status = 1 + self.debugging_dumps() + if (enable_exit_status and self.document + and (self.document.reporter.max_level + >= self.settings.exit_status_level)): + sys.exit(self.document.reporter.max_level + 10) + elif exit: + sys.exit(exit_status) + return output + + def debugging_dumps(self): + if not self.document: + return + if self.settings.dump_settings: + print >>sys.stderr, '\n::: Runtime settings:' + print >>sys.stderr, pprint.pformat(self.settings.__dict__) + if self.settings.dump_internals: + print >>sys.stderr, '\n::: Document internals:' + print >>sys.stderr, pprint.pformat(self.document.__dict__) + if self.settings.dump_transforms: + print >>sys.stderr, '\n::: Transforms applied:' + print >>sys.stderr, (' (priority, transform class, ' + 'pending node details, keyword args)') + print >>sys.stderr, pprint.pformat( + [(priority, '%s.%s' % (xclass.__module__, xclass.__name__), + pending and pending.details, kwargs) + for priority, xclass, pending, kwargs + in self.document.transformer.applied]) + if self.settings.dump_pseudo_xml: + print >>sys.stderr, '\n::: Pseudo-XML:' + print >>sys.stderr, self.document.pformat().encode( + 'raw_unicode_escape') + + def report_Exception(self, error): + if isinstance(error, utils.SystemMessage): + self.report_SystemMessage(error) + elif isinstance(error, UnicodeEncodeError): + self.report_UnicodeError(error) + else: + print >>sys.stderr, '%s: %s' % (error.__class__.__name__, error) + print >>sys.stderr, ("""\ +Exiting due to error. Use "--traceback" to diagnose. +Please report errors to . +Include "--traceback" output, Docutils version (%s [%s]), +Python version (%s), your OS type & version, and the +command line used.""" % (__version__, __version_details__, + sys.version.split()[0])) + + def report_SystemMessage(self, error): + print >>sys.stderr, ('Exiting due to level-%s (%s) system message.' + % (error.level, + utils.Reporter.levels[error.level])) + + def report_UnicodeError(self, error): + data = error.object[error.start:error.end] + sys.stderr.write( + '%s: %s\n' + '\n' + 'The specified output encoding (%s) cannot\n' + 'handle all of the output.\n' + 'Try setting "--output-encoding-error-handler" to\n' + '\n' + '* "xmlcharrefreplace" (for HTML & XML output);\n' + ' the output will contain "%s" and should be usable.\n' + '* "backslashreplace" (for other output formats);\n' + ' look for "%s" in the output.\n' + '* "replace"; look for "?" in the output.\n' + '\n' + '"--output-encoding-error-handler" is currently set to "%s".\n' + '\n' + 'Exiting due to error. Use "--traceback" to diagnose.\n' + 'If the advice above doesn\'t eliminate the error,\n' + 'please report it to .\n' + 'Include "--traceback" output, Docutils version (%s),\n' + 'Python version (%s), your OS type & version, and the\n' + 'command line used.\n' + % (error.__class__.__name__, error, + self.settings.output_encoding, + data.encode('ascii', 'xmlcharrefreplace'), + data.encode('ascii', 'backslashreplace'), + self.settings.output_encoding_error_handler, + __version__, sys.version.split()[0])) + +default_usage = '%prog [options] [ []]' +default_description = ('Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +def publish_cmdline(reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=1, argv=None, + usage=default_usage, description=default_description): + """ + Set up & run a `Publisher` for command-line-based file I/O (input and + output file paths taken automatically from the command line). Return the + encoded string output also. + + Parameters: see `publish_programmatically` for the remainder. + + - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``. + - `usage`: Usage string, output if there's a problem parsing the command + line. + - `description`: Program description, output for the "--help" option + (along with command-line option descriptions). + """ + pub = Publisher(reader, parser, writer, settings=settings) + pub.set_components(reader_name, parser_name, writer_name) + output = pub.publish( + argv, usage, description, settings_spec, settings_overrides, + config_section=config_section, enable_exit_status=enable_exit_status) + return output + +def publish_file(source=None, source_path=None, + destination=None, destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, settings_overrides=None, + config_section=None, enable_exit_status=None): + """ + Set up & run a `Publisher` for programmatic use with file-like I/O. + Return the encoded string output also. + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source_class=io.FileInput, source=source, source_path=source_path, + destination_class=io.FileOutput, + destination=destination, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return output + +def publish_string(source, source_path=None, destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher` for programmatic use with string I/O. Return + the encoded string or Unicode string output. + + For encoded string output, be sure to set the 'output_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + output. Here's one way:: + + publish_string(..., settings_overrides={'output_encoding': 'unicode'}) + + Similarly for Unicode string input (`source`):: + + publish_string(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source_class=io.StringInput, source=source, source_path=source_path, + destination_class=io.StringOutput, + destination=None, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return output + +def publish_parts(source, source_path=None, source_class=io.StringInput, + destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher`, and return a dictionary of document parts. + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. For programmatic use with string I/O. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's how:: + + publish_parts(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source=source, source_path=source_path, source_class=source_class, + destination_class=io.StringOutput, + destination=None, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return pub.writer.parts + +def publish_doctree(source, source_path=None, + source_class=io.StringInput, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher` for programmatic use with string I/O. + Return the document tree. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's one way:: + + publish_doctree(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + pub = Publisher(reader=reader, parser=parser, writer=None, + settings=settings, + source_class=source_class, + destination_class=io.NullOutput) + pub.set_components(reader_name, parser_name, 'null') + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(None, None) + output = pub.publish(enable_exit_status=enable_exit_status) + return pub.document + +def publish_from_doctree(document, destination_path=None, + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher` to render from an existing document + tree data structure, for programmatic use with string I/O. Return + the encoded string output. + + Note that document.settings is overridden; if you want to use the settings + of the original `document`, pass settings=document.settings. + + Also, new document.transformer and document.reporter objects are + generated. + + For encoded string output, be sure to set the 'output_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + output. Here's one way:: + + publish_from_doctree( + ..., settings_overrides={'output_encoding': 'unicode'}) + + Parameters: `document` is a `docutils.nodes.document` object, an existing + document tree. + + Other parameters: see `publish_programmatically`. + """ + reader = docutils.readers.doctree.Reader(parser_name='null') + pub = Publisher(reader, None, writer, + source=io.DocTreeInput(document), + destination_class=io.StringOutput, settings=settings) + if not writer and writer_name: + pub.set_writer(writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_destination(None, destination_path) + return pub.publish(enable_exit_status=enable_exit_status) + +def publish_cmdline_to_binary(reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=1, argv=None, + usage=default_usage, description=default_description, + destination=None, destination_class=io.BinaryFileOutput + ): + """ + Set up & run a `Publisher` for command-line-based file I/O (input and + output file paths taken automatically from the command line). Return the + encoded string output also. + + This is just like publish_cmdline, except that it uses + io.BinaryFileOutput instead of io.FileOutput. + + Parameters: see `publish_programmatically` for the remainder. + + - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``. + - `usage`: Usage string, output if there's a problem parsing the command + line. + - `description`: Program description, output for the "--help" option + (along with command-line option descriptions). + """ + pub = Publisher(reader, parser, writer, settings=settings, + destination_class=destination_class) + pub.set_components(reader_name, parser_name, writer_name) + output = pub.publish( + argv, usage, description, settings_spec, settings_overrides, + config_section=config_section, enable_exit_status=enable_exit_status) + return output + +def publish_programmatically(source_class, source, source_path, + destination_class, destination, destination_path, + reader, reader_name, + parser, parser_name, + writer, writer_name, + settings, settings_spec, + settings_overrides, config_section, + enable_exit_status): + """ + Set up & run a `Publisher` for custom programmatic use. Return the + encoded string output and the Publisher object. + + Applications should not need to call this function directly. If it does + seem to be necessary to call this function directly, please write to the + Docutils-develop mailing list + . + + Parameters: + + * `source_class` **required**: The class for dynamically created source + objects. Typically `io.FileInput` or `io.StringInput`. + + * `source`: Type depends on `source_class`: + + - If `source_class` is `io.FileInput`: Either a file-like object + (must have 'read' and 'close' methods), or ``None`` + (`source_path` is opened). If neither `source` nor + `source_path` are supplied, `sys.stdin` is used. + + - If `source_class` is `io.StringInput` **required**: The input + string, either an encoded 8-bit string (set the + 'input_encoding' setting to the correct encoding) or a Unicode + string (set the 'input_encoding' setting to 'unicode'). + + * `source_path`: Type depends on `source_class`: + + - `io.FileInput`: Path to the input file, opened if no `source` + supplied. + + - `io.StringInput`: Optional. Path to the file or object that produced + `source`. Only used for diagnostic output. + + * `destination_class` **required**: The class for dynamically created + destination objects. Typically `io.FileOutput` or `io.StringOutput`. + + * `destination`: Type depends on `destination_class`: + + - `io.FileOutput`: Either a file-like object (must have 'write' and + 'close' methods), or ``None`` (`destination_path` is opened). If + neither `destination` nor `destination_path` are supplied, + `sys.stdout` is used. + + - `io.StringOutput`: Not used; pass ``None``. + + * `destination_path`: Type depends on `destination_class`: + + - `io.FileOutput`: Path to the output file. Opened if no `destination` + supplied. + + - `io.StringOutput`: Path to the file or object which will receive the + output; optional. Used for determining relative paths (stylesheets, + source links, etc.). + + * `reader`: A `docutils.readers.Reader` object. + + * `reader_name`: Name or alias of the Reader class to be instantiated if + no `reader` supplied. + + * `parser`: A `docutils.parsers.Parser` object. + + * `parser_name`: Name or alias of the Parser class to be instantiated if + no `parser` supplied. + + * `writer`: A `docutils.writers.Writer` object. + + * `writer_name`: Name or alias of the Writer class to be instantiated if + no `writer` supplied. + + * `settings`: A runtime settings (`docutils.frontend.Values`) object, for + dotted-attribute access to runtime settings. It's the end result of the + `SettingsSpec`, config file, and option processing. If `settings` is + passed, it's assumed to be complete and no further setting/config/option + processing is done. + + * `settings_spec`: A `docutils.SettingsSpec` subclass or object. Provides + extra application-specific settings definitions independently of + components. In other words, the application becomes a component, and + its settings data is processed along with that of the other components. + Used only if no `settings` specified. + + * `settings_overrides`: A dictionary containing application-specific + settings defaults that override the defaults of other components. + Used only if no `settings` specified. + + * `config_section`: A string, the name of the configuration file section + for this application. Overrides the ``config_section`` attribute + defined by `settings_spec`. Used only if no `settings` specified. + + * `enable_exit_status`: Boolean; enable exit status at end of processing? + """ + pub = Publisher(reader, parser, writer, settings=settings, + source_class=source_class, + destination_class=destination_class) + pub.set_components(reader_name, parser_name, writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(destination, destination_path) + output = pub.publish(enable_exit_status=enable_exit_status) + return output, pub diff --git a/python/helpers/docutils/docutils.conf b/python/helpers/docutils/docutils.conf new file mode 100644 index 000000000000..cdce8d6292a3 --- /dev/null +++ b/python/helpers/docutils/docutils.conf @@ -0,0 +1,5 @@ +# This configuration file is to prevent tools/buildhtml.py from +# processing text files in and below this directory. + +[buildhtml application] +prune: . diff --git a/python/helpers/docutils/examples.py b/python/helpers/docutils/examples.py new file mode 100644 index 000000000000..68980f3ceec3 --- /dev/null +++ b/python/helpers/docutils/examples.py @@ -0,0 +1,96 @@ +# $Id: examples.py 4800 2006-11-12 18:02:01Z goodger $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This module contains practical examples of Docutils client code. + +Importing this module from client code is not recommended; its contents are +subject to change in future Docutils releases. Instead, it is recommended +that you copy and paste the parts you need into your own code, modifying as +necessary. +""" + +from docutils import core, io + + +def html_parts(input_string, source_path=None, destination_path=None, + input_encoding='unicode', doctitle=1, initial_header_level=1): + """ + Given an input string, returns a dictionary of HTML document parts. + + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. + + Parameters: + + - `input_string`: A multi-line text string; required. + - `source_path`: Path to the source file or object. Optional, but useful + for diagnostic output (system messages). + - `destination_path`: Path to the file or object which will receive the + output; optional. Used for determining relative paths (stylesheets, + source links, etc.). + - `input_encoding`: The encoding of `input_string`. If it is an encoded + 8-bit string, provide the correct encoding. If it is a Unicode string, + use "unicode", the default. + - `doctitle`: Disable the promotion of a lone top-level section title to + document title (and subsequent section title to document subtitle + promotion); enabled by default. + - `initial_header_level`: The initial level for header elements (e.g. 1 + for "

"). + """ + overrides = {'input_encoding': input_encoding, + 'doctitle_xform': doctitle, + 'initial_header_level': initial_header_level} + parts = core.publish_parts( + source=input_string, source_path=source_path, + destination_path=destination_path, + writer_name='html', settings_overrides=overrides) + return parts + +def html_body(input_string, source_path=None, destination_path=None, + input_encoding='unicode', output_encoding='unicode', + doctitle=1, initial_header_level=1): + """ + Given an input string, returns an HTML fragment as a string. + + The return value is the contents of the element. + + Parameters (see `html_parts()` for the remainder): + + - `output_encoding`: The desired encoding of the output. If a Unicode + string is desired, use the default value of "unicode" . + """ + parts = html_parts( + input_string=input_string, source_path=source_path, + destination_path=destination_path, + input_encoding=input_encoding, doctitle=doctitle, + initial_header_level=initial_header_level) + fragment = parts['html_body'] + if output_encoding != 'unicode': + fragment = fragment.encode(output_encoding) + return fragment + +def internals(input_string, source_path=None, destination_path=None, + input_encoding='unicode', settings_overrides=None): + """ + Return the document tree and publisher, for exploring Docutils internals. + + Parameters: see `html_parts()`. + """ + if settings_overrides: + overrides = settings_overrides.copy() + else: + overrides = {} + overrides['input_encoding'] = input_encoding + output, pub = core.publish_programmatically( + source_class=io.StringInput, source=input_string, + source_path=source_path, + destination_class=io.NullOutput, destination=None, + destination_path=destination_path, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='null', + settings=None, settings_spec=None, settings_overrides=overrides, + config_section=None, enable_exit_status=None) + return pub.writer.document, pub diff --git a/python/helpers/docutils/frontend.py b/python/helpers/docutils/frontend.py new file mode 100644 index 000000000000..f274150205ed --- /dev/null +++ b/python/helpers/docutils/frontend.py @@ -0,0 +1,786 @@ +# $Id: frontend.py 6205 2009-11-30 08:11:08Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Command-line and common processing for Docutils front-end tools. + +Exports the following classes: + +* `OptionParser`: Standard Docutils command-line processing. +* `Option`: Customized version of `optparse.Option`; validation support. +* `Values`: Runtime settings; objects are simple structs + (``object.attribute``). Supports cumulative list settings (attributes). +* `ConfigParser`: Standard Docutils config file processing. + +Also exports the following functions: + +* Option callbacks: `store_multiple`, `read_config_file`. +* Setting validators: `validate_encoding`, + `validate_encoding_error_handler`, + `validate_encoding_and_error_handler`, `validate_boolean`, + `validate_threshold`, `validate_colon_separated_string_list`, + `validate_dependency_file`. +* `make_paths_absolute`. +* SettingSpec manipulation: `filter_settings_spec`. +""" + +__docformat__ = 'reStructuredText' + +import os +import os.path +import sys +import warnings +import ConfigParser as CP +import codecs +import docutils +import docutils.utils +import docutils.nodes +import optparse +from optparse import SUPPRESS_HELP + + +def store_multiple(option, opt, value, parser, *args, **kwargs): + """ + Store multiple values in `parser.values`. (Option callback.) + + Store `None` for each attribute named in `args`, and store the value for + each key (attribute name) in `kwargs`. + """ + for attribute in args: + setattr(parser.values, attribute, None) + for key, value in kwargs.items(): + setattr(parser.values, key, value) + +def read_config_file(option, opt, value, parser): + """ + Read a configuration file during option processing. (Option callback.) + """ + try: + new_settings = parser.get_config_file_settings(value) + except ValueError, error: + parser.error(error) + parser.values.update(new_settings, parser) + +def validate_encoding(setting, value, option_parser, + config_parser=None, config_section=None): + try: + codecs.lookup(value) + except LookupError: + raise (LookupError('setting "%s": unknown encoding: "%s"' + % (setting, value)), + None, sys.exc_info()[2]) + return value + +def validate_encoding_error_handler(setting, value, option_parser, + config_parser=None, config_section=None): + try: + codecs.lookup_error(value) + except AttributeError: # TODO: remove (only needed prior to Python 2.3) + if value not in ('strict', 'ignore', 'replace', 'xmlcharrefreplace'): + raise (LookupError( + 'unknown encoding error handler: "%s" (choices: ' + '"strict", "ignore", "replace", or "xmlcharrefreplace")' % value), + None, sys.exc_info()[2]) + except LookupError: + raise (LookupError( + 'unknown encoding error handler: "%s" (choices: ' + '"strict", "ignore", "replace", "backslashreplace", ' + '"xmlcharrefreplace", and possibly others; see documentation for ' + 'the Python ``codecs`` module)' % value), + None, sys.exc_info()[2]) + return value + +def validate_encoding_and_error_handler( + setting, value, option_parser, config_parser=None, config_section=None): + """ + Side-effect: if an error handler is included in the value, it is inserted + into the appropriate place as if it was a separate setting/option. + """ + if ':' in value: + encoding, handler = value.split(':') + validate_encoding_error_handler( + setting + '_error_handler', handler, option_parser, + config_parser, config_section) + if config_parser: + config_parser.set(config_section, setting + '_error_handler', + handler) + else: + setattr(option_parser.values, setting + '_error_handler', handler) + else: + encoding = value + validate_encoding(setting, encoding, option_parser, + config_parser, config_section) + return encoding + +def validate_boolean(setting, value, option_parser, + config_parser=None, config_section=None): + if isinstance(value, unicode): + try: + return option_parser.booleans[value.strip().lower()] + except KeyError: + raise (LookupError('unknown boolean value: "%s"' % value), + None, sys.exc_info()[2]) + return value + +def validate_nonnegative_int(setting, value, option_parser, + config_parser=None, config_section=None): + value = int(value) + if value < 0: + raise ValueError('negative value; must be positive or zero') + return value + +def validate_threshold(setting, value, option_parser, + config_parser=None, config_section=None): + try: + return int(value) + except ValueError: + try: + return option_parser.thresholds[value.lower()] + except (KeyError, AttributeError): + raise (LookupError('unknown threshold: %r.' % value), + None, sys.exc_info[2]) + +def validate_colon_separated_string_list( + setting, value, option_parser, config_parser=None, config_section=None): + if isinstance(value, unicode): + value = value.split(':') + else: + last = value.pop() + value.extend(last.split(':')) + return value + +def validate_url_trailing_slash( + setting, value, option_parser, config_parser=None, config_section=None): + if not value: + return './' + elif value.endswith('/'): + return value + else: + return value + '/' + +def validate_dependency_file(setting, value, option_parser, + config_parser=None, config_section=None): + try: + return docutils.utils.DependencyList(value) + except IOError: + return docutils.utils.DependencyList(None) + +def validate_strip_class(setting, value, option_parser, + config_parser=None, config_section=None): + if config_parser: # validate all values + class_values = value + else: # just validate the latest value + class_values = [value[-1]] + for class_value in class_values: + normalized = docutils.nodes.make_id(class_value) + if class_value != normalized: + raise ValueError('invalid class value %r (perhaps %r?)' + % (class_value, normalized)) + return value + +def make_paths_absolute(pathdict, keys, base_path=None): + """ + Interpret filesystem path settings relative to the `base_path` given. + + Paths are values in `pathdict` whose keys are in `keys`. Get `keys` from + `OptionParser.relative_path_settings`. + """ + if base_path is None: + base_path = os.getcwd() + for key in keys: + if key in pathdict: + value = pathdict[key] + if isinstance(value, list): + value = [make_one_path_absolute(base_path, path) + for path in value] + elif value: + value = make_one_path_absolute(base_path, value) + pathdict[key] = value + +def make_one_path_absolute(base_path, path): + return os.path.abspath(os.path.join(base_path, path)) + +def filter_settings_spec(settings_spec, *exclude, **replace): + """Return a copy of `settings_spec` excluding/replacing some settings. + + `settings_spec` is a tuple of configuration settings with a structure + described for docutils.SettingsSpec.settings_spec. + + Optional positional arguments are names of to-be-excluded settings. + Keyword arguments are option specification replacements. + (See the html4strict writer for an example.) + """ + settings = list(settings_spec) + # every third item is a sequence of option tuples + for i in range(2, len(settings), 3): + newopts = [] + for opt_spec in settings[i]: + # opt_spec is ("", [

\n') + self.in_document_title = len(self.body) + elif isinstance(node.parent, nodes.section): + tag = 'h%s' % (self.section_level + self.initial_header_level - 1) + self.body.append( + self.starttag(node, tag, '', CLASS='section-subtitle') + + self.starttag({}, 'span', '', CLASS='section-subtitle')) + self.context.append('\n' % tag) + + def depart_subtitle(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.subtitle = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_subtitle.extend(self.body) + del self.body[:] + + def visit_superscript(self, node): + self.body.append(self.starttag(node, 'sup', '')) + + def depart_superscript(self, node): + self.body.append('') + + def visit_system_message(self, node): + self.body.append(self.starttag(node, 'div', CLASS='system-message')) + self.body.append('

') + backref_text = '' + if len(node['backrefs']): + backrefs = node['backrefs'] + if len(backrefs) == 1: + backref_text = ('; backlink' + % backrefs[0]) + else: + i = 1 + backlinks = [] + for backref in backrefs: + backlinks.append('%s' % (backref, i)) + i += 1 + backref_text = ('; backlinks: %s' + % ', '.join(backlinks)) + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('System Message: %s/%s ' + '(%s%s)%s

\n' + % (node['type'], node['level'], + self.encode(node['source']), line, backref_text)) + + def depart_system_message(self, node): + self.body.append('\n') + + def visit_table(self, node): + classes = ' '.join(['docutils', self.settings.table_style]).strip() + self.body.append( + self.starttag(node, 'table', CLASS=classes, border="1")) + + def depart_table(self, node): + self.body.append('\n') + + def visit_target(self, node): + if not ('refuri' in node or 'refid' in node + or 'refname' in node): + self.body.append(self.starttag(node, 'span', '', CLASS='target')) + self.context.append('') + else: + self.context.append('') + + def depart_target(self, node): + self.body.append(self.context.pop()) + + def visit_tbody(self, node): + self.write_colspecs() + self.body.append(self.context.pop()) # '\n' or '' + self.body.append(self.starttag(node, 'tbody', valign='top')) + + def depart_tbody(self, node): + self.body.append('\n') + + def visit_term(self, node): + self.body.append(self.starttag(node, 'dt', '')) + + def depart_term(self, node): + """ + Leave the end tag to `self.visit_definition()`, in case there's a + classifier. + """ + pass + + def visit_tgroup(self, node): + # Mozilla needs : + self.body.append(self.starttag(node, 'colgroup')) + # Appended by thead or tbody: + self.context.append('\n') + node.stubs = [] + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + self.write_colspecs() + self.body.append(self.context.pop()) # '\n' + # There may or may not be a ; this is for to use: + self.context.append('') + self.body.append(self.starttag(node, 'thead', valign='bottom')) + + def depart_thead(self, node): + self.body.append('\n') + + def visit_title(self, node): + """Only 6 section levels are supported by HTML.""" + check_id = 0 + close_tag = '

\n' + if isinstance(node.parent, nodes.topic): + self.body.append( + self.starttag(node, 'p', '', CLASS='topic-title first')) + elif isinstance(node.parent, nodes.sidebar): + self.body.append( + self.starttag(node, 'p', '', CLASS='sidebar-title')) + elif isinstance(node.parent, nodes.Admonition): + self.body.append( + self.starttag(node, 'p', '', CLASS='admonition-title')) + elif isinstance(node.parent, nodes.table): + self.body.append( + self.starttag(node, 'caption', '')) + close_tag = '\n' + elif isinstance(node.parent, nodes.document): + self.body.append(self.starttag(node, 'h1', '', CLASS='title')) + close_tag = '\n' + self.in_document_title = len(self.body) + else: + assert isinstance(node.parent, nodes.section) + h_level = self.section_level + self.initial_header_level - 1 + atts = {} + if (len(node.parent) >= 2 and + isinstance(node.parent[1], nodes.subtitle)): + atts['CLASS'] = 'with-subtitle' + self.body.append( + self.starttag(node, 'h%s' % h_level, '', **atts)) + atts = {} + if node.hasattr('refid'): + atts['class'] = 'toc-backref' + atts['href'] = '#' + node['refid'] + if atts: + self.body.append(self.starttag({}, 'a', '', **atts)) + close_tag = '\n' % (h_level) + else: + close_tag = '\n' % (h_level) + self.context.append(close_tag) + + def depart_title(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.title = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_title.extend(self.body) + del self.body[:] + + def visit_title_reference(self, node): + self.body.append(self.starttag(node, 'cite', '')) + + def depart_title_reference(self, node): + self.body.append('') + + def visit_topic(self, node): + self.body.append(self.starttag(node, 'div', CLASS='topic')) + self.topic_classes = node['classes'] + + def depart_topic(self, node): + self.body.append('\n') + self.topic_classes = [] + + def visit_transition(self, node): + self.body.append(self.emptytag(node, 'hr', CLASS='docutils')) + + def depart_transition(self, node): + pass + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version', meta=None) + + def depart_version(self, node): + self.depart_docinfo_item() + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + + +class SimpleListChecker(nodes.GenericNodeVisitor): + + """ + Raise `nodes.NodeFound` if non-simple list item is encountered. + + Here "simple" means a list item containing nothing other than a single + paragraph, a simple list, or a paragraph followed by a simple list. + """ + + def default_visit(self, node): + raise nodes.NodeFound + + def visit_bullet_list(self, node): + pass + + def visit_enumerated_list(self, node): + pass + + def visit_list_item(self, node): + children = [] + for child in node.children: + if not isinstance(child, nodes.Invisible): + children.append(child) + if (children and isinstance(children[0], nodes.paragraph) + and (isinstance(children[-1], nodes.bullet_list) + or isinstance(children[-1], nodes.enumerated_list))): + children.pop() + if len(children) <= 1: + return + else: + raise nodes.NodeFound + + def visit_paragraph(self, node): + raise nodes.SkipNode + + def invisible_visit(self, node): + """Invisible nodes should be ignored.""" + raise nodes.SkipNode + + visit_comment = invisible_visit + visit_substitution_definition = invisible_visit + visit_target = invisible_visit + visit_pending = invisible_visit diff --git a/python/helpers/docutils/writers/html4css1/html4css1.css b/python/helpers/docutils/writers/html4css1/html4css1.css new file mode 100644 index 000000000000..437426301e26 --- /dev/null +++ b/python/helpers/docutils/writers/html4css1/html4css1.css @@ -0,0 +1,303 @@ +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 6387 2010-08-13 12:23:41Z milde $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: left } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } diff --git a/python/helpers/docutils/writers/html4css1/template.txt b/python/helpers/docutils/writers/html4css1/template.txt new file mode 100644 index 000000000000..2591bce35df2 --- /dev/null +++ b/python/helpers/docutils/writers/html4css1/template.txt @@ -0,0 +1,8 @@ +%(head_prefix)s +%(head)s +%(stylesheet)s +%(body_prefix)s +%(body_pre_docinfo)s +%(docinfo)s +%(body)s +%(body_suffix)s diff --git a/python/helpers/docutils/writers/latex2e/__init__.py b/python/helpers/docutils/writers/latex2e/__init__.py new file mode 100644 index 000000000000..c8ef32ef6879 --- /dev/null +++ b/python/helpers/docutils/writers/latex2e/__init__.py @@ -0,0 +1,2831 @@ +# -*- coding: utf8 -*- +# $Id: __init__.py 6385 2010-08-13 12:17:01Z milde $ +# Author: Engelbert Gruber +# Copyright: This module has been placed in the public domain. + +"""LaTeX2e document tree Writer.""" + +__docformat__ = 'reStructuredText' + +# code contributions from several people included, thanks to all. +# some named: David Abrahams, Julien Letessier, Lele Gaifax, and others. +# +# convention deactivate code by two # i.e. ##. + +import sys +import os +import time +import re +import string +from docutils import frontend, nodes, languages, writers, utils, io +from docutils.transforms import writer_aux + +# compatibility module for Python 2.3 +if not hasattr(string, 'Template'): + import docutils._string_template_compat + string.Template = docutils._string_template_compat.Template + +class Writer(writers.Writer): + + supported = ('latex','latex2e') + """Formats this writer supports.""" + + default_template = 'default.tex' + default_template_path = os.path.dirname(__file__) + + default_preamble = '\n'.join([r'% PDF Standard Fonts', + r'\usepackage{mathptmx} % Times', + r'\usepackage[scaled=.90]{helvet}', + r'\usepackage{courier}']) + settings_spec = ( + 'LaTeX-Specific Options', + None, + (('Specify documentclass. Default is "article".', + ['--documentclass'], + {'default': 'article', }), + ('Specify document options. Multiple options can be given, ' + 'separated by commas. Default is "a4paper".', + ['--documentoptions'], + {'default': 'a4paper', }), + ('Footnotes with numbers/symbols by Docutils. (default)', + ['--docutils-footnotes'], + {'default': True, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Alias for --docutils-footnotes (deprecated)', + ['--use-latex-footnotes'], + {'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Use figure floats for footnote text (deprecated)', + ['--figure-footnotes'], + {'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Format for footnote references: one of "superscript" or ' + '"brackets". Default is "superscript".', + ['--footnote-references'], + {'choices': ['superscript', 'brackets'], 'default': 'superscript', + 'metavar': '', + 'overrides': 'trim_footnote_reference_space'}), + ('Use \\cite command for citations. ', + ['--use-latex-citations'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Use figure floats for citations ' + '(might get mixed with real figures). (default)', + ['--figure-citations'], + {'dest': 'use_latex_citations', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Format for block quote attributions: one of "dash" (em-dash ' + 'prefix), "parentheses"/"parens", or "none". Default is "dash".', + ['--attribution'], + {'choices': ['dash', 'parentheses', 'parens', 'none'], + 'default': 'dash', 'metavar': ''}), + ('Specify LaTeX packages/stylesheets. ' + ' A style is referenced with \\usepackage if extension is ' + '".sty" or omitted and with \\input else. ' + ' Overrides previous --stylesheet and --stylesheet-path settings.', + ['--stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'stylesheet_path'}), + ('Like --stylesheet, but the path is rewritten ' + 'relative to the output file. ', + ['--stylesheet-path'], + {'metavar': '', 'overrides': 'stylesheet'}), + ('Link to the stylesheet(s) in the output file. (default)', + ['--link-stylesheet'], + {'dest': 'embed_stylesheet', 'action': 'store_false'}), + ('Embed the stylesheet(s) in the output file. ' + 'Stylesheets must be accessible during processing. ', + ['--embed-stylesheet'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Customization by LaTeX code in the preamble. ' + 'Default: select PDF standard fonts (Times, Helvetica, Courier).', + ['--latex-preamble'], + {'default': default_preamble}), + ('Specify the template file. Default: "%s".' % default_template, + ['--template'], + {'default': default_template, 'metavar': ''}), + ('Table of contents by LaTeX. (default) ', + ['--use-latex-toc'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Table of contents by Docutils (without page numbers). ', + ['--use-docutils-toc'], + {'dest': 'use_latex_toc', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Add parts on top of the section hierarchy.', + ['--use-part-section'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Attach author and date to the document info table. (default) ', + ['--use-docutils-docinfo'], + {'dest': 'use_latex_docinfo', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Attach author and date to the document title.', + ['--use-latex-docinfo'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ("Typeset abstract as topic. (default)", + ['--topic-abstract'], + {'dest': 'use_latex_abstract', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ("Use LaTeX abstract environment for the document's abstract. ", + ['--use-latex-abstract'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Color of any hyperlinks embedded in text ' + '(default: "blue", "0" to disable).', + ['--hyperlink-color'], {'default': 'blue'}), + ('Enable compound enumerators for nested enumerated lists ' + '(e.g. "1.2.a.ii"). Default: disabled.', + ['--compound-enumerators'], + {'default': None, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable compound enumerators for nested enumerated lists. ' + 'This is the default.', + ['--no-compound-enumerators'], + {'action': 'store_false', 'dest': 'compound_enumerators'}), + ('Enable section ("." subsection ...) prefixes for compound ' + 'enumerators. This has no effect without --compound-enumerators.' + 'Default: disabled.', + ['--section-prefix-for-enumerators'], + {'default': None, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable section prefixes for compound enumerators. ' + 'This is the default.', + ['--no-section-prefix-for-enumerators'], + {'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}), + ('Set the separator between section number and enumerator ' + 'for compound enumerated lists. Default is "-".', + ['--section-enumerator-separator'], + {'default': '-', 'metavar': ''}), + ('When possibile, use the specified environment for literal-blocks. ' + 'Default is quoting of whitespace and special chars.', + ['--literal-block-env'], + {'default': ''}), + ('When possibile, use verbatim for literal-blocks. ' + 'Compatibility alias for "--literal-block-env=verbatim".', + ['--use-verbatim-when-possible'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Table style. "standard" with horizontal and vertical lines, ' + '"booktabs" (LaTeX booktabs style) only horizontal lines ' + 'above and below the table and below the header or "borderless". ' + 'Default: "standard"', + ['--table-style'], + {'choices': ['standard', 'booktabs','nolines', 'borderless'], + 'default': 'standard', + 'metavar': ''}), + ('LaTeX graphicx package option. ' + 'Possible values are "dvips", "pdftex". "auto" includes LaTeX code ' + 'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. ' + 'Default is no option.', + ['--graphicx-option'], + {'default': ''}), + ('LaTeX font encoding. ' + 'Possible values are "", "T1" (default), "OT1", "LGR,T1" or ' + 'any other combination of options to the `fontenc` package. ', + ['--font-encoding'], + {'default': 'T1'}), + ('Per default the latex-writer puts the reference title into ' + 'hyperreferences. Specify "ref*" or "pageref*" to get the section ' + 'number or the page number.', + ['--reference-label'], + {'default': None, }), + ('Specify style and database for bibtex, for example ' + '"--use-bibtex=mystyle,mydb1,mydb2".', + ['--use-bibtex'], + {'default': None, }), + ),) + + settings_defaults = {'sectnum_depth': 0 # updated by SectNum transform + } + relative_path_settings = ('stylesheet_path',) + + config_section = 'latex2e writer' + config_section_dependencies = ('writers',) + + head_parts = ('head_prefix', 'requirements', 'latex_preamble', + 'stylesheet', 'fallbacks', 'pdfsetup', 'title', 'subtitle') + visitor_attributes = head_parts + ('body_pre_docinfo', 'docinfo', + 'dedication', 'abstract', 'body') + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = LaTeXTranslator + + # Override parent method to add latex-specific transforms + def get_transforms(self): + # call the parent class' method + transform_list = writers.Writer.get_transforms(self) + # print transform_list + # Convert specific admonitions to generic one + transform_list.append(writer_aux.Admonitions) + # TODO: footnote collection transform + # transform_list.append(footnotes.collect) + return transform_list + + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + # copy parts + for part in self.visitor_attributes: + setattr(self, part, getattr(visitor, part)) + # get template string from file + try: + file = open(self.document.settings.template, 'rb') + except IOError: + file = open(os.path.join(os.path.dirname(__file__), + self.document.settings.template), 'rb') + template = string.Template(unicode(file.read(), 'utf-8')) + file.close() + # fill template + self.assemble_parts() # create dictionary of parts + self.output = template.substitute(self.parts) + + def assemble_parts(self): + """Assemble the `self.parts` dictionary of output fragments.""" + writers.Writer.assemble_parts(self) + for part in self.visitor_attributes: + lines = getattr(self, part) + if part in self.head_parts: + if lines: + lines.append('') # to get a trailing newline + self.parts[part] = '\n'.join(lines) + else: + # body contains inline elements, so join without newline + self.parts[part] = ''.join(lines) + + +class Babel(object): + """Language specifics for LaTeX.""" + # country code by a.schlock. + # partly manually converted from iso and babel stuff, dialects and some + _ISO639_TO_BABEL = { + 'no': 'norsk', #XXX added by hand ( forget about nynorsk?) + 'gd': 'scottish', #XXX added by hand + 'hu': 'magyar', #XXX added by hand + 'pt': 'portuguese',#XXX added by hand + 'sl': 'slovenian', + 'af': 'afrikaans', + 'bg': 'bulgarian', + 'br': 'breton', + 'ca': 'catalan', + 'cs': 'czech', + 'cy': 'welsh', + 'da': 'danish', + 'fr': 'french', + # french, francais, canadien, acadian + 'de': 'ngerman', #XXX rather than german + # ngerman, naustrian, german, germanb, austrian + 'el': 'greek', + 'en': 'english', + # english, USenglish, american, UKenglish, british, canadian + 'eo': 'esperanto', + 'es': 'spanish', + 'et': 'estonian', + 'eu': 'basque', + 'fi': 'finnish', + 'ga': 'irish', + 'gl': 'galician', + 'he': 'hebrew', + 'hr': 'croatian', + 'hu': 'hungarian', + 'is': 'icelandic', + 'it': 'italian', + 'la': 'latin', + 'nl': 'dutch', + 'pl': 'polish', + 'pt': 'portuguese', + 'ro': 'romanian', + 'ru': 'russian', + 'sk': 'slovak', + 'sr': 'serbian', + 'sv': 'swedish', + 'tr': 'turkish', + 'uk': 'ukrainian' + } + + def __init__(self, lang): + self.language = lang + self.quote_index = 0 + self.quotes = ('``', "''") + self.setup = '' # language dependent configuration code + # double quotes are "active" in some languages (e.g. German). + # TODO: use \textquotedbl in OT1 font encoding? + self.literal_double_quote = u'"' + if self.language.startswith('de'): + self.quotes = (r'\glqq{}', r'\grqq{}') + self.literal_double_quote = ur'\dq{}' + if self.language.startswith('it'): + self.literal_double_quote = ur'{\char`\"}' + if self.language.startswith('es'): + # reset tilde ~ to the original binding (nobreakspace): + self.setup = ('\n' + r'\addto\shorthandsspanish{\spanishdeactivate{."~<>}}') + + def next_quote(self): + q = self.quotes[self.quote_index] + self.quote_index = (self.quote_index+1) % 2 + return q + + def quote_quotes(self,text): + t = None + for part in text.split('"'): + if t == None: + t = part + else: + t += self.next_quote() + part + return t + + def get_language(self): + lang = self.language.split('_')[0] # filter dialects + return self._ISO639_TO_BABEL.get(lang, "") + +# Building blocks for the latex preamble +# -------------------------------------- + +class SortableDict(dict): + """Dictionary with additional sorting methods + + Tip: use key starting with with '_' for sorting before small letters + and with '~' for sorting after small letters. + """ + def sortedkeys(self): + """Return sorted list of keys""" + keys = self.keys() + keys.sort() + return keys + + def sortedvalues(self): + """Return list of values sorted by keys""" + return [self[key] for key in self.sortedkeys()] + + +# PreambleCmds +# ````````````` +# A container for LaTeX code snippets that can be +# inserted into the preamble if required in the document. +# +# .. The package 'makecmds' would enable shorter definitions using the +# \providelength and \provideenvironment commands. +# However, it is pretty non-standard (texlive-latex-extra). + +class PreambleCmds(object): + """Building blocks for the latex preamble.""" + +PreambleCmds.abstract = r""" +% abstract title +\providecommand*{\DUtitleabstract}[1]{\centerline{\textbf{#1}}}""" + +PreambleCmds.admonition = r""" +% admonition (specially marked topic) +\providecommand{\DUadmonition}[2][class-arg]{% + % try \DUadmonition#1{#2}: + \ifcsname DUadmonition#1\endcsname% + \csname DUadmonition#1\endcsname{#2}% + \else + \begin{center} + \fbox{\parbox{0.9\textwidth}{#2}} + \end{center} + \fi +}""" + +## PreambleCmds.caption = r"""% configure caption layout +## \usepackage{caption} +## \captionsetup{singlelinecheck=false}% no exceptions for one-liners""" + +PreambleCmds.color = r"""\usepackage{color}""" + +PreambleCmds.docinfo = r""" +% docinfo (width of docinfo table) +\DUprovidelength{\DUdocinfowidth}{0.9\textwidth}""" +# PreambleCmds.docinfo._depends = 'providelength' + +PreambleCmds.embedded_package_wrapper = r"""\makeatletter +%% embedded stylesheet: %s +%s +\makeatother""" + +PreambleCmds.dedication = r""" +% dedication topic +\providecommand{\DUtopicdedication}[1]{\begin{center}#1\end{center}}""" + +PreambleCmds.error = r""" +% error admonition title +\providecommand*{\DUtitleerror}[1]{\DUtitle{\color{red}#1}}""" +# PreambleCmds.errortitle._depends = 'color' + +PreambleCmds.fieldlist = r""" +% fieldlist environment +\ifthenelse{\isundefined{\DUfieldlist}}{ + \newenvironment{DUfieldlist}% + {\quote\description} + {\enddescription\endquote} +}{}""" + +PreambleCmds.float_settings = r"""\usepackage{float} % float configuration +\floatplacement{figure}{H} % place figures here definitely""" + +PreambleCmds.footnotes = r"""% numeric or symbol footnotes with hyperlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +}""" + +PreambleCmds.footnote_floats = r"""% settings for footnotes as floats: +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50}""" + +PreambleCmds.graphicx_auto = r"""% Check output format +\ifx\pdftexversion\undefined + \usepackage{graphicx} +\else + \usepackage[pdftex]{graphicx} +\fi'))""" + + +PreambleCmds.inline = r""" +% inline markup (custom roles) +% \DUrole{#1}{#2} tries \DUrole#1{#2} +\providecommand*{\DUrole}[2]{% + \ifcsname DUrole#1\endcsname% + \csname DUrole#1\endcsname{#2}% + \else% backwards compatibility: try \docutilsrole#1{#2} + \ifcsname docutilsrole#1\endcsname% + \csname docutilsrole#1\endcsname{#2}% + \else% + #2% + \fi% + \fi% +}""" + +PreambleCmds.legend = r""" +% legend environment +\ifthenelse{\isundefined{\DUlegend}}{ + \newenvironment{DUlegend}{\small}{} +}{}""" + +PreambleCmds.lineblock = r""" +% lineblock environment +\DUprovidelength{\DUlineblockindent}{2.5em} +\ifthenelse{\isundefined{\DUlineblock}}{ + \newenvironment{DUlineblock}[1]{% + \list{}{\setlength{\partopsep}{\parskip} + \addtolength{\partopsep}{\baselineskip} + \setlength{\topsep}{0pt} + \setlength{\itemsep}{0.15\baselineskip} + \setlength{\parsep}{0pt} + \setlength{\leftmargin}{#1}} + \raggedright + } + {\endlist} +}{}""" +# PreambleCmds.lineblock._depends = 'providelength' + +PreambleCmds.linking = r""" +%% hyperlinks: +\ifthenelse{\isundefined{\hypersetup}}{ + \usepackage[unicode,colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref} + \urlstyle{same} %% normal text font (alternatives: tt, rm, sf) +}{}""" + +PreambleCmds.minitoc = r"""%% local table of contents +\usepackage{minitoc}""" + +PreambleCmds.optionlist = r""" +% optionlist environment +\providecommand*{\DUoptionlistlabel}[1]{\bf #1 \hfill} +\DUprovidelength{\DUoptionlistindent}{3cm} +\ifthenelse{\isundefined{\DUoptionlist}}{ + \newenvironment{DUoptionlist}{% + \list{}{\setlength{\labelwidth}{\DUoptionlistindent} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\DUoptionlistlabel}} + } + {\endlist} +}{}""" +# PreambleCmds.optionlist._depends = 'providelength' + +PreambleCmds.providelength = r""" +% providelength (provide a length variable and set default, if it is new) +\providecommand*{\DUprovidelength}[2]{ + \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{} +}""" + +PreambleCmds.rubric = r""" +% rubric (informal heading) +\providecommand*{\DUrubric}[2][class-arg]{% + \subsubsection*{\centering\textit{\textmd{#2}}}}""" + +PreambleCmds.sidebar = r""" +% sidebar (text outside the main text flow) +\providecommand{\DUsidebar}[2][class-arg]{% + \begin{center} + \colorbox[gray]{0.80}{\parbox{0.9\textwidth}{#2}} + \end{center} +}""" + +PreambleCmds.subtitle = r""" +% subtitle (for topic/sidebar) +\providecommand*{\DUsubtitle}[2][class-arg]{\par\emph{#2}\smallskip}""" + +PreambleCmds.table = r"""\usepackage{longtable} +\usepackage{array} +\setlength{\extrarowheight}{2pt} +\newlength{\DUtablewidth} % internal use in tables""" + +# Options [force,almostfull] prevent spurious error messages, see +# de.comp.text.tex/2005-12/msg01855 +PreambleCmds.textcomp = """\ +\\usepackage{textcomp} % text symbol macros""" + +PreambleCmds.documenttitle = r""" +%% Document title +\title{%s} +\author{%s} +\date{%s} +\maketitle +""" + +PreambleCmds.titlereference = r""" +% titlereference role +\providecommand*{\DUroletitlereference}[1]{\textsl{#1}}""" + +PreambleCmds.title = r""" +% title for topics, admonitions and sidebar +\providecommand*{\DUtitle}[2][class-arg]{% + % call \DUtitle#1{#2} if it exists: + \ifcsname DUtitle#1\endcsname% + \csname DUtitle#1\endcsname{#2}% + \else + \smallskip\noindent\textbf{#2}\smallskip% + \fi +}""" + +PreambleCmds.topic = r""" +% topic (quote with heading) +\providecommand{\DUtopic}[2][class-arg]{% + \ifcsname DUtopic#1\endcsname% + \csname DUtopic#1\endcsname{#2}% + \else + \begin{quote}#2\end{quote} + \fi +}""" + +PreambleCmds.transition = r""" +% transition (break, fancybreak, anonymous section) +\providecommand*{\DUtransition}[1][class-arg]{% + \hspace*{\fill}\hrulefill\hspace*{\fill} + \vskip 0.5\baselineskip +}""" + + +class DocumentClass(object): + """Details of a LaTeX document class.""" + + def __init__(self, document_class, with_part=False): + self.document_class = document_class + self._with_part = with_part + self.sections = ['section', 'subsection', 'subsubsection', + 'paragraph', 'subparagraph'] + if self.document_class in ('book', 'memoir', 'report', + 'scrbook', 'scrreprt'): + self.sections.insert(0, 'chapter') + if self._with_part: + self.sections.insert(0, 'part') + + def section(self, level): + """Return the LaTeX section name for section `level`. + + The name depends on the specific document class. + Level is 1,2,3..., as level 0 is the title. + """ + + if level <= len(self.sections): + return self.sections[level-1] + else: + return self.sections[-1] + + +class Table(object): + """Manage a table while traversing. + + Maybe change to a mixin defining the visit/departs, but then + class Table internal variables are in the Translator. + + Table style might be + + :standard: horizontal and vertical lines + :booktabs: only horizontal lines (requires "booktabs" LaTeX package) + :borderless: no borders around table cells + :nolines: alias for borderless + """ + def __init__(self,translator,latex_type,table_style): + self._translator = translator + self._latex_type = latex_type + self._table_style = table_style + self._open = 0 + # miscellaneous attributes + self._attrs = {} + self._col_width = [] + self._rowspan = [] + self.stubs = [] + self._in_thead = 0 + + def open(self): + self._open = 1 + self._col_specs = [] + self.caption = [] + self._attrs = {} + self._in_head = 0 # maybe context with search + def close(self): + self._open = 0 + self._col_specs = None + self.caption = [] + self._attrs = {} + self.stubs = [] + def is_open(self): + return self._open + + def set_table_style(self, table_style): + if not table_style in ('standard','booktabs','borderless','nolines'): + return + self._table_style = table_style + + def get_latex_type(self): + return self._latex_type + + def set(self,attr,value): + self._attrs[attr] = value + def get(self,attr): + if attr in self._attrs: + return self._attrs[attr] + return None + + def get_vertical_bar(self): + if self._table_style == 'standard': + return '|' + return '' + + # horizontal lines are drawn below a row, + def get_opening(self): + if self._latex_type == 'longtable': + # otherwise longtable might move before paragraph and subparagraph + prefix = '\\leavevmode\n' + else: + prefix = '' + prefix += '\setlength{\DUtablewidth}{\linewidth}' + return '%s\n\\begin{%s}[c]' % (prefix, self._latex_type) + + def get_closing(self): + line = '' + if self._table_style == 'booktabs': + line = '\\bottomrule\n' + elif self._table_style == 'standard': + lines = '\\hline\n' + return '%s\\end{%s}' % (line,self._latex_type) + + def visit_colspec(self, node): + self._col_specs.append(node) + # "stubs" list is an attribute of the tgroup element: + self.stubs.append(node.attributes.get('stub')) + + def get_colspecs(self): + """Return column specification for longtable. + + Assumes reST line length being 80 characters. + Table width is hairy. + + === === + ABC DEF + === === + + usually gets to narrow, therefore we add 1 (fiddlefactor). + """ + width = 80 + + total_width = 0.0 + # first see if we get too wide. + for node in self._col_specs: + colwidth = float(node['colwidth']+1) / width + total_width += colwidth + self._col_width = [] + self._rowspan = [] + # donot make it full linewidth + factor = 0.93 + if total_width > 1.0: + factor /= total_width + bar = self.get_vertical_bar() + latex_table_spec = '' + for node in self._col_specs: + colwidth = factor * float(node['colwidth']+1) / width + self._col_width.append(colwidth+0.005) + self._rowspan.append(0) + latex_table_spec += '%sp{%.3f\\DUtablewidth}' % (bar, colwidth+0.005) + return latex_table_spec+bar + + def get_column_width(self): + """Return columnwidth for current cell (not multicell).""" + return '%.2f\\DUtablewidth' % self._col_width[self._cell_in_row-1] + + def get_caption(self): + if not self.caption: + return '' + caption = ''.join(self.caption) + if 1 == self._translator.thead_depth(): + return r'\caption{%s}\\' '\n' % caption + return r'\caption[]{%s (... continued)}\\' '\n' % caption + + def need_recurse(self): + if self._latex_type == 'longtable': + return 1 == self._translator.thead_depth() + return 0 + + def visit_thead(self): + self._in_thead += 1 + if self._table_style == 'standard': + return ['\\hline\n'] + elif self._table_style == 'booktabs': + return ['\\toprule\n'] + return [] + def depart_thead(self): + a = [] + #if self._table_style == 'standard': + # a.append('\\hline\n') + if self._table_style == 'booktabs': + a.append('\\midrule\n') + if self._latex_type == 'longtable': + if 1 == self._translator.thead_depth(): + a.append('\\endfirsthead\n') + else: + a.append('\\endhead\n') + a.append(r'\multicolumn{%d}{c}' % len(self._col_specs) + + r'{\hfill ... continued on next page} \\') + a.append('\n\\endfoot\n\\endlastfoot\n') + # for longtable one could add firsthead, foot and lastfoot + self._in_thead -= 1 + return a + def visit_row(self): + self._cell_in_row = 0 + def depart_row(self): + res = [' \\\\\n'] + self._cell_in_row = None # remove cell counter + for i in range(len(self._rowspan)): + if (self._rowspan[i]>0): + self._rowspan[i] -= 1 + + if self._table_style == 'standard': + rowspans = [i+1 for i in range(len(self._rowspan)) + if (self._rowspan[i]<=0)] + if len(rowspans)==len(self._rowspan): + res.append('\\hline\n') + else: + cline = '' + rowspans.reverse() + # TODO merge clines + while 1: + try: + c_start = rowspans.pop() + except: + break + cline += '\\cline{%d-%d}\n' % (c_start,c_start) + res.append(cline) + return res + + def set_rowspan(self,cell,value): + try: + self._rowspan[cell] = value + except: + pass + def get_rowspan(self,cell): + try: + return self._rowspan[cell] + except: + return 0 + def get_entry_number(self): + return self._cell_in_row + def visit_entry(self): + self._cell_in_row += 1 + def is_stub_column(self): + if len(self.stubs) >= self._cell_in_row: + return self.stubs[self._cell_in_row-1] + return False + + +class LaTeXTranslator(nodes.NodeVisitor): + + # When options are given to the documentclass, latex will pass them + # to other packages, as done with babel. + # Dummy settings might be taken from document settings + + # Config setting defaults + # ----------------------- + + # TODO: use mixins for different implementations. + # list environment for docinfo. else tabularx + ## use_optionlist_for_docinfo = False # TODO: NOT YET IN USE + + # Use compound enumerations (1.A.1.) + compound_enumerators = 0 + + # If using compound enumerations, include section information. + section_prefix_for_enumerators = 0 + + # This is the character that separates the section ("." subsection ...) + # prefix from the regular list enumerator. + section_enumerator_separator = '-' + + # default link color + hyperlink_color = 'blue' + + # Auxiliary variables + # ------------------- + + has_latex_toc = False # is there a toc in the doc? (needed by minitoc) + is_toc_list = False # is the current bullet_list a ToC? + section_level = 0 + + # Flags to encode(): + # inside citation reference labels underscores dont need to be escaped + inside_citation_reference_label = False + verbatim = False # do not encode + insert_non_breaking_blanks = False # replace blanks by "~" + insert_newline = False # add latex newline commands + literal = False # literal text (block or inline) + + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + # Reporter + # ~~~~~~~~ + self.warn = self.document.reporter.warning + self.error = self.document.reporter.error + + # Settings + # ~~~~~~~~ + self.settings = settings = document.settings + self.latex_encoding = self.to_latex_encoding(settings.output_encoding) + self.use_latex_toc = settings.use_latex_toc + self.use_latex_docinfo = settings.use_latex_docinfo + self._use_latex_citations = settings.use_latex_citations + self.embed_stylesheet = settings.embed_stylesheet + self._reference_label = settings.reference_label + self.hyperlink_color = settings.hyperlink_color + self.compound_enumerators = settings.compound_enumerators + self.font_encoding = settings.font_encoding + self.section_prefix_for_enumerators = ( + settings.section_prefix_for_enumerators) + self.section_enumerator_separator = ( + settings.section_enumerator_separator.replace('_', '\\_')) + # literal blocks: + self.literal_block_env = '' + self.literal_block_options = '' + if settings.literal_block_env != '': + (none, + self.literal_block_env, + self.literal_block_options, + none ) = re.split('(\w+)(.*)', settings.literal_block_env) + elif settings.use_verbatim_when_possible: + self.literal_block_env = 'verbatim' + # + if self.settings.use_bibtex: + self.bibtex = self.settings.use_bibtex.split(',',1) + # TODO avoid errors on not declared citations. + else: + self.bibtex = None + # language: + # (labels, bibliographic_fields, and author_separators) + self.language = languages.get_language(settings.language_code) + self.babel = Babel(settings.language_code) + self.author_separator = self.language.author_separators[0] + self.d_options = [self.settings.documentoptions, + self.babel.get_language()] + self.d_options = ','.join([opt for opt in self.d_options if opt]) + self.d_class = DocumentClass(settings.documentclass, + settings.use_part_section) + # graphic package options: + if self.settings.graphicx_option == '': + self.graphicx_package = r'\usepackage{graphicx}' + elif self.settings.graphicx_option.lower() == 'auto': + self.graphicx_package = PreambleCmds.graphicx_auto + else: + self.graphicx_package = (r'\usepackage[%s]{graphicx}' % + self.settings.graphicx_option) + # footnotes: + self.docutils_footnotes = settings.docutils_footnotes + if settings.use_latex_footnotes: + self.docutils_footnotes = True + self.warn('`use_latex_footnotes` is deprecated. ' + 'The setting has been renamed to `docutils_footnotes` ' + 'and the alias will be removed in a future version.') + self.figure_footnotes = settings.figure_footnotes + if self.figure_footnotes: + self.docutils_footnotes = True + self.warn('The "figure footnotes" workaround/setting is strongly ' + 'deprecated and will be removed in a future version.') + + # Output collection stacks + # ~~~~~~~~~~~~~~~~~~~~~~~~ + + # Document parts + self.head_prefix = [r'\documentclass[%s]{%s}' % + (self.d_options, self.settings.documentclass)] + self.requirements = SortableDict() # made a list in depart_document() + self.latex_preamble = [settings.latex_preamble] + self.stylesheet = [] + self.fallbacks = SortableDict() # made a list in depart_document() + self.pdfsetup = [] # PDF properties (hyperref package) + self.title = [] + self.subtitle = [] + ## self.body_prefix = ['\\begin{document}\n'] + self.body_pre_docinfo = [] # title data and \maketitle + self.docinfo = [] + self.dedication = [] + self.abstract = [] + self.body = [] + ## self.body_suffix = ['\\end{document}\n'] + + # A heterogenous stack used in conjunction with the tree traversal. + # Make sure that the pops correspond to the pushes: + self.context = [] + + # Title metadata: + self.title_labels = [] + self.subtitle_labels = [] + # (if use_latex_docinfo: collects lists of + # author/organization/contact/address lines) + self.author_stack = [] + # date (the default supresses the "auto-date" feature of \maketitle) + self.date = [] + + # PDF properties: pdftitle, pdfauthor + # TODO?: pdfcreator, pdfproducer, pdfsubject, pdfkeywords + self.pdfinfo = [] + self.pdfauthor = [] + + # Stack of section counters so that we don't have to use_latex_toc. + # This will grow and shrink as processing occurs. + # Initialized for potential first-level sections. + self._section_number = [0] + + # The current stack of enumerations so that we can expand + # them into a compound enumeration. + self._enumeration_counters = [] + # The maximum number of enumeration counters we've used. + # If we go beyond this number, we need to create a new + # counter; otherwise, just reuse an old one. + self._max_enumeration_counters = 0 + + self._bibitems = [] + + # object for a table while proccessing. + self.table_stack = [] + self.active_table = Table(self, 'longtable', settings.table_style) + + # Where to collect the output of visitor methods (default: body) + self.out = self.body + self.out_stack = [] # stack of output collectors + + # Process settings + # ~~~~~~~~~~~~~~~~ + + # Static requirements + # TeX font encoding + if self.font_encoding: + encodings = [r'\usepackage[%s]{fontenc}' % self.font_encoding] + else: + encodings = [r'%\usepackage[OT1]{fontenc}'] # just a comment + # Docutils' output-encoding => TeX input encoding: + if self.latex_encoding != 'ascii': + encodings.append(r'\usepackage[%s]{inputenc}' + % self.latex_encoding) + self.requirements['_static'] = '\n'.join( + encodings + [ + r'\usepackage{ifthen}', + # multi-language support (language is in document options) + '\\usepackage{babel}%s' % self.babel.setup, + ]) + # page layout with typearea (if there are relevant document options) + if (settings.documentclass.find('scr') == -1 and + (self.d_options.find('DIV') != -1 or + self.d_options.find('BCOR') != -1)): + self.requirements['typearea'] = r'\usepackage{typearea}' + + # Stylesheets + # get list of style sheets from settings + styles = utils.get_stylesheet_list(settings) + # adapt path if --stylesheet_path is used + if settings.stylesheet_path and not(self.embed_stylesheet): + styles = [utils.relative_path(settings._destination, sheet) + for sheet in styles] + for sheet in styles: + (base, ext) = os.path.splitext(sheet) + is_package = ext in ['.sty', ''] + if self.embed_stylesheet: + if is_package: + sheet = base + '.sty' # adapt package name + # wrap in \makeatletter, \makeatother + wrapper = PreambleCmds.embedded_package_wrapper + else: + wrapper = '%% embedded stylesheet: %s\n%s' + settings.record_dependencies.add(sheet) + self.stylesheet.append(wrapper % + (sheet, io.FileInput(source_path=sheet, encoding='utf-8').read())) + else: # link to style sheet + if is_package: + self.stylesheet.append(r'\usepackage{%s}' % base) + else: + self.stylesheet.append(r'\input{%s}' % sheet) + + # PDF setup + if self.hyperlink_color == '0': + self.hyperlink_color = 'black' + self.colorlinks = 'false' + else: + self.colorlinks = 'true' + + # LaTeX Toc + # include all supported sections in toc and PDF bookmarks + # (or use documentclass-default (as currently))? + ## if self.use_latex_toc: + ## self.requirements['tocdepth'] = (r'\setcounter{tocdepth}{%d}' % + ## len(self.d_class.sections)) + + # LaTeX section numbering + if not self.settings.sectnum_xform: # section numbering by LaTeX: + # sectnum_depth: + # None "sectnum" directive without depth arg -> LaTeX default + # 0 no "sectnum" directive -> no section numbers + # else value of the "depth" argument: translate to LaTeX level + # -1 part (0 with "article" document class) + # 0 chapter (missing in "article" document class) + # 1 section + # 2 subsection + # 3 subsubsection + # 4 paragraph + # 5 subparagraph + if settings.sectnum_depth is not None: + # limit to supported levels + sectnum_depth = min(settings.sectnum_depth, + len(self.d_class.sections)) + # adjust to document class and use_part_section settings + if 'chapter' in self.d_class.sections: + sectnum_depth -= 1 + if self.d_class.sections[0] == 'part': + sectnum_depth -= 1 + self.requirements['sectnum_depth'] = ( + r'\setcounter{secnumdepth}{%d}' % sectnum_depth) + # start with specified number: + if (hasattr(settings, 'sectnum_start') and + settings.sectnum_start != 1): + self.requirements['sectnum_start'] = ( + r'\setcounter{%s}{%d}' % (self.d_class.sections[0], + settings.sectnum_start-1)) + # currently ignored (configure in a stylesheet): + ## settings.sectnum_prefix + ## settings.sectnum_suffix + + + # Auxiliary Methods + # ----------------- + + def to_latex_encoding(self,docutils_encoding): + """Translate docutils encoding name into LaTeX's. + + Default method is remove "-" and "_" chars from docutils_encoding. + """ + tr = { 'iso-8859-1': 'latin1', # west european + 'iso-8859-2': 'latin2', # east european + 'iso-8859-3': 'latin3', # esperanto, maltese + 'iso-8859-4': 'latin4', # north european, scandinavian, baltic + 'iso-8859-5': 'iso88595', # cyrillic (ISO) + 'iso-8859-9': 'latin5', # turkish + 'iso-8859-15': 'latin9', # latin9, update to latin1. + 'mac_cyrillic': 'maccyr', # cyrillic (on Mac) + 'windows-1251': 'cp1251', # cyrillic (on Windows) + 'koi8-r': 'koi8-r', # cyrillic (Russian) + 'koi8-u': 'koi8-u', # cyrillic (Ukrainian) + 'windows-1250': 'cp1250', # + 'windows-1252': 'cp1252', # + 'us-ascii': 'ascii', # ASCII (US) + # unmatched encodings + #'': 'applemac', + #'': 'ansinew', # windows 3.1 ansi + #'': 'ascii', # ASCII encoding for the range 32--127. + #'': 'cp437', # dos latin us + #'': 'cp850', # dos latin 1 + #'': 'cp852', # dos latin 2 + #'': 'decmulti', + #'': 'latin10', + #'iso-8859-6': '' # arabic + #'iso-8859-7': '' # greek + #'iso-8859-8': '' # hebrew + #'iso-8859-10': '' # latin6, more complete iso-8859-4 + } + encoding = docutils_encoding.lower() + if encoding in tr: + return tr[encoding] + # convert: latin-1, latin_1, utf-8 and similar things + encoding = encoding.replace('_', '').replace('-', '') + # strip the error handler + return encoding.split(':')[0] + + def language_label(self, docutil_label): + return self.language.labels[docutil_label] + + def ensure_math(self, text): + if not hasattr(self, 'ensure_math_re'): + chars = { # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div + 'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' , # ¬±²³µ¹×÷ + # TODO?: use texcomp instead. + } + self.ensure_math_re = re.compile('([%s])' % chars['latin1']) + text = self.ensure_math_re.sub(r'\\ensuremath{\1}', text) + return text + + def encode(self, text): + """Return text with 'problematic' characters escaped. + + Escape the ten special printing characters ``# $ % & ~ _ ^ \ { }``, + square brackets ``[ ]``, double quotes and (in OT1) ``< | >``. + + Separate ``-`` (and more in literal text) to prevent input ligatures. + + Translate non-supported Unicode characters. + """ + if self.verbatim: + return text + # Separate compound characters, e.g. '--' to '-{}-'. + separate_chars = '-' + # In monospace-font, we also separate ',,', '``' and "''" and some + # other characters which can't occur in non-literal text. + if self.literal: + separate_chars += ',`\'"<>' + # LaTeX encoding maps: + special_chars = { + ord('#'): ur'\#', + ord('$'): ur'\$', + ord('%'): ur'\%', + ord('&'): ur'\&', + ord('~'): ur'\textasciitilde{}', + ord('_'): ur'\_', + ord('^'): ur'\textasciicircum{}', + ord('\\'): ur'\textbackslash{}', + ord('{'): ur'\{', + ord('}'): ur'\}', + # Square brackets are ordinary chars and cannot be escaped with '\', + # so we put them in a group '{[}'. (Alternative: ensure that all + # macros with optional arguments are terminated with {} and text + # inside any optional argument is put in a group ``[{text}]``). + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + ord('['): ur'{[}', + ord(']'): ur'{]}' + } + # Unicode chars that are not recognized by LaTeX's utf8 encoding + unsupported_unicode_chars = { + 0x00A0: ur'~', # NO-BREAK SPACE + 0x00AD: ur'\-', # SOFT HYPHEN + # + 0x2011: ur'\hbox{-}', # NON-BREAKING HYPHEN + 0x21d4: ur'$\Leftrightarrow$', + # Docutils footnote symbols: + 0x2660: ur'$\spadesuit$', + 0x2663: ur'$\clubsuit$', + } + # Unicode chars that are recognized by LaTeX's utf8 encoding + unicode_chars = { + 0x200C: ur'\textcompwordmark', # ZERO WIDTH NON-JOINER + 0x2013: ur'\textendash{}', + 0x2014: ur'\textemdash{}', + 0x2018: ur'\textquoteleft{}', + 0x2019: ur'\textquoteright{}', + 0x201A: ur'\quotesinglbase{}', # SINGLE LOW-9 QUOTATION MARK + 0x201C: ur'\textquotedblleft{}', + 0x201D: ur'\textquotedblright{}', + 0x201E: ur'\quotedblbase{}', # DOUBLE LOW-9 QUOTATION MARK + 0x2030: ur'\textperthousand{}', # PER MILLE SIGN + 0x2031: ur'\textpertenthousand{}', # PER TEN THOUSAND SIGN + 0x2039: ur'\guilsinglleft{}', + 0x203A: ur'\guilsinglright{}', + 0x2423: ur'\textvisiblespace{}', # OPEN BOX + 0x2020: ur'\dag{}', + 0x2021: ur'\ddag{}', + 0x2026: ur'\dots{}', + 0x2122: ur'\texttrademark{}', + } + # Unicode chars that require a feature/package to render + pifont_chars = { + 0x2665: ur'\ding{170}', # black heartsuit + 0x2666: ur'\ding{169}', # black diamondsuit + } + # recognized with 'utf8', if textcomp is loaded + textcomp_chars = { + # Latin-1 Supplement + 0x00a2: ur'\textcent{}', # ¢ CENT SIGN + 0x00a4: ur'\textcurrency{}', # ¤ CURRENCY SYMBOL + 0x00a5: ur'\textyen{}', # ¥ YEN SIGN + 0x00a6: ur'\textbrokenbar{}', # ¦ BROKEN BAR + 0x00a7: ur'\textsection{}', # § SECTION SIGN + 0x00a8: ur'\textasciidieresis{}', # ¨ DIAERESIS + 0x00a9: ur'\textcopyright{}', # © COPYRIGHT SIGN + 0x00aa: ur'\textordfeminine{}', # ª FEMININE ORDINAL INDICATOR + 0x00ac: ur'\textlnot{}', # ¬ NOT SIGN + 0x00ae: ur'\textregistered{}', # ® REGISTERED SIGN + 0x00af: ur'\textasciimacron{}', # ¯ MACRON + 0x00b0: ur'\textdegree{}', # ° DEGREE SIGN + 0x00b1: ur'\textpm{}', # ± PLUS-MINUS SIGN + 0x00b2: ur'\texttwosuperior{}', # ² SUPERSCRIPT TWO + 0x00b3: ur'\textthreesuperior{}', # ³ SUPERSCRIPT THREE + 0x00b4: ur'\textasciiacute{}', # ´ ACUTE ACCENT + 0x00b5: ur'\textmu{}', # µ MICRO SIGN + 0x00b6: ur'\textparagraph{}', # ¶ PILCROW SIGN # not equal to \textpilcrow + 0x00b9: ur'\textonesuperior{}', # ¹ SUPERSCRIPT ONE + 0x00ba: ur'\textordmasculine{}', # º MASCULINE ORDINAL INDICATOR + 0x00bc: ur'\textonequarter{}', # 1/4 FRACTION + 0x00bd: ur'\textonehalf{}', # 1/2 FRACTION + 0x00be: ur'\textthreequarters{}', # 3/4 FRACTION + 0x00d7: ur'\texttimes{}', # × MULTIPLICATION SIGN + 0x00f7: ur'\textdiv{}', # ÷ DIVISION SIGN + # + 0x0192: ur'\textflorin{}', # LATIN SMALL LETTER F WITH HOOK + 0x02b9: ur'\textasciiacute{}', # MODIFIER LETTER PRIME + 0x02ba: ur'\textacutedbl{}', # MODIFIER LETTER DOUBLE PRIME + 0x2016: ur'\textbardbl{}', # DOUBLE VERTICAL LINE + 0x2022: ur'\textbullet{}', # BULLET + 0x2032: ur'\textasciiacute{}', # PRIME + 0x2033: ur'\textacutedbl{}', # DOUBLE PRIME + 0x2035: ur'\textasciigrave{}', # REVERSED PRIME + 0x2036: ur'\textgravedbl{}', # REVERSED DOUBLE PRIME + 0x203b: ur'\textreferencemark{}', # REFERENCE MARK + 0x203d: ur'\textinterrobang{}', # INTERROBANG + 0x2044: ur'\textfractionsolidus{}', # FRACTION SLASH + 0x2045: ur'\textlquill{}', # LEFT SQUARE BRACKET WITH QUILL + 0x2046: ur'\textrquill{}', # RIGHT SQUARE BRACKET WITH QUILL + 0x2052: ur'\textdiscount{}', # COMMERCIAL MINUS SIGN + 0x20a1: ur'\textcolonmonetary{}', # COLON SIGN + 0x20a3: ur'\textfrenchfranc{}', # FRENCH FRANC SIGN + 0x20a4: ur'\textlira{}', # LIRA SIGN + 0x20a6: ur'\textnaira{}', # NAIRA SIGN + 0x20a9: ur'\textwon{}', # WON SIGN + 0x20ab: ur'\textdong{}', # DONG SIGN + 0x20ac: ur'\texteuro{}', # EURO SIGN + 0x20b1: ur'\textpeso{}', # PESO SIGN + 0x20b2: ur'\textguarani{}', # GUARANI SIGN + 0x2103: ur'\textcelsius{}', # DEGREE CELSIUS + 0x2116: ur'\textnumero{}', # NUMERO SIGN + 0x2117: ur'\textcircledP{}', # SOUND RECORDING COYRIGHT + 0x211e: ur'\textrecipe{}', # PRESCRIPTION TAKE + 0x2120: ur'\textservicemark{}', # SERVICE MARK + 0x2122: ur'\texttrademark{}', # TRADE MARK SIGN + 0x2126: ur'\textohm{}', # OHM SIGN + 0x2127: ur'\textmho{}', # INVERTED OHM SIGN + 0x212e: ur'\textestimated{}', # ESTIMATED SYMBOL + 0x2190: ur'\textleftarrow{}', # LEFTWARDS ARROW + 0x2191: ur'\textuparrow{}', # UPWARDS ARROW + 0x2192: ur'\textrightarrow{}', # RIGHTWARDS ARROW + 0x2193: ur'\textdownarrow{}', # DOWNWARDS ARROW + 0x2212: ur'\textminus{}', # MINUS SIGN + 0x2217: ur'\textasteriskcentered{}', # ASTERISK OPERATOR + 0x221a: ur'\textsurd{}', # SQUARE ROOT + 0x2422: ur'\textblank{}', # BLANK SYMBOL + 0x25e6: ur'\textopenbullet{}', # WHITE BULLET + 0x25ef: ur'\textbigcircle{}', # LARGE CIRCLE + 0x266a: ur'\textmusicalnote{}', # EIGHTH NOTE + 0x26ad: ur'\textmarried{}', # MARRIAGE SYMBOL + 0x26ae: ur'\textdivorced{}', # DIVORCE SYMBOL + 0x27e8: ur'\textlangle{}', # MATHEMATICAL LEFT ANGLE BRACKET + 0x27e9: ur'\textrangle{}', # MATHEMATICAL RIGHT ANGLE BRACKET + } + # TODO: greek alphabet ... ? + # see also LaTeX codec + # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124 + # and unimap.py from TeXML + + # set up the translation table: + table = special_chars + # keep the underscore in citation references + if self.inside_citation_reference_label: + del(table[ord('_')]) + # Workarounds for OT1 font-encoding + if self.font_encoding in ['OT1', '']: + # * out-of-order characters in cmtt + if self.literal: + # replace underscore by underlined blank, + # because this has correct width. + table[ord('_')] = u'\\underline{~}' + # the backslash doesn't work, so we use a mirrored slash. + # \reflectbox is provided by graphicx: + self.requirements['graphicx'] = self.graphicx_package + table[ord('\\')] = ur'\reflectbox{/}' + # * ``< | >`` come out as different chars (except for cmtt): + else: + table[ord('|')] = ur'\textbar{}' + table[ord('<')] = ur'\textless{}' + table[ord('>')] = ur'\textgreater{}' + if self.insert_non_breaking_blanks: + table[ord(' ')] = ur'~' + if self.literal: + # double quotes are 'active' in some languages + table[ord('"')] = self.babel.literal_double_quote + else: + text = self.babel.quote_quotes(text) + # Unicode chars: + table.update(unsupported_unicode_chars) + table.update(pifont_chars) + if not self.latex_encoding.startswith('utf8'): + table.update(unicode_chars) + table.update(textcomp_chars) + # Characters that require a feature/package to render + for ch in text: + if ord(ch) in pifont_chars: + self.requirements['pifont'] = '\\usepackage{pifont}' + if ord(ch) in textcomp_chars: + self.requirements['textcomp'] = PreambleCmds.textcomp + + text = text.translate(table) + + # Break up input ligatures + for char in separate_chars * 2: + # Do it twice ("* 2") because otherwise we would replace + # '---' by '-{}--'. + text = text.replace(char + char, char + '{}' + char) + # Literal line breaks (in address or literal blocks): + if self.insert_newline: + # for blank lines, insert a protected space, to avoid + # ! LaTeX Error: There's no line here to end. + textlines = [line + '~'*(not line.lstrip()) + for line in text.split('\n')] + text = '\\\\\n'.join(textlines) + if self.literal and not self.insert_non_breaking_blanks: + # preserve runs of spaces but allow wrapping + text = text.replace(' ', ' ~') + if not self.latex_encoding.startswith('utf8'): + text = self.ensure_math(text) + return text + + def attval(self, text, + whitespace=re.compile('[\n\r\t\v\f]')): + """Cleanse, encode, and return attribute value text.""" + return self.encode(whitespace.sub(' ', text)) + + # TODO: is this used anywhere? (update or delete) + ## def astext(self): + ## """Assemble document parts and return as string.""" + ## head = '\n'.join(self.head_prefix + self.stylesheet + self.head) + ## body = ''.join(self.body_prefix + self.body + self.body_suffix) + ## return head + '\n' + body + + def is_inline(self, node): + """Check whether a node represents an inline element""" + return isinstance(node.parent, nodes.TextElement) + + def append_hypertargets(self, node): + """Append hypertargets for all ids of `node`""" + # hypertarget places the anchor at the target's baseline, + # so we raise it explicitely + self.out.append('%\n'.join(['\\raisebox{1em}{\\hypertarget{%s}{}}' % + id for id in node['ids']])) + + def ids_to_labels(self, node, set_anchor=True): + """Return list of label definitions for all ids of `node` + + If `set_anchor` is True, an anchor is set with \phantomsection. + """ + labels = ['\\label{%s}' % id for id in node.get('ids', [])] + if set_anchor and labels: + labels.insert(0, '\\phantomsection') + return labels + + def push_output_collector(self, new_out): + self.out_stack.append(self.out) + self.out = new_out + + def pop_output_collector(self): + self.out = self.out_stack.pop() + + # Visitor methods + # --------------- + + def visit_Text(self, node): + self.out.append(self.encode(node.astext())) + + def depart_Text(self, node): + pass + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + self.depart_docinfo_item(node) + + def visit_admonition(self, node): + self.fallbacks['admonition'] = PreambleCmds.admonition + if 'error' in node['classes']: + self.fallbacks['error'] = PreambleCmds.error + # strip the generic 'admonition' from the list of classes + node['classes'] = [cls for cls in node['classes'] + if cls != 'admonition'] + self.out.append('\n\\DUadmonition[%s]{\n' % ','.join(node['classes'])) + + def depart_admonition(self, node=None): + self.out.append('}\n') + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + def depart_author(self, node): + self.depart_docinfo_item(node) + + def visit_authors(self, node): + # not used: visit_author is called anyway for each author. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + self.out.append( '%\n\\begin{quote}\n') + + def depart_block_quote(self, node): + self.out.append( '\n\\end{quote}\n') + + def visit_bullet_list(self, node): + if self.is_toc_list: + self.out.append( '%\n\\begin{list}{}{}\n' ) + else: + self.out.append( '%\n\\begin{itemize}\n' ) + + def depart_bullet_list(self, node): + if self.is_toc_list: + self.out.append( '\n\\end{list}\n' ) + else: + self.out.append( '\n\\end{itemize}\n' ) + + def visit_superscript(self, node): + self.out.append(r'\textsuperscript{') + if node['classes']: + self.visit_inline(node) + + def depart_superscript(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_subscript(self, node): + self.out.append(r'\textsubscript{') # requires `fixltx2e` + if node['classes']: + self.visit_inline(node) + + def depart_subscript(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_caption(self, node): + self.out.append( '\\caption{' ) + + def depart_caption(self, node): + self.out.append('}\n') + + def visit_title_reference(self, node): + self.fallbacks['titlereference'] = PreambleCmds.titlereference + self.out.append(r'\DUroletitlereference{') + if node['classes']: + self.visit_inline(node) + + def depart_title_reference(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append( '}' ) + + def visit_citation(self, node): + # TODO maybe use cite bibitems + if self._use_latex_citations: + self.push_output_collector([]) + else: + # TODO: do we need these? + ## self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats + self.out.append(r'\begin{figure}[b]') + self.append_hypertargets(node) + + def depart_citation(self, node): + if self._use_latex_citations: + label = self.out[0] + text = ''.join(self.out[1:]) + self._bibitems.append([label, text]) + self.pop_output_collector() + else: + self.out.append('\\end{figure}\n') + + def visit_citation_reference(self, node): + if self._use_latex_citations: + if not self.inside_citation_reference_label: + self.out.append(r'\cite{') + self.inside_citation_reference_label = 1 + else: + assert self.body[-1] in (' ', '\n'),\ + 'unexpected non-whitespace while in reference label' + del self.body[-1] + else: + href = '' + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + self.out.append('[\\hyperlink{%s}{' % href) + + def depart_citation_reference(self, node): + if self._use_latex_citations: + followup_citation = False + # check for a following citation separated by a space or newline + next_siblings = node.traverse(descend=0, siblings=1, + include_self=0) + if len(next_siblings) > 1: + next = next_siblings[0] + if (isinstance(next, nodes.Text) and + next.astext() in (' ', '\n')): + if next_siblings[1].__class__ == node.__class__: + followup_citation = True + if followup_citation: + self.out.append(',') + else: + self.out.append('}') + self.inside_citation_reference_label = False + else: + self.out.append('}]') + + def visit_classifier(self, node): + self.out.append( '(\\textbf{' ) + + def depart_classifier(self, node): + self.out.append( '})\n' ) + + def visit_colspec(self, node): + self.active_table.visit_colspec(node) + + def depart_colspec(self, node): + pass + + def visit_comment(self, node): + # Precede every line with a comment sign, wrap in newlines + self.out.append('\n%% %s\n' % node.astext().replace('\n', '\n% ')) + raise nodes.SkipNode + + def depart_comment(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + def depart_contact(self, node): + self.depart_docinfo_item(node) + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def depart_copyright(self, node): + self.depart_docinfo_item(node) + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def depart_date(self, node): + self.depart_docinfo_item(node) + + def visit_decoration(self, node): + # header and footer + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + self.out.append('\n') + + def visit_definition_list(self, node): + self.out.append( '%\n\\begin{description}\n' ) + + def depart_definition_list(self, node): + self.out.append( '\\end{description}\n' ) + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_description(self, node): + self.out.append(' ') + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self.push_output_collector(self.docinfo) + + def depart_docinfo(self, node): + self.pop_output_collector() + # Some itmes (e.g. author) end up at other places + if self.docinfo: + # tabularx: automatic width of columns, no page breaks allowed. + self.requirements['tabularx'] = r'\usepackage{tabularx}' + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['docinfo'] = PreambleCmds.docinfo + # + self.docinfo.insert(0, '\n% Docinfo\n' + '\\begin{center}\n' + '\\begin{tabularx}{\\DUdocinfowidth}{lX}\n') + self.docinfo.append('\\end{tabularx}\n' + '\\end{center}\n') + + def visit_docinfo_item(self, node, name): + if name == 'author': + self.pdfauthor.append(self.attval(node.astext())) + if self.use_latex_docinfo: + if name in ('author', 'organization', 'contact', 'address'): + # We attach these to the last author. If any of them precedes + # the first author, put them in a separate "author" group + # (in lack of better semantics). + if name == 'author' or not self.author_stack: + self.author_stack.append([]) + if name == 'address': # newlines are meaningful + self.insert_newline = 1 + text = self.encode(node.astext()) + self.insert_newline = False + else: + text = self.attval(node.astext()) + self.author_stack[-1].append(text) + raise nodes.SkipNode + elif name == 'date': + self.date.append(self.attval(node.astext())) + raise nodes.SkipNode + self.out.append('\\textbf{%s}: &\n\t' % self.language_label(name)) + if name == 'address': + self.insert_newline = 1 + self.out.append('{\\raggedright\n') + self.context.append(' } \\\\\n') + else: + self.context.append(' \\\\\n') + + def depart_docinfo_item(self, node): + self.out.append(self.context.pop()) + # for address we did set insert_newline + self.insert_newline = False + + def visit_doctest_block(self, node): + self.visit_literal_block(node) + + def depart_doctest_block(self, node): + self.depart_literal_block(node) + + def visit_document(self, node): + # titled document? + if (self.use_latex_docinfo or len(node) and + isinstance(node[0], nodes.title)): + self.title_labels += self.ids_to_labels(node) + + def depart_document(self, node): + # Complete header with information gained from walkabout + # a) conditional requirements (before style sheet) + self.requirements = self.requirements.sortedvalues() + # b) coditional fallback definitions (after style sheet) + self.fallbacks = self.fallbacks.sortedvalues() + # c) PDF properties + self.pdfsetup.append(PreambleCmds.linking % (self.colorlinks, + self.hyperlink_color, + self.hyperlink_color)) + if self.pdfauthor: + authors = self.author_separator.join(self.pdfauthor) + self.pdfinfo.append(' pdfauthor={%s}' % authors) + if self.pdfinfo: + self.pdfsetup += [r'\hypersetup{'] + self.pdfinfo + ['}'] + # Complete body + # a) document title (part 'body_prefix'): + # NOTE: Docutils puts author/date into docinfo, so normally + # we do not want LaTeX author/date handling (via \maketitle). + # To deactivate it, we add \title, \author, \date, + # even if the arguments are empty strings. + if self.title or self.author_stack or self.date: + authors = ['\\\\\n'.join(author_entry) + for author_entry in self.author_stack] + title = [''.join(self.title)] + self.title_labels + if self.subtitle: + title += [r'\\ % subtitle', + r'\large{%s}' % ''.join(self.subtitle) + ] + self.subtitle_labels + self.body_pre_docinfo.append(PreambleCmds.documenttitle % ( + '%\n '.join(title), + ' \\and\n'.join(authors), + ', '.join(self.date))) + # b) bibliography + # TODO insertion point of bibliography should be configurable. + if self._use_latex_citations and len(self._bibitems)>0: + if not self.bibtex: + widest_label = '' + for bi in self._bibitems: + if len(widest_label) self._max_enumeration_counters: + self._max_enumeration_counters = len(self._enumeration_counters) + self.out.append('\\newcounter{%s}\n' % counter_name) + else: + self.out.append('\\setcounter{%s}{0}\n' % counter_name) + + self.out.append('\\begin{list}{%s\\%s{%s}%s}\n' % + (enum_prefix,enum_type,counter_name,enum_suffix)) + self.out.append('{\n') + self.out.append('\\usecounter{%s}\n' % counter_name) + # set start after usecounter, because it initializes to zero. + if 'start' in node: + self.out.append('\\addtocounter{%s}{%d}\n' % + (counter_name,node['start']-1)) + ## set rightmargin equal to leftmargin + self.out.append('\\setlength{\\rightmargin}{\\leftmargin}\n') + self.out.append('}\n') + + def depart_enumerated_list(self, node): + self.out.append('\\end{list}\n') + self._enumeration_counters.pop() + + def visit_field(self, node): + # real output is done in siblings: _argument, _body, _name + pass + + def depart_field(self, node): + self.out.append('\n') + ##self.out.append('%[depart_field]\n') + + def visit_field_argument(self, node): + self.out.append('%[visit_field_argument]\n') + + def depart_field_argument(self, node): + self.out.append('%[depart_field_argument]\n') + + def visit_field_body(self, node): + pass + + def depart_field_body(self, node): + if self.out is self.docinfo: + self.out.append(r'\\') + + def visit_field_list(self, node): + if self.out is not self.docinfo: + self.fallbacks['fieldlist'] = PreambleCmds.fieldlist + self.out.append('%\n\\begin{DUfieldlist}\n') + + def depart_field_list(self, node): + if self.out is not self.docinfo: + self.out.append('\\end{DUfieldlist}\n') + + def visit_field_name(self, node): + if self.out is self.docinfo: + self.out.append('\\textbf{') + else: + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + self.out.append('\\item[{') + + def depart_field_name(self, node): + if self.out is self.docinfo: + self.out.append('}: &') + else: + self.out.append(':}]') + + def visit_figure(self, node): + self.requirements['float_settings'] = PreambleCmds.float_settings + # ! the 'align' attribute should set "outer alignment" ! + # For "inner alignment" use LaTeX default alignment (similar to HTML) + ## if ('align' not in node.attributes or + ## node.attributes['align'] == 'center'): + ## align = '\n\\centering' + ## align_end = '' + ## else: + ## # TODO non vertical space for other alignments. + ## align = '\\begin{flush%s}' % node.attributes['align'] + ## align_end = '\\end{flush%s}' % node.attributes['align'] + ## self.out.append( '\\begin{figure}%s\n' % align ) + ## self.context.append( '%s\\end{figure}\n' % align_end ) + self.out.append('\\begin{figure}') + if node.get('ids'): + self.out += ['\n'] + self.ids_to_labels(node) + + def depart_figure(self, node): + self.out.append('\\end{figure}\n') + + def visit_footer(self, node): + self.push_output_collector([]) + self.out.append(r'\newcommand{\DUfooter}{') + + def depart_footer(self, node): + self.out.append('}') + self.requirements['~footer'] = ''.join(self.out) + self.pop_output_collector() + + def visit_footnote(self, node): + try: + backref = node['backrefs'][0] + except IndexError: + backref = node['ids'][0] # no backref, use self-ref instead + if self.settings.figure_footnotes: + self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats + self.out.append('\\begin{figure}[b]') + self.append_hypertargets(node) + if node.get('id') == node.get('name'): # explicite label + self.out += self.ids_to_labels(node) + elif self.docutils_footnotes: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + num,text = node.astext().split(None,1) + if self.settings.footnote_references == 'brackets': + num = '[%s]' % num + self.out.append('%%\n\\DUfootnotetext{%s}{%s}{%s}{' % + (node['ids'][0], backref, self.encode(num))) + if node['ids'] == node['names']: + self.out += self.ids_to_labels(node) + # mask newline to prevent spurious whitespace: + self.out.append('%') + ## else: # TODO: "real" LaTeX \footnote{}s + + def depart_footnote(self, node): + if self.figure_footnotes: + self.out.append('\\end{figure}\n') + else: + self.out.append('}\n') + + def visit_footnote_reference(self, node): + href = '' + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + # if not self.docutils_footnotes: + # TODO: insert footnote content at (or near) this place + # print "footnote-ref to", node['refid'] + # footnotes = (self.document.footnotes + + # self.document.autofootnotes + + # self.document.symbol_footnotes) + # for footnote in footnotes: + # # print footnote['ids'] + # if node.get('refid', '') in footnote['ids']: + # print 'matches', footnote['ids'] + format = self.settings.footnote_references + if format == 'brackets': + self.append_hypertargets(node) + self.out.append('\\hyperlink{%s}{[' % href) + self.context.append(']}') + else: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + self.out.append(r'\DUfootnotemark{%s}{%s}{' % + (node['ids'][0], href)) + self.context.append('}') + + def depart_footnote_reference(self, node): + self.out.append(self.context.pop()) + + # footnote/citation label + def label_delim(self, node, bracket, superscript): + if isinstance(node.parent, nodes.footnote): + if not self.figure_footnotes: + raise nodes.SkipNode + if self.settings.footnote_references == 'brackets': + self.out.append(bracket) + else: + self.out.append(superscript) + else: + assert isinstance(node.parent, nodes.citation) + if not self._use_latex_citations: + self.out.append(bracket) + + def visit_label(self, node): + """footnote or citation label: in brackets or as superscript""" + self.label_delim(node, '[', '\\textsuperscript{') + + def depart_label(self, node): + self.label_delim(node, ']', '}') + + # elements generated by the framework e.g. section numbers. + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + self.push_output_collector([]) + self.out.append(r'\newcommand{\DUheader}{') + + def depart_header(self, node): + self.out.append('}') + self.requirements['~header'] = ''.join(self.out) + self.pop_output_collector() + + def to_latex_length(self, length_str): + """Convert string with rst lenght to LaTeX""" + match = re.match('(\d*\.?\d*)\s*(\S*)', length_str) + if not match: + return length_str + value, unit = match.groups()[:2] + # no unit or "DTP" points (called 'bp' in TeX): + if unit in ('', 'pt'): + length_str = '%sbp' % value + # percentage: relate to current line width + elif unit == '%': + length_str = '%.3f\\linewidth' % (float(value)/100.0) + return length_str + + def visit_image(self, node): + self.requirements['graphicx'] = self.graphicx_package + attrs = node.attributes + # Add image URI to dependency list, assuming that it's + # referring to a local file. + self.settings.record_dependencies.add(attrs['uri']) + # alignment defaults: + if not 'align' in attrs: + # Set default align of image in a figure to 'center' + if isinstance(node.parent, nodes.figure): + attrs['align'] = 'center' + # query 'align-*' class argument + for cls in node['classes']: + if cls.startswith('align-'): + attrs['align'] = cls.split('-')[1] + # pre- and postfix (prefix inserted in reverse order) + pre = [] + post = [] + include_graphics_options = [] + display_style = ('block-', 'inline-')[self.is_inline(node)] + align_codes = { + # inline images: by default latex aligns the bottom. + 'bottom': ('', ''), + 'middle': (r'\raisebox{-0.5\height}{', '}'), + 'top': (r'\raisebox{-\height}{', '}'), + # block level images: + 'center': (r'\noindent\makebox[\textwidth][c]{', '}'), + 'left': (r'\noindent{', r'\hfill}'), + 'right': (r'\noindent{\hfill', '}'),} + if 'align' in attrs: + try: + align_code = align_codes[attrs['align']] + pre.append(align_code[0]) + post.append(align_code[1]) + except KeyError: + pass # TODO: warn? + if 'height' in attrs: + include_graphics_options.append('height=%s' % + self.to_latex_length(attrs['height'])) + if 'scale' in attrs: + include_graphics_options.append('scale=%f' % + (attrs['scale'] / 100.0)) + if 'width' in attrs: + include_graphics_options.append('width=%s' % + self.to_latex_length(attrs['width'])) + if not self.is_inline(node): + pre.append('\n') + post.append('\n') + pre.reverse() + self.out.extend(pre) + options = '' + if include_graphics_options: + options = '[%s]' % (','.join(include_graphics_options)) + self.out.append('\\includegraphics%s{%s}' % (options, attrs['uri'])) + self.out.extend(post) + + def depart_image(self, node): + if node.get('ids'): + self.out += self.ids_to_labels(node) + ['\n'] + + def visit_interpreted(self, node): + # @@@ Incomplete, pending a proper implementation on the + # Parser/Reader end. + self.visit_literal(node) + + def depart_interpreted(self, node): + self.depart_literal(node) + + def visit_legend(self, node): + self.fallbacks['legend'] = PreambleCmds.legend + self.out.append('\\begin{DUlegend}') + + def depart_legend(self, node): + self.out.append('\\end{DUlegend}\n') + + def visit_line(self, node): + self.out.append('\item[] ') + + def depart_line(self, node): + self.out.append('\n') + + def visit_line_block(self, node): + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['lineblock'] = PreambleCmds.lineblock + if isinstance(node.parent, nodes.line_block): + self.out.append('\\item[]\n' + '\\begin{DUlineblock}{\\DUlineblockindent}\n') + else: + self.out.append('\n\\begin{DUlineblock}{0em}\n') + + def depart_line_block(self, node): + self.out.append('\\end{DUlineblock}\n') + + def visit_list_item(self, node): + self.out.append('\n\\item ') + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.literal = True + self.out.append('\\texttt{') + if node['classes']: + self.visit_inline(node) + + def depart_literal(self, node): + self.literal = False + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + # Literal blocks are used for '::'-prefixed literal-indented + # blocks of text, where the inline markup is not recognized, + # but are also the product of the "parsed-literal" directive, + # where the markup is respected. + # + # In both cases, we want to use a typewriter/monospaced typeface. + # For "real" literal-blocks, we can use \verbatim, while for all + # the others we must use \mbox or \alltt. + # + # We can distinguish between the two kinds by the number of + # siblings that compose this node: if it is composed by a + # single element, it's either + # * a real one, + # * a parsed-literal that does not contain any markup, or + # * a parsed-literal containing just one markup construct. + def is_plaintext(self, node): + """Check whether a node can be typeset verbatim""" + return (len(node) == 1) and isinstance(node[0], nodes.Text) + + def visit_literal_block(self, node): + """Render a literal block.""" + # environments and packages to typeset literal blocks + packages = {'listing': r'\usepackage{moreverb}', + 'lstlisting': r'\usepackage{listings}', + 'Verbatim': r'\usepackage{fancyvrb}', + # 'verbatim': '', + 'verbatimtab': r'\usepackage{moreverb}'} + + if not self.active_table.is_open(): + # no quote inside tables, to avoid vertical space between + # table border and literal block. + # BUG: fails if normal text preceeds the literal block. + self.out.append('%\n\\begin{quote}') + self.context.append('\n\\end{quote}\n') + else: + self.out.append('\n') + self.context.append('\n') + if self.literal_block_env != '' and self.is_plaintext(node): + self.requirements['literal_block'] = packages.get( + self.literal_block_env, '') + self.verbatim = True + self.out.append('\\begin{%s}%s\n' % (self.literal_block_env, + self.literal_block_options)) + else: + self.literal = True + self.insert_newline = True + self.insert_non_breaking_blanks = True + self.out.append('{\\ttfamily \\raggedright \\noindent\n') + + def depart_literal_block(self, node): + if self.verbatim: + self.out.append('\n\\end{%s}\n' % self.literal_block_env) + self.verbatim = False + else: + self.out.append('\n}') + self.insert_non_breaking_blanks = False + self.insert_newline = False + self.literal = False + self.out.append(self.context.pop()) + + ## def visit_meta(self, node): + ## self.out.append('[visit_meta]\n') + # TODO: set keywords for pdf? + # But: + # The reStructuredText "meta" directive creates a "pending" node, + # which contains knowledge that the embedded "meta" node can only + # be handled by HTML-compatible writers. The "pending" node is + # resolved by the docutils.transforms.components.Filter transform, + # which checks that the calling writer supports HTML; if it doesn't, + # the "pending" node (and enclosed "meta" node) is removed from the + # document. + # --- docutils/docs/peps/pep-0258.html#transformer + + ## def depart_meta(self, node): + ## self.out.append('[depart_meta]\n') + + def visit_option(self, node): + if self.context[-1]: + # this is not the first option + self.out.append(', ') + + def depart_option(self, node): + # flag tha the first option is done. + self.context[-1] += 1 + + def visit_option_argument(self, node): + """Append the delimiter betweeen an option and its argument to body.""" + self.out.append(node.get('delimiter', ' ')) + + def depart_option_argument(self, node): + pass + + def visit_option_group(self, node): + self.out.append('\n\\item[') + # flag for first option + self.context.append(0) + + def depart_option_group(self, node): + self.context.pop() # the flag + self.out.append('] ') + + def visit_option_list(self, node): + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['optionlist'] = PreambleCmds.optionlist + self.out.append('%\n\\begin{DUoptionlist}\n') + + def depart_option_list(self, node): + self.out.append('\n\\end{DUoptionlist}\n') + + def visit_option_list_item(self, node): + pass + + def depart_option_list_item(self, node): + pass + + def visit_option_string(self, node): + ##self.out.append(self.starttag(node, 'span', '', CLASS='option')) + pass + + def depart_option_string(self, node): + ##self.out.append('') + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + self.depart_docinfo_item(node) + + def visit_paragraph(self, node): + # no newline if the paragraph is first in a list item + if ((isinstance(node.parent, nodes.list_item) or + isinstance(node.parent, nodes.description)) and + node is node.parent[0]): + return + index = node.parent.index(node) + if (isinstance(node.parent, nodes.compound) and + index > 0 and + not isinstance(node.parent[index - 1], nodes.paragraph) and + not isinstance(node.parent[index - 1], nodes.compound)): + return + self.out.append('\n') + if node.get('ids'): + self.out += self.ids_to_labels(node) + ['\n'] + + def depart_paragraph(self, node): + self.out.append('\n') + + def visit_problematic(self, node): + self.requirements['color'] = PreambleCmds.color + self.out.append('%\n') + self.append_hypertargets(node) + self.out.append(r'\hyperlink{%s}{\textbf{\color{red}' % node['refid']) + + def depart_problematic(self, node): + self.out.append('}}') + + def visit_raw(self, node): + if not 'latex' in node.get('format', '').split(): + raise nodes.SkipNode + if node['classes']: + self.visit_inline(node) + # append "as-is" skipping any LaTeX-encoding + self.verbatim = True + + def depart_raw(self, node): + self.verbatim = False + if node['classes']: + self.depart_inline(node) + + def has_unbalanced_braces(self, string): + """Test whether there are unmatched '{' or '}' characters.""" + level = 0 + for ch in string: + if ch == '{': + level += 1 + if ch == '}': + level -= 1 + if level < 0: + return True + return level != 0 + + def visit_reference(self, node): + # We need to escape #, \, and % if we use the URL in a command. + special_chars = {ord('#'): ur'\#', + ord('%'): ur'\%', + ord('\\'): ur'\\', + } + # external reference (URL) + if 'refuri' in node: + href = unicode(node['refuri']).translate(special_chars) + # problematic chars double caret and unbalanced braces: + if href.find('^^') != -1 or self.has_unbalanced_braces(href): + self.error( + 'External link "%s" not supported by LaTeX.\n' + ' (Must not contain "^^" or unbalanced braces.)' % href) + if node['refuri'] == node.astext(): + self.out.append(r'\url{%s}' % href) + raise nodes.SkipNode + self.out.append(r'\href{%s}{' % href) + return + # internal reference + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + else: + raise AssertionError('Unknown reference.') + if not self.is_inline(node): + self.out.append('\n') + self.out.append('\\hyperref[%s]{' % href) + if self._reference_label: + self.out.append('\\%s{%s}}' % + (self._reference_label, href.replace('#', ''))) + raise nodes.SkipNode + + def depart_reference(self, node): + self.out.append('}') + if not self.is_inline(node): + self.out.append('\n') + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + def depart_revision(self, node): + self.depart_docinfo_item(node) + + def visit_section(self, node): + self.section_level += 1 + # Initialize counter for potential subsections: + self._section_number.append(0) + # Counter for this section's level (initialized by parent section): + self._section_number[self.section_level - 1] += 1 + + def depart_section(self, node): + # Remove counter for potential subsections: + self._section_number.pop() + self.section_level -= 1 + + def visit_sidebar(self, node): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['sidebar'] = PreambleCmds.sidebar + self.out.append('\n\\DUsidebar{\n') + + def depart_sidebar(self, node): + self.out.append('}\n') + + attribution_formats = {'dash': ('---', ''), + 'parentheses': ('(', ')'), + 'parens': ('(', ')'), + 'none': ('', '')} + + def visit_attribution(self, node): + prefix, suffix = self.attribution_formats[self.settings.attribution] + self.out.append('\n\\begin{flushright}\n') + self.out.append(prefix) + self.context.append(suffix) + + def depart_attribution(self, node): + self.out.append(self.context.pop() + '\n') + self.out.append('\\end{flushright}\n') + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + def depart_status(self, node): + self.depart_docinfo_item(node) + + def visit_strong(self, node): + self.out.append('\\textbf{') + if node['classes']: + self.visit_inline(node) + + def depart_strong(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_substitution_definition(self, node): + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.unimplemented_visit(node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.document): + self.push_output_collector(self.subtitle) + self.subtitle_labels += self.ids_to_labels(node, set_anchor=False) + # section subtitle: "starred" (no number, not in ToC) + elif isinstance(node.parent, nodes.section): + self.out.append(r'\%s*{' % + self.d_class.section(self.section_level + 1)) + else: + self.fallbacks['subtitle'] = PreambleCmds.subtitle + self.out.append('\n\\DUsubtitle[%s]{' % node.parent.tagname) + + def depart_subtitle(self, node): + if isinstance(node.parent, nodes.document): + self.pop_output_collector() + else: + self.out.append('}\n') + + def visit_system_message(self, node): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['title'] = PreambleCmds.title + node['classes'] = ['system-message'] + self.visit_admonition(node) + self.out.append('\\DUtitle[system-message]{system-message}\n') + self.append_hypertargets(node) + try: + line = ', line~%s' % node['line'] + except KeyError: + line = '' + self.out.append('\n\n{\color{red}%s/%s} in \\texttt{%s}%s\n' % + (node['type'], node['level'], + self.encode(node['source']), line)) + if len(node['backrefs']) == 1: + self.out.append('\n\\hyperlink{%s}{' % node['backrefs'][0]) + self.context.append('}') + else: + backrefs = ['\\hyperlink{%s}{%d}' % (href, i+1) + for (i, href) in enumerate(node['backrefs'])] + self.context.append('backrefs: ' + ' '.join(backrefs)) + + def depart_system_message(self, node): + self.out.append(self.context.pop()) + self.depart_admonition() + + def visit_table(self, node): + self.requirements['table'] = PreambleCmds.table + if self.active_table.is_open(): + self.table_stack.append(self.active_table) + # nesting longtable does not work (e.g. 2007-04-18) + self.active_table = Table(self,'tabular',self.settings.table_style) + self.active_table.open() + for cls in node['classes']: + self.active_table.set_table_style(cls) + if self.active_table._table_style == 'booktabs': + self.requirements['booktabs'] = r'\usepackage{booktabs}' + self.out.append('\n' + self.active_table.get_opening()) + + def depart_table(self, node): + self.out.append(self.active_table.get_closing() + '\n') + self.active_table.close() + if len(self.table_stack)>0: + self.active_table = self.table_stack.pop() + else: + self.active_table.set_table_style(self.settings.table_style) + # Insert hyperlabel after (long)table, as + # other places (beginning, caption) result in LaTeX errors. + if node.get('ids'): + self.out += self.ids_to_labels(node, set_anchor=False) + ['\n'] + + def visit_target(self, node): + # Skip indirect targets: + if ('refuri' in node # external hyperlink + or 'refid' in node # resolved internal link + or 'refname' in node): # unresolved internal link + ## self.out.append('%% %s\n' % node) # for debugging + return + self.out.append('%\n') + # do we need an anchor (\phantomsection)? + set_anchor = not(isinstance(node.parent, nodes.caption) or + isinstance(node.parent, nodes.title)) + # TODO: where else can/must we omit the \phantomsection? + self.out += self.ids_to_labels(node, set_anchor) + + def depart_target(self, node): + pass + + def visit_tbody(self, node): + # BUG write preamble if not yet done (colspecs not []) + # for tables without heads. + if not self.active_table.get('preamble written'): + self.visit_thead(None) + self.depart_thead(None) + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + """definition list term""" + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + self.out.append('\\item[{') + + def depart_term(self, node): + # \leavevmode results in a line break if the + # term is followed by an item list. + self.out.append('}] \leavevmode ') + + def visit_tgroup(self, node): + #self.out.append(self.starttag(node, 'colgroup')) + #self.context.append('\n') + pass + + def depart_tgroup(self, node): + pass + + _thead_depth = 0 + def thead_depth (self): + return self._thead_depth + + def visit_thead(self, node): + self._thead_depth += 1 + if 1 == self.thead_depth(): + self.out.append('{%s}\n' % self.active_table.get_colspecs()) + self.active_table.set('preamble written',1) + self.out.append(self.active_table.get_caption()) + self.out.extend(self.active_table.visit_thead()) + + def depart_thead(self, node): + if node is not None: + self.out.extend(self.active_table.depart_thead()) + if self.active_table.need_recurse(): + node.walkabout(self) + self._thead_depth -= 1 + + def bookmark(self, node): + """Return label and pdfbookmark string for titles.""" + result = [''] + if self.settings.sectnum_xform: # "starred" section cmd + # add to the toc and pdfbookmarks + section_name = self.d_class.section(max(self.section_level, 1)) + section_title = self.encode(node.astext()) + result.append(r'\phantomsection') + result.append(r'\addcontentsline{toc}{%s}{%s}' % + (section_name, section_title)) + result += self.ids_to_labels(node.parent, set_anchor=False) + return '%\n '.join(result) + '%\n' + + def visit_title(self, node): + """Append section and other titles.""" + # Document title + if node.parent.tagname == 'document': + self.push_output_collector(self.title) + self.context.append('') + self.pdfinfo.append(' pdftitle={%s},' % + self.encode(node.astext())) + # Topic titles (topic, admonition, sidebar) + elif (isinstance(node.parent, nodes.topic) or + isinstance(node.parent, nodes.admonition) or + isinstance(node.parent, nodes.sidebar)): + self.fallbacks['title'] = PreambleCmds.title + classes = ','.join(node.parent['classes']) + if not classes: + classes = node.tagname + self.out.append('\\DUtitle[%s]{' % classes) + self.context.append('}\n') + # Table caption + elif isinstance(node.parent, nodes.table): + self.push_output_collector(self.active_table.caption) + self.context.append('') + # Section title + else: + self.out.append('\n\n') + self.out.append('%' + '_' * 75) + self.out.append('\n\n') + # + section_name = self.d_class.section(self.section_level) + # number sections? + if (self.settings.sectnum_xform # numbering by Docutils + or (self.section_level > len(self.d_class.sections))): + section_star = '*' + else: # LaTeX numbered sections + section_star = '' + self.out.append(r'\%s%s{' % (section_name, section_star)) + # System messages heading in red: + if ('system-messages' in node.parent['classes']): + self.requirements['color'] = PreambleCmds.color + self.out.append('\color{red}') + # label and ToC entry: + self.context.append(self.bookmark(node) + '}\n') + # MAYBE postfix paragraph and subparagraph with \leavemode to + # ensure floats stay in the section and text starts on a new line. + + def depart_title(self, node): + self.out.append(self.context.pop()) + if (isinstance(node.parent, nodes.table) or + node.parent.tagname == 'document'): + self.pop_output_collector() + + def minitoc(self, node, title, depth): + """Generate a local table of contents with LaTeX package minitoc""" + section_name = self.d_class.section(self.section_level) + # name-prefix for current section level + minitoc_names = {'part': 'part', 'chapter': 'mini'} + if 'chapter' not in self.d_class.sections: + minitoc_names['section'] = 'sect' + try: + minitoc_name = minitoc_names[section_name] + except KeyError: # minitoc only supports part- and toplevel + self.warn('Skipping local ToC at %s level.\n' % section_name + + ' Feature not supported with option "use-latex-toc"', + base_node=node) + return + # Requirements/Setup + self.requirements['minitoc'] = PreambleCmds.minitoc + self.requirements['minitoc-'+minitoc_name] = (r'\do%stoc' % + minitoc_name) + # depth: (Docutils defaults to unlimited depth) + maxdepth = len(self.d_class.sections) + self.requirements['minitoc-%s-depth' % minitoc_name] = ( + r'\mtcsetdepth{%stoc}{%d}' % (minitoc_name, maxdepth)) + # Process 'depth' argument (!Docutils stores a relative depth while + # minitoc expects an absolute depth!): + offset = {'sect': 1, 'mini': 0, 'part': 0} + if 'chapter' in self.d_class.sections: + offset['part'] = -1 + if depth: + self.out.append('\\setcounter{%stocdepth}{%d}' % + (minitoc_name, depth + offset[minitoc_name])) + # title: + self.out.append('\\mtcsettitle{%stoc}{%s}\n' % (minitoc_name, title)) + # the toc-generating command: + self.out.append('\\%stoc\n' % minitoc_name) + + def visit_topic(self, node): + # Topic nodes can be generic topic, abstract, dedication, or ToC. + # table of contents: + if 'contents' in node['classes']: + self.out.append('\n') + self.out += self.ids_to_labels(node) + # add contents to PDF bookmarks sidebar + if isinstance(node.next_node(), nodes.title): + self.out.append('\n\\pdfbookmark[%d]{%s}{%s}\n' % + (self.section_level+1, + node.next_node().astext(), + node.get('ids', ['contents'])[0] + )) + if self.use_latex_toc: + title = '' + if isinstance(node.next_node(), nodes.title): + title = self.encode(node.pop(0).astext()) + depth = node.get('depth', 0) + if 'local' in node['classes']: + self.minitoc(node, title, depth) + self.context.append('') + return + if depth: + self.out.append('\\setcounter{tocdepth}{%d}\n' % depth) + if title != 'Contents': + self.out.append('\\renewcommand{\\contentsname}{%s}\n' % + title) + self.out.append('\\tableofcontents\n\n') + self.has_latex_toc = True + else: # Docutils generated contents list + # set flag for visit_bullet_list() and visit_title() + self.is_toc_list = True + self.context.append('') + elif ('abstract' in node['classes'] and + self.settings.use_latex_abstract): + self.push_output_collector(self.abstract) + self.out.append('\\begin{abstract}') + self.context.append('\\end{abstract}\n') + if isinstance(node.next_node(), nodes.title): + node.pop(0) # LaTeX provides its own title + else: + self.fallbacks['topic'] = PreambleCmds.topic + # special topics: + if 'abstract' in node['classes']: + self.fallbacks['abstract'] = PreambleCmds.abstract + self.push_output_collector(self.abstract) + if 'dedication' in node['classes']: + self.fallbacks['dedication'] = PreambleCmds.dedication + self.push_output_collector(self.dedication) + self.out.append('\n\\DUtopic[%s]{\n' % ','.join(node['classes'])) + self.context.append('}\n') + + def depart_topic(self, node): + self.out.append(self.context.pop()) + self.is_toc_list = False + if ('abstract' in node['classes'] or + 'dedication' in node['classes']): + self.pop_output_collector() + + def visit_inline(self, node): # , i.e. custom roles + # insert fallback definition + self.fallbacks['inline'] = PreambleCmds.inline + self.out += [r'\DUrole{%s}{' % cls for cls in node['classes']] + self.context.append('}' * (len(node['classes']))) + + def depart_inline(self, node): + self.out.append(self.context.pop()) + + def visit_rubric(self, node): + self.fallbacks['rubric'] = PreambleCmds.rubric + self.out.append('\n\\DUrubric{') + self.context.append('}\n') + + def depart_rubric(self, node): + self.out.append(self.context.pop()) + + def visit_transition(self, node): + self.fallbacks['transition'] = PreambleCmds.transition + self.out.append('\n\n') + self.out.append('%' + '_' * 75 + '\n') + self.out.append(r'\DUtransition') + self.out.append('\n\n') + + def depart_transition(self, node): + pass + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def depart_version(self, node): + self.depart_docinfo_item(node) + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' % + node.__class__.__name__) + +# def unknown_visit(self, node): +# def default_visit(self, node): + +# vim: set ts=4 et ai : diff --git a/python/helpers/docutils/writers/latex2e/default.tex b/python/helpers/docutils/writers/latex2e/default.tex new file mode 100644 index 000000000000..c6e8ec3bdb0d --- /dev/null +++ b/python/helpers/docutils/writers/latex2e/default.tex @@ -0,0 +1,14 @@ +% generated by Docutils +$head_prefix\usepackage{fixltx2e} % LaTeX patches, \textsubscript +\usepackage{cmap} % fix search and cut-and-paste in PDF +$requirements +%%% Custom LaTeX preamble +$latex_preamble +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +%%% Body +\begin{document} +$body_pre_docinfo$docinfo$dedication$abstract$body +\end{document} diff --git a/python/helpers/docutils/writers/latex2e/docutils-05-compat.sty b/python/helpers/docutils/writers/latex2e/docutils-05-compat.sty new file mode 100644 index 000000000000..7d0323e2a889 --- /dev/null +++ b/python/helpers/docutils/writers/latex2e/docutils-05-compat.sty @@ -0,0 +1,732 @@ +% ================================================================== +% Changes to the Docutils latex2e writer since version 0.5 +% ================================================================== +% +% A backwards compatibility style sheet +% ************************************* +% +% :Author: Guenter Milde +% :Contact: milde@users.berlios.de +% :Revision: $Revision: 6152 $ +% :Date: $Date: 2009-02-24$ +% :Copyright: © 2009 G. Milde, +% Released without warranties or conditions of any kind +% under the terms of the Apache License, Version 2.0 +% http://www.apache.org/licenses/LICENSE-2.0 +% :Abstract: This file documents changes and provides a style for best +% possible compatibility to the behaviour of the `latex2e` +% writer of Doctutils release 0.5. +% +% :: + +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{docutils-05-compat} +[2009/03/26 v0.1 compatibility with rst2latex from Docutils 0.5] + +% .. contents:: +% :depth: 3 +% +% Usage +% ===== +% +% * To get an (almost) identic look for your old documents, +% place ``docutils-05-compat.sty`` in the TEXINPUT path (e.g. +% the current work directory) and pass the +% ``--stylesheet=docutils-05-compat`` option to ``rst2latex.py``. +% +% * To use your custom stylesheets without change, add them to the +% compatibility style, e.g. +% ``--stylesheet="docutils-05-compat,mystyle.tex``. +% +% .. tip:: As the changes include bug fixes that are partly reverted by this +% style, it is recommended to adapt the stylesheets to the new version or +% copy just the relevant parts of this style into them. +% +% Changes since 0.5 +% ================= +% +% Bugfixes +% -------- +% +% * Newlines around comments, targets and references prevent run-together +% paragraphs. +% +% + An image directive with hyperlink reference or target did not start a +% new paragraph (e.g. the first two image examples in +% standalone_rst_latex.tex). +% +% + Paragraphs were not separated if there was a (hyper) target definition +% inbetween. +% +% + Paragraphs did run together, if separated by a comment-paragraph in the +% rst source. +% +% * Fixed missing and spurious internal links/targets. +% Internal links now take you to the correct place. +% +% * Verbose and linked system messages. +% +% * `Figure and image alignment`_ now conforms to the rst definition. +% +% * Put `header and footer directive`__ content in \DUheader respective +% \DUfooter macros (ignored by the default style/template). +% +% (They were put inside hard-coded markup at the top/bottom of the document +% without an option to get them on every page.) +% +% __ ../ref/rst/directives.html#document-header-footer +% +% * Render doctest blocks as literal blocks (fixes bug [1586058] doctest block +% nested in admonition). I.e. +% +% + indent doctest blocks by nesting in a quote environment. This is also +% the rendering by the HTML writer (html4css2.css). +% + apply the ``--literal-block-env`` setting also to doctest blocks. +% +% .. warning:: +% (``--literal-block-env=verbatim`` and +% ``--literal-block-env=lstlistings`` fail with literal or doctest +% blocks nested in an admonition. +% +% * Two-way hyperlinked footnotes and support for symbol footnotes and +% ``--footnote-references=brackets`` with ``--use-latex-footnotes``. +% +% * The packages `fixltx2e` (providing LaTeX patches and the \textsubscript +% command) and `cmap` (including character maps in the generated PDF for +% better search and copy-and-paste operations) are now always loaded +% (configurable with custom templates_). +% +% Backwards compatibility: +% "Bug for bug compatibility" is not provided. +% +% +% New configuration setting defaults +% ---------------------------------- +% +% - font-encoding: "T1" (formerly implicitely set by 'ae'). +% - use-latex-toc: true (ToC with page numbers). +% - use-latex-footnotes: true (no mixup with figures). +% +% Backwards compatibility: +% Reset to the former defaults with: +% +% | font-encoding: '' +% | use-latex-toc: False +% | use-latex-footnotes: False +% +% (in the config file) or the command line options: +% +% ``--figure-footnotes --use-docutils-toc --font-encoding=''`` +% +% +% Cleaner LaTeX source +% -------------------- +% +% New features: +% * Remove redundant "double protection" from the encoding of the "special +% printing characters" and square brackets, e.g. ``\%`` instead of +% ``{\%}``. +% * Remove some spurious whitespace, e.g. ``\item [what:] -> \item[what:]``. +% * Use conventional style for "named" macros, e.g. ``\dots{}`` instead of +% ``{\dots}`` +% +% Backwards compatibility: +% Changes do not affect the output. +% +% +% LaTeX style sheets +% ------------------ +% +% New Feature: +% LaTeX packages can be used as ``--stylesheet`` argument without +% restriction. +% +% Implementation: +% Use ``\usepackage`` if style sheet ends with ``.sty`` or has no +% extension and ``\input`` else. +% +% Rationale: +% while ``\input`` works with extension as well as without extension, +% ``\usepackage`` expects the package name without extension. (The latex2e +% writer will strip a ``.sty`` extension.) +% +% +% Backwards compatibility: +% Up to Docutils 0.5, if no filename extension is given in the +% ``stylesheet`` argument, ``.tex`` is assumed (by latex). +% +% Since Docutils 0.6, a stylesheet without filename extension is assumed to +% be a LaTeX package (``*.sty``) and referenced with the ``\usepackage`` +% command. +% +% .. important:: +% Always specify the extension if you want the style sheet to be +% ``\input`` by LaTeX. +% +% +% Templates +% --------- +% +% New Feature: +% Advanced configuration via custom templates. +% +% Implementation: +% A ``--template`` option and config setting allows specification of a +% template file. +% +% See the `LaTeX writer documentation`__ for details. +% +% __ latex.html#templates +% +% +% Custom roles +% ------------ +% +% New Feature: failsave implementation +% As with classes to HTML objects, class arguments are silently ignored if +% there is no styling rule for this class in a custom style sheet. +% +% New Feature: custom roles based on standard roles +% As class support needs to be handled by the LaTeX writer, this feature was +% not present "automatically" (as in HTML). Modified visit/depart_*() +% methods for the standard roles now call visit/depart_inline() if there are +% class arguments to the node. +% +% Backwards compatibility: +% The implementation is fully backwards compatible. (SVN versions 5742 to +% 5861 contained an implementation that did not work with commands expecting +% an argument.) +% +% Length units +% ------------ +% +% New Features: +% 1. Add default unit if none given. +% A poll on docutils-users favoured ``bp`` (Big Point: 1 bp = 1/72 in). +% +% 2. Do not change ``px`` to ``pt``. +% +% 3. Lengths specified in the document with unit "pt" will be written with +% unit "bp" to the LaTeX source. +% +% Rationale: +% 1. prevent LaTeX error "missing unit". +% +% 2. ``px`` is a valid unit in pdftex since version 1.3.0 released on +% 2005-02-04: +% +% 1px defaults to 1bp (or 72dpi), but can be changed with the +% ``\pdfpxdimen`` primitive.:: + + \pdfpxdimen=1in % 1 dpi + \divide\pdfpxdimen by 96 % 96 dpi + +% -- http://www.tug.org/applications/pdftex/NEWS +% +% Modern TeX distributions use pdftex also for dvi generation (i.e. +% ``latex`` actually calls ``pdftex`` with some options). +% +% 3. In Docutils (as well as CSS) the unit symbol "pt" denotes the +% `Postscript point` or `DTP point` while LaTeX uses "pt" for the `LaTeX +% point`, which is unknown to Docutils and 0.3 % smaller. +% +% The `DTP point` is available in LaTeX as "bp" (big point): +% +% 1 pt = 1/72.25 in < 1 bp = 1/72 in +% +% +% Backwards compatibility: +% Images with width specification in ``px`` come out slightly (0.3 %) larger: +% +% 1 px = 1 bp = 1/72 in > 1 pt = 1/72.25 in +% +% This can be reset with :: + + \pdfpxdimen=1pt + +% .. caution:: It is impossible to revert the change of lengths specified with +% "pt" or without unit in a style sheet, however the 0.3 % change will be +% imperceptible in most cases. +% +% .. admonition:: Error ``illegal unit px`` +% +% The unit ``px`` is not defined in "pure" LaTeX, but introduced by the +% `pdfTeX` converter on 2005-02-04. `pdfTeX` is used in all modern LaTeX +% distributions (since ca. 2006) also for conversion into DVI. +% +% If you convert the LaTeX source with a legacy program, you might get the +% error ``illegal unit px``. +% +% If updating LaTeX is not an option, just remove the ``px`` from the length +% specification. HTML/CSS will default to ``px`` while the `latexe2` writer +% will add the fallback unit ``bp``. +% +% +% Font encoding +% ------------- +% +% New feature: +% Do not mix font-encoding and font settings: do not load the obsolete +% `ae` and `aeguill` packages unless explicitely required via the +% ``--stylesheet`` option. +% +% :font-encoding = "": do not load `ae` and `aeguill`, i.e. +% +% * do not change font settings, +% * do not use the fontenc package +% (implicitely loaded via `ae`), +% * use LaTeX default font encoding (OT1) +% +% :font-encoding = "OT1": load `fontenc` with ``\usepackage[OT1]{fontenc}`` +% +% Example: +% ``--font-encoding=LGR,T1`` becomes ``\usepackage[LGR,T1]{fontenc}`` +% (Latin, Latin-1 Supplement, and Greek) +% +% +% Backwards compatibility: +% Load the ae and aeguill packages if fontenc is not used. +% +% .. tip:: Using `ae` is not recommended. A similar look (but better +% implementation) can be achieved with the packages `lmodern`, `cmsuper`, +% or `cmlgr` all providing Computer Modern look-alikes in vector format and +% T1 encoding, e.g. ``--font-encoding=T1 --stylesheet=lmodern``. +% +% Sub- and superscript as text +% ---------------------------- +% +% New feature: +% Set sub- and superscript role argument in text mode not as math. +% +% Pass the role content to ``\textsubscript`` or ``\textsuperscript``. +% +% Backwards compatibility: +% The old implementation set the role content in Math mode, where +% +% * whitespace is ignored, +% * a different command set and font setting scheme is active, +% * Latin letters are typeset italic but numbers upright. +% +% Although it is possible to redefine ``\textsubscript`` and +% ``\textsuperscript`` to typeset the content in math-mode, this can lead to +% errors with certain input and is therefore not done in this style sheet. +% +% .. tip:: To get italic subscripts, define and use in your document +% `custom roles`_ like ``.. role:: sub(subscript)`` and +% ``.. role:: super(superscript)`` and define the "role commands":: + + \newcommand{\DUrolesub}{\itshape} + \newcommand{\DUrolesuper}{\itshape} + +% Alternatively, if you want all sub- and superscripts in italic, redefine +% the macros:: + + %% \let\DUsup\textsubscript + %% \let\DUsuper\textsuperscript + %% \renewcommand*{\textsubscript}{\DUsub\itshape} + %% \renewcommand*{\textsuperscript}{\DUsuper\itshape} + +% This is not fully backwards compatible, as it will also set numbers in +% italic shape and not ignore whitespace. +% +% Page layout +% ----------- +% +% New features: +% * Margins are configurable via the ``DIV=...`` document option. +% +% * The ``\raggedbottom`` setting is no longer inserted into the document. It +% is the default for article and report classes. If requested in combination +% with a book class, it can be given in a custom style sheet. +% +% Backwards compatibility: +% Up to version 0.5, use of `typearea` and a DIV setting of 12 were +% hard-coded into the latex2e writer :: + + \usepackage{typearea} + \typearea{12} + +% and the vertical alignment of lower boundary of the text area in book +% classes disabled via :: + + \raggedbottom + + +% ToC and section numbers +% ----------------------- +% +% Better conformance to Docutils specifications. +% +% New feature: +% * The "depth" argument of the "contents" and "sectnum" directives is +% respected. +% +% * section numbering independent of 'use-latex-toc': +% +% + sections are only numbered if there is a "sectnum" directive in the +% document +% +% + section numbering by LaTeX if the "sectnum_xforms" config setting is +% False. +% +% Backwards compatibility: +% +% The previous behaviour was to always number sections if 'use-latex-toc' is +% true, using the document class defaults. It cannot be restored +% universally, the following code sets the default values of the "article" +% document class:: + + \setcounter{secnumdepth}{3} + \setcounter{tocdepth}{3} + +% .. TODO or not to do? (Back-compatibility problems) +% * The default "depth" of the LaTeX-created ToC and the LaTeX section +% numbering is increased to the number of supported section levels. +% +% New feature: +% If 'use-latex-toc' is set, local tables of content are typeset using the +% 'minitoc' package (instead of being ignored). +% +% Backwards compatibility: +% Disable the creation of local ToCs (ignoring all special commands) by +% replacing ``\usepackage{minitoc} with ``\usepackage{mtcoff}``. +% +% +% Default font in admonitions and sidebar +% --------------------------------------- +% +% New feature: +% Use default font in admonitions and sidebar. +% +% Backward compatibility: +% See the fallback definitions for admonitions_, `topic title`_ and +% `sidebar`_. +% +% +% Figure placement +% ---------------- +% +% New feature: +% Use ``\floatplacement`` from the `float` package instead of +% "hard-coded" optional argument for the global setting. +% +% Default to ``\floatplacement{figure}{H}`` (here definitely). This +% corresponds most closely to the source and HTML placement (principle of +% least surprise). +% +% Backwards compatibility: +% Set the global default back to the previous used value:: + + \usepackage{float} + \floatplacement{figure}{htbp} % here, top, bottom, extra-page + + +% Figure and image alignment +% -------------------------- +% +% New features: +% +% a) Fix behaviour of 'align' argument to a figure (do not align figure +% contents). +% +% As the 'figwidth' argument is still ignored and the "natural width" of a +% figure in LaTeX is 100% \textwidth, setting the 'align' argument of a +% figure has currently no effect on the LaTeX output. +% +% b) Set default align of image in a figure to 'center'. +% +% c) Also center images that are wider than textwidth. +% +% d) Align images with class "align-[right|center|left]" (allows setting the +% alignment of an image in a figure). +% +% Backwards compatibility: +% There is no "automatic" way to reverse these changes via a style sheet. +% +% a) The alignment of the image can be set with the "align-left", +% "align-center" and "align-right" class arguments. +% +% As previously, the caption of a figure is aligned according to the +% document class -- configurable with a style sheet using the "caption" +% package. +% +% b) See a) +% +% c) Set the alignment of "oversized" images to "left" to get back the +% old placement. +% +% Shorter preamble +% ---------------- +% +% New feature: +% The document preamble is pruned to contain only relevant commands and +% settings. +% +% Packages that are no longer required +% ```````````````````````````````````` +% +% The following packages where required in pre-0.5 versions and still loaded +% with version 0.5:: + +\usepackage{shortvrb} +\usepackage{amsmath} + + +% Packages that are conditionally loaded +% `````````````````````````````````````` +% +% Additional to the `typearea` for `page layout`_, the following packages are +% only loaded if actually required by doctree elements: +% +% Tables +% ^^^^^^ +% +% Standard package for tables across several pages:: + +\usepackage{longtable} + +% Extra space between text in tables and the line above them +% ('array' is implicitely loaded by 'tabularx', see below):: + +\usepackage{array} +\setlength{\extrarowheight}{2pt} + +% Table cells spanning multiple rows:: + +\usepackage{multirow} + +% Docinfo +% ^^^^^^^ +% +% One-page tables with auto-width columns:: + +\usepackage{tabularx} + +% Images +% ^^^^^^ +% Include graphic files:: + +\usepackage{graphicx} + +% Problematic, Sidebar +% ^^^^^^^^^^^^^^^^^^^^ +% Set text and/or background colour, coloured boxes with ``\colorbox``:: + +\usepackage{color} + +% Floats for footnotes settings +% ````````````````````````````` +% +% Settings for the use of floats for footnotes are only included if +% +% * the option "use-latex-footnotes" is False, and +% * there is at least one footnote in the document. +% +% :: + +% begin: floats for footnotes tweaking. +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50} +% end floats for footnotes + + +% Special lengths, commands, and environments +% ------------------------------------------- +% +% Removed definitions +% ``````````````````` +% +% admonition width +% ^^^^^^^^^^^^^^^^ +% The ``admonitionwith`` lenght is replaced by the more powerful +% ``\DUadmonition`` command (see admonitions_). +% +% Backwards compatibility: +% The default value (90 % of the textwidth) is unchanged. +% +% To configure the admonition width, you must redefine the ``DUadmonition`` +% command instead of changing the ``admonitionwith`` length value. +% +% +% Renamed definitions (now conditional) +% ````````````````````````````````````` +% +% The names for special doctree elements are now prefixed with ``DU``. +% +% Up to version 0.5, all definitions were included in the preamble (before the +% style sheet) of every document -- even if not used in the body. Since +% version 0.6, fallback definitions are included after the style sheet and +% only if required. +% +% Customization is done by an alternative definition in a style sheet with +% ``\newcommand`` instead of the former ``\renewcommand``. +% +% The following code provides the old definitions and maps them (or their +% custom variants) to the new interface. +% +% docinfo width +% ^^^^^^^^^^^^^ +% :: + +\newlength{\docinfowidth} +\setlength{\docinfowidth}{0.9\textwidth} + +\newlength{\DUdocinfowidth} +\AtBeginDocument{\setlength{\DUdocinfowidth}{\docinfowidth}} + +% line block +% ^^^^^^^^^^ +% :: + +\newlength{\lineblockindentation} +\setlength{\lineblockindentation}{2.5em} +\newenvironment{lineblock}[1] +{\begin{list}{} + {\setlength{\partopsep}{\parskip} + \addtolength{\partopsep}{\baselineskip} + \topsep0pt\itemsep0.15\baselineskip\parsep0pt + \leftmargin#1} + \raggedright} +{\end{list}} + +\newlength{\DUlineblockindent} +\AtBeginDocument{\setlength{\DUlineblockindent}{\lineblockindentation}} +\newenvironment{DUlineblock}[1] + {\begin{lineblock}{#1}} + {\end{lineblock}} + +% local line width +% ^^^^^^^^^^^^^^^^ +% +% The ``\locallinewidth`` length for internal use in tables is replaced +% by ``\DUtablewidth``. It was never intended for customization:: + +\newlength{\locallinewidth} + +% option lists +% ^^^^^^^^^^^^ +% :: + +\newcommand{\optionlistlabel}[1]{\bf #1 \hfill} +\newenvironment{optionlist}[1] +{\begin{list}{} + {\setlength{\labelwidth}{#1} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\optionlistlabel}} +}{\end{list}} + +\newcommand{\DUoptionlistlabel}{\optionlistlabel} +\newenvironment{DUoptionlist} + {\begin{optionlist}{3cm}} + {\end{optionlist}} + +% rubric +% ^^^^^^ +% Now less prominent (not bold, normal size) restore with:: + +\newcommand{\rubric}[1]{\subsection*{~\hfill {\it #1} \hfill ~}} +\newcommand{\DUrubric}[2][class-arg]{\rubric{#2}} + +% title reference role +% ^^^^^^^^^^^^^^^^^^^^ +% :: + +\newcommand{\titlereference}[1]{\textsl{#1}} +\newcommand{\DUroletitlereference}[1]{\titlereference{#1}} + + +% New definitions +% ``````````````` +% +% New Feature: +% Enable customization of some more Docutils elements with special commands +% +% :admonition: ``DUadmonition`` command (replacing ``\admonitionwidth``), +% :field list: ``DUfieldlist`` environment, +% :legend: ``DUlegend`` environment, +% :sidebar: ``\DUsidebar``, ``\DUtitle``, and +% ``DUsubtitle`` commands, +% :topic: ``\DUtopic`` and ``\DUtitle`` commands, +% :transition: ``\DUtransition`` command. +% :footnotes: ``\DUfootnotemark`` and ``\DUfootnotetext`` commands with +% hyperlink support using the Docutils-provided footnote label. +% +% Backwards compatibility: +% In most cases, the default definition corresponds to the previously used +% construct. The following definitions restore the old behaviour in case of +% changes. +% +% admonitions +% ^^^^^^^^^^^ +% Use sans-serif fonts:: + +\newcommand{\DUadmonition}[2][class-arg]{% + \begin{center} + \fbox{\parbox{0.9\textwidth}{\sffamily #2}} + \end{center} +} + +% dedication +% ^^^^^^^^^^ +% Do not center:: + +\newcommand{\DUtopicdedication}[1]{#1} + +% But center the title:: + +\newcommand*{\DUtitlededication}[1]{\centerline{\textbf{#1}}} + +% sidebar +% ^^^^^^^ +% Use sans-serif fonts, a frame, and a darker shade of grey:: + +\providecommand{\DUsidebar}[2][class-arg]{% + \begin{center} + \sffamily + \fbox{\colorbox[gray]{0.80}{\parbox{0.9\textwidth}{#2}}} + \end{center} +} + +% sidebar sub-title +% ^^^^^^^^^^^^^^^^^ +% Bold instead of emphasized:: + +\providecommand*{\DUsubtitlesidebar}[1]{\hspace*{\fill}\\ + \textbf{#1}\smallskip} + +% topic +% ^^^^^ +% No quote but normal text:: + +\newcommand{\DUtopic}[2][class-arg]{% + \ifcsname DUtopic#1\endcsname% + \csname DUtopic#1\endcsname{#2}% + \else + #2 + \fi +} + +% topic title +% ^^^^^^^^^^^ +% Title for "topics" (admonitions, sidebar). +% +% Larger font size:: + +\providecommand*{\DUtitletopic}[1]{\textbf{\large #1}\smallskip} + +% transition +% ^^^^^^^^^^ +% Do not add vertical space after the transition. :: + +\providecommand*{\DUtransition}[1][class-arg]{% + \hspace*{\fill}\hrulefill\hspace*{\fill}} diff --git a/python/helpers/docutils/writers/latex2e/titlepage.tex b/python/helpers/docutils/writers/latex2e/titlepage.tex new file mode 100644 index 000000000000..f7f35f9be558 --- /dev/null +++ b/python/helpers/docutils/writers/latex2e/titlepage.tex @@ -0,0 +1,16 @@ +% generated by Docutils +$head_prefix$requirements +%%% Custom LaTeX preamble +$latex_preamble +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +%%% Body +\begin{document} +\begin{titlepage} +$body_pre_docinfo$docinfo$dedication$abstract +\thispagestyle{empty} +\end{titlepage} +$body +\end{document} diff --git a/python/helpers/docutils/writers/manpage.py b/python/helpers/docutils/writers/manpage.py new file mode 100644 index 000000000000..e4e46b389219 --- /dev/null +++ b/python/helpers/docutils/writers/manpage.py @@ -0,0 +1,1104 @@ +# -*- coding: utf-8 -*- +# $Id: manpage.py 6378 2010-07-12 19:30:24Z grubert $ +# Author: Engelbert Gruber +# Copyright: This module is put into the public domain. + +""" +Simple man page writer for reStructuredText. + +Man pages (short for "manual pages") contain system documentation on unix-like +systems. The pages are grouped in numbered sections: + + 1 executable programs and shell commands + 2 system calls + 3 library functions + 4 special files + 5 file formats + 6 games + 7 miscellaneous + 8 system administration + +Man pages are written *troff*, a text file formatting system. + +See http://www.tldp.org/HOWTO/Man-Page for a start. + +Man pages have no subsection only parts. +Standard parts + + NAME , + SYNOPSIS , + DESCRIPTION , + OPTIONS , + FILES , + SEE ALSO , + BUGS , + +and + + AUTHOR . + +A unix-like system keeps an index of the DESCRIPTIONs, which is accesable +by the command whatis or apropos. + +""" + +__docformat__ = 'reStructuredText' + +import re + +import docutils +from docutils import nodes, writers, languages +import roman + +FIELD_LIST_INDENT = 7 +DEFINITION_LIST_INDENT = 7 +OPTION_LIST_INDENT = 7 +BLOCKQOUTE_INDENT = 3.5 + +# Define two macros so man/roff can calculate the +# indent/unindent margins by itself +MACRO_DEF = (r""". +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +""") + +class Writer(writers.Writer): + + supported = ('manpage',) + """Formats this writer supports.""" + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = Translator + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + self.output = visitor.astext() + + +class Table: + def __init__(self): + self._rows = [] + self._options = ['center'] + self._tab_char = '\t' + self._coldefs = [] + def new_row(self): + self._rows.append([]) + def append_separator(self, separator): + """Append the separator for table head.""" + self._rows.append([separator]) + def append_cell(self, cell_lines): + """cell_lines is an array of lines""" + start = 0 + if len(cell_lines) > 0 and cell_lines[0] == '.sp\n': + start = 1 + self._rows[-1].append(cell_lines[start:]) + if len(self._coldefs) < len(self._rows[-1]): + self._coldefs.append('l') + def _minimize_cell(self, cell_lines): + """Remove leading and trailing blank and ``.sp`` lines""" + while (cell_lines and cell_lines[0] in ('\n', '.sp\n')): + del cell_lines[0] + while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')): + del cell_lines[-1] + def as_list(self): + text = ['.TS\n'] + text.append(' '.join(self._options) + ';\n') + text.append('|%s|.\n' % ('|'.join(self._coldefs))) + for row in self._rows: + # row = array of cells. cell = array of lines. + text.append('_\n') # line above + text.append('T{\n') + for i in range(len(row)): + cell = row[i] + self._minimize_cell(cell) + text.extend(cell) + if not text[-1].endswith('\n'): + text[-1] += '\n' + if i < len(row)-1: + text.append('T}'+self._tab_char+'T{\n') + else: + text.append('T}\n') + text.append('_\n') + text.append('.TE\n') + return text + +class Translator(nodes.NodeVisitor): + """""" + + words_and_spaces = re.compile(r'\S+| +|\n') + document_start = """Man page generated from reStructeredText.""" + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = settings = document.settings + lcode = settings.language_code + self.language = languages.get_language(lcode) + self.head = [] + self.body = [] + self.foot = [] + self.section_level = 0 + self.context = [] + self.topic_class = '' + self.colspecs = [] + self.compact_p = 1 + self.compact_simple = None + # the list style "*" bullet or "#" numbered + self._list_char = [] + # writing the header .TH and .SH NAME is postboned after + # docinfo. + self._docinfo = { + "title" : "", "title_upper": "", + "subtitle" : "", + "manual_section" : "", "manual_group" : "", + "author" : [], + "date" : "", + "copyright" : "", + "version" : "", + } + self._docinfo_keys = [] # a list to keep the sequence as in source. + self._docinfo_names = {} # to get name from text not normalized. + self._in_docinfo = None + self._active_table = None + self._in_literal = False + self.header_written = 0 + self._line_block = 0 + self.authors = [] + self.section_level = 0 + self._indent = [0] + # central definition of simple processing rules + # what to output on : visit, depart + # Do not use paragraph requests ``.PP`` because these set indentation. + # use ``.sp``. Remove superfluous ``.sp`` in ``astext``. + # + # Fonts are put on a stack, the top one is used. + # ``.ft P`` or ``\\fP`` pop from stack. + # ``B`` bold, ``I`` italic, ``R`` roman should be available. + # Hopefully ``C`` courier too. + self.defs = { + 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'), + 'definition_list_item' : ('.TP', ''), + 'field_name' : ('.TP\n.B ', '\n'), + 'literal' : ('\\fB', '\\fP'), + 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'), + + 'option_list_item' : ('.TP\n', ''), + + 'reference' : (r'\fI\%', r'\fP'), + 'emphasis': ('\\fI', '\\fP'), + 'strong' : ('\\fB', '\\fP'), + 'term' : ('\n.B ', '\n'), + 'title_reference' : ('\\fI', '\\fP'), + + 'topic-title' : ('.SS ',), + 'sidebar-title' : ('.SS ',), + + 'problematic' : ('\n.nf\n', '\n.fi\n'), + } + # NOTE do not specify the newline before a dot-command, but ensure + # it is there. + + def comment_begin(self, text): + """Return commented version of the passed text WITHOUT end of + line/comment.""" + prefix = '.\\" ' + out_text = ''.join( + [(prefix + in_line + '\n') + for in_line in text.split('\n')]) + return out_text + + def comment(self, text): + """Return commented version of the passed text.""" + return self.comment_begin(text)+'.\n' + + def ensure_eol(self): + """Ensure the last line in body is terminated by new line.""" + if self.body[-1][-1] != '\n': + self.body.append('\n') + + def astext(self): + """Return the final formatted document as a string.""" + if not self.header_written: + # ensure we get a ".TH" as viewers require it. + self.head.append(self.header()) + # filter body + for i in xrange(len(self.body)-1, 0, -1): + # remove superfluous vertical gaps. + if self.body[i] == '.sp\n': + if self.body[i - 1][:4] in ('.BI ','.IP '): + self.body[i] = '.\n' + elif (self.body[i - 1][:3] == '.B ' and + self.body[i - 2][:4] == '.TP\n'): + self.body[i] = '.\n' + elif (self.body[i - 1] == '\n' and + self.body[i - 2][0] != '.' and + (self.body[i - 3][:7] == '.TP\n.B ' + or self.body[i - 3][:4] == '\n.B ') + ): + self.body[i] = '.\n' + return ''.join(self.head + self.body + self.foot) + + def deunicode(self, text): + text = text.replace(u'\xa0', '\\ ') + text = text.replace(u'\u2020', '\\(dg') + return text + + def visit_Text(self, node): + text = node.astext() + text = text.replace('\\','\\e') + replace_pairs = [ + (u'-', ur'\-'), + (u'\'', ur'\(aq'), + (u'´', ur'\''), + (u'`', ur'\(ga'), + ] + for (in_char, out_markup) in replace_pairs: + text = text.replace(in_char, out_markup) + # unicode + text = self.deunicode(text) + if self._in_literal: + # prevent interpretation of "." at line start + if text[0] == '.': + text = '\\&' + text + text = text.replace('\n.', '\n\\&.') + self.body.append(text) + + def depart_Text(self, node): + pass + + def list_start(self, node): + class enum_char: + enum_style = { + 'bullet' : '\\(bu', + 'emdash' : '\\(em', + } + + def __init__(self, style): + self._style = style + if node.has_key('start'): + self._cnt = node['start'] - 1 + else: + self._cnt = 0 + self._indent = 2 + if style == 'arabic': + # indentation depends on number of childrens + # and start value. + self._indent = len(str(len(node.children))) + self._indent += len(str(self._cnt)) + 1 + elif style == 'loweralpha': + self._cnt += ord('a') - 1 + self._indent = 3 + elif style == 'upperalpha': + self._cnt += ord('A') - 1 + self._indent = 3 + elif style.endswith('roman'): + self._indent = 5 + + def next(self): + if self._style == 'bullet': + return self.enum_style[self._style] + elif self._style == 'emdash': + return self.enum_style[self._style] + self._cnt += 1 + # TODO add prefix postfix + if self._style == 'arabic': + return "%d." % self._cnt + elif self._style in ('loweralpha', 'upperalpha'): + return "%c." % self._cnt + elif self._style.endswith('roman'): + res = roman.toRoman(self._cnt) + '.' + if self._style.startswith('upper'): + return res.upper() + return res.lower() + else: + return "%d." % self._cnt + def get_width(self): + return self._indent + def __repr__(self): + return 'enum_style-%s' % list(self._style) + + if node.has_key('enumtype'): + self._list_char.append(enum_char(node['enumtype'])) + else: + self._list_char.append(enum_char('bullet')) + if len(self._list_char) > 1: + # indent nested lists + self.indent(self._list_char[-2].get_width()) + else: + self.indent(self._list_char[-1].get_width()) + + def list_end(self): + self.dedent() + self._list_char.pop() + + def header(self): + tmpl = (".TH %(title_upper)s %(manual_section)s" + " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" + ".SH NAME\n" + "%(title)s \- %(subtitle)s\n") + return tmpl % self._docinfo + + def append_header(self): + """append header with .TH and .SH NAME""" + # NOTE before everything + # .TH title_upper section date source manual + if self.header_written: + return + self.body.append(self.header()) + self.body.append(MACRO_DEF) + self.header_written = 1 + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + pass + + def visit_admonition(self, node, name=None): + if name: + self.body.append('.IP %s\n' % + self.language.labels.get(name, name)) + + def depart_admonition(self, node): + self.body.append('.RE\n') + + def visit_attention(self, node): + self.visit_admonition(node, 'attention') + + depart_attention = depart_admonition + + def visit_docinfo_item(self, node, name): + if name == 'author': + self._docinfo[name].append(node.astext()) + else: + self._docinfo[name] = node.astext() + self._docinfo_keys.append(name) + raise nodes.SkipNode + + def depart_docinfo_item(self, node): + pass + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + depart_author = depart_docinfo_item + + def visit_authors(self, node): + # _author is called anyway. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + # BUG/HACK: indent alway uses the _last_ indention, + # thus we need two of them. + self.indent(BLOCKQOUTE_INDENT) + self.indent(0) + + def depart_block_quote(self, node): + self.dedent() + self.dedent() + + def visit_bullet_list(self, node): + self.list_start(node) + + def depart_bullet_list(self, node): + self.list_end() + + def visit_caption(self, node): + pass + + def depart_caption(self, node): + pass + + def visit_caution(self, node): + self.visit_admonition(node, 'caution') + + depart_caution = depart_admonition + + def visit_citation(self, node): + num, text = node.astext().split(None, 1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % num) + + def depart_citation(self, node): + pass + + def visit_citation_reference(self, node): + self.body.append('['+node.astext()+']') + raise nodes.SkipNode + + def visit_classifier(self, node): + pass + + def depart_classifier(self, node): + pass + + def visit_colspec(self, node): + self.colspecs.append(node) + + def depart_colspec(self, node): + pass + + def write_colspecs(self): + self.body.append("%s.\n" % ('L '*len(self.colspecs))) + + def visit_comment(self, node, + sub=re.compile('-(?=-)').sub): + self.body.append(self.comment(node.astext())) + raise nodes.SkipNode + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + depart_contact = depart_docinfo_item + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def visit_danger(self, node): + self.visit_admonition(node, 'danger') + + depart_danger = depart_admonition + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + pass + + def visit_definition_list(self, node): + self.indent(DEFINITION_LIST_INDENT) + + def depart_definition_list(self, node): + self.dedent() + + def visit_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][0]) + + def depart_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][1]) + + def visit_description(self, node): + pass + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self._in_docinfo = 1 + + def depart_docinfo(self, node): + self._in_docinfo = None + # NOTE nothing should be written before this + self.append_header() + + def visit_doctest_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_doctest_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_document(self, node): + # no blank line between comment and header. + self.body.append(self.comment(self.document_start).rstrip()+'\n') + # writing header is postboned + self.header_written = 0 + + def depart_document(self, node): + if self._docinfo['author']: + self.body.append('.SH AUTHOR\n%s\n' + % ', '.join(self._docinfo['author'])) + skip = ('author', 'copyright', 'date', + 'manual_group', 'manual_section', + 'subtitle', + 'title', 'title_upper', 'version') + for name in self._docinfo_keys: + if name == 'address': + self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % ( + self.language.labels.get(name, name), + self.defs['indent'][0] % 0, + self.defs['indent'][0] % BLOCKQOUTE_INDENT, + self._docinfo[name], + self.defs['indent'][1], + self.defs['indent'][1])) + elif not name in skip: + if name in self._docinfo_names: + label = self._docinfo_names[name] + else: + label = self.language.labels.get(name, name) + self.body.append("\n%s: %s\n" % (label, self._docinfo[name])) + if self._docinfo['copyright']: + self.body.append('.SH COPYRIGHT\n%s\n' + % self._docinfo['copyright']) + self.body.append(self.comment( + 'Generated by docutils manpage writer.\n')) + + def visit_emphasis(self, node): + self.body.append(self.defs['emphasis'][0]) + + def depart_emphasis(self, node): + self.body.append(self.defs['emphasis'][1]) + + def visit_entry(self, node): + # a cell in a table row + if 'morerows' in node: + self.document.reporter.warning('"table row spanning" not supported', + base_node=node) + if 'morecols' in node: + self.document.reporter.warning( + '"table cell spanning" not supported', base_node=node) + self.context.append(len(self.body)) + + def depart_entry(self, node): + start = self.context.pop() + self._active_table.append_cell(self.body[start:]) + del self.body[start:] + + def visit_enumerated_list(self, node): + self.list_start(node) + + def depart_enumerated_list(self, node): + self.list_end() + + def visit_error(self, node): + self.visit_admonition(node, 'error') + + depart_error = depart_admonition + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_body(self, node): + if self._in_docinfo: + name_normalized = self._field_name.lower().replace(" ","_") + self._docinfo_names[name_normalized] = self._field_name + self.visit_docinfo_item(node, name_normalized) + raise nodes.SkipNode + + def depart_field_body(self, node): + pass + + def visit_field_list(self, node): + self.indent(FIELD_LIST_INDENT) + + def depart_field_list(self, node): + self.dedent() + + def visit_field_name(self, node): + if self._in_docinfo: + self._field_name = node.astext() + raise nodes.SkipNode + else: + self.body.append(self.defs['field_name'][0]) + + def depart_field_name(self, node): + self.body.append(self.defs['field_name'][1]) + + def visit_figure(self, node): + self.indent(2.5) + self.indent(0) + + def depart_figure(self, node): + self.dedent() + self.dedent() + + def visit_footer(self, node): + self.document.reporter.warning('"footer" not supported', + base_node=node) + + def depart_footer(self, node): + pass + + def visit_footnote(self, node): + num, text = node.astext().split(None, 1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % self.deunicode(num)) + + def depart_footnote(self, node): + pass + + def footnote_backrefs(self, node): + self.document.reporter.warning('"footnote_backrefs" not supported', + base_node=node) + + def visit_footnote_reference(self, node): + self.body.append('['+self.deunicode(node.astext())+']') + raise nodes.SkipNode + + def depart_footnote_reference(self, node): + pass + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + raise NotImplementedError, node.astext() + + def depart_header(self, node): + pass + + def visit_hint(self, node): + self.visit_admonition(node, 'hint') + + depart_hint = depart_admonition + + def visit_subscript(self, node): + self.body.append('\\s-2\\d') + + def depart_subscript(self, node): + self.body.append('\\u\\s0') + + def visit_superscript(self, node): + self.body.append('\\s-2\\u') + + def depart_superscript(self, node): + self.body.append('\\d\\s0') + + def visit_attribution(self, node): + self.body.append('\\(em ') + + def depart_attribution(self, node): + self.body.append('\n') + + def visit_image(self, node): + self.document.reporter.warning('"image" not supported', + base_node=node) + text = [] + if 'alt' in node.attributes: + text.append(node.attributes['alt']) + if 'uri' in node.attributes: + text.append(node.attributes['uri']) + self.body.append('[image: %s]\n' % ('/'.join(text))) + raise nodes.SkipNode + + def visit_important(self, node): + self.visit_admonition(node, 'important') + + depart_important = depart_admonition + + def visit_label(self, node): + # footnote and citation + if (isinstance(node.parent, nodes.footnote) + or isinstance(node.parent, nodes.citation)): + raise nodes.SkipNode + self.document.reporter.warning('"unsupported "label"', + base_node=node) + self.body.append('[') + + def depart_label(self, node): + self.body.append(']\n') + + def visit_legend(self, node): + pass + + def depart_legend(self, node): + pass + + # WHAT should we use .INDENT, .UNINDENT ? + def visit_line_block(self, node): + self._line_block += 1 + if self._line_block == 1: + # TODO: separate inline blocks from previous paragraphs + # see http://hg.intevation.org/mercurial/crew/rev/9c142ed9c405 + # self.body.append('.sp\n') + # but it does not work for me. + self.body.append('.nf\n') + else: + self.body.append('.in +2\n') + + def depart_line_block(self, node): + self._line_block -= 1 + if self._line_block == 0: + self.body.append('.fi\n') + self.body.append('.sp\n') + else: + self.body.append('.in -2\n') + + def visit_line(self, node): + pass + + def depart_line(self, node): + self.body.append('\n') + + def visit_list_item(self, node): + # man 7 man argues to use ".IP" instead of ".TP" + self.body.append('.IP %s %d\n' % ( + self._list_char[-1].next(), + self._list_char[-1].get_width(),)) + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.body.append(self.defs['literal'][0]) + + def depart_literal(self, node): + self.body.append(self.defs['literal'][1]) + + def visit_literal_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_literal_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_meta(self, node): + raise NotImplementedError, node.astext() + + def depart_meta(self, node): + pass + + def visit_note(self, node): + self.visit_admonition(node, 'note') + + depart_note = depart_admonition + + def indent(self, by=0.5): + # if we are in a section ".SH" there already is a .RS + step = self._indent[-1] + self._indent.append(by) + self.body.append(self.defs['indent'][0] % step) + + def dedent(self): + self._indent.pop() + self.body.append(self.defs['indent'][1]) + + def visit_option_list(self, node): + self.indent(OPTION_LIST_INDENT) + + def depart_option_list(self, node): + self.dedent() + + def visit_option_list_item(self, node): + # one item of the list + self.body.append(self.defs['option_list_item'][0]) + + def depart_option_list_item(self, node): + self.body.append(self.defs['option_list_item'][1]) + + def visit_option_group(self, node): + # as one option could have several forms it is a group + # options without parameter bold only, .B, -v + # options with parameter bold italic, .BI, -f file + # + # we do not know if .B or .BI + self.context.append('.B') # blind guess + self.context.append(len(self.body)) # to be able to insert later + self.context.append(0) # option counter + + def depart_option_group(self, node): + self.context.pop() # the counter + start_position = self.context.pop() + text = self.body[start_position:] + del self.body[start_position:] + self.body.append('%s%s\n' % (self.context.pop(), ''.join(text))) + + def visit_option(self, node): + # each form of the option will be presented separately + if self.context[-1] > 0: + self.body.append(', ') + if self.context[-3] == '.BI': + self.body.append('\\') + self.body.append(' ') + + def depart_option(self, node): + self.context[-1] += 1 + + def visit_option_string(self, node): + # do not know if .B or .BI + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + self.context[-3] = '.BI' # bold/italic alternate + if node['delimiter'] != ' ': + self.body.append('\\fB%s ' % node['delimiter']) + elif self.body[len(self.body)-1].endswith('='): + # a blank only means no blank in output, just changing font + self.body.append(' ') + else: + # blank backslash blank, switch font then a blank + self.body.append(' \\ ') + + def depart_option_argument(self, node): + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + pass + + def visit_paragraph(self, node): + # ``.PP`` : Start standard indented paragraph. + # ``.LP`` : Start block paragraph, all except the first. + # ``.P [type]`` : Start paragraph type. + # NOTE dont use paragraph starts because they reset indentation. + # ``.sp`` is only vertical space + self.ensure_eol() + self.body.append('.sp\n') + + def depart_paragraph(self, node): + self.body.append('\n') + + def visit_problematic(self, node): + self.body.append(self.defs['problematic'][0]) + + def depart_problematic(self, node): + self.body.append(self.defs['problematic'][1]) + + def visit_raw(self, node): + if node.get('format') == 'manpage': + self.body.append(node.astext() + "\n") + # Keep non-manpage raw text out of output: + raise nodes.SkipNode + + def visit_reference(self, node): + """E.g. link or email address.""" + self.body.append(self.defs['reference'][0]) + + def depart_reference(self, node): + self.body.append(self.defs['reference'][1]) + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + depart_revision = depart_docinfo_item + + def visit_row(self, node): + self._active_table.new_row() + + def depart_row(self, node): + pass + + def visit_section(self, node): + self.section_level += 1 + + def depart_section(self, node): + self.section_level -= 1 + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + depart_status = depart_docinfo_item + + def visit_strong(self, node): + self.body.append(self.defs['strong'][0]) + + def depart_strong(self, node): + self.body.append(self.defs['strong'][1]) + + def visit_substitution_definition(self, node): + """Internal only.""" + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.document.reporter.warning('"substitution_reference" not supported', + base_node=node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['strong'][0]) + elif isinstance(node.parent, nodes.document): + self.visit_docinfo_item(node, 'subtitle') + elif isinstance(node.parent, nodes.section): + self.body.append(self.defs['strong'][0]) + + def depart_subtitle(self, node): + # document subtitle calls SkipNode + self.body.append(self.defs['strong'][1]+'\n.PP\n') + + def visit_system_message(self, node): + # TODO add report_level + #if node['level'] < self.document.reporter['writer'].report_level: + # Level is too low to display: + # raise nodes.SkipNode + attr = {} + backref_text = '' + if node.hasattr('id'): + attr['name'] = node['id'] + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('.IP "System Message: %s/%s (%s:%s)"\n' + % (node['type'], node['level'], node['source'], line)) + + def depart_system_message(self, node): + pass + + def visit_table(self, node): + self._active_table = Table() + + def depart_table(self, node): + self.ensure_eol() + self.body.extend(self._active_table.as_list()) + self._active_table = None + + def visit_target(self, node): + # targets are in-document hyper targets, without any use for man-pages. + raise nodes.SkipNode + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + self.body.append(self.defs['term'][0]) + + def depart_term(self, node): + self.body.append(self.defs['term'][1]) + + def visit_tgroup(self, node): + pass + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + # MAYBE double line '=' + pass + + def depart_thead(self, node): + # MAYBE double line '=' + pass + + def visit_tip(self, node): + self.visit_admonition(node, 'tip') + + depart_tip = depart_admonition + + def visit_title(self, node): + if isinstance(node.parent, nodes.topic): + self.body.append(self.defs['topic-title'][0]) + elif isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['sidebar-title'][0]) + elif isinstance(node.parent, nodes.admonition): + self.body.append('.IP "') + elif self.section_level == 0: + self._docinfo['title'] = node.astext() + # document title for .TH + self._docinfo['title_upper'] = node.astext().upper() + raise nodes.SkipNode + elif self.section_level == 1: + self.body.append('.SH %s\n' % self.deunicode(node.astext().upper())) + raise nodes.SkipNode + else: + self.body.append('.SS ') + + def depart_title(self, node): + if isinstance(node.parent, nodes.admonition): + self.body.append('"') + self.body.append('\n') + + def visit_title_reference(self, node): + """inline citation reference""" + self.body.append(self.defs['title_reference'][0]) + + def depart_title_reference(self, node): + self.body.append(self.defs['title_reference'][1]) + + def visit_topic(self, node): + pass + + def depart_topic(self, node): + pass + + def visit_sidebar(self, node): + pass + + def depart_sidebar(self, node): + pass + + def visit_rubric(self, node): + pass + + def depart_rubric(self, node): + pass + + def visit_transition(self, node): + # .PP Begin a new paragraph and reset prevailing indent. + # .sp N leaves N lines of blank space. + # .ce centers the next line + self.body.append('\n.sp\n.ce\n----\n') + + def depart_transition(self, node): + self.body.append('\n.ce 0\n.sp\n') + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def visit_warning(self, node): + self.visit_admonition(node, 'warning') + + depart_warning = depart_admonition + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + +# vim: set fileencoding=utf-8 et ts=4 ai : diff --git a/python/helpers/docutils/writers/newlatex2e/__init__.py b/python/helpers/docutils/writers/newlatex2e/__init__.py new file mode 100644 index 000000000000..c04208df7fe0 --- /dev/null +++ b/python/helpers/docutils/writers/newlatex2e/__init__.py @@ -0,0 +1,836 @@ +# $Id: __init__.py 5738 2008-11-30 08:59:04Z grubert $ +# Author: Lea Wiemann +# Copyright: This module has been placed in the public domain. + +""" +LaTeX2e document tree Writer. +""" + +# Thanks to Engelbert Gruber and various contributors for the original +# LaTeX writer, some code and many ideas of which have been used for +# this writer. + +__docformat__ = 'reStructuredText' + + +import re +import os.path + +import docutils +from docutils import nodes, writers, utils +from docutils.writers.newlatex2e import unicode_map +from docutils.transforms import writer_aux + + +class Writer(writers.Writer): + + supported = ('newlatex', 'newlatex2e') + """Formats this writer supports.""" + + default_stylesheet = 'base.tex' + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + settings_spec = ( + 'LaTeX-Specific Options', + 'Note that this LaTeX writer is still EXPERIMENTAL and not ' + 'feature-complete. ', + (('Specify a stylesheet file. The path is used verbatim to include ' + 'the file. Overrides --stylesheet-path.', + ['--stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'stylesheet_path'}), + ('Specify a stylesheet file, relative to the current working ' + 'directory. Overrides --stylesheet. Default: "%s"' + % default_stylesheet_path, + ['--stylesheet-path'], + {'metavar': '', 'overrides': 'stylesheet', + 'default': default_stylesheet_path}), + ('Specify a user stylesheet file. See --stylesheet.', + ['--user-stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'user_stylesheet_path'}), + ('Specify a user stylesheet file. See --stylesheet-path.', + ['--user-stylesheet-path'], + {'metavar': '', 'overrides': 'user_stylesheet'}) + ),) + + settings_defaults = { + # Many Unicode characters are provided by unicode_map.py, so + # we can default to latin-1. + 'output_encoding': 'latin-1', + 'output_encoding_error_handler': 'strict', + # Since we are using superscript footnotes, it is necessary to + # trim whitespace in front of footnote references. + 'trim_footnote_reference_space': 1, + # Currently unsupported: + 'docinfo_xform': 0, + # During development: + 'traceback': 1 + } + + relative_path_settings = ('stylesheet_path', 'user_stylesheet_path') + + config_section = 'newlatex2e writer' + config_section_dependencies = ('writers',) + + output = None + """Final translated form of `document`.""" + + def get_transforms(self): + return writers.Writer.get_transforms(self) + [ + writer_aux.Compound, writer_aux.Admonitions] + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = LaTeXTranslator + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + assert not visitor.context, 'context not empty: %s' % visitor.context + self.output = visitor.astext() + self.head = visitor.header + self.body = visitor.body + + +class LaTeXException(Exception): + """ + Exception base class to for exceptions which influence the + automatic generation of LaTeX code. + """ + + +class SkipAttrParentLaTeX(LaTeXException): + """ + Do not generate ``\DECattr`` and ``\renewcommand{\DEVparent}{...}`` for this + node. + + To be raised from ``before_...`` methods. + """ + + +class SkipParentLaTeX(LaTeXException): + """ + Do not generate ``\renewcommand{\DEVparent}{...}`` for this node. + + To be raised from ``before_...`` methods. + """ + + +class LaTeXTranslator(nodes.SparseNodeVisitor): + + # Country code by a.schlock. + # Partly manually converted from iso and babel stuff. + iso639_to_babel = { + 'no': 'norsk', # added by hand + 'gd': 'scottish', # added by hand + 'sl': 'slovenian', + 'af': 'afrikaans', + 'bg': 'bulgarian', + 'br': 'breton', + 'ca': 'catalan', + 'cs': 'czech', + 'cy': 'welsh', + 'da': 'danish', + 'fr': 'french', + # french, francais, canadien, acadian + 'de': 'ngerman', + # ngerman, naustrian, german, germanb, austrian + 'el': 'greek', + 'en': 'english', + # english, USenglish, american, UKenglish, british, canadian + 'eo': 'esperanto', + 'es': 'spanish', + 'et': 'estonian', + 'eu': 'basque', + 'fi': 'finnish', + 'ga': 'irish', + 'gl': 'galician', + 'he': 'hebrew', + 'hr': 'croatian', + 'hu': 'hungarian', + 'is': 'icelandic', + 'it': 'italian', + 'la': 'latin', + 'nl': 'dutch', + 'pl': 'polish', + 'pt': 'portuguese', + 'ro': 'romanian', + 'ru': 'russian', + 'sk': 'slovak', + 'sr': 'serbian', + 'sv': 'swedish', + 'tr': 'turkish', + 'uk': 'ukrainian' + } + + # Start with left double quote. + left_quote = 1 + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = document.settings + self.header = [] + self.body = [] + self.context = [] + self.stylesheet_path = utils.get_stylesheet_reference( + self.settings, os.path.join(os.getcwd(), 'dummy')) + if self.stylesheet_path: + self.settings.record_dependencies.add(self.stylesheet_path) + # This ugly hack will be cleaned up when refactoring the + # stylesheet mess. + self.settings.stylesheet = self.settings.user_stylesheet + self.settings.stylesheet_path = self.settings.user_stylesheet_path + self.user_stylesheet_path = utils.get_stylesheet_reference( + self.settings, os.path.join(os.getcwd(), 'dummy')) + if self.user_stylesheet_path: + self.settings.record_dependencies.add(self.user_stylesheet_path) + + lang = self.settings.language_code or '' + if lang.startswith('de'): + self.double_quote_replacment = "{\\dq}" + elif lang.startswith('it'): + self.double_quote_replacment = r'{\char`\"}' + else: + self.double_quote_replacment = None + + self.write_header() + + def write_header(self): + a = self.header.append + a('%% Generated by Docutils %s .' + % docutils.__version__) + a('') + a('% Docutils settings:') + lang = self.settings.language_code or '' + a(r'\providecommand{\DEVlanguageiso}{%s}' % lang) + a(r'\providecommand{\DEVlanguagebabel}{%s}' % self.iso639_to_babel.get( + lang, self.iso639_to_babel.get(lang.split('_')[0], ''))) + a('') + if self.user_stylesheet_path: + a('% User stylesheet:') + a(r'\input{%s}' % self.user_stylesheet_path) + a('% Docutils stylesheet:') + a(r'\input{%s}' % self.stylesheet_path) + a('') + a('% Default definitions for Docutils nodes:') + for node_name in nodes.node_class_names: + a(r'\providecommand{\DN%s}[1]{#1}' % node_name.replace('_', '')) + a('') + a('% Auxiliary definitions:') + for attr in (r'\DEVparent \DEVattrlen \DEVtitleastext ' + r'\DEVsinglebackref \DEVmultiplebackrefs' + ).split(): + # Later set using \renewcommand. + a(r'\providecommand{%s}{DOCUTILSUNINITIALIZEDVARIABLE}' % attr) + for attr in (r'\DEVparagraphindented \DEVhassubtitle').split(): + # Initialize as boolean variables. + a(r'\providecommand{%s}{false}' % attr) + a('\n\n') + + unicode_map = unicode_map.unicode_map # comprehensive Unicode map + # Fix problems with unimap.py. + unicode_map.update({ + # We have AE or T1 encoding, so "``" etc. work. The macros + # from unimap.py may *not* work. + u'\u201C': '{``}', + u'\u201D': "{''}", + u'\u201E': '{,,}', + }) + + character_map = { + '\\': r'{\textbackslash}', + '{': r'{\{}', + '}': r'{\}}', + '$': r'{\$}', + '&': r'{\&}', + '%': r'{\%}', + '#': r'{\#}', + '[': r'{[}', + ']': r'{]}', + '-': r'{-}', + '`': r'{`}', + "'": r"{'}", + ',': r'{,}', + '"': r'{"}', + '|': r'{\textbar}', + '<': r'{\textless}', + '>': r'{\textgreater}', + '^': r'{\textasciicircum}', + '~': r'{\textasciitilde}', + '_': r'{\DECtextunderscore}', + } + character_map.update(unicode_map) + #character_map.update(special_map) + + # `att_map` is for encoding attributes. According to + # , + # the following characters are special: # $ % & ~ _ ^ \ { } + # These work without special treatment in macro parameters: + # $, &, ~, _, ^ + att_map = {'#': '\\#', + '%': '\\%', + # We cannot do anything about backslashes. + '\\': '', + '{': '\\{', + '}': '\\}', + # The quotation mark may be redefined by babel. + '"': '"{}', + } + att_map.update(unicode_map) + + def encode(self, text, attval=None): + """ + Encode special characters in ``text`` and return it. + + If attval is true, preserve as much as possible verbatim (used + in attribute value encoding). If attval is 'width' or + 'height', `text` is interpreted as a length value. + """ + if attval in ('width', 'height'): + match = re.match(r'([0-9.]+)(\S*)$', text) + assert match, '%s="%s" must be a length' % (attval, text) + value, unit = match.groups() + if unit == '%': + value = str(float(value) / 100) + unit = r'\DECrelativeunit' + elif unit in ('', 'px'): + # If \DECpixelunit is "pt", this gives the same notion + # of pixels as graphicx. This is a bit of a hack. + value = str(float(value) * 0.75) + unit = '\DECpixelunit' + return '%s%s' % (value, unit) + if attval: + get = self.att_map.get + else: + get = self.character_map.get + text = ''.join([get(c, c) for c in text]) + if (self.literal_block or self.inline_literal) and not attval: + # NB: We can have inline literals within literal blocks. + # Shrink '\r\n'. + text = text.replace('\r\n', '\n') + # Convert space. If "{ }~~~~~" is wrapped (at the + # brace-enclosed space "{ }"), the following non-breaking + # spaces ("~~~~") do *not* wind up at the beginning of the + # next line. Also note that no hyphenation is done if the + # breaking space ("{ }") comes *after* the non-breaking + # spaces. + if self.literal_block: + # Replace newlines with real newlines. + text = text.replace('\n', '\mbox{}\\\\{}') + replace_fn = self.encode_replace_for_literal_block_spaces + else: + replace_fn = self.encode_replace_for_inline_literal_spaces + text = re.sub(r'\s+', replace_fn, text) + # Protect hyphens; if we don't, line breaks will be + # possible at the hyphens and even the \textnhtt macro + # from the hyphenat package won't change that. + text = text.replace('-', r'\mbox{-}') + text = text.replace("'", r'{\DECtextliteralsinglequote}') + if self.double_quote_replacment is not None: + text = text.replace('"', self.double_quote_replacment) + + return text + else: + if not attval: + # Replace space with single protected space. + text = re.sub(r'\s+', '{ }', text) + # Replace double quotes with macro calls. + L = [] + for part in text.split(self.character_map['"']): + if L: + # Insert quote. + L.append(self.left_quote and r'{\DECtextleftdblquote}' + or r'{\DECtextrightdblquote}') + self.left_quote = not self.left_quote + L.append(part) + return ''.join(L) + else: + return text + + def encode_replace_for_literal_block_spaces(self, match): + return '~' * len(match.group()) + + def encode_replace_for_inline_literal_spaces(self, match): + return '{ }' + '~' * (len(match.group()) - 1) + + def astext(self): + return '\n'.join(self.header) + (''.join(self.body)) + + def append(self, text, newline='%\n'): + """ + Append text, stripping newlines, producing nice LaTeX code. + """ + lines = [' ' * self.indentation_level + line + newline + for line in text.splitlines(0)] + self.body.append(''.join(lines)) + + def visit_Text(self, node): + self.append(self.encode(node.astext())) + + def depart_Text(self, node): + pass + + def is_indented(self, paragraph): + """Return true if `paragraph` should be first-line-indented.""" + assert isinstance(paragraph, nodes.paragraph) + siblings = [n for n in paragraph.parent if + self.is_visible(n) and not isinstance(n, nodes.Titular)] + index = siblings.index(paragraph) + if ('continued' in paragraph['classes'] or + index > 0 and isinstance(siblings[index-1], nodes.transition)): + return 0 + # Indent all but the first paragraphs. + return index > 0 + + def before_paragraph(self, node): + self.append(r'\renewcommand{\DEVparagraphindented}{%s}' + % (self.is_indented(node) and 'true' or 'false')) + + def before_title(self, node): + self.append(r'\renewcommand{\DEVtitleastext}{%s}' + % self.encode(node.astext())) + self.append(r'\renewcommand{\DEVhassubtitle}{%s}' + % ((len(node.parent) > 2 and + isinstance(node.parent[1], nodes.subtitle)) + and 'true' or 'false')) + + def before_generated(self, node): + if 'sectnum' in node['classes']: + node[0] = node[0].strip() + + literal_block = 0 + + def visit_literal_block(self, node): + self.literal_block = 1 + + def depart_literal_block(self, node): + self.literal_block = 0 + + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + inline_literal = 0 + + def visit_literal(self, node): + self.inline_literal += 1 + + def depart_literal(self, node): + self.inline_literal -= 1 + + def _make_encodable(self, text): + """ + Return text (a unicode object) with all unencodable characters + replaced with '?'. + + Thus, the returned unicode string is guaranteed to be encodable. + """ + encoding = self.settings.output_encoding + return text.encode(encoding, 'replace').decode(encoding) + + def visit_comment(self, node): + """ + Insert the comment unchanged into the document, replacing + unencodable characters with '?'. + + (This is done in order not to fail if comments contain unencodable + characters, because our default encoding is not UTF-8.) + """ + self.append('\n'.join(['% ' + self._make_encodable(line) for line + in node.astext().splitlines(0)]), newline='\n') + raise nodes.SkipChildren + + def before_topic(self, node): + if 'contents' in node['classes']: + for bullet_list in list(node.traverse(nodes.bullet_list)): + p = bullet_list.parent + if isinstance(p, nodes.list_item): + p.parent.insert(p.parent.index(p) + 1, bullet_list) + del p[1] + for paragraph in node.traverse(nodes.paragraph): + paragraph.attributes.update(paragraph[0].attributes) + paragraph[:] = paragraph[0] + paragraph.parent['tocrefid'] = paragraph['refid'] + node['contents'] = 1 + else: + node['contents'] = 0 + + bullet_list_level = 0 + + def visit_bullet_list(self, node): + self.append(r'\DECsetbullet{\labelitem%s}' % + ['i', 'ii', 'iii', 'iv'][min(self.bullet_list_level, 3)]) + self.bullet_list_level += 1 + + def depart_bullet_list(self, node): + self.bullet_list_level -= 1 + + enum_styles = {'arabic': 'arabic', 'loweralpha': 'alph', 'upperalpha': + 'Alph', 'lowerroman': 'roman', 'upperroman': 'Roman'} + + enum_counter = 0 + + def visit_enumerated_list(self, node): + # We create our own enumeration list environment. This allows + # to set the style and starting value and unlimited nesting. + # Maybe the actual creation (\DEC) can be moved to the + # stylesheet? + self.enum_counter += 1 + enum_prefix = self.encode(node['prefix']) + enum_suffix = self.encode(node['suffix']) + enum_type = '\\' + self.enum_styles.get(node['enumtype'], r'arabic') + start = node.get('start', 1) - 1 + counter = 'Denumcounter%d' % self.enum_counter + self.append(r'\DECmakeenumeratedlist{%s}{%s}{%s}{%s}{%s}{' + % (enum_prefix, enum_type, enum_suffix, counter, start)) + # for Emacs: } + + def depart_enumerated_list(self, node): + self.append('}') # for Emacs: { + + def before_list_item(self, node): + # XXX needs cleanup. + if (len(node) and (isinstance(node[-1], nodes.TextElement) or + isinstance(node[-1], nodes.Text)) and + node.parent.index(node) == len(node.parent) - 1): + node['lastitem'] = 'true' + + before_line = before_list_item + + def before_raw(self, node): + if 'latex' in node.get('format', '').split(): + # We're inserting the text in before_raw and thus outside + # of \DN... and \DECattr in order to make grouping with + # curly brackets work. + self.append(node.astext()) + raise nodes.SkipChildren + + def process_backlinks(self, node, type): + """ + Add LaTeX handling code for backlinks of footnote or citation + node `node`. `type` is either 'footnote' or 'citation'. + """ + self.append(r'\renewcommand{\DEVsinglebackref}{}') + self.append(r'\renewcommand{\DEVmultiplebackrefs}{}') + if len(node['backrefs']) > 1: + refs = [] + for i in range(len(node['backrefs'])): + # \DECmulticitationbacklink or \DECmultifootnotebacklink. + refs.append(r'\DECmulti%sbacklink{%s}{%s}' + % (type, node['backrefs'][i], i + 1)) + self.append(r'\renewcommand{\DEVmultiplebackrefs}{(%s){ }}' + % ', '.join(refs)) + elif len(node['backrefs']) == 1: + self.append(r'\renewcommand{\DEVsinglebackref}{%s}' + % node['backrefs'][0]) + + def visit_footnote(self, node): + self.process_backlinks(node, 'footnote') + + def visit_citation(self, node): + self.process_backlinks(node, 'citation') + + def before_table(self, node): + # A table contains exactly one tgroup. See before_tgroup. + pass + + def before_tgroup(self, node): + widths = [] + total_width = 0 + for i in range(int(node['cols'])): + assert isinstance(node[i], nodes.colspec) + widths.append(int(node[i]['colwidth']) + 1) + total_width += widths[-1] + del node[:len(widths)] + tablespec = '|' + for w in widths: + # 0.93 is probably wrong in many cases. XXX Find a + # solution which works *always*. + tablespec += r'p{%s\textwidth}|' % (0.93 * w / + max(total_width, 60)) + self.append(r'\DECmaketable{%s}{' % tablespec) + self.context.append('}') + raise SkipAttrParentLaTeX + + def depart_tgroup(self, node): + self.append(self.context.pop()) + + def before_row(self, node): + raise SkipAttrParentLaTeX + + def before_thead(self, node): + raise SkipAttrParentLaTeX + + def before_tbody(self, node): + raise SkipAttrParentLaTeX + + def is_simply_entry(self, node): + return (len(node) == 1 and isinstance(node[0], nodes.paragraph) or + len(node) == 0) + + def before_entry(self, node): + is_leftmost = 0 + if node.hasattr('morerows'): + self.document.reporter.severe('Rowspans are not supported.') + # Todo: Add empty cells below rowspanning cell and issue + # warning instead of severe. + if node.hasattr('morecols'): + # The author got a headache trying to implement + # multicolumn support. + if not self.is_simply_entry(node): + self.document.reporter.severe( + 'Colspanning table cells may only contain one paragraph.') + # Todo: Same as above. + # The number of columns this entry spans (as a string). + colspan = int(node['morecols']) + 1 + del node['morecols'] + else: + colspan = 1 + # Macro to call -- \DECcolspan or \DECcolspanleft. + macro_name = r'\DECcolspan' + if node.parent.index(node) == 0: + # Leftmost column. + macro_name += 'left' + is_leftmost = 1 + if colspan > 1: + self.append('%s{%s}{' % (macro_name, colspan)) + self.context.append('}') + else: + # Do not add a multicolumn with colspan 1 beacuse we need + # at least one non-multicolumn cell per column to get the + # desired column widths, and we can only do colspans with + # cells consisting of only one paragraph. + if not is_leftmost: + self.append(r'\DECsubsequententry{') + self.context.append('}') + else: + self.context.append('') + if isinstance(node.parent.parent, nodes.thead): + node['tableheaderentry'] = 'true' + + # Don't add \renewcommand{\DEVparent}{...} because there must + # not be any non-expandable commands in front of \multicolumn. + raise SkipParentLaTeX + + def depart_entry(self, node): + self.append(self.context.pop()) + + def before_substitution_definition(self, node): + raise nodes.SkipNode + + indentation_level = 0 + + def node_name(self, node): + return node.__class__.__name__.replace('_', '') + + # Attribute propagation order. + attribute_order = ['align', 'classes', 'ids'] + + def attribute_cmp(self, a1, a2): + """ + Compare attribute names `a1` and `a2`. Used in + propagate_attributes to determine propagation order. + + See built-in function `cmp` for return value. + """ + if a1 in self.attribute_order and a2 in self.attribute_order: + return cmp(self.attribute_order.index(a1), + self.attribute_order.index(a2)) + if (a1 in self.attribute_order) != (a2 in self.attribute_order): + # Attributes not in self.attribute_order come last. + return a1 in self.attribute_order and -1 or 1 + else: + return cmp(a1, a2) + + def propagate_attributes(self, node): + # Propagate attributes using \DECattr macros. + node_name = self.node_name(node) + attlist = [] + if isinstance(node, nodes.Element): + attlist = node.attlist() + attlist.sort(lambda pair1, pair2: self.attribute_cmp(pair1[0], + pair2[0])) + # `numatts` may be greater than len(attlist) due to list + # attributes. + numatts = 0 + pass_contents = self.pass_contents(node) + for key, value in attlist: + if isinstance(value, list): + self.append(r'\renewcommand{\DEVattrlen}{%s}' % len(value)) + for i in range(len(value)): + self.append(r'\DECattr{%s}{%s}{%s}{%s}{' % + (i+1, key, self.encode(value[i], attval=key), + node_name)) + if not pass_contents: + self.append('}') + numatts += len(value) + else: + self.append(r'\DECattr{}{%s}{%s}{%s}{' % + (key, self.encode(unicode(value), attval=key), + node_name)) + if not pass_contents: + self.append('}') + numatts += 1 + if pass_contents: + self.context.append('}' * numatts) # for Emacs: { + else: + self.context.append('') + + def visit_docinfo(self, node): + raise NotImplementedError('Docinfo not yet implemented.') + + def visit_document(self, node): + document = node + # Move IDs into TextElements. This won't work for images. + # Need to review this. + for node in document.traverse(nodes.Element): + if 'ids' in node and not isinstance(node, + nodes.TextElement): + next_text_element = node.next_node(nodes.TextElement) + if next_text_element: + next_text_element['ids'].extend(node['ids']) + node['ids'] = [] + + def pass_contents(self, node): + r""" + Return True if the node contents should be passed in + \DN{} and \DECattr{}{}{}{}{}. + Return False if the node contents should be passed in + \DECvisit \DECdepart, and no + attribute handler should be called. + """ + # Passing the whole document or whole sections as parameters + # to \DN... or \DECattr causes LaTeX to run out of memory. + return not isinstance(node, (nodes.document, nodes.section)) + + def dispatch_visit(self, node): + skip_attr = skip_parent = 0 + # TreePruningException to be propagated. + tree_pruning_exception = None + if hasattr(self, 'before_' + node.__class__.__name__): + try: + getattr(self, 'before_' + node.__class__.__name__)(node) + except SkipParentLaTeX: + skip_parent = 1 + except SkipAttrParentLaTeX: + skip_attr = 1 + skip_parent = 1 + except nodes.SkipNode: + raise + except (nodes.SkipChildren, nodes.SkipSiblings), instance: + tree_pruning_exception = instance + except nodes.SkipDeparture: + raise NotImplementedError( + 'SkipDeparture not usable in LaTeX writer') + + if not isinstance(node, nodes.Text): + node_name = self.node_name(node) + # attribute_deleters will be appended to self.context. + attribute_deleters = [] + if not skip_parent and not isinstance(node, nodes.document): + self.append(r'\renewcommand{\DEVparent}{%s}' + % self.node_name(node.parent)) + for name, value in node.attlist(): + if not isinstance(value, list) and not ':' in name: + # For non-list and non-special (like + # 'xml:preserve') attributes, set + # \DEVcurrentNA to the + # attribute value, so that the value of the + # attribute is available in the node handler + # and all children. + macro = r'\DEVcurrentN%sA%s' % (node_name, name) + self.append(r'\def%s{%s}' % ( + macro, self.encode(unicode(value), attval=name))) + # Make the attribute undefined afterwards. + attribute_deleters.append(r'\let%s=\relax' % macro) + self.context.append('\n'.join(attribute_deleters)) + if self.pass_contents(node): + # Call \DN{}. + self.append(r'\DN%s{' % node_name) + self.context.append('}') + else: + # Call \DECvisit + # \DECdepart. (Maybe we should use LaTeX + # environments for this?) + self.append(r'\DECvisit%s' % node_name) + self.context.append(r'\DECdepart%s' % node_name) + self.indentation_level += 1 + if not skip_attr: + self.propagate_attributes(node) + else: + self.context.append('') + + if (isinstance(node, nodes.TextElement) and + not isinstance(node.parent, nodes.TextElement)): + # Reset current quote to left. + self.left_quote = 1 + + # Call visit_... method. + try: + nodes.SparseNodeVisitor.dispatch_visit(self, node) + except LaTeXException: + raise NotImplementedError( + 'visit_... methods must not raise LaTeXExceptions') + + if tree_pruning_exception: + # Propagate TreePruningException raised in before_... method. + raise tree_pruning_exception + + def is_invisible(self, node): + # Return true if node is invisible or moved away in the LaTeX + # rendering. + return (not isinstance(node, nodes.Text) and + (isinstance(node, nodes.Invisible) or + isinstance(node, nodes.footnote) or + isinstance(node, nodes.citation) or + # Assume raw nodes to be invisible. + isinstance(node, nodes.raw) or + # Floating image or figure. + node.get('align') in ('left', 'right'))) + + def is_visible(self, node): + return not self.is_invisible(node) + + def needs_space(self, node): + """Two nodes for which `needs_space` is true need auxiliary space.""" + # Return true if node is a visible block-level element. + return ((isinstance(node, nodes.Body) or + isinstance(node, nodes.topic)) and + not (self.is_invisible(node) or + isinstance(node.parent, nodes.TextElement))) + + def always_needs_space(self, node): + """ + Always add space around nodes for which `always_needs_space()` + is true, regardless of whether the other node needs space as + well. (E.g. transition next to section.) + """ + return isinstance(node, nodes.transition) + + def dispatch_departure(self, node): + # Call departure method. + nodes.SparseNodeVisitor.dispatch_departure(self, node) + + if not isinstance(node, nodes.Text): + # Close attribute and node handler call (\DN...{...}). + self.indentation_level -= 1 + self.append(self.context.pop() + self.context.pop()) + # Delete \DECcurrentN... attribute macros. + self.append(self.context.pop()) + # Get next sibling. + next_node = node.next_node( + ascend=0, siblings=1, descend=0, + condition=self.is_visible) + # Insert space if necessary. + if (self.needs_space(node) and self.needs_space(next_node) or + self.always_needs_space(node) or + self.always_needs_space(next_node)): + if isinstance(node, nodes.paragraph) and isinstance(next_node, nodes.paragraph): + # Space between paragraphs. + self.append(r'\DECparagraphspace') + else: + # One of the elements is not a paragraph. + self.append(r'\DECauxiliaryspace') diff --git a/python/helpers/docutils/writers/newlatex2e/base.tex b/python/helpers/docutils/writers/newlatex2e/base.tex new file mode 100644 index 000000000000..4955e60930e6 --- /dev/null +++ b/python/helpers/docutils/writers/newlatex2e/base.tex @@ -0,0 +1,1180 @@ +% System stylesheet for the new LaTeX writer, newlatex2e. + +% Major parts of the rendering are done in this stylesheet and not in the +% Python module. + +% For development notes, see notes.txt. + +% User documentation (in the stylesheet for now; that may change though): + +% Naming conventions: +% All uppercase letters in macro names have a specific meaning. +% \D...: All macros introduced by the Docutils LaTeX writer start with "D". +% \DS: Setup function (called at the bottom of this stylesheet). +% \DN{}: Handler for Docutils document tree node `node`; called by +% the Python module. +% \DEV: External variable, set by the Python module. +% \DEC: External command. It is called by the Python module and must be +% defined in this stylesheet. +% \DNA{}{}{}{}{}: +% Attribute handler for `attribute` set on nodes of type `nodename`. +% See below for a discussion of attribute handlers. +% \DA{}{}{}{}{}: +% Attribute handler for all `attribute`. Called only when no specific +% \DNA handler is defined. +% \DNC{}: +% Handler for `class`, when set on nodes of type `nodename`. +% \DC{}: +% Handler for `class`. Called only when no specific \DNC +% handler is defined. +% \D: Generic variable or function. + +% Attribute handlers: +% TODO + +% --------------------------------------------------------------------------- + +% Having to intersperse code with \makeatletter-\makeatother pairs is very +% annoying, so we call \makeatletter at the top and \makeatother at the +% bottom. Just be aware that you cannot use "@" as a text character inside +% this stylesheet. +\makeatletter + +% Print-mode (as opposed to online mode e.g. with Adobe Reader). +% This causes for example blue hyperlinks. +\providecommand{\Dprinting}{false} + +% \DSearly is called right after \documentclass. +\providecommand{\DSearly}{} +% \DSlate is called at the end of the stylesheet (right before the document +% tree). +\providecommand{\DSlate}{} + +% Use the KOMA script article class. +\providecommand{\Ddocumentclass}{scrartcl} +\providecommand{\Ddocumentoptions}{a4paper} +\providecommand{\DSdocumentclass}{ + \documentclass[\Ddocumentoptions]{\Ddocumentclass} } + +% Todo: This should be movable to the bottom, but it isn't as long as +% we use \usepackage commands at the top level of this stylesheet +% (which we shouldn't). +\DSdocumentclass + +\providecommand{\DSpackages}{ + % Load miscellaneous packages. + % Note 1: Many of the packages loaded here are used throughout this stylesheet. + % If one of these packages does not work on your system or in your scenario, + % please let us know, so we can consider making the package optional. + % Note 2: It would appear cleaner to load packages where they are used. + % However, since using a wrong package loading order can lead to *very* + % subtle bugs, we centralize the loading of most packages here. + \DSfontencoding % load font encoding packages + \DSlanguage % load babel + % Using \ifthenelse conditionals. + \usepackage{ifthen} % before hyperref (really!) + % There is not support for *not* using hyperref because it's used in many + % places. If this is a problem (e.g. because hyperref doesn't work on your + % system), please let us know. + \usepackage[colorlinks=false,pdfborder={0 0 0}]{hyperref} + % Get color, e.g. for links and system messages. + \usepackage{color} + % Get \textnhtt macro (non-hyphenating type writer). + \usepackage{hyphenat} + % For sidebars. + \usepackage{picins} + % We use longtable to create tables. + \usepackage{longtable} + % Images. + \usepackage{graphicx} + % These packages might be useful (some just add magic pixie dust), so + % evaluate them: + %\usepackage{fixmath} + %\usepackage{amsmath} + % Add some missing symbols like \textonehalf. + \usepackage{textcomp} +} + +\providecommand{\DSfontencoding}{ + % Set up font encoding. Called by \DSpackages. + % AE is a T1 emulation. It provides mostly the same characters and + % features as T1-encoded fonts but doesn't use bitmap fonts (which are + % unsuitable for online reading and subtle for printers). + \usepackage{ae} + % Provide the characters not contained in AE from EC bitmap fonts. + \usepackage{aecompl} + % Guillemets ("<<", ">>") in AE. + \usepackage{aeguill} +} + +\providecommand{\DSsymbols}{% + % Fix up symbols. + % The Euro symbol in Computer Modern looks, um, funny. Let's get a + % proper Euro symbol. + \usepackage{eurosym}% + \renewcommand{\texteuro}{\euro}% +} + +% Taken from +% +% and modified. Used with permission. +\providecommand{\Dprovidelength}[2]{% + \begingroup% + \escapechar\m@ne% + \xdef\@gtempa{{\string#1}}% + \endgroup% + \expandafter\@ifundefined\@gtempa% + {\newlength{#1}\setlength{#1}{#2}}% + {}% +} + +\providecommand{\Dprovidecounter}[2]{% + % Like \newcounter except that it doesn't crash if the counter + % already exists. + \@ifundefined{c@#1}{\newcounter{#1}\setcounter{#1}{#2}}{} +} + +\Dprovidelength{\Dboxparindent}{\parindent} + +\providecommand{\Dmakebox}[1]{% + % Make a centered, frameless box. Useful e.g. for block quotes. + % Do not use minipages here, but create pseudo-lists to allow + % page-breaking. (Don't use KOMA-script's addmargin environment + % because it messes up bullet lists.) + \Dmakelistenvironment{}{}{% + \setlength{\parskip}{0pt}% + \setlength{\parindent}{\Dboxparindent}% + \item{#1}% + }% +} + +\providecommand{\Dmakefbox}[1]{% + % Make a centered, framed box. Useful e.g. for admonitions. + \vspace{0.4\baselineskip}% + \begin{center}% + \fbox{% + \begin{minipage}[t]{0.9\linewidth}% + \setlength{\parindent}{\Dboxparindent}% + #1% + \end{minipage}% + }% + \end{center}% + \vspace{0.4\baselineskip}% +} + +% We do not currently recognize the difference between an end-sentence and a +% mid-sentence period (". " vs. ". " in plain text). So \frenchspacing is +% appropriate. +\providecommand{\DSfrenchspacing}{\frenchspacing} + + +\Dprovidelength{\Dblocklevelvspace}{% + % Space between block-level elements other than paragraphs. + 0.7\baselineskip plus 0.3\baselineskip minus 0.2\baselineskip% +} +\providecommand{\DECauxiliaryspace}{% + \ifthenelse{\equal{\Dneedvspace}{true}}{\vspace{\Dblocklevelvspace}}{}% + \par\noindent% +} +\providecommand{\DECparagraphspace}{\par} +\providecommand{\Dneedvspace}{true} + +\providecommand{\DSlanguage}{% + % Set up babel. + \usepackage[\DEVlanguagebabel]{babel} +} + +\providecommand{\Difdefined}[3]{\@ifundefined{#1}{#3}{#2}} + +% Handler for 'classes' attribute (called for each class attribute). +\providecommand{\DAclasses}[5]{% + % Dispatch to \DNC. + \Difdefined{DN#4C#3}{% + % Pass only contents, nothing else! + \csname DN#4C#3\endcsname{#5}% + }{% + % Otherwise, dispatch to \DC. + \Difdefined{DC#3}{% + \csname DC#3\endcsname{#5}% + }{% + #5% + }% + }% +} + +\providecommand{\DECattr}[5]{% + % Global attribute dispatcher, called inside the document tree. + % Parameters: + % 1. Attribute number. + % 2. Attribute name. + % 3. Attribute value. + % 4. Node name. + % 5. Node contents. + \Difdefined{DN#4A#2}{% + % Dispatch to \DNA. + \csname DN#4A#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{\Difdefined{DA#2}{% + % Otherwise dispatch to \DA. + \csname DA#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{% + % Otherwise simply run the contents without calling a handler. + #5% + }}% +} + +% ---------- Link handling ---------- +% Targets and references. + +\providecommand{\Draisedlink}[1]{% + % Anchors are placed on the base line by default. This is a bad thing for + % inline context, so we raise the anchor (normally by \baselineskip). + \Hy@raisedlink{#1}% +} + +% References. +% We're assuming here that the "refid" and "refuri" attributes occur +% only in inline context (in TextElements). +\providecommand{\DArefid}[5]{% + \ifthenelse{\equal{#4}{reference}}{% + \Dexplicitreference{\##3}{#5}% + }{% + % If this is not a target node (targets with refids are + % uninteresting and should be silently dropped). + \ifthenelse{\not\equal{#4}{target}}{% + % If this is a footnote reference, call special macro. + \ifthenelse{\equal{#4}{footnotereference}}{% + \Dimplicitfootnotereference{\##3}{#5}% + }{% + \ifthenelse{\equal{#4}{citationreference}}{% + \Dimplicitcitationreference{\##3}{#5}% + }{% + \Dimplicitreference{\##3}{#5}% + }% + }% + }{}% + }% +} +\providecommand{\DArefuri}[5]{% + \ifthenelse{\equal{#4}{target}}{% + % The node name is 'target', so this is a hyperlink target, like this: + % .. _mytarget: URI + % Hyperlink targets are ignored because they are invisible. + }{% + % If a non-target node has a refuri attribute, it must be an explicit URI + % reference (i.e. node name is 'reference'). + \Durireference{#3}{#5}% + }% +} +% Targets. +\providecommand{\DAids}[5]{% + \label{#3}% + \ifthenelse{\equal{#4}{footnotereference}}{% + {% + \renewcommand{\HyperRaiseLinkDefault}{% + % Dirty hack to make backrefs to footnote references work. + % For some reason, \baselineskip is 0pt in fn references. + 0.5\Doriginalbaselineskip% + }% + \Draisedlink{\hypertarget{#3}{}}#5% + }% + }{% + \Draisedlink{\hypertarget{#3}{}}#5% + }% +} +\providecommand{\Dimplicitreference}[2]{% + % Create implicit reference to ID. Implicit references occur + % e.g. in TOC-backlinks of section titles. Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{#2}% +} +\providecommand{\Dimplicitfootnotereference}[2]{% + % Ditto, but for the special case of footnotes. + % We want them to be rendered like explicit references. + \Dexplicitreference{#1}{#2}% +} +\providecommand{\Dimplicitcitationreference}[2]{% + % Ditto for citation references. + \Dimplicitfootnotereference{#1}{#2}% +} +\providecommand{\Dcolorexplicitreference}{% + \ifthenelse{\equal{\Dprinting}{true}}{\color{black}}{\color{blue}}% +} +\providecommand{\Dexplicitreference}[2]{% + % Create explicit reference to ID, e.g. created with "foo_". + % Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{{\Dcolorexplicitreference#2}}% +} +\providecommand{\Dcolorurireference}{\Dcolorexplicitreference} +\providecommand{\Durireference}[2]{% + % Create reference to URI. Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{{\Dcolorurireference#2}}% +} + +\Dprovidecounter{Dpdfbookmarkid}{0}% +\providecommand{\Dpdfbookmark}[1]{% + % Temporarily decrement Desctionlevel counter. + \addtocounter{Dsectionlevel}{-1}% + %\typeout{\arabic{Dsectionlevel}}% + %\typeout{#1}% + %\typeout{docutils\roman{Dpdfbookmarkid}}% + %\typeout{}% + \pdfbookmark[\arabic{Dsectionlevel}]{#1}{docutils\arabic{Dpdfbookmarkid}}% + \addtocounter{Dsectionlevel}{1}% + \addtocounter{Dpdfbookmarkid}{1}% +} +% ---------- End of Link Handling ---------- + +\providecommand{\DNparagraph}[1]{% + \ifthenelse{\equal{\DEVparagraphindented}{true}}{\indent}{\noindent}% + #1% +} +\providecommand{\Dformatboxtitle}[1]{{\Large\textbf{#1}}} +\providecommand{\Dformatboxsubtitle}[1]{{\large\textbf{#1}}} +\providecommand{\Dtopictitle}[1]{% + \Difinsidetoc{\vspace{1em}\par}{}% + \noindent\Dformatboxtitle{#1}% + \ifthenelse{\equal{\DEVhassubtitle}{false}}{\vspace{1em}}{\vspace{0.5em}}% + \par% +} +\providecommand{\Dadmonitiontitle}[1]{% + \Dtopictitle{#1}% +} +\providecommand{\Dtopicsubtitle}[1]{% + \noindent\Dformatboxsubtitle{#1}% + \vspace{1em}% + \par% +} +\providecommand{\Dsidebartitle}[1]{\Dtopictitle{#1}} +\providecommand{\Dsidebarsubtitle}[1]{\Dtopicsubtitle{#1}} +\providecommand{\Ddocumenttitle}[1]{% + \begin{center}{\Huge#1}\end{center}% + \ifthenelse{\equal{\DEVhassubtitle}{true}}{\vspace{0.1cm}}{\vspace{1cm}}% +} +\providecommand{\Ddocumentsubtitle}[1]{% + \begin{center}{\huge#1}\end{center}% + \vspace{1cm}% +} +% Can be overwritten by user stylesheet. +\providecommand{\Dformatsectiontitle}[1]{#1} +\providecommand{\Dformatsectionsubtitle}[1]{\Dformatsectiontitle{#1}} +\providecommand{\Dbookmarksectiontitle}[1]{% + % Return text suitable for use in \section*, \subsection*, etc., + % containing a PDF bookmark. Parameter: The title (as node tree). + \Draisedlink{\Dpdfbookmark{\DEVtitleastext}}% + #1% +} +\providecommand{\Dsectiontitlehook}[1]{#1} +\providecommand{\Dsectiontitle}[1]{% + \Dsectiontitlehook{% + \Ddispatchsectiontitle{\Dbookmarksectiontitle{\Dformatsectiontitle{#1}}}% + }% +} +\providecommand{\Ddispatchsectiontitle}[1]{% + \@ifundefined{Dsectiontitle\roman{Dsectionlevel}}{% + \Ddeepsectiontitle{#1}% + }{% + \csname Dsectiontitle\roman{Dsectionlevel}\endcsname{#1}% + }% +} +\providecommand{\Ddispatchsectionsubtitle}[1]{% + \Ddispatchsectiontitle{#1}% +} +\providecommand{\Dsectiontitlei}[1]{\section*{#1}} +\providecommand{\Dsectiontitleii}[1]{\subsection*{#1}} +\providecommand{\Ddeepsectiontitle}[1]{% + % Anything below \subsubsection (like \paragraph or \subparagraph) + % is useless because it uses the same font. The only way to + % (visually) distinguish such deeply nested sections is to use + % section numbering. + \subsubsection*{#1}% +} +\providecommand{\Dsectionsubtitlehook}[1]{#1} +\Dprovidelength{\Dsectionsubtitleraisedistance}{0.7em} +\providecommand{\Dsectionsubtitlescaling}{0.85} +\providecommand{\Dsectionsubtitle}[1]{% + \Dsectionsubtitlehook{% + % Move the subtitle nearer to the title. + \vspace{-\Dsectionsubtitleraisedistance}% + % Don't create a PDF bookmark. + \Ddispatchsectionsubtitle{% + \Dformatsectionsubtitle{\scalebox{\Dsectionsubtitlescaling}{#1}}% + }% + }% +} +\providecommand{\DNtitle}[1]{% + % Dispatch to \Dtitle. + \csname D\DEVparent title\endcsname{#1}% +} +\providecommand{\DNsubtitle}[1]{% + % Dispatch to \Dsubtitle. + \csname D\DEVparent subtitle\endcsname{#1}% +} + +\providecommand{\DNliteralblock}[1]{% + \Dmakelistenvironment{}{% + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + \setlength{\leftmargin}{0pt}% + }{}% + \setlength{\rightmargin}{0pt}% + }{% + \raggedright\item\noindent\nohyphens{\textnhtt{#1\Dfinalstrut}}% + }% +} +\providecommand{\DNdoctestblock}[1]{\DNliteralblock{#1}} +\providecommand{\DNliteral}[1]{\textnhtt{#1}} +\providecommand{\DNemphasis}[1]{\emph{#1}} +\providecommand{\DNstrong}[1]{\textbf{#1}} +\providecommand{\DECvisitdocument}{\begin{document}\noindent} +\providecommand{\DECdepartdocument}{\end{document}} +\providecommand{\DNtopic}[1]{% + \ifthenelse{\equal{\DEVcurrentNtopicAcontents}{1}}{% + \addtocounter{Dtoclevel}{1}% + \par\noindent% + #1% + \addtocounter{Dtoclevel}{-1}% + }{% + \par\noindent% + \Dmakebox{#1}% + }% +} +\providecommand{\DNadmonition}[1]{% + \DNtopic{#1}% +} +\providecommand{\Dformatrubric}[1]{\textbf{#1}} +\Dprovidelength{\Dprerubricspace}{0.3em} +\providecommand{\DNrubric}[1]{% + \vspace{\Dprerubricspace}\par\noindent\Dformatrubric{#1}\par% +} + +\providecommand{\Dbullet}{} +\providecommand{\DECsetbullet}[1]{\renewcommand{\Dbullet}{#1}} +\providecommand{\DNbulletlist}[1]{% + \Difinsidetoc{% + \Dtocbulletlist{#1}% + }{% + \Dmakelistenvironment{\Dbullet}{}{#1}% + }% +} +% Todo: So what on earth is @pnumwidth? +\renewcommand{\@pnumwidth}{2.2em} +\providecommand{\DNlistitem}[1]{% + \Difinsidetoc{% + \ifthenelse{\equal{\theDtoclevel}{1}\and\equal{\Dlocaltoc}{false}}{% + {% + \par\addvspace{1em}\noindent% + \sectfont% + #1\hfill\pageref{\DEVcurrentNlistitemAtocrefid}% + }% + }{% + \@dottedtocline{0}{\Dtocindent}{0em}{#1}{% + \pageref{\DEVcurrentNlistitemAtocrefid}% + }% + }% + }{% + \item{#1}% + }% +} +\providecommand{\DNenumeratedlist}[1]{#1} +\Dprovidecounter{Dsectionlevel}{0} +\providecommand{\Dvisitsectionhook}{} +\providecommand{\Ddepartsectionhook}{} +\providecommand{\DECvisitsection}{% + \addtocounter{Dsectionlevel}{1}% + \Dvisitsectionhook% +} +\providecommand{\DECdepartsection}{% + \Ddepartsectionhook% + \addtocounter{Dsectionlevel}{-1}% +} + +% Using \_ will cause hyphenation after _ even in \textnhtt-typewriter +% because the hyphenat package redefines \_. So we use +% \textunderscore here. +\providecommand{\DECtextunderscore}{\textunderscore} + +\providecommand{\Dtextinlineliteralfirstspace}{{ }} +\providecommand{\Dtextinlineliteralsecondspace}{{~}} + +\Dprovidelength{\Dlistspacing}{0.8\baselineskip} + +\providecommand{\Dsetlistrightmargin}{% + \ifthenelse{\lengthtest{\linewidth>12em}}{% + % Equal margins. + \setlength{\rightmargin}{\leftmargin}% + }{% + % If the line is narrower than 10em, we don't remove any further + % space from the right. + \setlength{\rightmargin}{0pt}% + }% +} +\providecommand{\Dresetlistdepth}{false} +\Dprovidelength{\Doriginallabelsep}{\labelsep} +\providecommand{\Dmakelistenvironment}[3]{% + % Make list environment with support for unlimited nesting and with + % reasonable default lengths. Parameters: + % 1. Label (same as in list environment). + % 2. Spacing (same as in list environment). + % 3. List contents (contents of list environment). + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + % Unfortunately, vertical spacing doesn't work correctly when + % using lists inside tabular environments, so we use a minipage. + \begin{minipage}[t]{\linewidth}% + }{}% + {% + \renewcommand{\Dneedvspace}{false}% + % \parsep0.5\baselineskip + \renewcommand{\Dresetlistdepth}{false}% + \ifnum \@listdepth>5% + \protect\renewcommand{\Dresetlistdepth}{true}% + \@listdepth=5% + \fi% + \begin{list}{% + #1% + }{% + \setlength{\itemsep}{0pt}% + \setlength{\partopsep}{0pt}% + \setlength{\topsep}{0pt}% + % List should take 90% of total width. + \setlength{\leftmargin}{0.05\linewidth}% + \ifthenelse{\lengthtest{\leftmargin<1.8em}}{% + \setlength{\leftmargin}{1.8em}% + }{}% + \setlength{\labelsep}{\Doriginallabelsep}% + \Dsetlistrightmargin% + #2% + }{% + #3% + }% + \end{list}% + \ifthenelse{\equal{\Dresetlistdepth}{true}}{\@listdepth=5}{}% + }% + \ifthenelse{\equal{\Dinsidetabular}{true}}{\end{minipage}}{}% +} +\providecommand{\Dfinalstrut}{\@finalstrut\@arstrutbox} +\providecommand{\DAlastitem}[5]{#5\Dfinalstrut} + +\Dprovidelength{\Ditemsep}{0pt} +\providecommand{\DECmakeenumeratedlist}[6]{% + % Make enumerated list. + % Parameters: + % - prefix + % - type (\arabic, \roman, ...) + % - suffix + % - suggested counter name + % - start number - 1 + % - list contents + \newcounter{#4}% + \Dmakelistenvironment{#1#2{#4}#3}{% + % Use as much space as needed for the label. + \setlength{\labelwidth}{10em}% + % Reserve enough space so that the label doesn't go beyond the + % left margin of preceding paragraphs. Like that: + % + % A paragraph. + % + % 1. First item. + \setlength{\leftmargin}{2.5em}% + \Dsetlistrightmargin% + \setlength{\itemsep}{\Ditemsep}% + % Use counter recommended by Python module. + \usecounter{#4}% + % Set start value. + \addtocounter{#4}{#5}% + }{% + % The list contents. + #6% + }% +} + + +% Single quote in literal mode. \textquotesingle from package +% textcomp has wrong width when using package ae, so we use a normal +% single curly quote here. +\providecommand{\DECtextliteralsinglequote}{'} + + +% "Tabular lists" are field lists and options lists (not definition +% lists because there the term always appears on its own line). We'll +% use the terminology of field lists now ("field", "field name", +% "field body"), but the same is also analogously applicable to option +% lists. +% +% We want these lists to be breakable across pages. We cannot +% automatically get the narrowest possible size for the left column +% (i.e. the field names or option groups) because tabularx does not +% support multi-page tables, ltxtable needs to have the table in an +% external file and we don't want to clutter the user's directories +% with auxiliary files created by the filecontents environment, and +% ltablex is not included in teTeX. +% +% Thus we set a fixed length for the left column and use list +% environments. This also has the nice side effect that breaking is +% now possible anywhere, not just between fields. +% +% Note that we are creating a distinct list environment for each +% field. There is no macro for a whole tabular list! +\Dprovidelength{\Dtabularlistfieldnamewidth}{6em} +\Dprovidelength{\Dtabularlistfieldnamesep}{0.5em} +\providecommand{\Dinsidetabular}{false} +\providecommand{\Dsavefieldname}{} +\providecommand{\Dsavefieldbody}{} +\Dprovidelength{\Dusedfieldnamewidth}{0pt} +\Dprovidelength{\Drealfieldnamewidth}{0pt} +\providecommand{\Dtabularlistfieldname}[1]{\renewcommand{\Dsavefieldname}{#1}} +\providecommand{\Dtabularlistfieldbody}[1]{\renewcommand{\Dsavefieldbody}{#1}} +\Dprovidelength{\Dparskiptemp}{0pt} +\providecommand{\Dtabularlistfield}[1]{% + {% + % This only saves field name and field body in \Dsavefieldname and + % \Dsavefieldbody, resp. It does not insert any text into the + % document. + #1% + % Recalculate the real field name width everytime we encounter a + % tabular list field because it may have been changed using a + % "raw" node. + \setlength{\Drealfieldnamewidth}{\Dtabularlistfieldnamewidth}% + \addtolength{\Drealfieldnamewidth}{\Dtabularlistfieldnamesep}% + \Dmakelistenvironment{% + \makebox[\Drealfieldnamewidth][l]{\Dsavefieldname}% + }{% + \setlength{\labelwidth}{\Drealfieldnamewidth}% + \setlength{\leftmargin}{\Drealfieldnamewidth}% + \setlength{\rightmargin}{0pt}% + \setlength{\labelsep}{0pt}% + }{% + \item% + \settowidth{\Dusedfieldnamewidth}{\Dsavefieldname}% + \setlength{\Dparskiptemp}{\parskip}% + \ifthenelse{% + \lengthtest{\Dusedfieldnamewidth>\Dtabularlistfieldnamewidth}% + }{% + \mbox{}\par% + \setlength{\parskip}{0pt}% + }{}% + \Dsavefieldbody% + \setlength{\parskip}{\Dparskiptemp}% + %XXX Why did we need this? + %\@finalstrut\@arstrutbox% + }% + \par% + }% +} + +\providecommand{\Dformatfieldname}[1]{\textbf{#1:}} +\providecommand{\DNfieldlist}[1]{#1} +\providecommand{\DNfield}[1]{\Dtabularlistfield{#1}} +\providecommand{\DNfieldname}[1]{% + \Dtabularlistfieldname{% + \Dformatfieldname{#1}% + }% +} +\providecommand{\DNfieldbody}[1]{\Dtabularlistfieldbody{#1}} + +\providecommand{\Dformatoptiongroup}[1]{% + % Format option group, e.g. "-f file, --input file". + \texttt{#1}% +} +\providecommand{\Dformatoption}[1]{% + % Format option, e.g. "-f file". + % Put into mbox to avoid line-breaking at spaces. + \mbox{#1}% +} +\providecommand{\Dformatoptionstring}[1]{% + % Format option string, e.g. "-f". + #1% +} +\providecommand{\Dformatoptionargument}[1]{% + % Format option argument, e.g. "file". + \textsl{#1}% +} +\providecommand{\Dformatoptiondescription}[1]{% + % Format option description, e.g. + % "\DNparagraph{Read input data from file.}" + #1% +} +\providecommand{\DNoptionlist}[1]{#1} +\providecommand{\Doptiongroupjoiner}{,{ }} +\providecommand{\Disfirstoption}{% + % Auxiliary macro indicating if a given option is the first child + % of its option group (if it's not, it has to preceded by + % \Doptiongroupjoiner). + false% +} +\providecommand{\DNoptionlistitem}[1]{% + \Dtabularlistfield{#1}% +} +\providecommand{\DNoptiongroup}[1]{% + \renewcommand{\Disfirstoption}{true}% + \Dtabularlistfieldname{\Dformatoptiongroup{#1}}% +} +\providecommand{\DNoption}[1]{% + % If this is not the first option in this option group, add a + % joiner. + \ifthenelse{\equal{\Disfirstoption}{true}}{% + \renewcommand{\Disfirstoption}{false}% + }{% + \Doptiongroupjoiner% + }% + \Dformatoption{#1}% +} +\providecommand{\DNoptionstring}[1]{\Dformatoptionstring{#1}} +\providecommand{\DNoptionargument}[1]{{ }\Dformatoptionargument{#1}} +\providecommand{\DNdescription}[1]{% + \Dtabularlistfieldbody{\Dformatoptiondescription{#1}}% +} + +\providecommand{\DNdefinitionlist}[1]{% + \begin{description}% + \parskip0pt% + #1% + \end{description}% +} +\providecommand{\DNdefinitionlistitem}[1]{% + % LaTeX expects the label in square brackets; we provide an empty + % label. + \item[]#1% +} +\providecommand{\Dformatterm}[1]{#1} +\providecommand{\DNterm}[1]{\hspace{-5pt}\Dformatterm{#1}} +% I'm still not sure what's the best rendering for classifiers. The +% colon syntax is used by reStructuredText, so it's at least WYSIWYG. +% Use slanted text because italic would cause too much emphasis. +\providecommand{\Dformatclassifier}[1]{\textsl{#1}} +\providecommand{\DNclassifier}[1]{~:~\Dformatclassifier{#1}} +\providecommand{\Dformatdefinition}[1]{#1} +\providecommand{\DNdefinition}[1]{\par\Dformatdefinition{#1}} + +\providecommand{\Dlineblockindentation}{2.5em} +\providecommand{\DNlineblock}[1]{% + \Dmakelistenvironment{}{% + \ifthenelse{\equal{\DEVparent}{lineblock}}{% + % Parent is a line block, so indent. + \setlength{\leftmargin}{\Dlineblockindentation}% + }{% + % At top level; don't indent. + \setlength{\leftmargin}{0pt}% + }% + \setlength{\rightmargin}{0pt}% + \setlength{\parsep}{0pt}% + }{% + #1% + }% +} +\providecommand{\DNline}[1]{\item#1} + +\providecommand{\DNtransition}{% + \raisebox{0.25em}{\parbox{\linewidth}{\hspace*{\fill}\hrulefill\hrulefill\hspace*{\fill}}}% +} + +\providecommand{\Dformatblockquote}[1]{% + % Format contents of block quote. + % This occurs in block-level context, so we cannot use \textsl. + {\slshape#1}% +} +\providecommand{\Dformatattribution}[1]{---\textup{#1}} +\providecommand{\DNblockquote}[1]{% + \Dmakebox{% + \Dformatblockquote{#1} + }% +} +\providecommand{\DNattribution}[1]{% + \par% + \begin{flushright}\Dformatattribution{#1}\end{flushright}% +} + + +% Sidebars: +% Vertical and horizontal margins. +\Dprovidelength{\Dsidebarvmargin}{0.5em} +\Dprovidelength{\Dsidebarhmargin}{1em} +% Padding (space between contents and frame). +\Dprovidelength{\Dsidebarpadding}{1em} +% Frame width. +\Dprovidelength{\Dsidebarframewidth}{2\fboxrule} +% Position ("l" or "r"). +\providecommand{\Dsidebarposition}{r} +% Width. +\Dprovidelength{\Dsidebarwidth}{0.45\linewidth} +\providecommand{\DNsidebar}[1]{ + \parpic[\Dsidebarposition]{% + \begin{minipage}[t]{\Dsidebarwidth}% + % Doing this with nested minipages is ugly, but I haven't found + % another way to place vertical space before and after the fbox. + \vspace{\Dsidebarvmargin}% + {% + \setlength{\fboxrule}{\Dsidebarframewidth}% + \setlength{\fboxsep}{\Dsidebarpadding}% + \fbox{% + \begin{minipage}[t]{\linewidth}% + \setlength{\parindent}{\Dboxparindent}% + #1% + \end{minipage}% + }% + }% + \vspace{\Dsidebarvmargin}% + \end{minipage}% + }% +} + + +% Citations and footnotes. +\providecommand{\Dformatfootnote}[1]{% + % Format footnote. + {% + \footnotesize#1% + % \par is necessary for LaTeX to adjust baselineskip to the + % changed font size. + \par% + }% +} +\providecommand{\Dformatcitation}[1]{\Dformatfootnote{#1}} +\Dprovidelength{\Doriginalbaselineskip}{0pt} +\providecommand{\DNfootnotereference}[1]{% + {% + % \baselineskip is 0pt in \textsuperscript, so we save it here. + \setlength{\Doriginalbaselineskip}{\baselineskip}% + \textsuperscript{#1}% + }% +} +\providecommand{\DNcitationreference}[1]{{[}#1{]}} +\Dprovidelength{\Dfootnotesep}{3.5pt} +\providecommand{\Dsetfootnotespacing}{% + % Spacing commands executed at the beginning of footnotes. + \setlength{\parindent}{0pt}% + \hspace{1em}% +} +\providecommand{\DNfootnote}[1]{% + % See ltfloat.dtx for details. + {% + \insert\footins{% + % BUG: This is too small if the user adds + % \onehalfspacing or \doublespace. + \vspace{\Dfootnotesep}% + \Dsetfootnotespacing% + \Dformatfootnote{#1}% + }% + }% +} +\providecommand{\DNcitation}[1]{\DNfootnote{#1}} +\providecommand{\Dformatfootnotelabel}[1]{% + % Keep \footnotesize in footnote labels (\textsuperscript would + % reduce the font size even more). + \textsuperscript{\footnotesize#1{ }}% +} +\providecommand{\Dformatcitationlabel}[1]{{[}#1{]}{ }} +\providecommand{\Dformatmultiplebackrefs}[1]{% + % If in printing mode, do not write out multiple backrefs. + \ifthenelse{\equal{\Dprinting}{true}}{}{\textsl{#1}}% +} +\providecommand{\Dthislabel}{} +\providecommand{\DNlabel}[1]{% + % Footnote or citatation label. + \renewcommand{\Dthislabel}{#1}% + \ifthenelse{\not\equal{\DEVsinglebackref}{}}{% + \let\Doriginallabel=\Dthislabel% + \def\Dthislabel{% + \Dsinglefootnotebacklink{\DEVsinglebackref}{\Doriginallabel}% + }% + }{}% + \ifthenelse{\equal{\DEVparent}{footnote}}{% + % Footnote label. + \Dformatfootnotelabel{\Dthislabel}% + }{% + \ifthenelse{\equal{\DEVparent}{citation}}{% + % Citation label. + \Dformatcitationlabel{\Dthislabel}% + }{}% + }% + % If there are multiple backrefs, add them now. + \Dformatmultiplebackrefs{\DEVmultiplebackrefs}% +} +\providecommand{\Dsinglefootnotebacklink}[2]{% + % Create normal backlink of a footnote label. Parameters: + % 1. ID. + % 2. Link text. + % Treat like a footnote reference. + \Dimplicitfootnotereference{\##1}{#2}% +} +\providecommand{\DECmultifootnotebacklink}[2]{% + % Create generated backlink, as in (1, 2). Parameters: + % 1. ID. + % 2. Link text. + % Treat like a footnote reference. + \Dimplicitfootnotereference{\##1}{#2}% +} +\providecommand{\Dsinglecitationbacklink}[2]{\Dsinglefootnotebacklink{#1}{#2}} +\providecommand{\DECmulticitationbacklink}[2]{\DECmultifootnotebacklink{#1}{#2}} + + +\providecommand{\DECmaketable}[2]{% + % Make table. Parameters: + % 1. Table spec (like "|p|p|"). + % 2. Table contents. + {% + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + % Inside longtable; we cannot have nested longtables. + \begin{tabular}{#1}% + \hline% + #2% + \end{tabular}% + }{% + \renewcommand{\Dinsidetabular}{true}% + \begin{longtable}{#1}% + \hline% + #2% + \end{longtable}% + }% + }% +} +\providecommand{\DNthead}[1]{% + #1% + \endhead% +} +\providecommand{\DNrow}[1]{% + #1\tabularnewline% + \hline% +} +\providecommand{\Dinsidemulticolumn}{false} +\providecommand{\Dcompensatingmulticol}[3]{% + \multicolumn{#1}{#2}{% + {% + \renewcommand{\Dinsidemulticolumn}{true}% + % Compensate for weird missing vertical space at top of paragraph. + \raisebox{-2.5pt}{#3}% + }% + }% +} +\providecommand{\DECcolspan}[2]{% + % Take care of the morecols attribute (but incremented by 1). + &% + \Dcompensatingmulticol{#1}{l|}{#2}% +} +\providecommand{\DECcolspanleft}[2]{% + % Like \Dmorecols, but called for the leftmost entries in a table + % row. + \Dcompensatingmulticol{#1}{|l|}{#2}% +} +\providecommand{\DECsubsequententry}[1]{% + % +} +\providecommand{\DNentry}[1]{% + % The following sequence adds minimal vertical space above the top + % lines of the first cell paragraph, so that vertical space is + % balanced at the top and bottom of table cells. + \ifthenelse{\equal{\Dinsidemulticolumn}{false}}{% + \vspace{-1em}\vspace{-\parskip}\par% + }{}% + #1% + % No need to add an ampersand ("&"); that's done by \DECsubsequententry. +} +\providecommand{\DAtableheaderentry}[5]{\Dformattableheaderentry{#5}} +\providecommand{\Dformattableheaderentry}[1]{{\bfseries#1}} + + +\providecommand{\DNsystemmessage}[1]{% + {% + \ifthenelse{\equal{\Dprinting}{false}}{\color{red}}{}% + \bfseries% + #1% + }% +} + + +\providecommand{\Dinsidehalign}{false} +\newsavebox{\Dalignedimagebox} +\Dprovidelength{\Dalignedimagewidth}{0pt} +\providecommand{\Dhalign}[2]{% + % Horizontally align the contents to the left or right so that the + % text flows around it. + % Parameters: + % 1. l or r + % 2. Contents. + \renewcommand{\Dinsidehalign}{true}% + % For some obscure reason \parpic consumes some vertical space. + \vspace{-3pt}% + % Now we do something *really* ugly, but this enables us to wrap the + % image in a minipage while still allowing tight frames when + % class=border (see \DNimageCborder). + \sbox{\Dalignedimagebox}{#2}% + \settowidth{\Dalignedimagewidth}{\usebox{\Dalignedimagebox}}% + \parpic[#1]{% + \begin{minipage}[b]{\Dalignedimagewidth}% + % Compensate for previously added space, but not entirely. + \vspace*{2.0pt}% + \vspace*{\Dfloatimagetopmargin}% + \usebox{\Dalignedimagebox}% + \vspace*{1.5pt}% + \vspace*{\Dfloatimagebottommargin}% + \end{minipage}% + }% + \renewcommand{\Dinsidehalign}{false}% +} + + +% Maximum width of an image. +\providecommand{\Dimagemaxwidth}{\linewidth} +\providecommand{\Dfloatimagemaxwidth}{0.5\linewidth} +% Auxiliary variable. +\Dprovidelength{\Dcurrentimagewidth}{0pt} +\providecommand{\DNimageAalign}[5]{% + \ifthenelse{\equal{#3}{left}}{% + \Dhalign{l}{#5}% + }{% + \ifthenelse{\equal{#3}{right}}{% + \Dhalign{r}{#5}% + }{% + \ifthenelse{\equal{#3}{center}}{% + % Text floating around centered figures is a bad idea. Thus + % we use a center environment. Note that no extra space is + % added by the writer, so the space added by the center + % environment is fine. + \begin{center}#5\end{center}% + }{% + #5% + }% + }% + }% +} +% Base path for images. +\providecommand{\Dimagebase}{} +% Auxiliary command. Current image path. +\providecommand{\Dimagepath}{} +\providecommand{\DNimageAuri}[5]{% + % Insert image. We treat the URI like a path here. + \renewcommand{\Dimagepath}{\Dimagebase#3}% + \Difdefined{DcurrentNimageAwidth}{% + \Dwidthimage{\DEVcurrentNimageAwidth}{\Dimagepath}% + }{% + \Dsimpleimage{\Dimagepath}% + }% +} +\Dprovidelength{\Dfloatimagevmargin}{0pt} +\providecommand{\Dfloatimagetopmargin}{\Dfloatimagevmargin} +\providecommand{\Dfloatimagebottommargin}{\Dfloatimagevmargin} +\providecommand{\Dwidthimage}[2]{% + % Image with specified width. + % Parameters: + % 1. Image width. + % 2. Image path. + % Need to make bottom-alignment dependent on align attribute (add + % functional test first). Need to observe height attribute. + %\begin{minipage}[b]{#1}% + \includegraphics[width=#1,height=\textheight,keepaspectratio]{#2}% + %\end{minipage}% +} +\providecommand{\Dcurrentimagemaxwidth}{} +\providecommand{\Dsimpleimage}[1]{% + % Insert image, without much parametrization. + \settowidth{\Dcurrentimagewidth}{\includegraphics{#1}}% + \ifthenelse{\equal{\Dinsidehalign}{true}}{% + \renewcommand{\Dcurrentimagemaxwidth}{\Dfloatimagemaxwidth}% + }{% + \renewcommand{\Dcurrentimagemaxwidth}{\Dimagemaxwidth}% + }% + \ifthenelse{\lengthtest{\Dcurrentimagewidth>\Dcurrentimagemaxwidth}}{% + \Dwidthimage{\Dcurrentimagemaxwidth}{#1}% + }{% + \Dwidthimage{\Dcurrentimagewidth}{#1}% + }% +} +\providecommand{\Dwidthimage}[2]{% + % Image with specified width. + % Parameters: + % 1. Image width. + % 2. Image path. + \Dwidthimage{#1}{#2}% +} + +% Figures. +\providecommand{\DNfigureAalign}[5]{% + % Hack to make it work Right Now. + %\def\DEVcurrentNimageAwidth{\DEVcurrentNfigureAwidth}% + % + %\def\DEVcurrentNimageAwidth{\linewidth}% + \DNimageAalign{#1}{#2}{#3}{#4}{% + \begin{minipage}[b]{0.4\linewidth}#5\end{minipage}}% + %\let\DEVcurrentNimageAwidth=\relax% + % + %\let\DEVcurrentNimageAwidth=\relax% +} +\providecommand{\DNcaption}[1]{\par\noindent{\slshape#1}} +\providecommand{\DNlegend}[1]{\DECauxiliaryspace#1} + +\providecommand{\DCborder}[1]{\fbox{#1}} +% No padding between image and border. +\providecommand{\DNimageCborder}[1]{\frame{#1}} + + +% Need to replace with language-specific stuff. Maybe look at +% csquotes.sty and ask the author for permission to use parts of it. +\providecommand{\DECtextleftdblquote}{``} +\providecommand{\DECtextrightdblquote}{''} + +% Table of contents: +\Dprovidelength{\Dtocininitialsectnumwidth}{2.4em} +\Dprovidelength{\Dtocadditionalsectnumwidth}{0.7em} +% Level inside a table of contents. While this is at -1, we are not +% inside a TOC. +\Dprovidecounter{Dtoclevel}{-1}% +\providecommand{\Dlocaltoc}{false}% +\providecommand{\DNtopicClocal}[1]{% + \renewcommand{\Dlocaltoc}{true}% + \addtolength{\Dtocsectnumwidth}{2\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocindent}{-2\Dtocadditionalsectnumwidth}% + #1% + \addtolength{\Dtocindent}{2\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocsectnumwidth}{-2\Dtocadditionalsectnumwidth}% + \renewcommand{\Dlocaltoc}{false}% +} +\Dprovidelength{\Dtocindent}{0pt}% +\Dprovidelength{\Dtocsectnumwidth}{\Dtocininitialsectnumwidth} +% Compensate for one additional TOC indentation space so that the +% top-level is unindented. +\addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth} +\addtolength{\Dtocindent}{-\Dtocsectnumwidth} +\providecommand{\Difinsidetoc}[2]{% + \ifthenelse{\not\equal{\theDtoclevel}{-1}}{#1}{#2}% +} +\providecommand{\DNgeneratedCsectnum}[1]{% + \Difinsidetoc{% + % Section number inside TOC. + \makebox[\Dtocsectnumwidth][l]{#1}% + }{% + % Section number inside section title. + #1\quad% + }% +} +\providecommand{\Dtocbulletlist}[1]{% + \addtocounter{Dtoclevel}{1}% + \addtolength{\Dtocindent}{\Dtocsectnumwidth}% + \addtolength{\Dtocsectnumwidth}{\Dtocadditionalsectnumwidth}% + #1% + \addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocindent}{-\Dtocsectnumwidth}% + \addtocounter{Dtoclevel}{-1}% +} + + +% For \DECpixelunit, the length value is pre-multiplied with 0.75, so by +% specifying "pt" we get the same notion of "pixel" as graphicx. +\providecommand{\DECpixelunit}{pt} +% Normally lengths are relative to the current linewidth. +\providecommand{\DECrelativeunit}{\linewidth} + + +% ACTION: These commands actually *do* something. +% Ultimately, everything should be done here, and no active content should be +% above (not even \usepackage). + +\DSearly +\DSpackages +\DSfrenchspacing +\DSsymbols +\DSlate + +\makeatother diff --git a/python/helpers/docutils/writers/newlatex2e/notes.txt b/python/helpers/docutils/writers/newlatex2e/notes.txt new file mode 100644 index 000000000000..a34b8fe3bede --- /dev/null +++ b/python/helpers/docutils/writers/newlatex2e/notes.txt @@ -0,0 +1,79 @@ +New LaTeX Writer +================ + +:Copyright: This document has been placed in the public domain. + +The new LaTeX writer (newlatex2e) is in active development. These are +development notes -- edit ahead! Ultimately, they will be moved to +the global to-do list, but while newlatex2e is incomplete, they remain +here. + +* It appears that all visit_ methods can be turned into before_ + methods (and renamed thereafter). + +* Also pass raw text (foo_bar) and not only renderable text (foo{\_}bar). + See http://article.gmane.org/gmane.text.docutils.user/2516. + +* Try the commands mentioned in + . + +* :: + + 3.1.6. How can I make a document portable to both latex and pdflatex + Contributed by: Christian Kumpf + Check for the existence of the variable \pdfoutput: + \newif\ifpdf + \ifx\pdfoutput\undefined + \pdffalse % we are not running PDFLaTeX + \else + \pdfoutput=1 % we are running PDFLaTeX + \pdftrue + \fi + Then use your new variable \ifpdf + \ifpdf + \usepackage[pdftex]{graphicx} + \pdfcompresslevel=9 + \else + \usepackage{graphicx} + \fi + +* Need to get some simple docinfo field handling. Move language look-up logic to nodes.py? + Same for admonitions. + +* Footnotes should be placed on the same page as their references. + However, there may be multiple references per footnote, so + we'll probably need an option and some sophisticated handling for this. + +* Make sure we don't break ligatures (and possibly hyphenation) with zealous brace protection. + See http://article.gmane.org/gmane.text.docutils.user/2586. + +* Tables inside of footnotes have too large vertical margins. + Need a "reduced vertical margin" mode, maybe? + +* There's not enough vertical space between fields:: + + :Name: + Paragraph. + Paragraph. + :Name: + Paragraph. + Paragraph. + +* Another edge case with too much vertical margin:: + + +--------------------+ + | :Name: | + | Paragraph. | + | :Name: | + | Paragraph. | + +--------------------+ + +* We want to support underscores in citation references, they need to + appear unescaped. + +* If there's raw code between paragraphs, it gets appended to the last + paragraph unless we do ``\par``. That's a little bit ugly. Can we + fix this? (Change paragraph handling maybe?) + +* Test that, say, all Latin 1 characters are renderable. (And + possibly test more characters.) diff --git a/python/helpers/docutils/writers/newlatex2e/tests.txt b/python/helpers/docutils/writers/newlatex2e/tests.txt new file mode 100644 index 000000000000..b456b89f7df7 --- /dev/null +++ b/python/helpers/docutils/writers/newlatex2e/tests.txt @@ -0,0 +1,7 @@ +These tests will have to be migrated to a test suite (either +functional tests or unit tests). + +:: + + Newline followed by + *an asterisk. diff --git a/python/helpers/docutils/writers/newlatex2e/unicode_map.py b/python/helpers/docutils/writers/newlatex2e/unicode_map.py new file mode 100644 index 000000000000..c0d63b6fe07d --- /dev/null +++ b/python/helpers/docutils/writers/newlatex2e/unicode_map.py @@ -0,0 +1,2369 @@ +# $Id$ +# Author: Lea Wiemann +# Copyright: This file has been placed in the public domain. + +# This is a mapping of Unicode characters to LaTeX equivalents. +# The information has been extracted from +# , written by +# David Carlisle and Sebastian Rahtz. +# +# The extraction has been done by the "create_unimap.py" script +# located at . + +unicode_map = {u'\xa0': '$~$', +u'\xa1': '{\\textexclamdown}', +u'\xa2': '{\\textcent}', +u'\xa3': '{\\textsterling}', +u'\xa4': '{\\textcurrency}', +u'\xa5': '{\\textyen}', +u'\xa6': '{\\textbrokenbar}', +u'\xa7': '{\\textsection}', +u'\xa8': '{\\textasciidieresis}', +u'\xa9': '{\\textcopyright}', +u'\xaa': '{\\textordfeminine}', +u'\xab': '{\\guillemotleft}', +u'\xac': '$\\lnot$', +u'\xad': '$\\-$', +u'\xae': '{\\textregistered}', +u'\xaf': '{\\textasciimacron}', +u'\xb0': '{\\textdegree}', +u'\xb1': '$\\pm$', +u'\xb2': '${^2}$', +u'\xb3': '${^3}$', +u'\xb4': '{\\textasciiacute}', +u'\xb5': '$\\mathrm{\\mu}$', +u'\xb6': '{\\textparagraph}', +u'\xb7': '$\\cdot$', +u'\xb8': '{\\c{}}', +u'\xb9': '${^1}$', +u'\xba': '{\\textordmasculine}', +u'\xbb': '{\\guillemotright}', +u'\xbc': '{\\textonequarter}', +u'\xbd': '{\\textonehalf}', +u'\xbe': '{\\textthreequarters}', +u'\xbf': '{\\textquestiondown}', +u'\xc0': '{\\`{A}}', +u'\xc1': "{\\'{A}}", +u'\xc2': '{\\^{A}}', +u'\xc3': '{\\~{A}}', +u'\xc4': '{\\"{A}}', +u'\xc5': '{\\AA}', +u'\xc6': '{\\AE}', +u'\xc7': '{\\c{C}}', +u'\xc8': '{\\`{E}}', +u'\xc9': "{\\'{E}}", +u'\xca': '{\\^{E}}', +u'\xcb': '{\\"{E}}', +u'\xcc': '{\\`{I}}', +u'\xcd': "{\\'{I}}", +u'\xce': '{\\^{I}}', +u'\xcf': '{\\"{I}}', +u'\xd0': '{\\DH}', +u'\xd1': '{\\~{N}}', +u'\xd2': '{\\`{O}}', +u'\xd3': "{\\'{O}}", +u'\xd4': '{\\^{O}}', +u'\xd5': '{\\~{O}}', +u'\xd6': '{\\"{O}}', +u'\xd7': '{\\texttimes}', +u'\xd8': '{\\O}', +u'\xd9': '{\\`{U}}', +u'\xda': "{\\'{U}}", +u'\xdb': '{\\^{U}}', +u'\xdc': '{\\"{U}}', +u'\xdd': "{\\'{Y}}", +u'\xde': '{\\TH}', +u'\xdf': '{\\ss}', +u'\xe0': '{\\`{a}}', +u'\xe1': "{\\'{a}}", +u'\xe2': '{\\^{a}}', +u'\xe3': '{\\~{a}}', +u'\xe4': '{\\"{a}}', +u'\xe5': '{\\aa}', +u'\xe6': '{\\ae}', +u'\xe7': '{\\c{c}}', +u'\xe8': '{\\`{e}}', +u'\xe9': "{\\'{e}}", +u'\xea': '{\\^{e}}', +u'\xeb': '{\\"{e}}', +u'\xec': '{\\`{\\i}}', +u'\xed': "{\\'{\\i}}", +u'\xee': '{\\^{\\i}}', +u'\xef': '{\\"{\\i}}', +u'\xf0': '{\\dh}', +u'\xf1': '{\\~{n}}', +u'\xf2': '{\\`{o}}', +u'\xf3': "{\\'{o}}", +u'\xf4': '{\\^{o}}', +u'\xf5': '{\\~{o}}', +u'\xf6': '{\\"{o}}', +u'\xf7': '$\\div$', +u'\xf8': '{\\o}', +u'\xf9': '{\\`{u}}', +u'\xfa': "{\\'{u}}", +u'\xfb': '{\\^{u}}', +u'\xfc': '{\\"{u}}', +u'\xfd': "{\\'{y}}", +u'\xfe': '{\\th}', +u'\xff': '{\\"{y}}', +u'\u0100': '{\\={A}}', +u'\u0101': '{\\={a}}', +u'\u0102': '{\\u{A}}', +u'\u0103': '{\\u{a}}', +u'\u0104': '{\\k{A}}', +u'\u0105': '{\\k{a}}', +u'\u0106': "{\\'{C}}", +u'\u0107': "{\\'{c}}", +u'\u0108': '{\\^{C}}', +u'\u0109': '{\\^{c}}', +u'\u010a': '{\\.{C}}', +u'\u010b': '{\\.{c}}', +u'\u010c': '{\\v{C}}', +u'\u010d': '{\\v{c}}', +u'\u010e': '{\\v{D}}', +u'\u010f': '{\\v{d}}', +u'\u0110': '{\\DJ}', +u'\u0111': '{\\dj}', +u'\u0112': '{\\={E}}', +u'\u0113': '{\\={e}}', +u'\u0114': '{\\u{E}}', +u'\u0115': '{\\u{e}}', +u'\u0116': '{\\.{E}}', +u'\u0117': '{\\.{e}}', +u'\u0118': '{\\k{E}}', +u'\u0119': '{\\k{e}}', +u'\u011a': '{\\v{E}}', +u'\u011b': '{\\v{e}}', +u'\u011c': '{\\^{G}}', +u'\u011d': '{\\^{g}}', +u'\u011e': '{\\u{G}}', +u'\u011f': '{\\u{g}}', +u'\u0120': '{\\.{G}}', +u'\u0121': '{\\.{g}}', +u'\u0122': '{\\c{G}}', +u'\u0123': '{\\c{g}}', +u'\u0124': '{\\^{H}}', +u'\u0125': '{\\^{h}}', +u'\u0126': '{{\\fontencoding{LELA}\\selectfont\\char40}}', +u'\u0127': '$\\Elzxh$', +u'\u0128': '{\\~{I}}', +u'\u0129': '{\\~{\\i}}', +u'\u012a': '{\\={I}}', +u'\u012b': '{\\={\\i}}', +u'\u012c': '{\\u{I}}', +u'\u012d': '{\\u{\\i}}', +u'\u012e': '{\\k{I}}', +u'\u012f': '{\\k{i}}', +u'\u0130': '{\\.{I}}', +u'\u0131': '{\\i}', +u'\u0132': '{IJ}', +u'\u0133': '{ij}', +u'\u0134': '{\\^{J}}', +u'\u0135': '{\\^{\\j}}', +u'\u0136': '{\\c{K}}', +u'\u0137': '{\\c{k}}', +u'\u0138': '{{\\fontencoding{LELA}\\selectfont\\char91}}', +u'\u0139': "{\\'{L}}", +u'\u013a': "{\\'{l}}", +u'\u013b': '{\\c{L}}', +u'\u013c': '{\\c{l}}', +u'\u013d': '{\\v{L}}', +u'\u013e': '{\\v{l}}', +u'\u013f': '{{\\fontencoding{LELA}\\selectfont\\char201}}', +u'\u0140': '{{\\fontencoding{LELA}\\selectfont\\char202}}', +u'\u0141': '{\\L}', +u'\u0142': '{\\l}', +u'\u0143': "{\\'{N}}", +u'\u0144': "{\\'{n}}", +u'\u0145': '{\\c{N}}', +u'\u0146': '{\\c{n}}', +u'\u0147': '{\\v{N}}', +u'\u0148': '{\\v{n}}', +u'\u0149': "{'n}", +u'\u014a': '{\\NG}', +u'\u014b': '{\\ng}', +u'\u014c': '{\\={O}}', +u'\u014d': '{\\={o}}', +u'\u014e': '{\\u{O}}', +u'\u014f': '{\\u{o}}', +u'\u0150': '{\\H{O}}', +u'\u0151': '{\\H{o}}', +u'\u0152': '{\\OE}', +u'\u0153': '{\\oe}', +u'\u0154': "{\\'{R}}", +u'\u0155': "{\\'{r}}", +u'\u0156': '{\\c{R}}', +u'\u0157': '{\\c{r}}', +u'\u0158': '{\\v{R}}', +u'\u0159': '{\\v{r}}', +u'\u015a': "{\\'{S}}", +u'\u015b': "{\\'{s}}", +u'\u015c': '{\\^{S}}', +u'\u015d': '{\\^{s}}', +u'\u015e': '{\\c{S}}', +u'\u015f': '{\\c{s}}', +u'\u0160': '{\\v{S}}', +u'\u0161': '{\\v{s}}', +u'\u0162': '{\\c{T}}', +u'\u0163': '{\\c{t}}', +u'\u0164': '{\\v{T}}', +u'\u0165': '{\\v{t}}', +u'\u0166': '{{\\fontencoding{LELA}\\selectfont\\char47}}', +u'\u0167': '{{\\fontencoding{LELA}\\selectfont\\char63}}', +u'\u0168': '{\\~{U}}', +u'\u0169': '{\\~{u}}', +u'\u016a': '{\\={U}}', +u'\u016b': '{\\={u}}', +u'\u016c': '{\\u{U}}', +u'\u016d': '{\\u{u}}', +u'\u016e': '{\\r{U}}', +u'\u016f': '{\\r{u}}', +u'\u0170': '{\\H{U}}', +u'\u0171': '{\\H{u}}', +u'\u0172': '{\\k{U}}', +u'\u0173': '{\\k{u}}', +u'\u0174': '{\\^{W}}', +u'\u0175': '{\\^{w}}', +u'\u0176': '{\\^{Y}}', +u'\u0177': '{\\^{y}}', +u'\u0178': '{\\"{Y}}', +u'\u0179': "{\\'{Z}}", +u'\u017a': "{\\'{z}}", +u'\u017b': '{\\.{Z}}', +u'\u017c': '{\\.{z}}', +u'\u017d': '{\\v{Z}}', +u'\u017e': '{\\v{z}}', +u'\u0192': '$f$', +u'\u0195': '{\\texthvlig}', +u'\u019e': '{\\textnrleg}', +u'\u01aa': '$\\eth$', +u'\u01ba': '{{\\fontencoding{LELA}\\selectfont\\char195}}', +u'\u01c2': '{\\textdoublepipe}', +u'\u01f5': "{\\'{g}}", +u'\u0250': '$\\Elztrna$', +u'\u0252': '$\\Elztrnsa$', +u'\u0254': '$\\Elzopeno$', +u'\u0256': '$\\Elzrtld$', +u'\u0258': '{{\\fontencoding{LEIP}\\selectfont\\char61}}', +u'\u0259': '$\\Elzschwa$', +u'\u025b': '$\\varepsilon$', +u'\u0261': '{g}', +u'\u0263': '$\\Elzpgamma$', +u'\u0264': '$\\Elzpbgam$', +u'\u0265': '$\\Elztrnh$', +u'\u026c': '$\\Elzbtdl$', +u'\u026d': '$\\Elzrtll$', +u'\u026f': '$\\Elztrnm$', +u'\u0270': '$\\Elztrnmlr$', +u'\u0271': '$\\Elzltlmr$', +u'\u0272': '{\\Elzltln}', +u'\u0273': '$\\Elzrtln$', +u'\u0277': '$\\Elzclomeg$', +u'\u0278': '{\\textphi}', +u'\u0279': '$\\Elztrnr$', +u'\u027a': '$\\Elztrnrl$', +u'\u027b': '$\\Elzrttrnr$', +u'\u027c': '$\\Elzrl$', +u'\u027d': '$\\Elzrtlr$', +u'\u027e': '$\\Elzfhr$', +u'\u027f': '{{\\fontencoding{LEIP}\\selectfont\\char202}}', +u'\u0282': '$\\Elzrtls$', +u'\u0283': '$\\Elzesh$', +u'\u0287': '$\\Elztrnt$', +u'\u0288': '$\\Elzrtlt$', +u'\u028a': '$\\Elzpupsil$', +u'\u028b': '$\\Elzpscrv$', +u'\u028c': '$\\Elzinvv$', +u'\u028d': '$\\Elzinvw$', +u'\u028e': '$\\Elztrny$', +u'\u0290': '$\\Elzrtlz$', +u'\u0292': '$\\Elzyogh$', +u'\u0294': '$\\Elzglst$', +u'\u0295': '$\\Elzreglst$', +u'\u0296': '$\\Elzinglst$', +u'\u029e': '{\\textturnk}', +u'\u02a4': '$\\Elzdyogh$', +u'\u02a7': '$\\Elztesh$', +u'\u02bc': "{'}", +u'\u02c7': '{\\textasciicaron}', +u'\u02c8': '$\\Elzverts$', +u'\u02cc': '$\\Elzverti$', +u'\u02d0': '$\\Elzlmrk$', +u'\u02d1': '$\\Elzhlmrk$', +u'\u02d2': '$\\Elzsbrhr$', +u'\u02d3': '$\\Elzsblhr$', +u'\u02d4': '$\\Elzrais$', +u'\u02d5': '$\\Elzlow$', +u'\u02d8': '{\\textasciibreve}', +u'\u02d9': '{\\textperiodcentered}', +u'\u02da': '{\\r{}}', +u'\u02db': '{\\k{}}', +u'\u02dc': '{\\texttildelow}', +u'\u02dd': '{\\H{}}', +u'\u02e5': '{\\tone{55}}', +u'\u02e6': '{\\tone{44}}', +u'\u02e7': '{\\tone{33}}', +u'\u02e8': '{\\tone{22}}', +u'\u02e9': '{\\tone{11}}', +u'\u0300': '{\\`}', +u'\u0301': "{\\'}", +u'\u0302': '{\\^}', +u'\u0303': '{\\~}', +u'\u0304': '{\\=}', +u'\u0306': '{\\u}', +u'\u0307': '{\\.}', +u'\u0308': '{\\"}', +u'\u030a': '{\\r}', +u'\u030b': '{\\H}', +u'\u030c': '{\\v}', +u'\u030f': '{\\cyrchar\\C}', +u'\u0311': '{{\\fontencoding{LECO}\\selectfont\\char177}}', +u'\u0318': '{{\\fontencoding{LECO}\\selectfont\\char184}}', +u'\u0319': '{{\\fontencoding{LECO}\\selectfont\\char185}}', +u'\u0321': '$\\Elzpalh$', +u'\u0322': '{\\Elzrh}', +u'\u0327': '{\\c}', +u'\u0328': '{\\k}', +u'\u032a': '$\\Elzsbbrg$', +u'\u032b': '{{\\fontencoding{LECO}\\selectfont\\char203}}', +u'\u032f': '{{\\fontencoding{LECO}\\selectfont\\char207}}', +u'\u0335': '{\\Elzxl}', +u'\u0336': '{\\Elzbar}', +u'\u0337': '{{\\fontencoding{LECO}\\selectfont\\char215}}', +u'\u0338': '{{\\fontencoding{LECO}\\selectfont\\char216}}', +u'\u033a': '{{\\fontencoding{LECO}\\selectfont\\char218}}', +u'\u033b': '{{\\fontencoding{LECO}\\selectfont\\char219}}', +u'\u033c': '{{\\fontencoding{LECO}\\selectfont\\char220}}', +u'\u033d': '{{\\fontencoding{LECO}\\selectfont\\char221}}', +u'\u0361': '{{\\fontencoding{LECO}\\selectfont\\char225}}', +u'\u0386': "{\\'{A}}", +u'\u0388': "{\\'{E}}", +u'\u0389': "{\\'{H}}", +u'\u038a': "{\\'{}{I}}", +u'\u038c': "{\\'{}O}", +u'\u038e': "$\\mathrm{'Y}$", +u'\u038f': "$\\mathrm{'\\Omega}$", +u'\u0390': '$\\acute{\\ddot{\\iota}}$', +u'\u0391': '$\\Alpha$', +u'\u0392': '$\\Beta$', +u'\u0393': '$\\Gamma$', +u'\u0394': '$\\Delta$', +u'\u0395': '$\\Epsilon$', +u'\u0396': '$\\Zeta$', +u'\u0397': '$\\Eta$', +u'\u0398': '$\\Theta$', +u'\u0399': '$\\Iota$', +u'\u039a': '$\\Kappa$', +u'\u039b': '$\\Lambda$', +u'\u039c': '$M$', +u'\u039d': '$N$', +u'\u039e': '$\\Xi$', +u'\u039f': '$O$', +u'\u03a0': '$\\Pi$', +u'\u03a1': '$\\Rho$', +u'\u03a3': '$\\Sigma$', +u'\u03a4': '$\\Tau$', +u'\u03a5': '$\\Upsilon$', +u'\u03a6': '$\\Phi$', +u'\u03a7': '$\\Chi$', +u'\u03a8': '$\\Psi$', +u'\u03a9': '$\\Omega$', +u'\u03aa': '$\\mathrm{\\ddot{I}}$', +u'\u03ab': '$\\mathrm{\\ddot{Y}}$', +u'\u03ac': "{\\'{$\\alpha$}}", +u'\u03ad': '$\\acute{\\epsilon}$', +u'\u03ae': '$\\acute{\\eta}$', +u'\u03af': '$\\acute{\\iota}$', +u'\u03b0': '$\\acute{\\ddot{\\upsilon}}$', +u'\u03b1': '$\\alpha$', +u'\u03b2': '$\\beta$', +u'\u03b3': '$\\gamma$', +u'\u03b4': '$\\delta$', +u'\u03b5': '$\\epsilon$', +u'\u03b6': '$\\zeta$', +u'\u03b7': '$\\eta$', +u'\u03b8': '{\\texttheta}', +u'\u03b9': '$\\iota$', +u'\u03ba': '$\\kappa$', +u'\u03bb': '$\\lambda$', +u'\u03bc': '$\\mu$', +u'\u03bd': '$\\nu$', +u'\u03be': '$\\xi$', +u'\u03bf': '$o$', +u'\u03c0': '$\\pi$', +u'\u03c1': '$\\rho$', +u'\u03c2': '$\\varsigma$', +u'\u03c3': '$\\sigma$', +u'\u03c4': '$\\tau$', +u'\u03c5': '$\\upsilon$', +u'\u03c6': '$\\varphi$', +u'\u03c7': '$\\chi$', +u'\u03c8': '$\\psi$', +u'\u03c9': '$\\omega$', +u'\u03ca': '$\\ddot{\\iota}$', +u'\u03cb': '$\\ddot{\\upsilon}$', +u'\u03cc': "{\\'{o}}", +u'\u03cd': '$\\acute{\\upsilon}$', +u'\u03ce': '$\\acute{\\omega}$', +u'\u03d0': '{\\Pisymbol{ppi022}{87}}', +u'\u03d1': '{\\textvartheta}', +u'\u03d2': '$\\Upsilon$', +u'\u03d5': '$\\phi$', +u'\u03d6': '$\\varpi$', +u'\u03da': '$\\Stigma$', +u'\u03dc': '$\\Digamma$', +u'\u03dd': '$\\digamma$', +u'\u03de': '$\\Koppa$', +u'\u03e0': '$\\Sampi$', +u'\u03f0': '$\\varkappa$', +u'\u03f1': '$\\varrho$', +u'\u03f4': '{\\textTheta}', +u'\u03f6': '$\\backepsilon$', +u'\u0401': '{\\cyrchar\\CYRYO}', +u'\u0402': '{\\cyrchar\\CYRDJE}', +u'\u0403': "{\\cyrchar{\\'\\CYRG}}", +u'\u0404': '{\\cyrchar\\CYRIE}', +u'\u0405': '{\\cyrchar\\CYRDZE}', +u'\u0406': '{\\cyrchar\\CYRII}', +u'\u0407': '{\\cyrchar\\CYRYI}', +u'\u0408': '{\\cyrchar\\CYRJE}', +u'\u0409': '{\\cyrchar\\CYRLJE}', +u'\u040a': '{\\cyrchar\\CYRNJE}', +u'\u040b': '{\\cyrchar\\CYRTSHE}', +u'\u040c': "{\\cyrchar{\\'\\CYRK}}", +u'\u040e': '{\\cyrchar\\CYRUSHRT}', +u'\u040f': '{\\cyrchar\\CYRDZHE}', +u'\u0410': '{\\cyrchar\\CYRA}', +u'\u0411': '{\\cyrchar\\CYRB}', +u'\u0412': '{\\cyrchar\\CYRV}', +u'\u0413': '{\\cyrchar\\CYRG}', +u'\u0414': '{\\cyrchar\\CYRD}', +u'\u0415': '{\\cyrchar\\CYRE}', +u'\u0416': '{\\cyrchar\\CYRZH}', +u'\u0417': '{\\cyrchar\\CYRZ}', +u'\u0418': '{\\cyrchar\\CYRI}', +u'\u0419': '{\\cyrchar\\CYRISHRT}', +u'\u041a': '{\\cyrchar\\CYRK}', +u'\u041b': '{\\cyrchar\\CYRL}', +u'\u041c': '{\\cyrchar\\CYRM}', +u'\u041d': '{\\cyrchar\\CYRN}', +u'\u041e': '{\\cyrchar\\CYRO}', +u'\u041f': '{\\cyrchar\\CYRP}', +u'\u0420': '{\\cyrchar\\CYRR}', +u'\u0421': '{\\cyrchar\\CYRS}', +u'\u0422': '{\\cyrchar\\CYRT}', +u'\u0423': '{\\cyrchar\\CYRU}', +u'\u0424': '{\\cyrchar\\CYRF}', +u'\u0425': '{\\cyrchar\\CYRH}', +u'\u0426': '{\\cyrchar\\CYRC}', +u'\u0427': '{\\cyrchar\\CYRCH}', +u'\u0428': '{\\cyrchar\\CYRSH}', +u'\u0429': '{\\cyrchar\\CYRSHCH}', +u'\u042a': '{\\cyrchar\\CYRHRDSN}', +u'\u042b': '{\\cyrchar\\CYRERY}', +u'\u042c': '{\\cyrchar\\CYRSFTSN}', +u'\u042d': '{\\cyrchar\\CYREREV}', +u'\u042e': '{\\cyrchar\\CYRYU}', +u'\u042f': '{\\cyrchar\\CYRYA}', +u'\u0430': '{\\cyrchar\\cyra}', +u'\u0431': '{\\cyrchar\\cyrb}', +u'\u0432': '{\\cyrchar\\cyrv}', +u'\u0433': '{\\cyrchar\\cyrg}', +u'\u0434': '{\\cyrchar\\cyrd}', +u'\u0435': '{\\cyrchar\\cyre}', +u'\u0436': '{\\cyrchar\\cyrzh}', +u'\u0437': '{\\cyrchar\\cyrz}', +u'\u0438': '{\\cyrchar\\cyri}', +u'\u0439': '{\\cyrchar\\cyrishrt}', +u'\u043a': '{\\cyrchar\\cyrk}', +u'\u043b': '{\\cyrchar\\cyrl}', +u'\u043c': '{\\cyrchar\\cyrm}', +u'\u043d': '{\\cyrchar\\cyrn}', +u'\u043e': '{\\cyrchar\\cyro}', +u'\u043f': '{\\cyrchar\\cyrp}', +u'\u0440': '{\\cyrchar\\cyrr}', +u'\u0441': '{\\cyrchar\\cyrs}', +u'\u0442': '{\\cyrchar\\cyrt}', +u'\u0443': '{\\cyrchar\\cyru}', +u'\u0444': '{\\cyrchar\\cyrf}', +u'\u0445': '{\\cyrchar\\cyrh}', +u'\u0446': '{\\cyrchar\\cyrc}', +u'\u0447': '{\\cyrchar\\cyrch}', +u'\u0448': '{\\cyrchar\\cyrsh}', +u'\u0449': '{\\cyrchar\\cyrshch}', +u'\u044a': '{\\cyrchar\\cyrhrdsn}', +u'\u044b': '{\\cyrchar\\cyrery}', +u'\u044c': '{\\cyrchar\\cyrsftsn}', +u'\u044d': '{\\cyrchar\\cyrerev}', +u'\u044e': '{\\cyrchar\\cyryu}', +u'\u044f': '{\\cyrchar\\cyrya}', +u'\u0451': '{\\cyrchar\\cyryo}', +u'\u0452': '{\\cyrchar\\cyrdje}', +u'\u0453': "{\\cyrchar{\\'\\cyrg}}", +u'\u0454': '{\\cyrchar\\cyrie}', +u'\u0455': '{\\cyrchar\\cyrdze}', +u'\u0456': '{\\cyrchar\\cyrii}', +u'\u0457': '{\\cyrchar\\cyryi}', +u'\u0458': '{\\cyrchar\\cyrje}', +u'\u0459': '{\\cyrchar\\cyrlje}', +u'\u045a': '{\\cyrchar\\cyrnje}', +u'\u045b': '{\\cyrchar\\cyrtshe}', +u'\u045c': "{\\cyrchar{\\'\\cyrk}}", +u'\u045e': '{\\cyrchar\\cyrushrt}', +u'\u045f': '{\\cyrchar\\cyrdzhe}', +u'\u0460': '{\\cyrchar\\CYROMEGA}', +u'\u0461': '{\\cyrchar\\cyromega}', +u'\u0462': '{\\cyrchar\\CYRYAT}', +u'\u0464': '{\\cyrchar\\CYRIOTE}', +u'\u0465': '{\\cyrchar\\cyriote}', +u'\u0466': '{\\cyrchar\\CYRLYUS}', +u'\u0467': '{\\cyrchar\\cyrlyus}', +u'\u0468': '{\\cyrchar\\CYRIOTLYUS}', +u'\u0469': '{\\cyrchar\\cyriotlyus}', +u'\u046a': '{\\cyrchar\\CYRBYUS}', +u'\u046c': '{\\cyrchar\\CYRIOTBYUS}', +u'\u046d': '{\\cyrchar\\cyriotbyus}', +u'\u046e': '{\\cyrchar\\CYRKSI}', +u'\u046f': '{\\cyrchar\\cyrksi}', +u'\u0470': '{\\cyrchar\\CYRPSI}', +u'\u0471': '{\\cyrchar\\cyrpsi}', +u'\u0472': '{\\cyrchar\\CYRFITA}', +u'\u0474': '{\\cyrchar\\CYRIZH}', +u'\u0478': '{\\cyrchar\\CYRUK}', +u'\u0479': '{\\cyrchar\\cyruk}', +u'\u047a': '{\\cyrchar\\CYROMEGARND}', +u'\u047b': '{\\cyrchar\\cyromegarnd}', +u'\u047c': '{\\cyrchar\\CYROMEGATITLO}', +u'\u047d': '{\\cyrchar\\cyromegatitlo}', +u'\u047e': '{\\cyrchar\\CYROT}', +u'\u047f': '{\\cyrchar\\cyrot}', +u'\u0480': '{\\cyrchar\\CYRKOPPA}', +u'\u0481': '{\\cyrchar\\cyrkoppa}', +u'\u0482': '{\\cyrchar\\cyrthousands}', +u'\u0488': '{\\cyrchar\\cyrhundredthousands}', +u'\u0489': '{\\cyrchar\\cyrmillions}', +u'\u048c': '{\\cyrchar\\CYRSEMISFTSN}', +u'\u048d': '{\\cyrchar\\cyrsemisftsn}', +u'\u048e': '{\\cyrchar\\CYRRTICK}', +u'\u048f': '{\\cyrchar\\cyrrtick}', +u'\u0490': '{\\cyrchar\\CYRGUP}', +u'\u0491': '{\\cyrchar\\cyrgup}', +u'\u0492': '{\\cyrchar\\CYRGHCRS}', +u'\u0493': '{\\cyrchar\\cyrghcrs}', +u'\u0494': '{\\cyrchar\\CYRGHK}', +u'\u0495': '{\\cyrchar\\cyrghk}', +u'\u0496': '{\\cyrchar\\CYRZHDSC}', +u'\u0497': '{\\cyrchar\\cyrzhdsc}', +u'\u0498': '{\\cyrchar\\CYRZDSC}', +u'\u0499': '{\\cyrchar\\cyrzdsc}', +u'\u049a': '{\\cyrchar\\CYRKDSC}', +u'\u049b': '{\\cyrchar\\cyrkdsc}', +u'\u049c': '{\\cyrchar\\CYRKVCRS}', +u'\u049d': '{\\cyrchar\\cyrkvcrs}', +u'\u049e': '{\\cyrchar\\CYRKHCRS}', +u'\u049f': '{\\cyrchar\\cyrkhcrs}', +u'\u04a0': '{\\cyrchar\\CYRKBEAK}', +u'\u04a1': '{\\cyrchar\\cyrkbeak}', +u'\u04a2': '{\\cyrchar\\CYRNDSC}', +u'\u04a3': '{\\cyrchar\\cyrndsc}', +u'\u04a4': '{\\cyrchar\\CYRNG}', +u'\u04a5': '{\\cyrchar\\cyrng}', +u'\u04a6': '{\\cyrchar\\CYRPHK}', +u'\u04a7': '{\\cyrchar\\cyrphk}', +u'\u04a8': '{\\cyrchar\\CYRABHHA}', +u'\u04a9': '{\\cyrchar\\cyrabhha}', +u'\u04aa': '{\\cyrchar\\CYRSDSC}', +u'\u04ab': '{\\cyrchar\\cyrsdsc}', +u'\u04ac': '{\\cyrchar\\CYRTDSC}', +u'\u04ad': '{\\cyrchar\\cyrtdsc}', +u'\u04ae': '{\\cyrchar\\CYRY}', +u'\u04af': '{\\cyrchar\\cyry}', +u'\u04b0': '{\\cyrchar\\CYRYHCRS}', +u'\u04b1': '{\\cyrchar\\cyryhcrs}', +u'\u04b2': '{\\cyrchar\\CYRHDSC}', +u'\u04b3': '{\\cyrchar\\cyrhdsc}', +u'\u04b4': '{\\cyrchar\\CYRTETSE}', +u'\u04b5': '{\\cyrchar\\cyrtetse}', +u'\u04b6': '{\\cyrchar\\CYRCHRDSC}', +u'\u04b7': '{\\cyrchar\\cyrchrdsc}', +u'\u04b8': '{\\cyrchar\\CYRCHVCRS}', +u'\u04b9': '{\\cyrchar\\cyrchvcrs}', +u'\u04ba': '{\\cyrchar\\CYRSHHA}', +u'\u04bb': '{\\cyrchar\\cyrshha}', +u'\u04bc': '{\\cyrchar\\CYRABHCH}', +u'\u04bd': '{\\cyrchar\\cyrabhch}', +u'\u04be': '{\\cyrchar\\CYRABHCHDSC}', +u'\u04bf': '{\\cyrchar\\cyrabhchdsc}', +u'\u04c0': '{\\cyrchar\\CYRpalochka}', +u'\u04c3': '{\\cyrchar\\CYRKHK}', +u'\u04c4': '{\\cyrchar\\cyrkhk}', +u'\u04c7': '{\\cyrchar\\CYRNHK}', +u'\u04c8': '{\\cyrchar\\cyrnhk}', +u'\u04cb': '{\\cyrchar\\CYRCHLDSC}', +u'\u04cc': '{\\cyrchar\\cyrchldsc}', +u'\u04d4': '{\\cyrchar\\CYRAE}', +u'\u04d5': '{\\cyrchar\\cyrae}', +u'\u04d8': '{\\cyrchar\\CYRSCHWA}', +u'\u04d9': '{\\cyrchar\\cyrschwa}', +u'\u04e0': '{\\cyrchar\\CYRABHDZE}', +u'\u04e1': '{\\cyrchar\\cyrabhdze}', +u'\u04e8': '{\\cyrchar\\CYROTLD}', +u'\u04e9': '{\\cyrchar\\cyrotld}', +u'\u2002': '{\\hspace{0.6em}}', +u'\u2003': '{\\hspace{1em}}', +u'\u2004': '{\\hspace{0.33em}}', +u'\u2005': '{\\hspace{0.25em}}', +u'\u2006': '{\\hspace{0.166em}}', +u'\u2007': '{\\hphantom{0}}', +u'\u2008': '{\\hphantom{,}}', +u'\u2009': '{\\hspace{0.167em}}', +u'\u200a': '$\\mkern1mu$', +u'\u2010': '{-}', +u'\u2013': '{\\textendash}', +u'\u2014': '{\\textemdash}', +u'\u2015': '{\\rule{1em}{1pt}}', +u'\u2016': '$\\Vert$', +u'\u2018': '{`}', +u'\u2019': "{'}", +u'\u201a': '{,}', +u'\u201b': '$\\Elzreapos$', +u'\u201c': '{\\textquotedblleft}', +u'\u201d': '{\\textquotedblright}', +u'\u201e': '{,,}', +u'\u2020': '{\\textdagger}', +u'\u2021': '{\\textdaggerdbl}', +u'\u2022': '{\\textbullet}', +u'\u2024': '{.}', +u'\u2025': '{..}', +u'\u2026': '{\\ldots}', +u'\u2030': '{\\textperthousand}', +u'\u2031': '{\\textpertenthousand}', +u'\u2032': "${'}$", +u'\u2033': "${''}$", +u'\u2034': "${'''}$", +u'\u2035': '$\\backprime$', +u'\u2039': '{\\guilsinglleft}', +u'\u203a': '{\\guilsinglright}', +u'\u2057': "$''''$", +u'\u205f': '{\\mkern4mu}', +u'\u2060': '{\\nolinebreak}', +u'\u20a7': '{\\ensuremath{\\Elzpes}}', +u'\u20ac': '{\\mbox{\\texteuro}}', +u'\u20db': '$\\dddot$', +u'\u20dc': '$\\ddddot$', +u'\u2102': '$\\mathbb{C}$', +u'\u210a': '{\\mathscr{g}}', +u'\u210b': '$\\mathscr{H}$', +u'\u210c': '$\\mathfrak{H}$', +u'\u210d': '$\\mathbb{H}$', +u'\u210f': '$\\hslash$', +u'\u2110': '$\\mathscr{I}$', +u'\u2111': '$\\mathfrak{I}$', +u'\u2112': '$\\mathscr{L}$', +u'\u2113': '$\\mathscr{l}$', +u'\u2115': '$\\mathbb{N}$', +u'\u2116': '{\\cyrchar\\textnumero}', +u'\u2118': '$\\wp$', +u'\u2119': '$\\mathbb{P}$', +u'\u211a': '$\\mathbb{Q}$', +u'\u211b': '$\\mathscr{R}$', +u'\u211c': '$\\mathfrak{R}$', +u'\u211d': '$\\mathbb{R}$', +u'\u211e': '$\\Elzxrat$', +u'\u2122': '{\\texttrademark}', +u'\u2124': '$\\mathbb{Z}$', +u'\u2126': '$\\Omega$', +u'\u2127': '$\\mho$', +u'\u2128': '$\\mathfrak{Z}$', +u'\u2129': '$\\ElsevierGlyph{2129}$', +u'\u212b': '{\\AA}', +u'\u212c': '$\\mathscr{B}$', +u'\u212d': '$\\mathfrak{C}$', +u'\u212f': '$\\mathscr{e}$', +u'\u2130': '$\\mathscr{E}$', +u'\u2131': '$\\mathscr{F}$', +u'\u2133': '$\\mathscr{M}$', +u'\u2134': '$\\mathscr{o}$', +u'\u2135': '$\\aleph$', +u'\u2136': '$\\beth$', +u'\u2137': '$\\gimel$', +u'\u2138': '$\\daleth$', +u'\u2153': '$\\textfrac{1}{3}$', +u'\u2154': '$\\textfrac{2}{3}$', +u'\u2155': '$\\textfrac{1}{5}$', +u'\u2156': '$\\textfrac{2}{5}$', +u'\u2157': '$\\textfrac{3}{5}$', +u'\u2158': '$\\textfrac{4}{5}$', +u'\u2159': '$\\textfrac{1}{6}$', +u'\u215a': '$\\textfrac{5}{6}$', +u'\u215b': '$\\textfrac{1}{8}$', +u'\u215c': '$\\textfrac{3}{8}$', +u'\u215d': '$\\textfrac{5}{8}$', +u'\u215e': '$\\textfrac{7}{8}$', +u'\u2190': '$\\leftarrow$', +u'\u2191': '$\\uparrow$', +u'\u2192': '$\\rightarrow$', +u'\u2193': '$\\downarrow$', +u'\u2194': '$\\leftrightarrow$', +u'\u2195': '$\\updownarrow$', +u'\u2196': '$\\nwarrow$', +u'\u2197': '$\\nearrow$', +u'\u2198': '$\\searrow$', +u'\u2199': '$\\swarrow$', +u'\u219a': '$\\nleftarrow$', +u'\u219b': '$\\nrightarrow$', +u'\u219c': '$\\arrowwaveright$', +u'\u219d': '$\\arrowwaveright$', +u'\u219e': '$\\twoheadleftarrow$', +u'\u21a0': '$\\twoheadrightarrow$', +u'\u21a2': '$\\leftarrowtail$', +u'\u21a3': '$\\rightarrowtail$', +u'\u21a6': '$\\mapsto$', +u'\u21a9': '$\\hookleftarrow$', +u'\u21aa': '$\\hookrightarrow$', +u'\u21ab': '$\\looparrowleft$', +u'\u21ac': '$\\looparrowright$', +u'\u21ad': '$\\leftrightsquigarrow$', +u'\u21ae': '$\\nleftrightarrow$', +u'\u21b0': '$\\Lsh$', +u'\u21b1': '$\\Rsh$', +u'\u21b3': '$\\ElsevierGlyph{21B3}$', +u'\u21b6': '$\\curvearrowleft$', +u'\u21b7': '$\\curvearrowright$', +u'\u21ba': '$\\circlearrowleft$', +u'\u21bb': '$\\circlearrowright$', +u'\u21bc': '$\\leftharpoonup$', +u'\u21bd': '$\\leftharpoondown$', +u'\u21be': '$\\upharpoonright$', +u'\u21bf': '$\\upharpoonleft$', +u'\u21c0': '$\\rightharpoonup$', +u'\u21c1': '$\\rightharpoondown$', +u'\u21c2': '$\\downharpoonright$', +u'\u21c3': '$\\downharpoonleft$', +u'\u21c4': '$\\rightleftarrows$', +u'\u21c5': '$\\dblarrowupdown$', +u'\u21c6': '$\\leftrightarrows$', +u'\u21c7': '$\\leftleftarrows$', +u'\u21c8': '$\\upuparrows$', +u'\u21c9': '$\\rightrightarrows$', +u'\u21ca': '$\\downdownarrows$', +u'\u21cb': '$\\leftrightharpoons$', +u'\u21cc': '$\\rightleftharpoons$', +u'\u21cd': '$\\nLeftarrow$', +u'\u21ce': '$\\nLeftrightarrow$', +u'\u21cf': '$\\nRightarrow$', +u'\u21d0': '$\\Leftarrow$', +u'\u21d1': '$\\Uparrow$', +u'\u21d2': '$\\Rightarrow$', +u'\u21d3': '$\\Downarrow$', +u'\u21d4': '$\\Leftrightarrow$', +u'\u21d5': '$\\Updownarrow$', +u'\u21da': '$\\Lleftarrow$', +u'\u21db': '$\\Rrightarrow$', +u'\u21dd': '$\\rightsquigarrow$', +u'\u21f5': '$\\DownArrowUpArrow$', +u'\u2200': '$\\forall$', +u'\u2201': '$\\complement$', +u'\u2202': '$\\partial$', +u'\u2203': '$\\exists$', +u'\u2204': '$\\nexists$', +u'\u2205': '$\\varnothing$', +u'\u2207': '$\\nabla$', +u'\u2208': '$\\in$', +u'\u2209': '$\\not\\in$', +u'\u220b': '$\\ni$', +u'\u220c': '$\\not\\ni$', +u'\u220f': '$\\prod$', +u'\u2210': '$\\coprod$', +u'\u2211': '$\\sum$', +u'\u2212': '{-}', +u'\u2213': '$\\mp$', +u'\u2214': '$\\dotplus$', +u'\u2216': '$\\setminus$', +u'\u2217': '${_\\ast}$', +u'\u2218': '$\\circ$', +u'\u2219': '$\\bullet$', +u'\u221a': '$\\surd$', +u'\u221d': '$\\propto$', +u'\u221e': '$\\infty$', +u'\u221f': '$\\rightangle$', +u'\u2220': '$\\angle$', +u'\u2221': '$\\measuredangle$', +u'\u2222': '$\\sphericalangle$', +u'\u2223': '$\\mid$', +u'\u2224': '$\\nmid$', +u'\u2225': '$\\parallel$', +u'\u2226': '$\\nparallel$', +u'\u2227': '$\\wedge$', +u'\u2228': '$\\vee$', +u'\u2229': '$\\cap$', +u'\u222a': '$\\cup$', +u'\u222b': '$\\int$', +u'\u222c': '$\\int\\!\\int$', +u'\u222d': '$\\int\\!\\int\\!\\int$', +u'\u222e': '$\\oint$', +u'\u222f': '$\\surfintegral$', +u'\u2230': '$\\volintegral$', +u'\u2231': '$\\clwintegral$', +u'\u2232': '$\\ElsevierGlyph{2232}$', +u'\u2233': '$\\ElsevierGlyph{2233}$', +u'\u2234': '$\\therefore$', +u'\u2235': '$\\because$', +u'\u2237': '$\\Colon$', +u'\u2238': '$\\ElsevierGlyph{2238}$', +u'\u223a': '$\\mathbin{{:}\\!\\!{-}\\!\\!{:}}$', +u'\u223b': '$\\homothetic$', +u'\u223c': '$\\sim$', +u'\u223d': '$\\backsim$', +u'\u223e': '$\\lazysinv$', +u'\u2240': '$\\wr$', +u'\u2241': '$\\not\\sim$', +u'\u2242': '$\\ElsevierGlyph{2242}$', +u'\u2243': '$\\simeq$', +u'\u2244': '$\\not\\simeq$', +u'\u2245': '$\\cong$', +u'\u2246': '$\\approxnotequal$', +u'\u2247': '$\\not\\cong$', +u'\u2248': '$\\approx$', +u'\u2249': '$\\not\\approx$', +u'\u224a': '$\\approxeq$', +u'\u224b': '$\\tildetrpl$', +u'\u224c': '$\\allequal$', +u'\u224d': '$\\asymp$', +u'\u224e': '$\\Bumpeq$', +u'\u224f': '$\\bumpeq$', +u'\u2250': '$\\doteq$', +u'\u2251': '$\\doteqdot$', +u'\u2252': '$\\fallingdotseq$', +u'\u2253': '$\\risingdotseq$', +u'\u2254': '{:=}', +u'\u2255': '$=:$', +u'\u2256': '$\\eqcirc$', +u'\u2257': '$\\circeq$', +u'\u2259': '$\\estimates$', +u'\u225a': '$\\ElsevierGlyph{225A}$', +u'\u225b': '$\\starequal$', +u'\u225c': '$\\triangleq$', +u'\u225f': '$\\ElsevierGlyph{225F}$', +u'\u2260': '$\\not =$', +u'\u2261': '$\\equiv$', +u'\u2262': '$\\not\\equiv$', +u'\u2264': '$\\leq$', +u'\u2265': '$\\geq$', +u'\u2266': '$\\leqq$', +u'\u2267': '$\\geqq$', +u'\u2268': '$\\lneqq$', +u'\u2269': '$\\gneqq$', +u'\u226a': '$\\ll$', +u'\u226b': '$\\gg$', +u'\u226c': '$\\between$', +u'\u226d': '$\\not\\kern-0.3em\\times$', +u'\u226e': '$\\not<$', +u'\u226f': '$\\not>$', +u'\u2270': '$\\not\\leq$', +u'\u2271': '$\\not\\geq$', +u'\u2272': '$\\lessequivlnt$', +u'\u2273': '$\\greaterequivlnt$', +u'\u2274': '$\\ElsevierGlyph{2274}$', +u'\u2275': '$\\ElsevierGlyph{2275}$', +u'\u2276': '$\\lessgtr$', +u'\u2277': '$\\gtrless$', +u'\u2278': '$\\notlessgreater$', +u'\u2279': '$\\notgreaterless$', +u'\u227a': '$\\prec$', +u'\u227b': '$\\succ$', +u'\u227c': '$\\preccurlyeq$', +u'\u227d': '$\\succcurlyeq$', +u'\u227e': '$\\precapprox$', +u'\u227f': '$\\succapprox$', +u'\u2280': '$\\not\\prec$', +u'\u2281': '$\\not\\succ$', +u'\u2282': '$\\subset$', +u'\u2283': '$\\supset$', +u'\u2284': '$\\not\\subset$', +u'\u2285': '$\\not\\supset$', +u'\u2286': '$\\subseteq$', +u'\u2287': '$\\supseteq$', +u'\u2288': '$\\not\\subseteq$', +u'\u2289': '$\\not\\supseteq$', +u'\u228a': '$\\subsetneq$', +u'\u228b': '$\\supsetneq$', +u'\u228e': '$\\uplus$', +u'\u228f': '$\\sqsubset$', +u'\u2290': '$\\sqsupset$', +u'\u2291': '$\\sqsubseteq$', +u'\u2292': '$\\sqsupseteq$', +u'\u2293': '$\\sqcap$', +u'\u2294': '$\\sqcup$', +u'\u2295': '$\\oplus$', +u'\u2296': '$\\ominus$', +u'\u2297': '$\\otimes$', +u'\u2298': '$\\oslash$', +u'\u2299': '$\\odot$', +u'\u229a': '$\\circledcirc$', +u'\u229b': '$\\circledast$', +u'\u229d': '$\\circleddash$', +u'\u229e': '$\\boxplus$', +u'\u229f': '$\\boxminus$', +u'\u22a0': '$\\boxtimes$', +u'\u22a1': '$\\boxdot$', +u'\u22a2': '$\\vdash$', +u'\u22a3': '$\\dashv$', +u'\u22a4': '$\\top$', +u'\u22a5': '$\\perp$', +u'\u22a7': '$\\truestate$', +u'\u22a8': '$\\forcesextra$', +u'\u22a9': '$\\Vdash$', +u'\u22aa': '$\\Vvdash$', +u'\u22ab': '$\\VDash$', +u'\u22ac': '$\\nvdash$', +u'\u22ad': '$\\nvDash$', +u'\u22ae': '$\\nVdash$', +u'\u22af': '$\\nVDash$', +u'\u22b2': '$\\vartriangleleft$', +u'\u22b3': '$\\vartriangleright$', +u'\u22b4': '$\\trianglelefteq$', +u'\u22b5': '$\\trianglerighteq$', +u'\u22b6': '$\\original$', +u'\u22b7': '$\\image$', +u'\u22b8': '$\\multimap$', +u'\u22b9': '$\\hermitconjmatrix$', +u'\u22ba': '$\\intercal$', +u'\u22bb': '$\\veebar$', +u'\u22be': '$\\rightanglearc$', +u'\u22c0': '$\\ElsevierGlyph{22C0}$', +u'\u22c1': '$\\ElsevierGlyph{22C1}$', +u'\u22c2': '$\\bigcap$', +u'\u22c3': '$\\bigcup$', +u'\u22c4': '$\\diamond$', +u'\u22c5': '$\\cdot$', +u'\u22c6': '$\\star$', +u'\u22c7': '$\\divideontimes$', +u'\u22c8': '$\\bowtie$', +u'\u22c9': '$\\ltimes$', +u'\u22ca': '$\\rtimes$', +u'\u22cb': '$\\leftthreetimes$', +u'\u22cc': '$\\rightthreetimes$', +u'\u22cd': '$\\backsimeq$', +u'\u22ce': '$\\curlyvee$', +u'\u22cf': '$\\curlywedge$', +u'\u22d0': '$\\Subset$', +u'\u22d1': '$\\Supset$', +u'\u22d2': '$\\Cap$', +u'\u22d3': '$\\Cup$', +u'\u22d4': '$\\pitchfork$', +u'\u22d6': '$\\lessdot$', +u'\u22d7': '$\\gtrdot$', +u'\u22d8': '$\\verymuchless$', +u'\u22d9': '$\\verymuchgreater$', +u'\u22da': '$\\lesseqgtr$', +u'\u22db': '$\\gtreqless$', +u'\u22de': '$\\curlyeqprec$', +u'\u22df': '$\\curlyeqsucc$', +u'\u22e2': '$\\not\\sqsubseteq$', +u'\u22e3': '$\\not\\sqsupseteq$', +u'\u22e5': '$\\Elzsqspne$', +u'\u22e6': '$\\lnsim$', +u'\u22e7': '$\\gnsim$', +u'\u22e8': '$\\precedesnotsimilar$', +u'\u22e9': '$\\succnsim$', +u'\u22ea': '$\\ntriangleleft$', +u'\u22eb': '$\\ntriangleright$', +u'\u22ec': '$\\ntrianglelefteq$', +u'\u22ed': '$\\ntrianglerighteq$', +u'\u22ee': '$\\vdots$', +u'\u22ef': '$\\cdots$', +u'\u22f0': '$\\upslopeellipsis$', +u'\u22f1': '$\\downslopeellipsis$', +u'\u2305': '{\\barwedge}', +u'\u2306': '$\\perspcorrespond$', +u'\u2308': '$\\lceil$', +u'\u2309': '$\\rceil$', +u'\u230a': '$\\lfloor$', +u'\u230b': '$\\rfloor$', +u'\u2315': '$\\recorder$', +u'\u2316': '$\\mathchar"2208$', +u'\u231c': '$\\ulcorner$', +u'\u231d': '$\\urcorner$', +u'\u231e': '$\\llcorner$', +u'\u231f': '$\\lrcorner$', +u'\u2322': '$\\frown$', +u'\u2323': '$\\smile$', +u'\u2329': '$\\langle$', +u'\u232a': '$\\rangle$', +u'\u233d': '$\\ElsevierGlyph{E838}$', +u'\u23a3': '$\\Elzdlcorn$', +u'\u23b0': '$\\lmoustache$', +u'\u23b1': '$\\rmoustache$', +u'\u2423': '{\\textvisiblespace}', +u'\u2460': '{\\ding{172}}', +u'\u2461': '{\\ding{173}}', +u'\u2462': '{\\ding{174}}', +u'\u2463': '{\\ding{175}}', +u'\u2464': '{\\ding{176}}', +u'\u2465': '{\\ding{177}}', +u'\u2466': '{\\ding{178}}', +u'\u2467': '{\\ding{179}}', +u'\u2468': '{\\ding{180}}', +u'\u2469': '{\\ding{181}}', +u'\u24c8': '$\\circledS$', +u'\u2506': '$\\Elzdshfnc$', +u'\u2519': '$\\Elzsqfnw$', +u'\u2571': '$\\diagup$', +u'\u25a0': '{\\ding{110}}', +u'\u25a1': '$\\square$', +u'\u25aa': '$\\blacksquare$', +u'\u25ad': '$\\fbox{~~}$', +u'\u25af': '$\\Elzvrecto$', +u'\u25b1': '$\\ElsevierGlyph{E381}$', +u'\u25b2': '{\\ding{115}}', +u'\u25b3': '$\\bigtriangleup$', +u'\u25b4': '$\\blacktriangle$', +u'\u25b5': '$\\vartriangle$', +u'\u25b8': '$\\blacktriangleright$', +u'\u25b9': '$\\triangleright$', +u'\u25bc': '{\\ding{116}}', +u'\u25bd': '$\\bigtriangledown$', +u'\u25be': '$\\blacktriangledown$', +u'\u25bf': '$\\triangledown$', +u'\u25c2': '$\\blacktriangleleft$', +u'\u25c3': '$\\triangleleft$', +u'\u25c6': '{\\ding{117}}', +u'\u25ca': '$\\lozenge$', +u'\u25cb': '$\\bigcirc$', +u'\u25cf': '{\\ding{108}}', +u'\u25d0': '$\\Elzcirfl$', +u'\u25d1': '$\\Elzcirfr$', +u'\u25d2': '$\\Elzcirfb$', +u'\u25d7': '{\\ding{119}}', +u'\u25d8': '$\\Elzrvbull$', +u'\u25e7': '$\\Elzsqfl$', +u'\u25e8': '$\\Elzsqfr$', +u'\u25ea': '$\\Elzsqfse$', +u'\u25ef': '$\\bigcirc$', +u'\u2605': '{\\ding{72}}', +u'\u2606': '{\\ding{73}}', +u'\u260e': '{\\ding{37}}', +u'\u261b': '{\\ding{42}}', +u'\u261e': '{\\ding{43}}', +u'\u263e': '{\\rightmoon}', +u'\u263f': '{\\mercury}', +u'\u2640': '{\\venus}', +u'\u2642': '{\\male}', +u'\u2643': '{\\jupiter}', +u'\u2644': '{\\saturn}', +u'\u2645': '{\\uranus}', +u'\u2646': '{\\neptune}', +u'\u2647': '{\\pluto}', +u'\u2648': '{\\aries}', +u'\u2649': '{\\taurus}', +u'\u264a': '{\\gemini}', +u'\u264b': '{\\cancer}', +u'\u264c': '{\\leo}', +u'\u264d': '{\\virgo}', +u'\u264e': '{\\libra}', +u'\u264f': '{\\scorpio}', +u'\u2650': '{\\sagittarius}', +u'\u2651': '{\\capricornus}', +u'\u2652': '{\\aquarius}', +u'\u2653': '{\\pisces}', +u'\u2660': '{\\ding{171}}', +u'\u2662': '$\\diamond$', +u'\u2663': '{\\ding{168}}', +u'\u2665': '{\\ding{170}}', +u'\u2666': '{\\ding{169}}', +u'\u2669': '{\\quarternote}', +u'\u266a': '{\\eighthnote}', +u'\u266d': '$\\flat$', +u'\u266e': '$\\natural$', +u'\u266f': '$\\sharp$', +u'\u2701': '{\\ding{33}}', +u'\u2702': '{\\ding{34}}', +u'\u2703': '{\\ding{35}}', +u'\u2704': '{\\ding{36}}', +u'\u2706': '{\\ding{38}}', +u'\u2707': '{\\ding{39}}', +u'\u2708': '{\\ding{40}}', +u'\u2709': '{\\ding{41}}', +u'\u270c': '{\\ding{44}}', +u'\u270d': '{\\ding{45}}', +u'\u270e': '{\\ding{46}}', +u'\u270f': '{\\ding{47}}', +u'\u2710': '{\\ding{48}}', +u'\u2711': '{\\ding{49}}', +u'\u2712': '{\\ding{50}}', +u'\u2713': '{\\ding{51}}', +u'\u2714': '{\\ding{52}}', +u'\u2715': '{\\ding{53}}', +u'\u2716': '{\\ding{54}}', +u'\u2717': '{\\ding{55}}', +u'\u2718': '{\\ding{56}}', +u'\u2719': '{\\ding{57}}', +u'\u271a': '{\\ding{58}}', +u'\u271b': '{\\ding{59}}', +u'\u271c': '{\\ding{60}}', +u'\u271d': '{\\ding{61}}', +u'\u271e': '{\\ding{62}}', +u'\u271f': '{\\ding{63}}', +u'\u2720': '{\\ding{64}}', +u'\u2721': '{\\ding{65}}', +u'\u2722': '{\\ding{66}}', +u'\u2723': '{\\ding{67}}', +u'\u2724': '{\\ding{68}}', +u'\u2725': '{\\ding{69}}', +u'\u2726': '{\\ding{70}}', +u'\u2727': '{\\ding{71}}', +u'\u2729': '{\\ding{73}}', +u'\u272a': '{\\ding{74}}', +u'\u272b': '{\\ding{75}}', +u'\u272c': '{\\ding{76}}', +u'\u272d': '{\\ding{77}}', +u'\u272e': '{\\ding{78}}', +u'\u272f': '{\\ding{79}}', +u'\u2730': '{\\ding{80}}', +u'\u2731': '{\\ding{81}}', +u'\u2732': '{\\ding{82}}', +u'\u2733': '{\\ding{83}}', +u'\u2734': '{\\ding{84}}', +u'\u2735': '{\\ding{85}}', +u'\u2736': '{\\ding{86}}', +u'\u2737': '{\\ding{87}}', +u'\u2738': '{\\ding{88}}', +u'\u2739': '{\\ding{89}}', +u'\u273a': '{\\ding{90}}', +u'\u273b': '{\\ding{91}}', +u'\u273c': '{\\ding{92}}', +u'\u273d': '{\\ding{93}}', +u'\u273e': '{\\ding{94}}', +u'\u273f': '{\\ding{95}}', +u'\u2740': '{\\ding{96}}', +u'\u2741': '{\\ding{97}}', +u'\u2742': '{\\ding{98}}', +u'\u2743': '{\\ding{99}}', +u'\u2744': '{\\ding{100}}', +u'\u2745': '{\\ding{101}}', +u'\u2746': '{\\ding{102}}', +u'\u2747': '{\\ding{103}}', +u'\u2748': '{\\ding{104}}', +u'\u2749': '{\\ding{105}}', +u'\u274a': '{\\ding{106}}', +u'\u274b': '{\\ding{107}}', +u'\u274d': '{\\ding{109}}', +u'\u274f': '{\\ding{111}}', +u'\u2750': '{\\ding{112}}', +u'\u2751': '{\\ding{113}}', +u'\u2752': '{\\ding{114}}', +u'\u2756': '{\\ding{118}}', +u'\u2758': '{\\ding{120}}', +u'\u2759': '{\\ding{121}}', +u'\u275a': '{\\ding{122}}', +u'\u275b': '{\\ding{123}}', +u'\u275c': '{\\ding{124}}', +u'\u275d': '{\\ding{125}}', +u'\u275e': '{\\ding{126}}', +u'\u2761': '{\\ding{161}}', +u'\u2762': '{\\ding{162}}', +u'\u2763': '{\\ding{163}}', +u'\u2764': '{\\ding{164}}', +u'\u2765': '{\\ding{165}}', +u'\u2766': '{\\ding{166}}', +u'\u2767': '{\\ding{167}}', +u'\u2776': '{\\ding{182}}', +u'\u2777': '{\\ding{183}}', +u'\u2778': '{\\ding{184}}', +u'\u2779': '{\\ding{185}}', +u'\u277a': '{\\ding{186}}', +u'\u277b': '{\\ding{187}}', +u'\u277c': '{\\ding{188}}', +u'\u277d': '{\\ding{189}}', +u'\u277e': '{\\ding{190}}', +u'\u277f': '{\\ding{191}}', +u'\u2780': '{\\ding{192}}', +u'\u2781': '{\\ding{193}}', +u'\u2782': '{\\ding{194}}', +u'\u2783': '{\\ding{195}}', +u'\u2784': '{\\ding{196}}', +u'\u2785': '{\\ding{197}}', +u'\u2786': '{\\ding{198}}', +u'\u2787': '{\\ding{199}}', +u'\u2788': '{\\ding{200}}', +u'\u2789': '{\\ding{201}}', +u'\u278a': '{\\ding{202}}', +u'\u278b': '{\\ding{203}}', +u'\u278c': '{\\ding{204}}', +u'\u278d': '{\\ding{205}}', +u'\u278e': '{\\ding{206}}', +u'\u278f': '{\\ding{207}}', +u'\u2790': '{\\ding{208}}', +u'\u2791': '{\\ding{209}}', +u'\u2792': '{\\ding{210}}', +u'\u2793': '{\\ding{211}}', +u'\u2794': '{\\ding{212}}', +u'\u2798': '{\\ding{216}}', +u'\u2799': '{\\ding{217}}', +u'\u279a': '{\\ding{218}}', +u'\u279b': '{\\ding{219}}', +u'\u279c': '{\\ding{220}}', +u'\u279d': '{\\ding{221}}', +u'\u279e': '{\\ding{222}}', +u'\u279f': '{\\ding{223}}', +u'\u27a0': '{\\ding{224}}', +u'\u27a1': '{\\ding{225}}', +u'\u27a2': '{\\ding{226}}', +u'\u27a3': '{\\ding{227}}', +u'\u27a4': '{\\ding{228}}', +u'\u27a5': '{\\ding{229}}', +u'\u27a6': '{\\ding{230}}', +u'\u27a7': '{\\ding{231}}', +u'\u27a8': '{\\ding{232}}', +u'\u27a9': '{\\ding{233}}', +u'\u27aa': '{\\ding{234}}', +u'\u27ab': '{\\ding{235}}', +u'\u27ac': '{\\ding{236}}', +u'\u27ad': '{\\ding{237}}', +u'\u27ae': '{\\ding{238}}', +u'\u27af': '{\\ding{239}}', +u'\u27b1': '{\\ding{241}}', +u'\u27b2': '{\\ding{242}}', +u'\u27b3': '{\\ding{243}}', +u'\u27b4': '{\\ding{244}}', +u'\u27b5': '{\\ding{245}}', +u'\u27b6': '{\\ding{246}}', +u'\u27b7': '{\\ding{247}}', +u'\u27b8': '{\\ding{248}}', +u'\u27b9': '{\\ding{249}}', +u'\u27ba': '{\\ding{250}}', +u'\u27bb': '{\\ding{251}}', +u'\u27bc': '{\\ding{252}}', +u'\u27bd': '{\\ding{253}}', +u'\u27be': '{\\ding{254}}', +u'\u27f5': '$\\longleftarrow$', +u'\u27f6': '$\\longrightarrow$', +u'\u27f7': '$\\longleftrightarrow$', +u'\u27f8': '$\\Longleftarrow$', +u'\u27f9': '$\\Longrightarrow$', +u'\u27fa': '$\\Longleftrightarrow$', +u'\u27fc': '$\\longmapsto$', +u'\u27ff': '$\\sim\\joinrel\\leadsto$', +u'\u2905': '$\\ElsevierGlyph{E212}$', +u'\u2912': '$\\UpArrowBar$', +u'\u2913': '$\\DownArrowBar$', +u'\u2923': '$\\ElsevierGlyph{E20C}$', +u'\u2924': '$\\ElsevierGlyph{E20D}$', +u'\u2925': '$\\ElsevierGlyph{E20B}$', +u'\u2926': '$\\ElsevierGlyph{E20A}$', +u'\u2927': '$\\ElsevierGlyph{E211}$', +u'\u2928': '$\\ElsevierGlyph{E20E}$', +u'\u2929': '$\\ElsevierGlyph{E20F}$', +u'\u292a': '$\\ElsevierGlyph{E210}$', +u'\u2933': '$\\ElsevierGlyph{E21C}$', +u'\u2936': '$\\ElsevierGlyph{E21A}$', +u'\u2937': '$\\ElsevierGlyph{E219}$', +u'\u2940': '$\\Elolarr$', +u'\u2941': '$\\Elorarr$', +u'\u2942': '$\\ElzRlarr$', +u'\u2944': '$\\ElzrLarr$', +u'\u2947': '$\\Elzrarrx$', +u'\u294e': '$\\LeftRightVector$', +u'\u294f': '$\\RightUpDownVector$', +u'\u2950': '$\\DownLeftRightVector$', +u'\u2951': '$\\LeftUpDownVector$', +u'\u2952': '$\\LeftVectorBar$', +u'\u2953': '$\\RightVectorBar$', +u'\u2954': '$\\RightUpVectorBar$', +u'\u2955': '$\\RightDownVectorBar$', +u'\u2956': '$\\DownLeftVectorBar$', +u'\u2957': '$\\DownRightVectorBar$', +u'\u2958': '$\\LeftUpVectorBar$', +u'\u2959': '$\\LeftDownVectorBar$', +u'\u295a': '$\\LeftTeeVector$', +u'\u295b': '$\\RightTeeVector$', +u'\u295c': '$\\RightUpTeeVector$', +u'\u295d': '$\\RightDownTeeVector$', +u'\u295e': '$\\DownLeftTeeVector$', +u'\u295f': '$\\DownRightTeeVector$', +u'\u2960': '$\\LeftUpTeeVector$', +u'\u2961': '$\\LeftDownTeeVector$', +u'\u296e': '$\\UpEquilibrium$', +u'\u296f': '$\\ReverseUpEquilibrium$', +u'\u2970': '$\\RoundImplies$', +u'\u297c': '$\\ElsevierGlyph{E214}$', +u'\u297d': '$\\ElsevierGlyph{E215}$', +u'\u2980': '$\\Elztfnc$', +u'\u2985': '$\\ElsevierGlyph{3018}$', +u'\u2986': '$\\Elroang$', +u'\u2993': '$<\\kern-0.58em($', +u'\u2994': '$\\ElsevierGlyph{E291}$', +u'\u2999': '$\\Elzddfnc$', +u'\u299c': '$\\Angle$', +u'\u29a0': '$\\Elzlpargt$', +u'\u29b5': '$\\ElsevierGlyph{E260}$', +u'\u29b6': '$\\ElsevierGlyph{E61B}$', +u'\u29ca': '$\\ElzLap$', +u'\u29cb': '$\\Elzdefas$', +u'\u29cf': '$\\LeftTriangleBar$', +u'\u29d0': '$\\RightTriangleBar$', +u'\u29dc': '$\\ElsevierGlyph{E372}$', +u'\u29eb': '$\\blacklozenge$', +u'\u29f4': '$\\RuleDelayed$', +u'\u2a04': '$\\Elxuplus$', +u'\u2a05': '$\\ElzThr$', +u'\u2a06': '$\\Elxsqcup$', +u'\u2a07': '$\\ElzInf$', +u'\u2a08': '$\\ElzSup$', +u'\u2a0d': '$\\ElzCint$', +u'\u2a0f': '$\\clockoint$', +u'\u2a10': '$\\ElsevierGlyph{E395}$', +u'\u2a16': '$\\sqrint$', +u'\u2a25': '$\\ElsevierGlyph{E25A}$', +u'\u2a2a': '$\\ElsevierGlyph{E25B}$', +u'\u2a2d': '$\\ElsevierGlyph{E25C}$', +u'\u2a2e': '$\\ElsevierGlyph{E25D}$', +u'\u2a2f': '$\\ElzTimes$', +u'\u2a34': '$\\ElsevierGlyph{E25E}$', +u'\u2a35': '$\\ElsevierGlyph{E25E}$', +u'\u2a3c': '$\\ElsevierGlyph{E259}$', +u'\u2a3f': '$\\amalg$', +u'\u2a53': '$\\ElzAnd$', +u'\u2a54': '$\\ElzOr$', +u'\u2a55': '$\\ElsevierGlyph{E36E}$', +u'\u2a56': '$\\ElOr$', +u'\u2a5e': '$\\perspcorrespond$', +u'\u2a5f': '$\\Elzminhat$', +u'\u2a63': '$\\ElsevierGlyph{225A}$', +u'\u2a6e': '$\\stackrel{*}{=}$', +u'\u2a75': '$\\Equal$', +u'\u2a7d': '$\\leqslant$', +u'\u2a7e': '$\\geqslant$', +u'\u2a85': '$\\lessapprox$', +u'\u2a86': '$\\gtrapprox$', +u'\u2a87': '$\\lneq$', +u'\u2a88': '$\\gneq$', +u'\u2a89': '$\\lnapprox$', +u'\u2a8a': '$\\gnapprox$', +u'\u2a8b': '$\\lesseqqgtr$', +u'\u2a8c': '$\\gtreqqless$', +u'\u2a95': '$\\eqslantless$', +u'\u2a96': '$\\eqslantgtr$', +u'\u2a9d': '$\\Pisymbol{ppi020}{117}$', +u'\u2a9e': '$\\Pisymbol{ppi020}{105}$', +u'\u2aa1': '$\\NestedLessLess$', +u'\u2aa2': '$\\NestedGreaterGreater$', +u'\u2aaf': '$\\preceq$', +u'\u2ab0': '$\\succeq$', +u'\u2ab5': '$\\precneqq$', +u'\u2ab6': '$\\succneqq$', +u'\u2ab7': '$\\precapprox$', +u'\u2ab8': '$\\succapprox$', +u'\u2ab9': '$\\precnapprox$', +u'\u2aba': '$\\succnapprox$', +u'\u2ac5': '$\\subseteqq$', +u'\u2ac6': '$\\supseteqq$', +u'\u2acb': '$\\subsetneqq$', +u'\u2acc': '$\\supsetneqq$', +u'\u2aeb': '$\\ElsevierGlyph{E30D}$', +u'\u2af6': '$\\Elztdcol$', +u'\u2afd': '${{/}\\!\\!{/}}$', +u'\u300a': '$\\ElsevierGlyph{300A}$', +u'\u300b': '$\\ElsevierGlyph{300B}$', +u'\u3018': '$\\ElsevierGlyph{3018}$', +u'\u3019': '$\\ElsevierGlyph{3019}$', +u'\u301a': '$\\openbracketleft$', +u'\u301b': '$\\openbracketright$', +u'\ufb00': '{ff}', +u'\ufb01': '{fi}', +u'\ufb02': '{fl}', +u'\ufb03': '{ffi}', +u'\ufb04': '{ffl}', +u'\U0001d400': '$\\mathbf{A}$', +u'\U0001d401': '$\\mathbf{B}$', +u'\U0001d402': '$\\mathbf{C}$', +u'\U0001d403': '$\\mathbf{D}$', +u'\U0001d404': '$\\mathbf{E}$', +u'\U0001d405': '$\\mathbf{F}$', +u'\U0001d406': '$\\mathbf{G}$', +u'\U0001d407': '$\\mathbf{H}$', +u'\U0001d408': '$\\mathbf{I}$', +u'\U0001d409': '$\\mathbf{J}$', +u'\U0001d40a': '$\\mathbf{K}$', +u'\U0001d40b': '$\\mathbf{L}$', +u'\U0001d40c': '$\\mathbf{M}$', +u'\U0001d40d': '$\\mathbf{N}$', +u'\U0001d40e': '$\\mathbf{O}$', +u'\U0001d40f': '$\\mathbf{P}$', +u'\U0001d410': '$\\mathbf{Q}$', +u'\U0001d411': '$\\mathbf{R}$', +u'\U0001d412': '$\\mathbf{S}$', +u'\U0001d413': '$\\mathbf{T}$', +u'\U0001d414': '$\\mathbf{U}$', +u'\U0001d415': '$\\mathbf{V}$', +u'\U0001d416': '$\\mathbf{W}$', +u'\U0001d417': '$\\mathbf{X}$', +u'\U0001d418': '$\\mathbf{Y}$', +u'\U0001d419': '$\\mathbf{Z}$', +u'\U0001d41a': '$\\mathbf{a}$', +u'\U0001d41b': '$\\mathbf{b}$', +u'\U0001d41c': '$\\mathbf{c}$', +u'\U0001d41d': '$\\mathbf{d}$', +u'\U0001d41e': '$\\mathbf{e}$', +u'\U0001d41f': '$\\mathbf{f}$', +u'\U0001d420': '$\\mathbf{g}$', +u'\U0001d421': '$\\mathbf{h}$', +u'\U0001d422': '$\\mathbf{i}$', +u'\U0001d423': '$\\mathbf{j}$', +u'\U0001d424': '$\\mathbf{k}$', +u'\U0001d425': '$\\mathbf{l}$', +u'\U0001d426': '$\\mathbf{m}$', +u'\U0001d427': '$\\mathbf{n}$', +u'\U0001d428': '$\\mathbf{o}$', +u'\U0001d429': '$\\mathbf{p}$', +u'\U0001d42a': '$\\mathbf{q}$', +u'\U0001d42b': '$\\mathbf{r}$', +u'\U0001d42c': '$\\mathbf{s}$', +u'\U0001d42d': '$\\mathbf{t}$', +u'\U0001d42e': '$\\mathbf{u}$', +u'\U0001d42f': '$\\mathbf{v}$', +u'\U0001d430': '$\\mathbf{w}$', +u'\U0001d431': '$\\mathbf{x}$', +u'\U0001d432': '$\\mathbf{y}$', +u'\U0001d433': '$\\mathbf{z}$', +u'\U0001d434': '$\\mathsl{A}$', +u'\U0001d435': '$\\mathsl{B}$', +u'\U0001d436': '$\\mathsl{C}$', +u'\U0001d437': '$\\mathsl{D}$', +u'\U0001d438': '$\\mathsl{E}$', +u'\U0001d439': '$\\mathsl{F}$', +u'\U0001d43a': '$\\mathsl{G}$', +u'\U0001d43b': '$\\mathsl{H}$', +u'\U0001d43c': '$\\mathsl{I}$', +u'\U0001d43d': '$\\mathsl{J}$', +u'\U0001d43e': '$\\mathsl{K}$', +u'\U0001d43f': '$\\mathsl{L}$', +u'\U0001d440': '$\\mathsl{M}$', +u'\U0001d441': '$\\mathsl{N}$', +u'\U0001d442': '$\\mathsl{O}$', +u'\U0001d443': '$\\mathsl{P}$', +u'\U0001d444': '$\\mathsl{Q}$', +u'\U0001d445': '$\\mathsl{R}$', +u'\U0001d446': '$\\mathsl{S}$', +u'\U0001d447': '$\\mathsl{T}$', +u'\U0001d448': '$\\mathsl{U}$', +u'\U0001d449': '$\\mathsl{V}$', +u'\U0001d44a': '$\\mathsl{W}$', +u'\U0001d44b': '$\\mathsl{X}$', +u'\U0001d44c': '$\\mathsl{Y}$', +u'\U0001d44d': '$\\mathsl{Z}$', +u'\U0001d44e': '$\\mathsl{a}$', +u'\U0001d44f': '$\\mathsl{b}$', +u'\U0001d450': '$\\mathsl{c}$', +u'\U0001d451': '$\\mathsl{d}$', +u'\U0001d452': '$\\mathsl{e}$', +u'\U0001d453': '$\\mathsl{f}$', +u'\U0001d454': '$\\mathsl{g}$', +u'\U0001d456': '$\\mathsl{i}$', +u'\U0001d457': '$\\mathsl{j}$', +u'\U0001d458': '$\\mathsl{k}$', +u'\U0001d459': '$\\mathsl{l}$', +u'\U0001d45a': '$\\mathsl{m}$', +u'\U0001d45b': '$\\mathsl{n}$', +u'\U0001d45c': '$\\mathsl{o}$', +u'\U0001d45d': '$\\mathsl{p}$', +u'\U0001d45e': '$\\mathsl{q}$', +u'\U0001d45f': '$\\mathsl{r}$', +u'\U0001d460': '$\\mathsl{s}$', +u'\U0001d461': '$\\mathsl{t}$', +u'\U0001d462': '$\\mathsl{u}$', +u'\U0001d463': '$\\mathsl{v}$', +u'\U0001d464': '$\\mathsl{w}$', +u'\U0001d465': '$\\mathsl{x}$', +u'\U0001d466': '$\\mathsl{y}$', +u'\U0001d467': '$\\mathsl{z}$', +u'\U0001d468': '$\\mathbit{A}$', +u'\U0001d469': '$\\mathbit{B}$', +u'\U0001d46a': '$\\mathbit{C}$', +u'\U0001d46b': '$\\mathbit{D}$', +u'\U0001d46c': '$\\mathbit{E}$', +u'\U0001d46d': '$\\mathbit{F}$', +u'\U0001d46e': '$\\mathbit{G}$', +u'\U0001d46f': '$\\mathbit{H}$', +u'\U0001d470': '$\\mathbit{I}$', +u'\U0001d471': '$\\mathbit{J}$', +u'\U0001d472': '$\\mathbit{K}$', +u'\U0001d473': '$\\mathbit{L}$', +u'\U0001d474': '$\\mathbit{M}$', +u'\U0001d475': '$\\mathbit{N}$', +u'\U0001d476': '$\\mathbit{O}$', +u'\U0001d477': '$\\mathbit{P}$', +u'\U0001d478': '$\\mathbit{Q}$', +u'\U0001d479': '$\\mathbit{R}$', +u'\U0001d47a': '$\\mathbit{S}$', +u'\U0001d47b': '$\\mathbit{T}$', +u'\U0001d47c': '$\\mathbit{U}$', +u'\U0001d47d': '$\\mathbit{V}$', +u'\U0001d47e': '$\\mathbit{W}$', +u'\U0001d47f': '$\\mathbit{X}$', +u'\U0001d480': '$\\mathbit{Y}$', +u'\U0001d481': '$\\mathbit{Z}$', +u'\U0001d482': '$\\mathbit{a}$', +u'\U0001d483': '$\\mathbit{b}$', +u'\U0001d484': '$\\mathbit{c}$', +u'\U0001d485': '$\\mathbit{d}$', +u'\U0001d486': '$\\mathbit{e}$', +u'\U0001d487': '$\\mathbit{f}$', +u'\U0001d488': '$\\mathbit{g}$', +u'\U0001d489': '$\\mathbit{h}$', +u'\U0001d48a': '$\\mathbit{i}$', +u'\U0001d48b': '$\\mathbit{j}$', +u'\U0001d48c': '$\\mathbit{k}$', +u'\U0001d48d': '$\\mathbit{l}$', +u'\U0001d48e': '$\\mathbit{m}$', +u'\U0001d48f': '$\\mathbit{n}$', +u'\U0001d490': '$\\mathbit{o}$', +u'\U0001d491': '$\\mathbit{p}$', +u'\U0001d492': '$\\mathbit{q}$', +u'\U0001d493': '$\\mathbit{r}$', +u'\U0001d494': '$\\mathbit{s}$', +u'\U0001d495': '$\\mathbit{t}$', +u'\U0001d496': '$\\mathbit{u}$', +u'\U0001d497': '$\\mathbit{v}$', +u'\U0001d498': '$\\mathbit{w}$', +u'\U0001d499': '$\\mathbit{x}$', +u'\U0001d49a': '$\\mathbit{y}$', +u'\U0001d49b': '$\\mathbit{z}$', +u'\U0001d49c': '$\\mathscr{A}$', +u'\U0001d49e': '$\\mathscr{C}$', +u'\U0001d49f': '$\\mathscr{D}$', +u'\U0001d4a2': '$\\mathscr{G}$', +u'\U0001d4a5': '$\\mathscr{J}$', +u'\U0001d4a6': '$\\mathscr{K}$', +u'\U0001d4a9': '$\\mathscr{N}$', +u'\U0001d4aa': '$\\mathscr{O}$', +u'\U0001d4ab': '$\\mathscr{P}$', +u'\U0001d4ac': '$\\mathscr{Q}$', +u'\U0001d4ae': '$\\mathscr{S}$', +u'\U0001d4af': '$\\mathscr{T}$', +u'\U0001d4b0': '$\\mathscr{U}$', +u'\U0001d4b1': '$\\mathscr{V}$', +u'\U0001d4b2': '$\\mathscr{W}$', +u'\U0001d4b3': '$\\mathscr{X}$', +u'\U0001d4b4': '$\\mathscr{Y}$', +u'\U0001d4b5': '$\\mathscr{Z}$', +u'\U0001d4b6': '$\\mathscr{a}$', +u'\U0001d4b7': '$\\mathscr{b}$', +u'\U0001d4b8': '$\\mathscr{c}$', +u'\U0001d4b9': '$\\mathscr{d}$', +u'\U0001d4bb': '$\\mathscr{f}$', +u'\U0001d4bd': '$\\mathscr{h}$', +u'\U0001d4be': '$\\mathscr{i}$', +u'\U0001d4bf': '$\\mathscr{j}$', +u'\U0001d4c0': '$\\mathscr{k}$', +u'\U0001d4c1': '$\\mathscr{l}$', +u'\U0001d4c2': '$\\mathscr{m}$', +u'\U0001d4c3': '$\\mathscr{n}$', +u'\U0001d4c5': '$\\mathscr{p}$', +u'\U0001d4c6': '$\\mathscr{q}$', +u'\U0001d4c7': '$\\mathscr{r}$', +u'\U0001d4c8': '$\\mathscr{s}$', +u'\U0001d4c9': '$\\mathscr{t}$', +u'\U0001d4ca': '$\\mathscr{u}$', +u'\U0001d4cb': '$\\mathscr{v}$', +u'\U0001d4cc': '$\\mathscr{w}$', +u'\U0001d4cd': '$\\mathscr{x}$', +u'\U0001d4ce': '$\\mathscr{y}$', +u'\U0001d4cf': '$\\mathscr{z}$', +u'\U0001d4d0': '$\\mathmit{A}$', +u'\U0001d4d1': '$\\mathmit{B}$', +u'\U0001d4d2': '$\\mathmit{C}$', +u'\U0001d4d3': '$\\mathmit{D}$', +u'\U0001d4d4': '$\\mathmit{E}$', +u'\U0001d4d5': '$\\mathmit{F}$', +u'\U0001d4d6': '$\\mathmit{G}$', +u'\U0001d4d7': '$\\mathmit{H}$', +u'\U0001d4d8': '$\\mathmit{I}$', +u'\U0001d4d9': '$\\mathmit{J}$', +u'\U0001d4da': '$\\mathmit{K}$', +u'\U0001d4db': '$\\mathmit{L}$', +u'\U0001d4dc': '$\\mathmit{M}$', +u'\U0001d4dd': '$\\mathmit{N}$', +u'\U0001d4de': '$\\mathmit{O}$', +u'\U0001d4df': '$\\mathmit{P}$', +u'\U0001d4e0': '$\\mathmit{Q}$', +u'\U0001d4e1': '$\\mathmit{R}$', +u'\U0001d4e2': '$\\mathmit{S}$', +u'\U0001d4e3': '$\\mathmit{T}$', +u'\U0001d4e4': '$\\mathmit{U}$', +u'\U0001d4e5': '$\\mathmit{V}$', +u'\U0001d4e6': '$\\mathmit{W}$', +u'\U0001d4e7': '$\\mathmit{X}$', +u'\U0001d4e8': '$\\mathmit{Y}$', +u'\U0001d4e9': '$\\mathmit{Z}$', +u'\U0001d4ea': '$\\mathmit{a}$', +u'\U0001d4eb': '$\\mathmit{b}$', +u'\U0001d4ec': '$\\mathmit{c}$', +u'\U0001d4ed': '$\\mathmit{d}$', +u'\U0001d4ee': '$\\mathmit{e}$', +u'\U0001d4ef': '$\\mathmit{f}$', +u'\U0001d4f0': '$\\mathmit{g}$', +u'\U0001d4f1': '$\\mathmit{h}$', +u'\U0001d4f2': '$\\mathmit{i}$', +u'\U0001d4f3': '$\\mathmit{j}$', +u'\U0001d4f4': '$\\mathmit{k}$', +u'\U0001d4f5': '$\\mathmit{l}$', +u'\U0001d4f6': '$\\mathmit{m}$', +u'\U0001d4f7': '$\\mathmit{n}$', +u'\U0001d4f8': '$\\mathmit{o}$', +u'\U0001d4f9': '$\\mathmit{p}$', +u'\U0001d4fa': '$\\mathmit{q}$', +u'\U0001d4fb': '$\\mathmit{r}$', +u'\U0001d4fc': '$\\mathmit{s}$', +u'\U0001d4fd': '$\\mathmit{t}$', +u'\U0001d4fe': '$\\mathmit{u}$', +u'\U0001d4ff': '$\\mathmit{v}$', +u'\U0001d500': '$\\mathmit{w}$', +u'\U0001d501': '$\\mathmit{x}$', +u'\U0001d502': '$\\mathmit{y}$', +u'\U0001d503': '$\\mathmit{z}$', +u'\U0001d504': '$\\mathfrak{A}$', +u'\U0001d505': '$\\mathfrak{B}$', +u'\U0001d507': '$\\mathfrak{D}$', +u'\U0001d508': '$\\mathfrak{E}$', +u'\U0001d509': '$\\mathfrak{F}$', +u'\U0001d50a': '$\\mathfrak{G}$', +u'\U0001d50d': '$\\mathfrak{J}$', +u'\U0001d50e': '$\\mathfrak{K}$', +u'\U0001d50f': '$\\mathfrak{L}$', +u'\U0001d510': '$\\mathfrak{M}$', +u'\U0001d511': '$\\mathfrak{N}$', +u'\U0001d512': '$\\mathfrak{O}$', +u'\U0001d513': '$\\mathfrak{P}$', +u'\U0001d514': '$\\mathfrak{Q}$', +u'\U0001d516': '$\\mathfrak{S}$', +u'\U0001d517': '$\\mathfrak{T}$', +u'\U0001d518': '$\\mathfrak{U}$', +u'\U0001d519': '$\\mathfrak{V}$', +u'\U0001d51a': '$\\mathfrak{W}$', +u'\U0001d51b': '$\\mathfrak{X}$', +u'\U0001d51c': '$\\mathfrak{Y}$', +u'\U0001d51e': '$\\mathfrak{a}$', +u'\U0001d51f': '$\\mathfrak{b}$', +u'\U0001d520': '$\\mathfrak{c}$', +u'\U0001d521': '$\\mathfrak{d}$', +u'\U0001d522': '$\\mathfrak{e}$', +u'\U0001d523': '$\\mathfrak{f}$', +u'\U0001d524': '$\\mathfrak{g}$', +u'\U0001d525': '$\\mathfrak{h}$', +u'\U0001d526': '$\\mathfrak{i}$', +u'\U0001d527': '$\\mathfrak{j}$', +u'\U0001d528': '$\\mathfrak{k}$', +u'\U0001d529': '$\\mathfrak{l}$', +u'\U0001d52a': '$\\mathfrak{m}$', +u'\U0001d52b': '$\\mathfrak{n}$', +u'\U0001d52c': '$\\mathfrak{o}$', +u'\U0001d52d': '$\\mathfrak{p}$', +u'\U0001d52e': '$\\mathfrak{q}$', +u'\U0001d52f': '$\\mathfrak{r}$', +u'\U0001d530': '$\\mathfrak{s}$', +u'\U0001d531': '$\\mathfrak{t}$', +u'\U0001d532': '$\\mathfrak{u}$', +u'\U0001d533': '$\\mathfrak{v}$', +u'\U0001d534': '$\\mathfrak{w}$', +u'\U0001d535': '$\\mathfrak{x}$', +u'\U0001d536': '$\\mathfrak{y}$', +u'\U0001d537': '$\\mathfrak{z}$', +u'\U0001d538': '$\\mathbb{A}$', +u'\U0001d539': '$\\mathbb{B}$', +u'\U0001d53b': '$\\mathbb{D}$', +u'\U0001d53c': '$\\mathbb{E}$', +u'\U0001d53d': '$\\mathbb{F}$', +u'\U0001d53e': '$\\mathbb{G}$', +u'\U0001d540': '$\\mathbb{I}$', +u'\U0001d541': '$\\mathbb{J}$', +u'\U0001d542': '$\\mathbb{K}$', +u'\U0001d543': '$\\mathbb{L}$', +u'\U0001d544': '$\\mathbb{M}$', +u'\U0001d546': '$\\mathbb{O}$', +u'\U0001d54a': '$\\mathbb{S}$', +u'\U0001d54b': '$\\mathbb{T}$', +u'\U0001d54c': '$\\mathbb{U}$', +u'\U0001d54d': '$\\mathbb{V}$', +u'\U0001d54e': '$\\mathbb{W}$', +u'\U0001d54f': '$\\mathbb{X}$', +u'\U0001d550': '$\\mathbb{Y}$', +u'\U0001d552': '$\\mathbb{a}$', +u'\U0001d553': '$\\mathbb{b}$', +u'\U0001d554': '$\\mathbb{c}$', +u'\U0001d555': '$\\mathbb{d}$', +u'\U0001d556': '$\\mathbb{e}$', +u'\U0001d557': '$\\mathbb{f}$', +u'\U0001d558': '$\\mathbb{g}$', +u'\U0001d559': '$\\mathbb{h}$', +u'\U0001d55a': '$\\mathbb{i}$', +u'\U0001d55b': '$\\mathbb{j}$', +u'\U0001d55c': '$\\mathbb{k}$', +u'\U0001d55d': '$\\mathbb{l}$', +u'\U0001d55e': '$\\mathbb{m}$', +u'\U0001d55f': '$\\mathbb{n}$', +u'\U0001d560': '$\\mathbb{o}$', +u'\U0001d561': '$\\mathbb{p}$', +u'\U0001d562': '$\\mathbb{q}$', +u'\U0001d563': '$\\mathbb{r}$', +u'\U0001d564': '$\\mathbb{s}$', +u'\U0001d565': '$\\mathbb{t}$', +u'\U0001d566': '$\\mathbb{u}$', +u'\U0001d567': '$\\mathbb{v}$', +u'\U0001d568': '$\\mathbb{w}$', +u'\U0001d569': '$\\mathbb{x}$', +u'\U0001d56a': '$\\mathbb{y}$', +u'\U0001d56b': '$\\mathbb{z}$', +u'\U0001d56c': '$\\mathslbb{A}$', +u'\U0001d56d': '$\\mathslbb{B}$', +u'\U0001d56e': '$\\mathslbb{C}$', +u'\U0001d56f': '$\\mathslbb{D}$', +u'\U0001d570': '$\\mathslbb{E}$', +u'\U0001d571': '$\\mathslbb{F}$', +u'\U0001d572': '$\\mathslbb{G}$', +u'\U0001d573': '$\\mathslbb{H}$', +u'\U0001d574': '$\\mathslbb{I}$', +u'\U0001d575': '$\\mathslbb{J}$', +u'\U0001d576': '$\\mathslbb{K}$', +u'\U0001d577': '$\\mathslbb{L}$', +u'\U0001d578': '$\\mathslbb{M}$', +u'\U0001d579': '$\\mathslbb{N}$', +u'\U0001d57a': '$\\mathslbb{O}$', +u'\U0001d57b': '$\\mathslbb{P}$', +u'\U0001d57c': '$\\mathslbb{Q}$', +u'\U0001d57d': '$\\mathslbb{R}$', +u'\U0001d57e': '$\\mathslbb{S}$', +u'\U0001d57f': '$\\mathslbb{T}$', +u'\U0001d580': '$\\mathslbb{U}$', +u'\U0001d581': '$\\mathslbb{V}$', +u'\U0001d582': '$\\mathslbb{W}$', +u'\U0001d583': '$\\mathslbb{X}$', +u'\U0001d584': '$\\mathslbb{Y}$', +u'\U0001d585': '$\\mathslbb{Z}$', +u'\U0001d586': '$\\mathslbb{a}$', +u'\U0001d587': '$\\mathslbb{b}$', +u'\U0001d588': '$\\mathslbb{c}$', +u'\U0001d589': '$\\mathslbb{d}$', +u'\U0001d58a': '$\\mathslbb{e}$', +u'\U0001d58b': '$\\mathslbb{f}$', +u'\U0001d58c': '$\\mathslbb{g}$', +u'\U0001d58d': '$\\mathslbb{h}$', +u'\U0001d58e': '$\\mathslbb{i}$', +u'\U0001d58f': '$\\mathslbb{j}$', +u'\U0001d590': '$\\mathslbb{k}$', +u'\U0001d591': '$\\mathslbb{l}$', +u'\U0001d592': '$\\mathslbb{m}$', +u'\U0001d593': '$\\mathslbb{n}$', +u'\U0001d594': '$\\mathslbb{o}$', +u'\U0001d595': '$\\mathslbb{p}$', +u'\U0001d596': '$\\mathslbb{q}$', +u'\U0001d597': '$\\mathslbb{r}$', +u'\U0001d598': '$\\mathslbb{s}$', +u'\U0001d599': '$\\mathslbb{t}$', +u'\U0001d59a': '$\\mathslbb{u}$', +u'\U0001d59b': '$\\mathslbb{v}$', +u'\U0001d59c': '$\\mathslbb{w}$', +u'\U0001d59d': '$\\mathslbb{x}$', +u'\U0001d59e': '$\\mathslbb{y}$', +u'\U0001d59f': '$\\mathslbb{z}$', +u'\U0001d5a0': '$\\mathsf{A}$', +u'\U0001d5a1': '$\\mathsf{B}$', +u'\U0001d5a2': '$\\mathsf{C}$', +u'\U0001d5a3': '$\\mathsf{D}$', +u'\U0001d5a4': '$\\mathsf{E}$', +u'\U0001d5a5': '$\\mathsf{F}$', +u'\U0001d5a6': '$\\mathsf{G}$', +u'\U0001d5a7': '$\\mathsf{H}$', +u'\U0001d5a8': '$\\mathsf{I}$', +u'\U0001d5a9': '$\\mathsf{J}$', +u'\U0001d5aa': '$\\mathsf{K}$', +u'\U0001d5ab': '$\\mathsf{L}$', +u'\U0001d5ac': '$\\mathsf{M}$', +u'\U0001d5ad': '$\\mathsf{N}$', +u'\U0001d5ae': '$\\mathsf{O}$', +u'\U0001d5af': '$\\mathsf{P}$', +u'\U0001d5b0': '$\\mathsf{Q}$', +u'\U0001d5b1': '$\\mathsf{R}$', +u'\U0001d5b2': '$\\mathsf{S}$', +u'\U0001d5b3': '$\\mathsf{T}$', +u'\U0001d5b4': '$\\mathsf{U}$', +u'\U0001d5b5': '$\\mathsf{V}$', +u'\U0001d5b6': '$\\mathsf{W}$', +u'\U0001d5b7': '$\\mathsf{X}$', +u'\U0001d5b8': '$\\mathsf{Y}$', +u'\U0001d5b9': '$\\mathsf{Z}$', +u'\U0001d5ba': '$\\mathsf{a}$', +u'\U0001d5bb': '$\\mathsf{b}$', +u'\U0001d5bc': '$\\mathsf{c}$', +u'\U0001d5bd': '$\\mathsf{d}$', +u'\U0001d5be': '$\\mathsf{e}$', +u'\U0001d5bf': '$\\mathsf{f}$', +u'\U0001d5c0': '$\\mathsf{g}$', +u'\U0001d5c1': '$\\mathsf{h}$', +u'\U0001d5c2': '$\\mathsf{i}$', +u'\U0001d5c3': '$\\mathsf{j}$', +u'\U0001d5c4': '$\\mathsf{k}$', +u'\U0001d5c5': '$\\mathsf{l}$', +u'\U0001d5c6': '$\\mathsf{m}$', +u'\U0001d5c7': '$\\mathsf{n}$', +u'\U0001d5c8': '$\\mathsf{o}$', +u'\U0001d5c9': '$\\mathsf{p}$', +u'\U0001d5ca': '$\\mathsf{q}$', +u'\U0001d5cb': '$\\mathsf{r}$', +u'\U0001d5cc': '$\\mathsf{s}$', +u'\U0001d5cd': '$\\mathsf{t}$', +u'\U0001d5ce': '$\\mathsf{u}$', +u'\U0001d5cf': '$\\mathsf{v}$', +u'\U0001d5d0': '$\\mathsf{w}$', +u'\U0001d5d1': '$\\mathsf{x}$', +u'\U0001d5d2': '$\\mathsf{y}$', +u'\U0001d5d3': '$\\mathsf{z}$', +u'\U0001d5d4': '$\\mathsfbf{A}$', +u'\U0001d5d5': '$\\mathsfbf{B}$', +u'\U0001d5d6': '$\\mathsfbf{C}$', +u'\U0001d5d7': '$\\mathsfbf{D}$', +u'\U0001d5d8': '$\\mathsfbf{E}$', +u'\U0001d5d9': '$\\mathsfbf{F}$', +u'\U0001d5da': '$\\mathsfbf{G}$', +u'\U0001d5db': '$\\mathsfbf{H}$', +u'\U0001d5dc': '$\\mathsfbf{I}$', +u'\U0001d5dd': '$\\mathsfbf{J}$', +u'\U0001d5de': '$\\mathsfbf{K}$', +u'\U0001d5df': '$\\mathsfbf{L}$', +u'\U0001d5e0': '$\\mathsfbf{M}$', +u'\U0001d5e1': '$\\mathsfbf{N}$', +u'\U0001d5e2': '$\\mathsfbf{O}$', +u'\U0001d5e3': '$\\mathsfbf{P}$', +u'\U0001d5e4': '$\\mathsfbf{Q}$', +u'\U0001d5e5': '$\\mathsfbf{R}$', +u'\U0001d5e6': '$\\mathsfbf{S}$', +u'\U0001d5e7': '$\\mathsfbf{T}$', +u'\U0001d5e8': '$\\mathsfbf{U}$', +u'\U0001d5e9': '$\\mathsfbf{V}$', +u'\U0001d5ea': '$\\mathsfbf{W}$', +u'\U0001d5eb': '$\\mathsfbf{X}$', +u'\U0001d5ec': '$\\mathsfbf{Y}$', +u'\U0001d5ed': '$\\mathsfbf{Z}$', +u'\U0001d5ee': '$\\mathsfbf{a}$', +u'\U0001d5ef': '$\\mathsfbf{b}$', +u'\U0001d5f0': '$\\mathsfbf{c}$', +u'\U0001d5f1': '$\\mathsfbf{d}$', +u'\U0001d5f2': '$\\mathsfbf{e}$', +u'\U0001d5f3': '$\\mathsfbf{f}$', +u'\U0001d5f4': '$\\mathsfbf{g}$', +u'\U0001d5f5': '$\\mathsfbf{h}$', +u'\U0001d5f6': '$\\mathsfbf{i}$', +u'\U0001d5f7': '$\\mathsfbf{j}$', +u'\U0001d5f8': '$\\mathsfbf{k}$', +u'\U0001d5f9': '$\\mathsfbf{l}$', +u'\U0001d5fa': '$\\mathsfbf{m}$', +u'\U0001d5fb': '$\\mathsfbf{n}$', +u'\U0001d5fc': '$\\mathsfbf{o}$', +u'\U0001d5fd': '$\\mathsfbf{p}$', +u'\U0001d5fe': '$\\mathsfbf{q}$', +u'\U0001d5ff': '$\\mathsfbf{r}$', +u'\U0001d600': '$\\mathsfbf{s}$', +u'\U0001d601': '$\\mathsfbf{t}$', +u'\U0001d602': '$\\mathsfbf{u}$', +u'\U0001d603': '$\\mathsfbf{v}$', +u'\U0001d604': '$\\mathsfbf{w}$', +u'\U0001d605': '$\\mathsfbf{x}$', +u'\U0001d606': '$\\mathsfbf{y}$', +u'\U0001d607': '$\\mathsfbf{z}$', +u'\U0001d608': '$\\mathsfsl{A}$', +u'\U0001d609': '$\\mathsfsl{B}$', +u'\U0001d60a': '$\\mathsfsl{C}$', +u'\U0001d60b': '$\\mathsfsl{D}$', +u'\U0001d60c': '$\\mathsfsl{E}$', +u'\U0001d60d': '$\\mathsfsl{F}$', +u'\U0001d60e': '$\\mathsfsl{G}$', +u'\U0001d60f': '$\\mathsfsl{H}$', +u'\U0001d610': '$\\mathsfsl{I}$', +u'\U0001d611': '$\\mathsfsl{J}$', +u'\U0001d612': '$\\mathsfsl{K}$', +u'\U0001d613': '$\\mathsfsl{L}$', +u'\U0001d614': '$\\mathsfsl{M}$', +u'\U0001d615': '$\\mathsfsl{N}$', +u'\U0001d616': '$\\mathsfsl{O}$', +u'\U0001d617': '$\\mathsfsl{P}$', +u'\U0001d618': '$\\mathsfsl{Q}$', +u'\U0001d619': '$\\mathsfsl{R}$', +u'\U0001d61a': '$\\mathsfsl{S}$', +u'\U0001d61b': '$\\mathsfsl{T}$', +u'\U0001d61c': '$\\mathsfsl{U}$', +u'\U0001d61d': '$\\mathsfsl{V}$', +u'\U0001d61e': '$\\mathsfsl{W}$', +u'\U0001d61f': '$\\mathsfsl{X}$', +u'\U0001d620': '$\\mathsfsl{Y}$', +u'\U0001d621': '$\\mathsfsl{Z}$', +u'\U0001d622': '$\\mathsfsl{a}$', +u'\U0001d623': '$\\mathsfsl{b}$', +u'\U0001d624': '$\\mathsfsl{c}$', +u'\U0001d625': '$\\mathsfsl{d}$', +u'\U0001d626': '$\\mathsfsl{e}$', +u'\U0001d627': '$\\mathsfsl{f}$', +u'\U0001d628': '$\\mathsfsl{g}$', +u'\U0001d629': '$\\mathsfsl{h}$', +u'\U0001d62a': '$\\mathsfsl{i}$', +u'\U0001d62b': '$\\mathsfsl{j}$', +u'\U0001d62c': '$\\mathsfsl{k}$', +u'\U0001d62d': '$\\mathsfsl{l}$', +u'\U0001d62e': '$\\mathsfsl{m}$', +u'\U0001d62f': '$\\mathsfsl{n}$', +u'\U0001d630': '$\\mathsfsl{o}$', +u'\U0001d631': '$\\mathsfsl{p}$', +u'\U0001d632': '$\\mathsfsl{q}$', +u'\U0001d633': '$\\mathsfsl{r}$', +u'\U0001d634': '$\\mathsfsl{s}$', +u'\U0001d635': '$\\mathsfsl{t}$', +u'\U0001d636': '$\\mathsfsl{u}$', +u'\U0001d637': '$\\mathsfsl{v}$', +u'\U0001d638': '$\\mathsfsl{w}$', +u'\U0001d639': '$\\mathsfsl{x}$', +u'\U0001d63a': '$\\mathsfsl{y}$', +u'\U0001d63b': '$\\mathsfsl{z}$', +u'\U0001d63c': '$\\mathsfbfsl{A}$', +u'\U0001d63d': '$\\mathsfbfsl{B}$', +u'\U0001d63e': '$\\mathsfbfsl{C}$', +u'\U0001d63f': '$\\mathsfbfsl{D}$', +u'\U0001d640': '$\\mathsfbfsl{E}$', +u'\U0001d641': '$\\mathsfbfsl{F}$', +u'\U0001d642': '$\\mathsfbfsl{G}$', +u'\U0001d643': '$\\mathsfbfsl{H}$', +u'\U0001d644': '$\\mathsfbfsl{I}$', +u'\U0001d645': '$\\mathsfbfsl{J}$', +u'\U0001d646': '$\\mathsfbfsl{K}$', +u'\U0001d647': '$\\mathsfbfsl{L}$', +u'\U0001d648': '$\\mathsfbfsl{M}$', +u'\U0001d649': '$\\mathsfbfsl{N}$', +u'\U0001d64a': '$\\mathsfbfsl{O}$', +u'\U0001d64b': '$\\mathsfbfsl{P}$', +u'\U0001d64c': '$\\mathsfbfsl{Q}$', +u'\U0001d64d': '$\\mathsfbfsl{R}$', +u'\U0001d64e': '$\\mathsfbfsl{S}$', +u'\U0001d64f': '$\\mathsfbfsl{T}$', +u'\U0001d650': '$\\mathsfbfsl{U}$', +u'\U0001d651': '$\\mathsfbfsl{V}$', +u'\U0001d652': '$\\mathsfbfsl{W}$', +u'\U0001d653': '$\\mathsfbfsl{X}$', +u'\U0001d654': '$\\mathsfbfsl{Y}$', +u'\U0001d655': '$\\mathsfbfsl{Z}$', +u'\U0001d656': '$\\mathsfbfsl{a}$', +u'\U0001d657': '$\\mathsfbfsl{b}$', +u'\U0001d658': '$\\mathsfbfsl{c}$', +u'\U0001d659': '$\\mathsfbfsl{d}$', +u'\U0001d65a': '$\\mathsfbfsl{e}$', +u'\U0001d65b': '$\\mathsfbfsl{f}$', +u'\U0001d65c': '$\\mathsfbfsl{g}$', +u'\U0001d65d': '$\\mathsfbfsl{h}$', +u'\U0001d65e': '$\\mathsfbfsl{i}$', +u'\U0001d65f': '$\\mathsfbfsl{j}$', +u'\U0001d660': '$\\mathsfbfsl{k}$', +u'\U0001d661': '$\\mathsfbfsl{l}$', +u'\U0001d662': '$\\mathsfbfsl{m}$', +u'\U0001d663': '$\\mathsfbfsl{n}$', +u'\U0001d664': '$\\mathsfbfsl{o}$', +u'\U0001d665': '$\\mathsfbfsl{p}$', +u'\U0001d666': '$\\mathsfbfsl{q}$', +u'\U0001d667': '$\\mathsfbfsl{r}$', +u'\U0001d668': '$\\mathsfbfsl{s}$', +u'\U0001d669': '$\\mathsfbfsl{t}$', +u'\U0001d66a': '$\\mathsfbfsl{u}$', +u'\U0001d66b': '$\\mathsfbfsl{v}$', +u'\U0001d66c': '$\\mathsfbfsl{w}$', +u'\U0001d66d': '$\\mathsfbfsl{x}$', +u'\U0001d66e': '$\\mathsfbfsl{y}$', +u'\U0001d66f': '$\\mathsfbfsl{z}$', +u'\U0001d670': '$\\mathtt{A}$', +u'\U0001d671': '$\\mathtt{B}$', +u'\U0001d672': '$\\mathtt{C}$', +u'\U0001d673': '$\\mathtt{D}$', +u'\U0001d674': '$\\mathtt{E}$', +u'\U0001d675': '$\\mathtt{F}$', +u'\U0001d676': '$\\mathtt{G}$', +u'\U0001d677': '$\\mathtt{H}$', +u'\U0001d678': '$\\mathtt{I}$', +u'\U0001d679': '$\\mathtt{J}$', +u'\U0001d67a': '$\\mathtt{K}$', +u'\U0001d67b': '$\\mathtt{L}$', +u'\U0001d67c': '$\\mathtt{M}$', +u'\U0001d67d': '$\\mathtt{N}$', +u'\U0001d67e': '$\\mathtt{O}$', +u'\U0001d67f': '$\\mathtt{P}$', +u'\U0001d680': '$\\mathtt{Q}$', +u'\U0001d681': '$\\mathtt{R}$', +u'\U0001d682': '$\\mathtt{S}$', +u'\U0001d683': '$\\mathtt{T}$', +u'\U0001d684': '$\\mathtt{U}$', +u'\U0001d685': '$\\mathtt{V}$', +u'\U0001d686': '$\\mathtt{W}$', +u'\U0001d687': '$\\mathtt{X}$', +u'\U0001d688': '$\\mathtt{Y}$', +u'\U0001d689': '$\\mathtt{Z}$', +u'\U0001d68a': '$\\mathtt{a}$', +u'\U0001d68b': '$\\mathtt{b}$', +u'\U0001d68c': '$\\mathtt{c}$', +u'\U0001d68d': '$\\mathtt{d}$', +u'\U0001d68e': '$\\mathtt{e}$', +u'\U0001d68f': '$\\mathtt{f}$', +u'\U0001d690': '$\\mathtt{g}$', +u'\U0001d691': '$\\mathtt{h}$', +u'\U0001d692': '$\\mathtt{i}$', +u'\U0001d693': '$\\mathtt{j}$', +u'\U0001d694': '$\\mathtt{k}$', +u'\U0001d695': '$\\mathtt{l}$', +u'\U0001d696': '$\\mathtt{m}$', +u'\U0001d697': '$\\mathtt{n}$', +u'\U0001d698': '$\\mathtt{o}$', +u'\U0001d699': '$\\mathtt{p}$', +u'\U0001d69a': '$\\mathtt{q}$', +u'\U0001d69b': '$\\mathtt{r}$', +u'\U0001d69c': '$\\mathtt{s}$', +u'\U0001d69d': '$\\mathtt{t}$', +u'\U0001d69e': '$\\mathtt{u}$', +u'\U0001d69f': '$\\mathtt{v}$', +u'\U0001d6a0': '$\\mathtt{w}$', +u'\U0001d6a1': '$\\mathtt{x}$', +u'\U0001d6a2': '$\\mathtt{y}$', +u'\U0001d6a3': '$\\mathtt{z}$', +u'\U0001d6a8': '$\\mathbf{\\Alpha}$', +u'\U0001d6a9': '$\\mathbf{\\Beta}$', +u'\U0001d6aa': '$\\mathbf{\\Gamma}$', +u'\U0001d6ab': '$\\mathbf{\\Delta}$', +u'\U0001d6ac': '$\\mathbf{\\Epsilon}$', +u'\U0001d6ad': '$\\mathbf{\\Zeta}$', +u'\U0001d6ae': '$\\mathbf{\\Eta}$', +u'\U0001d6af': '$\\mathbf{\\Theta}$', +u'\U0001d6b0': '$\\mathbf{\\Iota}$', +u'\U0001d6b1': '$\\mathbf{\\Kappa}$', +u'\U0001d6b2': '$\\mathbf{\\Lambda}$', +u'\U0001d6b3': '$M$', +u'\U0001d6b4': '$N$', +u'\U0001d6b5': '$\\mathbf{\\Xi}$', +u'\U0001d6b6': '$O$', +u'\U0001d6b7': '$\\mathbf{\\Pi}$', +u'\U0001d6b8': '$\\mathbf{\\Rho}$', +u'\U0001d6b9': '{\\mathbf{\\vartheta}}', +u'\U0001d6ba': '$\\mathbf{\\Sigma}$', +u'\U0001d6bb': '$\\mathbf{\\Tau}$', +u'\U0001d6bc': '$\\mathbf{\\Upsilon}$', +u'\U0001d6bd': '$\\mathbf{\\Phi}$', +u'\U0001d6be': '$\\mathbf{\\Chi}$', +u'\U0001d6bf': '$\\mathbf{\\Psi}$', +u'\U0001d6c0': '$\\mathbf{\\Omega}$', +u'\U0001d6c1': '$\\mathbf{\\nabla}$', +u'\U0001d6c2': '$\\mathbf{\\Alpha}$', +u'\U0001d6c3': '$\\mathbf{\\Beta}$', +u'\U0001d6c4': '$\\mathbf{\\Gamma}$', +u'\U0001d6c5': '$\\mathbf{\\Delta}$', +u'\U0001d6c6': '$\\mathbf{\\Epsilon}$', +u'\U0001d6c7': '$\\mathbf{\\Zeta}$', +u'\U0001d6c8': '$\\mathbf{\\Eta}$', +u'\U0001d6c9': '$\\mathbf{\\theta}$', +u'\U0001d6ca': '$\\mathbf{\\Iota}$', +u'\U0001d6cb': '$\\mathbf{\\Kappa}$', +u'\U0001d6cc': '$\\mathbf{\\Lambda}$', +u'\U0001d6cd': '$M$', +u'\U0001d6ce': '$N$', +u'\U0001d6cf': '$\\mathbf{\\Xi}$', +u'\U0001d6d0': '$O$', +u'\U0001d6d1': '$\\mathbf{\\Pi}$', +u'\U0001d6d2': '$\\mathbf{\\Rho}$', +u'\U0001d6d3': '$\\mathbf{\\varsigma}$', +u'\U0001d6d4': '$\\mathbf{\\Sigma}$', +u'\U0001d6d5': '$\\mathbf{\\Tau}$', +u'\U0001d6d6': '$\\mathbf{\\Upsilon}$', +u'\U0001d6d7': '$\\mathbf{\\Phi}$', +u'\U0001d6d8': '$\\mathbf{\\Chi}$', +u'\U0001d6d9': '$\\mathbf{\\Psi}$', +u'\U0001d6da': '$\\mathbf{\\Omega}$', +u'\U0001d6db': '$\\partial$', +u'\U0001d6dc': '$\\in$', +u'\U0001d6dd': '{\\mathbf{\\vartheta}}', +u'\U0001d6de': '{\\mathbf{\\varkappa}}', +u'\U0001d6df': '{\\mathbf{\\phi}}', +u'\U0001d6e0': '{\\mathbf{\\varrho}}', +u'\U0001d6e1': '{\\mathbf{\\varpi}}', +u'\U0001d6e2': '$\\mathsl{\\Alpha}$', +u'\U0001d6e3': '$\\mathsl{\\Beta}$', +u'\U0001d6e4': '$\\mathsl{\\Gamma}$', +u'\U0001d6e5': '$\\mathsl{\\Delta}$', +u'\U0001d6e6': '$\\mathsl{\\Epsilon}$', +u'\U0001d6e7': '$\\mathsl{\\Zeta}$', +u'\U0001d6e8': '$\\mathsl{\\Eta}$', +u'\U0001d6e9': '$\\mathsl{\\Theta}$', +u'\U0001d6ea': '$\\mathsl{\\Iota}$', +u'\U0001d6eb': '$\\mathsl{\\Kappa}$', +u'\U0001d6ec': '$\\mathsl{\\Lambda}$', +u'\U0001d6ed': '$M$', +u'\U0001d6ee': '$N$', +u'\U0001d6ef': '$\\mathsl{\\Xi}$', +u'\U0001d6f0': '$O$', +u'\U0001d6f1': '$\\mathsl{\\Pi}$', +u'\U0001d6f2': '$\\mathsl{\\Rho}$', +u'\U0001d6f3': '{\\mathsl{\\vartheta}}', +u'\U0001d6f4': '$\\mathsl{\\Sigma}$', +u'\U0001d6f5': '$\\mathsl{\\Tau}$', +u'\U0001d6f6': '$\\mathsl{\\Upsilon}$', +u'\U0001d6f7': '$\\mathsl{\\Phi}$', +u'\U0001d6f8': '$\\mathsl{\\Chi}$', +u'\U0001d6f9': '$\\mathsl{\\Psi}$', +u'\U0001d6fa': '$\\mathsl{\\Omega}$', +u'\U0001d6fb': '$\\mathsl{\\nabla}$', +u'\U0001d6fc': '$\\mathsl{\\Alpha}$', +u'\U0001d6fd': '$\\mathsl{\\Beta}$', +u'\U0001d6fe': '$\\mathsl{\\Gamma}$', +u'\U0001d6ff': '$\\mathsl{\\Delta}$', +u'\U0001d700': '$\\mathsl{\\Epsilon}$', +u'\U0001d701': '$\\mathsl{\\Zeta}$', +u'\U0001d702': '$\\mathsl{\\Eta}$', +u'\U0001d703': '$\\mathsl{\\Theta}$', +u'\U0001d704': '$\\mathsl{\\Iota}$', +u'\U0001d705': '$\\mathsl{\\Kappa}$', +u'\U0001d706': '$\\mathsl{\\Lambda}$', +u'\U0001d707': '$M$', +u'\U0001d708': '$N$', +u'\U0001d709': '$\\mathsl{\\Xi}$', +u'\U0001d70a': '$O$', +u'\U0001d70b': '$\\mathsl{\\Pi}$', +u'\U0001d70c': '$\\mathsl{\\Rho}$', +u'\U0001d70d': '$\\mathsl{\\varsigma}$', +u'\U0001d70e': '$\\mathsl{\\Sigma}$', +u'\U0001d70f': '$\\mathsl{\\Tau}$', +u'\U0001d710': '$\\mathsl{\\Upsilon}$', +u'\U0001d711': '$\\mathsl{\\Phi}$', +u'\U0001d712': '$\\mathsl{\\Chi}$', +u'\U0001d713': '$\\mathsl{\\Psi}$', +u'\U0001d714': '$\\mathsl{\\Omega}$', +u'\U0001d715': '$\\partial$', +u'\U0001d716': '$\\in$', +u'\U0001d717': '{\\mathsl{\\vartheta}}', +u'\U0001d718': '{\\mathsl{\\varkappa}}', +u'\U0001d719': '{\\mathsl{\\phi}}', +u'\U0001d71a': '{\\mathsl{\\varrho}}', +u'\U0001d71b': '{\\mathsl{\\varpi}}', +u'\U0001d71c': '$\\mathbit{\\Alpha}$', +u'\U0001d71d': '$\\mathbit{\\Beta}$', +u'\U0001d71e': '$\\mathbit{\\Gamma}$', +u'\U0001d71f': '$\\mathbit{\\Delta}$', +u'\U0001d720': '$\\mathbit{\\Epsilon}$', +u'\U0001d721': '$\\mathbit{\\Zeta}$', +u'\U0001d722': '$\\mathbit{\\Eta}$', +u'\U0001d723': '$\\mathbit{\\Theta}$', +u'\U0001d724': '$\\mathbit{\\Iota}$', +u'\U0001d725': '$\\mathbit{\\Kappa}$', +u'\U0001d726': '$\\mathbit{\\Lambda}$', +u'\U0001d727': '$M$', +u'\U0001d728': '$N$', +u'\U0001d729': '$\\mathbit{\\Xi}$', +u'\U0001d72a': '$O$', +u'\U0001d72b': '$\\mathbit{\\Pi}$', +u'\U0001d72c': '$\\mathbit{\\Rho}$', +u'\U0001d72d': '{\\mathbit{O}}', +u'\U0001d72e': '$\\mathbit{\\Sigma}$', +u'\U0001d72f': '$\\mathbit{\\Tau}$', +u'\U0001d730': '$\\mathbit{\\Upsilon}$', +u'\U0001d731': '$\\mathbit{\\Phi}$', +u'\U0001d732': '$\\mathbit{\\Chi}$', +u'\U0001d733': '$\\mathbit{\\Psi}$', +u'\U0001d734': '$\\mathbit{\\Omega}$', +u'\U0001d735': '$\\mathbit{\\nabla}$', +u'\U0001d736': '$\\mathbit{\\Alpha}$', +u'\U0001d737': '$\\mathbit{\\Beta}$', +u'\U0001d738': '$\\mathbit{\\Gamma}$', +u'\U0001d739': '$\\mathbit{\\Delta}$', +u'\U0001d73a': '$\\mathbit{\\Epsilon}$', +u'\U0001d73b': '$\\mathbit{\\Zeta}$', +u'\U0001d73c': '$\\mathbit{\\Eta}$', +u'\U0001d73d': '$\\mathbit{\\Theta}$', +u'\U0001d73e': '$\\mathbit{\\Iota}$', +u'\U0001d73f': '$\\mathbit{\\Kappa}$', +u'\U0001d740': '$\\mathbit{\\Lambda}$', +u'\U0001d741': '$M$', +u'\U0001d742': '$N$', +u'\U0001d743': '$\\mathbit{\\Xi}$', +u'\U0001d744': '$O$', +u'\U0001d745': '$\\mathbit{\\Pi}$', +u'\U0001d746': '$\\mathbit{\\Rho}$', +u'\U0001d747': '$\\mathbit{\\varsigma}$', +u'\U0001d748': '$\\mathbit{\\Sigma}$', +u'\U0001d749': '$\\mathbit{\\Tau}$', +u'\U0001d74a': '$\\mathbit{\\Upsilon}$', +u'\U0001d74b': '$\\mathbit{\\Phi}$', +u'\U0001d74c': '$\\mathbit{\\Chi}$', +u'\U0001d74d': '$\\mathbit{\\Psi}$', +u'\U0001d74e': '$\\mathbit{\\Omega}$', +u'\U0001d74f': '$\\partial$', +u'\U0001d750': '$\\in$', +u'\U0001d751': '{\\mathbit{\\vartheta}}', +u'\U0001d752': '{\\mathbit{\\varkappa}}', +u'\U0001d753': '{\\mathbit{\\phi}}', +u'\U0001d754': '{\\mathbit{\\varrho}}', +u'\U0001d755': '{\\mathbit{\\varpi}}', +u'\U0001d756': '$\\mathsfbf{\\Alpha}$', +u'\U0001d757': '$\\mathsfbf{\\Beta}$', +u'\U0001d758': '$\\mathsfbf{\\Gamma}$', +u'\U0001d759': '$\\mathsfbf{\\Delta}$', +u'\U0001d75a': '$\\mathsfbf{\\Epsilon}$', +u'\U0001d75b': '$\\mathsfbf{\\Zeta}$', +u'\U0001d75c': '$\\mathsfbf{\\Eta}$', +u'\U0001d75d': '$\\mathsfbf{\\Theta}$', +u'\U0001d75e': '$\\mathsfbf{\\Iota}$', +u'\U0001d75f': '$\\mathsfbf{\\Kappa}$', +u'\U0001d760': '$\\mathsfbf{\\Lambda}$', +u'\U0001d761': '$M$', +u'\U0001d762': '$N$', +u'\U0001d763': '$\\mathsfbf{\\Xi}$', +u'\U0001d764': '$O$', +u'\U0001d765': '$\\mathsfbf{\\Pi}$', +u'\U0001d766': '$\\mathsfbf{\\Rho}$', +u'\U0001d767': '{\\mathsfbf{\\vartheta}}', +u'\U0001d768': '$\\mathsfbf{\\Sigma}$', +u'\U0001d769': '$\\mathsfbf{\\Tau}$', +u'\U0001d76a': '$\\mathsfbf{\\Upsilon}$', +u'\U0001d76b': '$\\mathsfbf{\\Phi}$', +u'\U0001d76c': '$\\mathsfbf{\\Chi}$', +u'\U0001d76d': '$\\mathsfbf{\\Psi}$', +u'\U0001d76e': '$\\mathsfbf{\\Omega}$', +u'\U0001d76f': '$\\mathsfbf{\\nabla}$', +u'\U0001d770': '$\\mathsfbf{\\Alpha}$', +u'\U0001d771': '$\\mathsfbf{\\Beta}$', +u'\U0001d772': '$\\mathsfbf{\\Gamma}$', +u'\U0001d773': '$\\mathsfbf{\\Delta}$', +u'\U0001d774': '$\\mathsfbf{\\Epsilon}$', +u'\U0001d775': '$\\mathsfbf{\\Zeta}$', +u'\U0001d776': '$\\mathsfbf{\\Eta}$', +u'\U0001d777': '$\\mathsfbf{\\Theta}$', +u'\U0001d778': '$\\mathsfbf{\\Iota}$', +u'\U0001d779': '$\\mathsfbf{\\Kappa}$', +u'\U0001d77a': '$\\mathsfbf{\\Lambda}$', +u'\U0001d77b': '$M$', +u'\U0001d77c': '$N$', +u'\U0001d77d': '$\\mathsfbf{\\Xi}$', +u'\U0001d77e': '$O$', +u'\U0001d77f': '$\\mathsfbf{\\Pi}$', +u'\U0001d780': '$\\mathsfbf{\\Rho}$', +u'\U0001d781': '$\\mathsfbf{\\varsigma}$', +u'\U0001d782': '$\\mathsfbf{\\Sigma}$', +u'\U0001d783': '$\\mathsfbf{\\Tau}$', +u'\U0001d784': '$\\mathsfbf{\\Upsilon}$', +u'\U0001d785': '$\\mathsfbf{\\Phi}$', +u'\U0001d786': '$\\mathsfbf{\\Chi}$', +u'\U0001d787': '$\\mathsfbf{\\Psi}$', +u'\U0001d788': '$\\mathsfbf{\\Omega}$', +u'\U0001d789': '$\\partial$', +u'\U0001d78a': '$\\in$', +u'\U0001d78b': '{\\mathsfbf{\\vartheta}}', +u'\U0001d78c': '{\\mathsfbf{\\varkappa}}', +u'\U0001d78d': '{\\mathsfbf{\\phi}}', +u'\U0001d78e': '{\\mathsfbf{\\varrho}}', +u'\U0001d78f': '{\\mathsfbf{\\varpi}}', +u'\U0001d790': '$\\mathsfbfsl{\\Alpha}$', +u'\U0001d791': '$\\mathsfbfsl{\\Beta}$', +u'\U0001d792': '$\\mathsfbfsl{\\Gamma}$', +u'\U0001d793': '$\\mathsfbfsl{\\Delta}$', +u'\U0001d794': '$\\mathsfbfsl{\\Epsilon}$', +u'\U0001d795': '$\\mathsfbfsl{\\Zeta}$', +u'\U0001d796': '$\\mathsfbfsl{\\Eta}$', +u'\U0001d797': '$\\mathsfbfsl{\\vartheta}$', +u'\U0001d798': '$\\mathsfbfsl{\\Iota}$', +u'\U0001d799': '$\\mathsfbfsl{\\Kappa}$', +u'\U0001d79a': '$\\mathsfbfsl{\\Lambda}$', +u'\U0001d79b': '$M$', +u'\U0001d79c': '$N$', +u'\U0001d79d': '$\\mathsfbfsl{\\Xi}$', +u'\U0001d79e': '$O$', +u'\U0001d79f': '$\\mathsfbfsl{\\Pi}$', +u'\U0001d7a0': '$\\mathsfbfsl{\\Rho}$', +u'\U0001d7a1': '{\\mathsfbfsl{\\vartheta}}', +u'\U0001d7a2': '$\\mathsfbfsl{\\Sigma}$', +u'\U0001d7a3': '$\\mathsfbfsl{\\Tau}$', +u'\U0001d7a4': '$\\mathsfbfsl{\\Upsilon}$', +u'\U0001d7a5': '$\\mathsfbfsl{\\Phi}$', +u'\U0001d7a6': '$\\mathsfbfsl{\\Chi}$', +u'\U0001d7a7': '$\\mathsfbfsl{\\Psi}$', +u'\U0001d7a8': '$\\mathsfbfsl{\\Omega}$', +u'\U0001d7a9': '$\\mathsfbfsl{\\nabla}$', +u'\U0001d7aa': '$\\mathsfbfsl{\\Alpha}$', +u'\U0001d7ab': '$\\mathsfbfsl{\\Beta}$', +u'\U0001d7ac': '$\\mathsfbfsl{\\Gamma}$', +u'\U0001d7ad': '$\\mathsfbfsl{\\Delta}$', +u'\U0001d7ae': '$\\mathsfbfsl{\\Epsilon}$', +u'\U0001d7af': '$\\mathsfbfsl{\\Zeta}$', +u'\U0001d7b0': '$\\mathsfbfsl{\\Eta}$', +u'\U0001d7b1': '$\\mathsfbfsl{\\vartheta}$', +u'\U0001d7b2': '$\\mathsfbfsl{\\Iota}$', +u'\U0001d7b3': '$\\mathsfbfsl{\\Kappa}$', +u'\U0001d7b4': '$\\mathsfbfsl{\\Lambda}$', +u'\U0001d7b5': '$M$', +u'\U0001d7b6': '$N$', +u'\U0001d7b7': '$\\mathsfbfsl{\\Xi}$', +u'\U0001d7b8': '$O$', +u'\U0001d7b9': '$\\mathsfbfsl{\\Pi}$', +u'\U0001d7ba': '$\\mathsfbfsl{\\Rho}$', +u'\U0001d7bb': '$\\mathsfbfsl{\\varsigma}$', +u'\U0001d7bc': '$\\mathsfbfsl{\\Sigma}$', +u'\U0001d7bd': '$\\mathsfbfsl{\\Tau}$', +u'\U0001d7be': '$\\mathsfbfsl{\\Upsilon}$', +u'\U0001d7bf': '$\\mathsfbfsl{\\Phi}$', +u'\U0001d7c0': '$\\mathsfbfsl{\\Chi}$', +u'\U0001d7c1': '$\\mathsfbfsl{\\Psi}$', +u'\U0001d7c2': '$\\mathsfbfsl{\\Omega}$', +u'\U0001d7c3': '$\\partial$', +u'\U0001d7c4': '$\\in$', +u'\U0001d7c5': '{\\mathsfbfsl{\\vartheta}}', +u'\U0001d7c6': '{\\mathsfbfsl{\\varkappa}}', +u'\U0001d7c7': '{\\mathsfbfsl{\\phi}}', +u'\U0001d7c8': '{\\mathsfbfsl{\\varrho}}', +u'\U0001d7c9': '{\\mathsfbfsl{\\varpi}}', +u'\U0001d7ce': '$\\mathbf{0}$', +u'\U0001d7cf': '$\\mathbf{1}$', +u'\U0001d7d0': '$\\mathbf{2}$', +u'\U0001d7d1': '$\\mathbf{3}$', +u'\U0001d7d2': '$\\mathbf{4}$', +u'\U0001d7d3': '$\\mathbf{5}$', +u'\U0001d7d4': '$\\mathbf{6}$', +u'\U0001d7d5': '$\\mathbf{7}$', +u'\U0001d7d6': '$\\mathbf{8}$', +u'\U0001d7d7': '$\\mathbf{9}$', +u'\U0001d7d8': '$\\mathbb{0}$', +u'\U0001d7d9': '$\\mathbb{1}$', +u'\U0001d7da': '$\\mathbb{2}$', +u'\U0001d7db': '$\\mathbb{3}$', +u'\U0001d7dc': '$\\mathbb{4}$', +u'\U0001d7dd': '$\\mathbb{5}$', +u'\U0001d7de': '$\\mathbb{6}$', +u'\U0001d7df': '$\\mathbb{7}$', +u'\U0001d7e0': '$\\mathbb{8}$', +u'\U0001d7e1': '$\\mathbb{9}$', +u'\U0001d7e2': '$\\mathsf{0}$', +u'\U0001d7e3': '$\\mathsf{1}$', +u'\U0001d7e4': '$\\mathsf{2}$', +u'\U0001d7e5': '$\\mathsf{3}$', +u'\U0001d7e6': '$\\mathsf{4}$', +u'\U0001d7e7': '$\\mathsf{5}$', +u'\U0001d7e8': '$\\mathsf{6}$', +u'\U0001d7e9': '$\\mathsf{7}$', +u'\U0001d7ea': '$\\mathsf{8}$', +u'\U0001d7eb': '$\\mathsf{9}$', +u'\U0001d7ec': '$\\mathsfbf{0}$', +u'\U0001d7ed': '$\\mathsfbf{1}$', +u'\U0001d7ee': '$\\mathsfbf{2}$', +u'\U0001d7ef': '$\\mathsfbf{3}$', +u'\U0001d7f0': '$\\mathsfbf{4}$', +u'\U0001d7f1': '$\\mathsfbf{5}$', +u'\U0001d7f2': '$\\mathsfbf{6}$', +u'\U0001d7f3': '$\\mathsfbf{7}$', +u'\U0001d7f4': '$\\mathsfbf{8}$', +u'\U0001d7f5': '$\\mathsfbf{9}$', +u'\U0001d7f6': '$\\mathtt{0}$', +u'\U0001d7f7': '$\\mathtt{1}$', +u'\U0001d7f8': '$\\mathtt{2}$', +u'\U0001d7f9': '$\\mathtt{3}$', +u'\U0001d7fa': '$\\mathtt{4}$', +u'\U0001d7fb': '$\\mathtt{5}$', +u'\U0001d7fc': '$\\mathtt{6}$', +u'\U0001d7fd': '$\\mathtt{7}$', +u'\U0001d7fe': '$\\mathtt{8}$', +u'\U0001d7ff': '$\\mathtt{9}$'} diff --git a/python/helpers/docutils/writers/null.py b/python/helpers/docutils/writers/null.py new file mode 100644 index 000000000000..b870788a4490 --- /dev/null +++ b/python/helpers/docutils/writers/null.py @@ -0,0 +1,21 @@ +# $Id: null.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A do-nothing Writer. +""" + +from docutils import writers + + +class Writer(writers.UnfilteredWriter): + + supported = ('null',) + """Formats this writer supports.""" + + config_section = 'null writer' + config_section_dependencies = ('writers',) + + def translate(self): + pass diff --git a/python/helpers/docutils/writers/odf_odt/__init__.py b/python/helpers/docutils/writers/odf_odt/__init__.py new file mode 100644 index 000000000000..f7a41bf7ec7a --- /dev/null +++ b/python/helpers/docutils/writers/odf_odt/__init__.py @@ -0,0 +1,3131 @@ +# $Id: __init__.py 6381 2010-07-26 19:26:14Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Open Document Format (ODF) Writer. + +""" + +VERSION = '1.0a' + +__docformat__ = 'reStructuredText' + + +import sys +import os +import os.path +import tempfile +import zipfile +from xml.dom import minidom +import time +import re +import StringIO +import inspect +import imp +import copy +import docutils +from docutils import frontend, nodes, utils, writers, languages +from docutils.parsers import rst +from docutils.readers import standalone +from docutils.transforms import references + + +WhichElementTree = '' +try: + # 1. Try to use lxml. + #from lxml import etree + #WhichElementTree = 'lxml' + raise ImportError('Ignoring lxml') +except ImportError, e: + try: + # 2. Try to use ElementTree from the Python standard library. + from xml.etree import ElementTree as etree + WhichElementTree = 'elementtree' + except ImportError, e: + try: + # 3. Try to use a version of ElementTree installed as a separate + # product. + from elementtree import ElementTree as etree + WhichElementTree = 'elementtree' + except ImportError, e: + s1 = 'Must install either a version of Python containing ' \ + 'ElementTree (Python version >=2.5) or install ElementTree.' + raise ImportError(s1) + +# +# Import pygments and odtwriter pygments formatters if possible. +try: + import pygments + import pygments.lexers + from pygmentsformatter import OdtPygmentsProgFormatter, \ + OdtPygmentsLaTeXFormatter +except ImportError, exp: + pygments = None + +# +# Is the PIL imaging library installed? +try: + import Image +except ImportError, exp: + Image = None + +## import warnings +## warnings.warn('importing IPShellEmbed', UserWarning) +## from IPython.Shell import IPShellEmbed +## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ', +## '-po', 'Out<\\#>: ', '-nosep'] +## ipshell = IPShellEmbed(args, +## banner = 'Entering IPython. Press Ctrl-D to exit.', +## exit_msg = 'Leaving Interpreter, back to program.') + + +# +# ElementTree does not support getparent method (lxml does). +# This wrapper class and the following support functions provide +# that support for the ability to get the parent of an element. +# +if WhichElementTree == 'elementtree': + class _ElementInterfaceWrapper(etree._ElementInterface): + def __init__(self, tag, attrib=None): + etree._ElementInterface.__init__(self, tag, attrib) + if attrib is None: + attrib = {} + self.parent = None + def setparent(self, parent): + self.parent = parent + def getparent(self): + return self.parent + + +# +# Constants and globals + +SPACES_PATTERN = re.compile(r'( +)') +TABS_PATTERN = re.compile(r'(\t+)') +FILL_PAT1 = re.compile(r'^ +') +FILL_PAT2 = re.compile(r' {2,}') + +TABLESTYLEPREFIX = 'rststyle-table-' +TABLENAMEDEFAULT = '%s0' % TABLESTYLEPREFIX +TABLEPROPERTYNAMES = ('border', 'border-top', 'border-left', + 'border-right', 'border-bottom', ) + +GENERATOR_DESC = 'Docutils.org/odf_odt' + +NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0' + +CONTENT_NAMESPACE_DICT = CNSD = { +# 'office:version': '1.0', + 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dom': 'http://www.w3.org/2001/xml-events', + 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'math': 'http://www.w3.org/1998/Math/MathML', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'office': NAME_SPACE_1, + 'ooo': 'http://openoffice.org/2004/office', + 'oooc': 'http://openoffice.org/2004/calc', + 'ooow': 'http://openoffice.org/2004/writer', + 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + + 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xforms': 'http://www.w3.org/2002/xforms', + 'xlink': 'http://www.w3.org/1999/xlink', + 'xsd': 'http://www.w3.org/2001/XMLSchema', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + } + +STYLES_NAMESPACE_DICT = SNSD = { +# 'office:version': '1.0', + 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dom': 'http://www.w3.org/2001/xml-events', + 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'math': 'http://www.w3.org/1998/Math/MathML', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'office': NAME_SPACE_1, + 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'ooo': 'http://openoffice.org/2004/office', + 'oooc': 'http://openoffice.org/2004/calc', + 'ooow': 'http://openoffice.org/2004/writer', + 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xlink': 'http://www.w3.org/1999/xlink', + } + +MANIFEST_NAMESPACE_DICT = MANNSD = { + 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', +} + +META_NAMESPACE_DICT = METNSD = { +# 'office:version': '1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'office': NAME_SPACE_1, + 'ooo': 'http://openoffice.org/2004/office', + 'xlink': 'http://www.w3.org/1999/xlink', +} + +# +# Attribute dictionaries for use with ElementTree (not lxml), which +# does not support use of nsmap parameter on Element() and SubElement(). + +CONTENT_NAMESPACE_ATTRIB = { + 'office:version': '1.0', + 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:dom': 'http://www.w3.org/2001/xml-events', + 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'xmlns:math': 'http://www.w3.org/1998/Math/MathML', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:oooc': 'http://openoffice.org/2004/calc', + 'xmlns:ooow': 'http://openoffice.org/2004/writer', + 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xmlns:xforms': 'http://www.w3.org/2002/xforms', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema', + 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + } + +STYLES_NAMESPACE_ATTRIB = { + 'office:version': '1.0', + 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:dom': 'http://www.w3.org/2001/xml-events', + 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'xmlns:math': 'http://www.w3.org/1998/Math/MathML', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:oooc': 'http://openoffice.org/2004/calc', + 'xmlns:ooow': 'http://openoffice.org/2004/writer', + 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + } + +MANIFEST_NAMESPACE_ATTRIB = { + 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', +} + +META_NAMESPACE_ATTRIB = { + 'office:version': '1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', +} + + +# +# Functions +# + +# +# ElementTree support functions. +# In order to be able to get the parent of elements, must use these +# instead of the functions with same name provided by ElementTree. +# +def Element(tag, attrib=None, nsmap=None, nsdict=CNSD): + if attrib is None: + attrib = {} + tag, attrib = fix_ns(tag, attrib, nsdict) + if WhichElementTree == 'lxml': + el = etree.Element(tag, attrib, nsmap=nsmap) + else: + el = _ElementInterfaceWrapper(tag, attrib) + return el + +def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD): + if attrib is None: + attrib = {} + tag, attrib = fix_ns(tag, attrib, nsdict) + if WhichElementTree == 'lxml': + el = etree.SubElement(parent, tag, attrib, nsmap=nsmap) + else: + el = _ElementInterfaceWrapper(tag, attrib) + parent.append(el) + el.setparent(parent) + return el + +def fix_ns(tag, attrib, nsdict): + nstag = add_ns(tag, nsdict) + nsattrib = {} + for key, val in attrib.iteritems(): + nskey = add_ns(key, nsdict) + nsattrib[nskey] = val + return nstag, nsattrib + +def add_ns(tag, nsdict=CNSD): + if WhichElementTree == 'lxml': + nstag, name = tag.split(':') + ns = nsdict.get(nstag) + if ns is None: + raise RuntimeError, 'Invalid namespace prefix: %s' % nstag + tag = '{%s}%s' % (ns, name,) + return tag + +def ToString(et): + outstream = StringIO.StringIO() + et.write(outstream) + s1 = outstream.getvalue() + outstream.close() + return s1 + + +def escape_cdata(text): + text = text.replace("&", "&") + text = text.replace("<", "<") + text = text.replace(">", ">") + ascii = '' + for char in text: + if ord(char) >= ord("\x7f"): + ascii += "&#x%X;" % ( ord(char), ) + else: + ascii += char + return ascii + + + +WORD_SPLIT_PAT1 = re.compile(r'\b(\w*)\b\W*') + +def split_words(line): + # We need whitespace at the end of the string for our regexpr. + line += ' ' + words = [] + pos1 = 0 + mo = WORD_SPLIT_PAT1.search(line, pos1) + while mo is not None: + word = mo.groups()[0] + words.append(word) + pos1 = mo.end() + mo = WORD_SPLIT_PAT1.search(line, pos1) + return words + + +# +# Classes +# + + +class TableStyle(object): + def __init__(self, border=None, backgroundcolor=None): + self.border = border + self.backgroundcolor = backgroundcolor + def get_border_(self): + return self.border_ + def set_border_(self, border): + self.border_ = border + border = property(get_border_, set_border_) + def get_backgroundcolor_(self): + return self.backgroundcolor_ + def set_backgroundcolor_(self, backgroundcolor): + self.backgroundcolor_ = backgroundcolor + backgroundcolor = property(get_backgroundcolor_, set_backgroundcolor_) + +BUILTIN_DEFAULT_TABLE_STYLE = TableStyle( + border = '0.0007in solid #000000') + +# +# Information about the indentation level for lists nested inside +# other contexts, e.g. dictionary lists. +class ListLevel(object): + def __init__(self, level, sibling_level=True, nested_level=True): + self.level = level + self.sibling_level = sibling_level + self.nested_level = nested_level + def set_sibling(self, sibling_level): self.sibling_level = sibling_level + def get_sibling(self): return self.sibling_level + def set_nested(self, nested_level): self.nested_level = nested_level + def get_nested(self): return self.nested_level + def set_level(self, level): self.level = level + def get_level(self): return self.level + + +class Writer(writers.Writer): + + MIME_TYPE = 'application/vnd.oasis.opendocument.text' + EXTENSION = '.odt' + + supported = ('odt', ) + """Formats this writer supports.""" + + default_stylesheet = 'styles' + EXTENSION + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = ( + 'ODF-Specific Options', + None, + ( + ('Specify a stylesheet. ' + 'Default: "%s"' % default_stylesheet_path, + ['--stylesheet'], + { + 'default': default_stylesheet_path, + 'dest': 'stylesheet' + }), + ('Specify a configuration/mapping file relative to the ' + 'current working ' + 'directory for additional ODF options. ' + 'In particular, this file may contain a section named ' + '"Formats" that maps default style names to ' + 'names to be used in the resulting output file allowing for ' + 'adhering to external standards. ' + 'For more info and the format of the configuration/mapping file, ' + 'see the odtwriter doc.', + ['--odf-config-file'], + {'metavar': ''}), + ('Obfuscate email addresses to confuse harvesters while still ' + 'keeping email links usable with standards-compliant browsers.', + ['--cloak-email-addresses'], + {'default': False, + 'action': 'store_true', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Do not obfuscate email addresses.', + ['--no-cloak-email-addresses'], + {'default': False, + 'action': 'store_false', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Specify the thickness of table borders in thousands of a cm. ' + 'Default is 35.', + ['--table-border-thickness'], + {'default': None, + 'validator': frontend.validate_nonnegative_int}), + ('Add syntax highlighting in literal code blocks.', + ['--add-syntax-highlighting'], + {'default': False, + 'action': 'store_true', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Do not add syntax highlighting in literal code blocks. (default)', + ['--no-syntax-highlighting'], + {'default': False, + 'action': 'store_false', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Create sections for headers. (default)', + ['--create-sections'], + {'default': True, + 'action': 'store_true', + 'dest': 'create_sections', + 'validator': frontend.validate_boolean}), + ('Do not create sections for headers.', + ['--no-sections'], + {'default': True, + 'action': 'store_false', + 'dest': 'create_sections', + 'validator': frontend.validate_boolean}), + ('Create links.', + ['--create-links'], + {'default': False, + 'action': 'store_true', + 'dest': 'create_links', + 'validator': frontend.validate_boolean}), + ('Do not create links. (default)', + ['--no-links'], + {'default': False, + 'action': 'store_false', + 'dest': 'create_links', + 'validator': frontend.validate_boolean}), + ('Generate endnotes at end of document, not footnotes ' + 'at bottom of page.', + ['--endnotes-end-doc'], + {'default': False, + 'action': 'store_true', + 'dest': 'endnotes_end_doc', + 'validator': frontend.validate_boolean}), + ('Generate footnotes at bottom of page, not endnotes ' + 'at end of document. (default)', + ['--no-endnotes-end-doc'], + {'default': False, + 'action': 'store_false', + 'dest': 'endnotes_end_doc', + 'validator': frontend.validate_boolean}), + ('Generate a bullet list table of contents, not ' + 'an ODF/oowriter table of contents.', + ['--generate-list-toc'], + {'default': True, + 'action': 'store_false', + 'dest': 'generate_oowriter_toc', + 'validator': frontend.validate_boolean}), + ('Generate an ODF/oowriter table of contents, not ' + 'a bullet list. (default)', + ['--generate-oowriter-toc'], + {'default': True, + 'action': 'store_true', + 'dest': 'generate_oowriter_toc', + 'validator': frontend.validate_boolean}), + ('Specify the contents of an custom header line. ' + 'See odf_odt writer documentation for details ' + 'about special field character sequences.', + ['--custom-odt-header'], + { 'default': '', + 'dest': 'custom_header', + }), + ('Specify the contents of an custom footer line. ' + 'See odf_odt writer documentation for details ' + 'about special field character sequences.', + ['--custom-odt-footer'], + { 'default': '', + 'dest': 'custom_footer', + }), + ) + ) + + settings_defaults = { + 'output_encoding_error_handler': 'xmlcharrefreplace', + } + + relative_path_settings = ( + 'stylesheet_path', + ) + + config_section = 'opendocument odf writer' + config_section_dependencies = ( + 'writers', + ) + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = ODFTranslator + + def translate(self): + self.settings = self.document.settings + self.visitor = self.translator_class(self.document) + self.visitor.retrieve_styles(self.EXTENSION) + self.document.walkabout(self.visitor) + self.visitor.add_doc_title() + self.assemble_my_parts() + self.output = self.parts['whole'] + + def assemble_my_parts(self): + """Assemble the `self.parts` dictionary. Extend in subclasses. + """ + writers.Writer.assemble_parts(self) + f = tempfile.NamedTemporaryFile() + zfile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) + content = self.visitor.content_astext() + self.write_zip_str(zfile, 'content.xml', content) + self.write_zip_str(zfile, 'mimetype', self.MIME_TYPE) + s1 = self.create_manifest() + self.write_zip_str(zfile, 'META-INF/manifest.xml', s1) + s1 = self.create_meta() + self.write_zip_str(zfile, 'meta.xml', s1) + s1 = self.get_stylesheet() + self.write_zip_str(zfile, 'styles.xml', s1) + s1 = self.get_settings() + self.write_zip_str(zfile, 'settings.xml', s1) + self.store_embedded_files(zfile) + zfile.close() + f.seek(0) + whole = f.read() + f.close() + self.parts['whole'] = whole + self.parts['encoding'] = self.document.settings.output_encoding + self.parts['version'] = docutils.__version__ + + def write_zip_str(self, zfile, name, bytes): + localtime = time.localtime(time.time()) + zinfo = zipfile.ZipInfo(name, localtime) + # Add some standard UNIX file access permissions (-rw-r--r--). + zinfo.external_attr = (0x81a4 & 0xFFFF) << 16L + zinfo.compress_type = zipfile.ZIP_DEFLATED + zfile.writestr(zinfo, bytes) + + def store_embedded_files(self, zfile): + embedded_files = self.visitor.get_embedded_file_list() + for source, destination in embedded_files: + if source is None: + continue + try: + # encode/decode + destination1 = destination.decode('latin-1').encode('utf-8') + zfile.write(source, destination1, zipfile.ZIP_STORED) + except OSError, e: + self.document.reporter.warning( + "Can't open file %s." % (source, )) + + def get_settings(self): + """ + modeled after get_stylesheet + """ + stylespath = self.settings.stylesheet + zfile = zipfile.ZipFile(stylespath, 'r') + s1 = zfile.read('settings.xml') + zfile.close() + return s1 + + def get_stylesheet(self): + """Get the stylesheet from the visitor. + Ask the visitor to setup the page. + """ + s1 = self.visitor.setup_page() + return s1 + + def assemble_parts(self): + pass + + def create_manifest(self): + if WhichElementTree == 'lxml': + root = Element('manifest:manifest', + nsmap=MANIFEST_NAMESPACE_DICT, + nsdict=MANIFEST_NAMESPACE_DICT, + ) + else: + root = Element('manifest:manifest', + attrib=MANIFEST_NAMESPACE_ATTRIB, + nsdict=MANIFEST_NAMESPACE_DICT, + ) + doc = etree.ElementTree(root) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': self.MIME_TYPE, + 'manifest:full-path': '/', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'content.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'styles.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'meta.xml', + }, nsdict=MANNSD) + s1 = ToString(doc) + doc = minidom.parseString(s1) + s1 = doc.toprettyxml(' ') + return s1 + + def create_meta(self): + if WhichElementTree == 'lxml': + root = Element('office:document-meta', + nsmap=META_NAMESPACE_DICT, + nsdict=META_NAMESPACE_DICT, + ) + else: + root = Element('office:document-meta', + attrib=META_NAMESPACE_ATTRIB, + nsdict=META_NAMESPACE_DICT, + ) + doc = etree.ElementTree(root) + root = SubElement(root, 'office:meta', nsdict=METNSD) + el1 = SubElement(root, 'meta:generator', nsdict=METNSD) + el1.text = 'Docutils/rst2odf.py/%s' % (VERSION, ) + s1 = os.environ.get('USER', '') + el1 = SubElement(root, 'meta:initial-creator', nsdict=METNSD) + el1.text = s1 + s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime()) + el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD) + el1.text = s2 + el1 = SubElement(root, 'dc:creator', nsdict=METNSD) + el1.text = s1 + el1 = SubElement(root, 'dc:date', nsdict=METNSD) + el1.text = s2 + el1 = SubElement(root, 'dc:language', nsdict=METNSD) + el1.text = 'en-US' + el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD) + el1.text = '1' + el1 = SubElement(root, 'meta:editing-duration', nsdict=METNSD) + el1.text = 'PT00M01S' + title = self.visitor.get_title() + el1 = SubElement(root, 'dc:title', nsdict=METNSD) + if title: + el1.text = title + else: + el1.text = '[no title]' + meta_dict = self.visitor.get_meta_dict() + keywordstr = meta_dict.get('keywords') + if keywordstr is not None: + keywords = split_words(keywordstr) + for keyword in keywords: + el1 = SubElement(root, 'meta:keyword', nsdict=METNSD) + el1.text = keyword + description = meta_dict.get('description') + if description is not None: + el1 = SubElement(root, 'dc:description', nsdict=METNSD) + el1.text = description + s1 = ToString(doc) + #doc = minidom.parseString(s1) + #s1 = doc.toprettyxml(' ') + return s1 + +# class ODFTranslator(nodes.SparseNodeVisitor): + +class ODFTranslator(nodes.GenericNodeVisitor): + + used_styles = ( + 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem', + 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist', + 'bulletitem', 'bulletlist', + 'caption', 'legend', + 'centeredtextbody', 'codeblock', + 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname', + 'codeblock-keyword', 'codeblock-name', 'codeblock-number', + 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem', + 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist', + 'epigraph-enumitem', 'epigraph-enumlist', 'footer', + 'footnote', 'citation', + 'header', 'highlights', 'highlights-bulletitem', + 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist', + 'horizontalline', 'inlineliteral', 'quotation', 'rubric', + 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist', + 'title', + 'subtitle', + 'heading1', + 'heading2', + 'heading3', + 'heading4', + 'heading5', + 'heading6', + 'heading7', + 'admon-attention-hdr', + 'admon-attention-body', + 'admon-caution-hdr', + 'admon-caution-body', + 'admon-danger-hdr', + 'admon-danger-body', + 'admon-error-hdr', + 'admon-error-body', + 'admon-generic-hdr', + 'admon-generic-body', + 'admon-hint-hdr', + 'admon-hint-body', + 'admon-important-hdr', + 'admon-important-body', + 'admon-note-hdr', + 'admon-note-body', + 'admon-tip-hdr', + 'admon-tip-body', + 'admon-warning-hdr', + 'admon-warning-body', + 'tableoption', + 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c', + 'Table%d.%c%d', + 'lineblock1', + 'lineblock2', + 'lineblock3', + 'lineblock4', + 'lineblock5', + 'lineblock6', + 'image', 'figureframe', + ) + + def __init__(self, document): + #nodes.SparseNodeVisitor.__init__(self, document) + nodes.GenericNodeVisitor.__init__(self, document) + self.settings = document.settings + self.format_map = { } + if self.settings.odf_config_file: + from ConfigParser import ConfigParser + + parser = ConfigParser() + parser.read(self.settings.odf_config_file) + for rststyle, format in parser.items("Formats"): + if rststyle not in self.used_styles: + self.document.reporter.warning( + 'Style "%s" is not a style used by odtwriter.' % ( + rststyle, )) + self.format_map[rststyle] = format + self.section_level = 0 + self.section_count = 0 + # Create ElementTree content and styles documents. + if WhichElementTree == 'lxml': + root = Element( + 'office:document-content', + nsmap=CONTENT_NAMESPACE_DICT, + ) + else: + root = Element( + 'office:document-content', + attrib=CONTENT_NAMESPACE_ATTRIB, + ) + self.content_tree = etree.ElementTree(element=root) + self.current_element = root + SubElement(root, 'office:scripts') + SubElement(root, 'office:font-face-decls') + el = SubElement(root, 'office:automatic-styles') + self.automatic_styles = el + el = SubElement(root, 'office:body') + el = self.generate_content_element(el) + self.current_element = el + self.body_text_element = el + self.paragraph_style_stack = [self.rststyle('textbody'), ] + self.list_style_stack = [] + self.table_count = 0 + self.column_count = ord('A') - 1 + self.trace_level = -1 + self.optiontablestyles_generated = False + self.field_name = None + self.field_element = None + self.title = None + self.image_count = 0 + self.image_style_count = 0 + self.image_dict = {} + self.embedded_file_list = [] + self.syntaxhighlighting = 1 + self.syntaxhighlight_lexer = 'python' + self.header_content = [] + self.footer_content = [] + self.in_header = False + self.in_footer = False + self.blockstyle = '' + self.in_table_of_contents = False + self.table_of_content_index_body = None + self.list_level = 0 + self.footnote_ref_dict = {} + self.footnote_list = [] + self.footnote_chars_idx = 0 + self.footnote_level = 0 + self.pending_ids = [ ] + self.in_paragraph = False + self.found_doc_title = False + self.bumped_list_level_stack = [] + self.meta_dict = {} + self.line_block_level = 0 + self.line_indent_level = 0 + self.citation_id = None + self.style_index = 0 # use to form unique style names + self.str_stylesheet = '' + self.str_stylesheetcontent = '' + self.dom_stylesheet = None + self.table_styles = None + + def get_str_stylesheet(self): + return self.str_stylesheet + + def retrieve_styles(self, extension): + """Retrieve the stylesheet from either a .xml file or from + a .odt (zip) file. Return the content as a string. + """ + s2 = None + stylespath = self.settings.stylesheet + ext = os.path.splitext(stylespath)[1] + if ext == '.xml': + stylesfile = open(stylespath, 'r') + s1 = stylesfile.read() + stylesfile.close() + elif ext == extension: + zfile = zipfile.ZipFile(stylespath, 'r') + s1 = zfile.read('styles.xml') + s2 = zfile.read('content.xml') + zfile.close() + else: + raise RuntimeError, 'stylesheet path (%s) must be %s or .xml file' %(stylespath, extension) + self.str_stylesheet = s1 + self.str_stylesheetcontent = s2 + self.dom_stylesheet = etree.fromstring(self.str_stylesheet) + self.dom_stylesheetcontent = etree.fromstring(self.str_stylesheetcontent) + self.table_styles = self.extract_table_styles(s2) + + def extract_table_styles(self, styles_str): + root = etree.fromstring(styles_str) + table_styles = {} + auto_styles = root.find( + '{%s}automatic-styles' % (CNSD['office'], )) + for stylenode in auto_styles: + name = stylenode.get('{%s}name' % (CNSD['style'], )) + tablename = name.split('.')[0] + family = stylenode.get('{%s}family' % (CNSD['style'], )) + if name.startswith(TABLESTYLEPREFIX): + tablestyle = table_styles.get(tablename) + if tablestyle is None: + tablestyle = TableStyle() + table_styles[tablename] = tablestyle + if family == 'table': + properties = stylenode.find( + '{%s}table-properties' % (CNSD['style'], )) + property = properties.get('{%s}%s' % (CNSD['fo'], + 'background-color', )) + if property is not None and property != 'none': + tablestyle.backgroundcolor = property + elif family == 'table-cell': + properties = stylenode.find( + '{%s}table-cell-properties' % (CNSD['style'], )) + if properties is not None: + border = self.get_property(properties) + if border is not None: + tablestyle.border = border + return table_styles + + def get_property(self, stylenode): + border = None + for propertyname in TABLEPROPERTYNAMES: + border = stylenode.get('{%s}%s' % (CNSD['fo'], propertyname, )) + if border is not None and border != 'none': + return border + return border + + def add_doc_title(self): + text = self.settings.title + if text: + self.title = text + if not self.found_doc_title: + el = Element('text:p', attrib = { + 'text:style-name': self.rststyle('title'), + }) + el.text = text + self.body_text_element.insert(0, el) + + def rststyle(self, name, parameters=( )): + """ + Returns the style name to use for the given style. + + If `parameters` is given `name` must contain a matching number of ``%`` and + is used as a format expression with `parameters` as the value. + """ + name1 = name % parameters + stylename = self.format_map.get(name1, 'rststyle-%s' % name1) + return stylename + + def generate_content_element(self, root): + return SubElement(root, 'office:text') + + def setup_page(self): + self.setup_paper(self.dom_stylesheet) + if (len(self.header_content) > 0 or len(self.footer_content) > 0 or + self.settings.custom_header or self.settings.custom_footer): + self.add_header_footer(self.dom_stylesheet) + new_content = etree.tostring(self.dom_stylesheet) + return new_content + + def setup_paper(self, root_el): + try: + fin = os.popen("paperconf -s 2> /dev/null") + w, h = map(float, fin.read().split()) + fin.close() + except: + w, h = 612, 792 # default to Letter + def walk(el): + if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \ + not el.attrib.has_key("{%s}page-width" % SNSD["fo"]): + el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w + el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h + el.attrib["{%s}margin-left" % SNSD["fo"]] = \ + el.attrib["{%s}margin-right" % SNSD["fo"]] = \ + "%.3fpt" % (.1 * w) + el.attrib["{%s}margin-top" % SNSD["fo"]] = \ + el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \ + "%.3fpt" % (.1 * h) + else: + for subel in el.getchildren(): walk(subel) + walk(root_el) + + def add_header_footer(self, root_el): + automatic_styles = root_el.find( + '{%s}automatic-styles' % SNSD['office']) + path = '{%s}master-styles' % (NAME_SPACE_1, ) + master_el = root_el.find(path) + if master_el is None: + return + path = '{%s}master-page' % (SNSD['style'], ) + master_el = master_el.find(path) + if master_el is None: + return + el1 = master_el + if self.header_content or self.settings.custom_header: + if WhichElementTree == 'lxml': + el2 = SubElement(el1, 'style:header', nsdict=SNSD) + else: + el2 = SubElement(el1, 'style:header', + attrib=STYLES_NAMESPACE_ATTRIB, + nsdict=STYLES_NAMESPACE_DICT, + ) + for el in self.header_content: + attrkey = add_ns('text:style-name', nsdict=SNSD) + el.attrib[attrkey] = self.rststyle('header') + el2.append(el) + if self.settings.custom_header: + elcustom = self.create_custom_headfoot(el2, + self.settings.custom_header, 'header', automatic_styles) + if self.footer_content or self.settings.custom_footer: + if WhichElementTree == 'lxml': + el2 = SubElement(el1, 'style:footer', nsdict=SNSD) + else: + el2 = SubElement(el1, 'style:footer', + attrib=STYLES_NAMESPACE_ATTRIB, + nsdict=STYLES_NAMESPACE_DICT, + ) + for el in self.footer_content: + attrkey = add_ns('text:style-name', nsdict=SNSD) + el.attrib[attrkey] = self.rststyle('footer') + el2.append(el) + if self.settings.custom_footer: + elcustom = self.create_custom_headfoot(el2, + self.settings.custom_footer, 'footer', automatic_styles) + + code_none, code_field, code_text = range(3) + field_pat = re.compile(r'%(..?)%') + + def create_custom_headfoot(self, parent, text, style_name, automatic_styles): + current_element = None + field_iter = self.split_field_specifiers_iter(text) + for item in field_iter: + if item[0] == ODFTranslator.code_field: + if item[1] not in ('p', 'P', + 't1', 't2', 't3', 't4', + 'd1', 'd2', 'd3', 'd4', 'd5', + 's', 't', 'a'): + msg = 'bad field spec: %%%s%%' % (item[1], ) + raise RuntimeError, msg + if current_element is None: + parent = SubElement(parent, 'text:p', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + el1 = self.make_field_element(parent, + item[1], style_name, automatic_styles) + if el1 is None: + msg = 'bad field spec: %%%s%%' % (item[1], ) + raise RuntimeError, msg + else: + current_element = el1 + else: + if current_element is None: + parent = SubElement(parent, 'text:p', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + parent.text = item[1] + else: + current_element.tail = item[1] + + def make_field_element(self, parent, text, style_name, automatic_styles): + if text == 'p': + el1 = SubElement(parent, 'text:page-number', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:select-page': 'current', + }) + elif text == 'P': + el1 = SubElement(parent, 'text:page-count', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + elif text == 't1': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + elif text == 't2': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:seconds', attrib={ + 'number:style': 'long', + }) + elif text == 't3': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:am-pm') + elif text == 't4': + self.style_index += 1 + el1 = SubElement(parent, 'text:time', attrib={ + 'text:style-name': self.rststyle(style_name), + 'text:fixed': 'true', + 'style:data-style-name': 'rst-time-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:time-style', attrib={ + 'style:name': 'rst-time-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:hours', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:minutes', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ':' + el3 = SubElement(el2, 'number:seconds', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:am-pm') + elif text == 'd1': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:day', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:year') + elif text == 'd2': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:day', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '/' + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + elif text == 'd3': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:textual': 'true', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:day', attrib={ + }) + el3 = SubElement(el2, 'number:text') + el3.text = ', ' + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + elif text == 'd4': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'number:automatic-order': 'true', + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:month', attrib={ + 'number:textual': 'true', + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = ' ' + el3 = SubElement(el2, 'number:day', attrib={ + }) + el3 = SubElement(el2, 'number:text') + el3.text = ', ' + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + elif text == 'd5': + self.style_index += 1 + el1 = SubElement(parent, 'text:date', attrib={ + 'text:style-name': self.rststyle(style_name), + 'style:data-style-name': 'rst-date-style-%d' % self.style_index, + }) + el2 = SubElement(automatic_styles, 'number:date-style', attrib={ + 'style:name': 'rst-date-style-%d' % self.style_index, + 'xmlns:number': SNSD['number'], + 'xmlns:style': SNSD['style'], + }) + el3 = SubElement(el2, 'number:year', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '-' + el3 = SubElement(el2, 'number:month', attrib={ + 'number:style': 'long', + }) + el3 = SubElement(el2, 'number:text') + el3.text = '-' + el3 = SubElement(el2, 'number:day', attrib={ + 'number:style': 'long', + }) + elif text == 's': + el1 = SubElement(parent, 'text:subject', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + elif text == 't': + el1 = SubElement(parent, 'text:title', attrib={ + 'text:style-name': self.rststyle(style_name), + }) + elif text == 'a': + el1 = SubElement(parent, 'text:author-name', attrib={ + 'text:fixed': 'false', + }) + else: + el1 = None + return el1 + + def split_field_specifiers_iter(self, text): + pos1 = 0 + pos_end = len(text) + while True: + mo = ODFTranslator.field_pat.search(text, pos1) + if mo: + pos2 = mo.start() + if pos2 > pos1: + yield (ODFTranslator.code_text, text[pos1:pos2]) + yield (ODFTranslator.code_field, mo.group(1)) + pos1 = mo.end() + else: + break + trailing = text[pos1:] + if trailing: + yield (ODFTranslator.code_text, trailing) + + + def astext(self): + root = self.content_tree.getroot() + et = etree.ElementTree(root) + s1 = ToString(et) + return s1 + + def content_astext(self): + return self.astext() + + def set_title(self, title): self.title = title + def get_title(self): return self.title + def set_embedded_file_list(self, embedded_file_list): + self.embedded_file_list = embedded_file_list + def get_embedded_file_list(self): return self.embedded_file_list + def get_meta_dict(self): return self.meta_dict + + def process_footnotes(self): + for node, el1 in self.footnote_list: + backrefs = node.attributes.get('backrefs', []) + first = True + for ref in backrefs: + el2 = self.footnote_ref_dict.get(ref) + if el2 is not None: + if first: + first = False + el3 = copy.deepcopy(el1) + el2.append(el3) + else: + children = el2.getchildren() + if len(children) > 0: # and 'id' in el2.attrib: + child = children[0] + ref1 = child.text + attribkey = add_ns('text:id', nsdict=SNSD) + id1 = el2.get(attribkey, 'footnote-error') + if id1 is None: + id1 = '' + tag = add_ns('text:note-ref', nsdict=SNSD) + el2.tag = tag + if self.settings.endnotes_end_doc: + note_class = 'endnote' + else: + note_class = 'footnote' + el2.attrib.clear() + attribkey = add_ns('text:note-class', nsdict=SNSD) + el2.attrib[attribkey] = note_class + attribkey = add_ns('text:ref-name', nsdict=SNSD) + el2.attrib[attribkey] = id1 + attribkey = add_ns('text:reference-format', nsdict=SNSD) + el2.attrib[attribkey] = 'page' + el2.text = ref1 + + # + # Utility methods + + def append_child(self, tag, attrib=None, parent=None): + if parent is None: + parent = self.current_element + if attrib is None: + el = SubElement(parent, tag) + else: + el = SubElement(parent, tag, attrib) + return el + + def append_p(self, style, text=None): + result = self.append_child('text:p', attrib={ + 'text:style-name': self.rststyle(style)}) + self.append_pending_ids(result) + if text is not None: + result.text = text + return result + + def append_pending_ids(self, el): + if self.settings.create_links: + for id in self.pending_ids: + SubElement(el, 'text:reference-mark', attrib={ + 'text:name': id}) + self.pending_ids = [ ] + + def set_current_element(self, el): + self.current_element = el + + def set_to_parent(self): + self.current_element = self.current_element.getparent() + + def generate_labeled_block(self, node, label): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + el = self.append_p('blockindent') + return el + + def generate_labeled_line(self, node, label): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + el1.tail = node.astext() + return el + + def encode(self, text): + text = text.replace(u'\u00a0', " ") + return text + + # + # Visitor functions + # + # In alphabetic order, more or less. + # See docutils.docutils.nodes.node_class_names. + # + + def dispatch_visit(self, node): + """Override to catch basic attributes which many nodes have.""" + self.handle_basic_atts(node) + nodes.GenericNodeVisitor.dispatch_visit(self, node) + + def handle_basic_atts(self, node): + if isinstance(node, nodes.Element) and node['ids']: + self.pending_ids += node['ids'] + + def default_visit(self, node): + self.document.reporter.warning('missing visit_%s' % (node.tagname, )) + + def default_departure(self, node): + self.document.reporter.warning('missing depart_%s' % (node.tagname, )) + + def visit_Text(self, node): + # Skip nodes whose text has been processed in parent nodes. + if isinstance(node.parent, docutils.nodes.literal_block): + return + text = node.astext() + # Are we in mixed content? If so, add the text to the + # etree tail of the previous sibling element. + if len(self.current_element.getchildren()) > 0: + if self.current_element.getchildren()[-1].tail: + self.current_element.getchildren()[-1].tail += text + else: + self.current_element.getchildren()[-1].tail = text + else: + if self.current_element.text: + self.current_element.text += text + else: + self.current_element.text = text + + def depart_Text(self, node): + pass + + # + # Pre-defined fields + # + + def visit_address(self, node): + el = self.generate_labeled_block(node, 'Address: ') + self.set_current_element(el) + + def depart_address(self, node): + self.set_to_parent() + + def visit_author(self, node): + if isinstance(node.parent, nodes.authors): + el = self.append_p('blockindent') + else: + el = self.generate_labeled_block(node, 'Author: ') + self.set_current_element(el) + + def depart_author(self, node): + self.set_to_parent() + + def visit_authors(self, node): + label = 'Authors:' + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + + def depart_authors(self, node): + pass + + def visit_contact(self, node): + el = self.generate_labeled_block(node, 'Contact: ') + self.set_current_element(el) + + def depart_contact(self, node): + self.set_to_parent() + + def visit_copyright(self, node): + el = self.generate_labeled_block(node, 'Copyright: ') + self.set_current_element(el) + + def depart_copyright(self, node): + self.set_to_parent() + + def visit_date(self, node): + self.generate_labeled_line(node, 'Date: ') + + def depart_date(self, node): + pass + + def visit_organization(self, node): + el = self.generate_labeled_block(node, 'Organization: ') + self.set_current_element(el) + + def depart_organization(self, node): + self.set_to_parent() + + def visit_status(self, node): + el = self.generate_labeled_block(node, 'Status: ') + self.set_current_element(el) + + def depart_status(self, node): + self.set_to_parent() + + def visit_revision(self, node): + self.generate_labeled_line(node, 'Revision: ') + + def depart_revision(self, node): + pass + + def visit_version(self, node): + el = self.generate_labeled_line(node, 'Version: ') + #self.set_current_element(el) + + def depart_version(self, node): + #self.set_to_parent() + pass + + def visit_attribution(self, node): + el = self.append_p('attribution', node.astext()) + + def depart_attribution(self, node): + pass + + def visit_block_quote(self, node): + if 'epigraph' in node.attributes['classes']: + self.paragraph_style_stack.append(self.rststyle('epigraph')) + self.blockstyle = self.rststyle('epigraph') + elif 'highlights' in node.attributes['classes']: + self.paragraph_style_stack.append(self.rststyle('highlights')) + self.blockstyle = self.rststyle('highlights') + else: + self.paragraph_style_stack.append(self.rststyle('blockquote')) + self.blockstyle = self.rststyle('blockquote') + self.line_indent_level += 1 + + def depart_block_quote(self, node): + self.paragraph_style_stack.pop() + self.blockstyle = '' + self.line_indent_level -= 1 + + def visit_bullet_list(self, node): + self.list_level +=1 + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + pass + else: + if node.has_key('classes') and \ + 'auto-toc' in node.attributes['classes']: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('tocenumlist'), + }) + self.list_style_stack.append(self.rststyle('enumitem')) + else: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('tocbulletlist'), + }) + self.list_style_stack.append(self.rststyle('bulletitem')) + self.set_current_element(el) + else: + if self.blockstyle == self.rststyle('blockquote'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('blockquote-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('blockquote-bulletitem')) + elif self.blockstyle == self.rststyle('highlights'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('highlights-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('highlights-bulletitem')) + elif self.blockstyle == self.rststyle('epigraph'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('epigraph-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('epigraph-bulletitem')) + else: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('bulletlist'), + }) + self.list_style_stack.append(self.rststyle('bulletitem')) + self.set_current_element(el) + + def depart_bullet_list(self, node): + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + pass + else: + self.set_to_parent() + self.list_style_stack.pop() + else: + self.set_to_parent() + self.list_style_stack.pop() + self.list_level -=1 + + def visit_caption(self, node): + raise nodes.SkipChildren() + pass + + def depart_caption(self, node): + pass + + def visit_comment(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'office:annotation', attrib={}) + el2 = SubElement(el1, 'text:p', attrib={}) + el2.text = node.astext() + + def depart_comment(self, node): + pass + + def visit_compound(self, node): + # The compound directive currently receives no special treatment. + pass + + def depart_compound(self, node): + pass + + def visit_container(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + self.paragraph_style_stack.append(self.rststyle(styles[0])) + + def depart_container(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + self.paragraph_style_stack.pop() + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + self.paragraph_style_stack.append(self.rststyle('blockindent')) + self.bumped_list_level_stack.append(ListLevel(1)) + + def depart_definition(self, node): + self.paragraph_style_stack.pop() + self.bumped_list_level_stack.pop() + + def visit_definition_list(self, node): + pass + + def depart_definition_list(self, node): + pass + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_term(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + #el1.text = node.astext() + self.set_current_element(el1) + + def depart_term(self, node): + self.set_to_parent() + self.set_to_parent() + + def visit_classifier(self, node): + els = self.current_element.getchildren() + if len(els) > 0: + el = els[-1] + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('emphasis') + }) + el1.text = ' (%s)' % (node.astext(), ) + + def depart_classifier(self, node): + pass + + def visit_document(self, node): + pass + + def depart_document(self, node): + self.process_footnotes() + + def visit_docinfo(self, node): + self.section_level += 1 + self.section_count += 1 + if self.settings.create_sections: + el = self.append_child('text:section', attrib={ + 'text:name': 'Section%d' % self.section_count, + 'text:style-name': 'Sect%d' % self.section_level, + }) + self.set_current_element(el) + + def depart_docinfo(self, node): + self.section_level -= 1 + if self.settings.create_sections: + self.set_to_parent() + + def visit_emphasis(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('emphasis')}) + self.set_current_element(el) + + def depart_emphasis(self, node): + self.set_to_parent() + + def visit_enumerated_list(self, node): + el1 = self.current_element + if self.blockstyle == self.rststyle('blockquote'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('blockquote-enumlist'), + }) + self.list_style_stack.append(self.rststyle('blockquote-enumitem')) + elif self.blockstyle == self.rststyle('highlights'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('highlights-enumlist'), + }) + self.list_style_stack.append(self.rststyle('highlights-enumitem')) + elif self.blockstyle == self.rststyle('epigraph'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('epigraph-enumlist'), + }) + self.list_style_stack.append(self.rststyle('epigraph-enumitem')) + else: + liststylename = 'enumlist-%s' % (node.get('enumtype', 'arabic'), ) + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle(liststylename), + }) + self.list_style_stack.append(self.rststyle('enumitem')) + self.set_current_element(el2) + + def depart_enumerated_list(self, node): + self.set_to_parent() + self.list_style_stack.pop() + + def visit_list_item(self, node): + # If we are in a "bumped" list level, then wrap this + # list in an outer lists in order to increase the + # indentation level. + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + self.paragraph_style_stack.append( + self.rststyle('contents-%d' % (self.list_level, ))) + else: + el1 = self.append_child('text:list-item') + self.set_current_element(el1) + else: + el1 = self.append_child('text:list-item') + el3 = el1 + if len(self.bumped_list_level_stack) > 0: + level_obj = self.bumped_list_level_stack[-1] + if level_obj.get_sibling(): + level_obj.set_nested(False) + for level_obj1 in self.bumped_list_level_stack: + for idx in range(level_obj1.get_level()): + el2 = self.append_child('text:list', parent=el3) + el3 = self.append_child( + 'text:list-item', parent=el2) + self.paragraph_style_stack.append(self.list_style_stack[-1]) + self.set_current_element(el3) + + def depart_list_item(self, node): + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + self.paragraph_style_stack.pop() + else: + self.set_to_parent() + else: + if len(self.bumped_list_level_stack) > 0: + level_obj = self.bumped_list_level_stack[-1] + if level_obj.get_sibling(): + level_obj.set_nested(True) + for level_obj1 in self.bumped_list_level_stack: + for idx in range(level_obj1.get_level()): + self.set_to_parent() + self.set_to_parent() + self.paragraph_style_stack.pop() + self.set_to_parent() + + def visit_header(self, node): + self.in_header = True + + def depart_header(self, node): + self.in_header = False + + def visit_footer(self, node): + self.in_footer = True + + def depart_footer(self, node): + self.in_footer = False + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_list(self, node): + pass + + def depart_field_list(self, node): + pass + + def visit_field_name(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = node.astext() + + def depart_field_name(self, node): + pass + + def visit_field_body(self, node): + self.paragraph_style_stack.append(self.rststyle('blockindent')) + + def depart_field_body(self, node): + self.paragraph_style_stack.pop() + + def visit_figure(self, node): + pass + + def depart_figure(self, node): + pass + + def visit_footnote(self, node): + self.footnote_level += 1 + self.save_footnote_current = self.current_element + el1 = Element('text:note-body') + self.current_element = el1 + self.footnote_list.append((node, el1)) + if isinstance(node, docutils.nodes.citation): + self.paragraph_style_stack.append(self.rststyle('citation')) + else: + self.paragraph_style_stack.append(self.rststyle('footnote')) + + def depart_footnote(self, node): + self.paragraph_style_stack.pop() + self.current_element = self.save_footnote_current + self.footnote_level -= 1 + + footnote_chars = [ + '*', '**', '***', + '++', '+++', + '##', '###', + '@@', '@@@', + ] + + def visit_footnote_reference(self, node): + if self.footnote_level <= 0: + id = node.attributes['ids'][0] + refid = node.attributes.get('refid') + if refid is None: + refid = '' + if self.settings.endnotes_end_doc: + note_class = 'endnote' + else: + note_class = 'footnote' + el1 = self.append_child('text:note', attrib={ + 'text:id': '%s' % (refid, ), + 'text:note-class': note_class, + }) + note_auto = str(node.attributes.get('auto', 1)) + if isinstance(node, docutils.nodes.citation_reference): + citation = '[%s]' % node.astext() + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': citation, + }) + el2.text = citation + elif note_auto == '1': + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': node.astext(), + }) + el2.text = node.astext() + elif note_auto == '*': + if self.footnote_chars_idx >= len( + ODFTranslator.footnote_chars): + self.footnote_chars_idx = 0 + footnote_char = ODFTranslator.footnote_chars[ + self.footnote_chars_idx] + self.footnote_chars_idx += 1 + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': footnote_char, + }) + el2.text = footnote_char + self.footnote_ref_dict[id] = el1 + raise nodes.SkipChildren() + + def depart_footnote_reference(self, node): + pass + + def visit_citation(self, node): + for id in node.attributes['ids']: + self.citation_id = id + break + self.paragraph_style_stack.append(self.rststyle('blockindent')) + self.bumped_list_level_stack.append(ListLevel(1)) + + def depart_citation(self, node): + self.citation_id = None + self.paragraph_style_stack.pop() + self.bumped_list_level_stack.pop() + + def visit_citation_reference(self, node): + if self.settings.create_links: + id = node.attributes['refid'] + el = self.append_child('text:reference-ref', attrib={ + 'text:ref-name': '%s' % (id, ), + 'text:reference-format': 'text', + }) + el.text = '[' + self.set_current_element(el) + elif self.current_element.text is None: + self.current_element.text = '[' + else: + self.current_element.text += '[' + + def depart_citation_reference(self, node): + self.current_element.text += ']' + if self.settings.create_links: + self.set_to_parent() + + def visit_label(self, node): + if isinstance(node.parent, docutils.nodes.footnote): + raise nodes.SkipChildren() + elif self.citation_id is not None: + el = self.append_p('textbody') + self.set_current_element(el) + el.text = '[' + if self.settings.create_links: + el1 = self.append_child('text:reference-mark-start', attrib={ + 'text:name': '%s' % (self.citation_id, ), + }) + + def depart_label(self, node): + if isinstance(node.parent, docutils.nodes.footnote): + pass + elif self.citation_id is not None: + self.current_element.text += ']' + if self.settings.create_links: + el = self.append_child('text:reference-mark-end', attrib={ + 'text:name': '%s' % (self.citation_id, ), + }) + self.set_to_parent() + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def check_file_exists(self, path): + if os.path.exists(path): + return 1 + else: + return 0 + + def visit_image(self, node): + # Capture the image file. + if 'uri' in node.attributes: + source = node.attributes['uri'] + if not self.check_file_exists(source): + self.document.reporter.warning( + 'Cannot find image file %s.' % (source, )) + return + else: + return + if source in self.image_dict: + filename, destination = self.image_dict[source] + else: + self.image_count += 1 + filename = os.path.split(source)[1] + destination = 'Pictures/1%08x%s' % (self.image_count, filename, ) + spec = (os.path.abspath(source), destination,) + + self.embedded_file_list.append(spec) + self.image_dict[source] = (source, destination,) + # Is this a figure (containing an image) or just a plain image? + if self.in_paragraph: + el1 = self.current_element + else: + el1 = SubElement(self.current_element, 'text:p', + attrib={'text:style-name': self.rststyle('textbody')}) + el2 = el1 + if isinstance(node.parent, docutils.nodes.figure): + el3, el4, el5, caption = self.generate_figure(node, source, + destination, el2) + attrib = {} + el6, width = self.generate_image(node, source, destination, + el5, attrib) + if caption is not None: + el6.tail = caption + else: #if isinstance(node.parent, docutils.nodes.image): + el3 = self.generate_image(node, source, destination, el2) + + def depart_image(self, node): + pass + + def get_image_width_height(self, node, attr): + size = None + if attr in node.attributes: + size = node.attributes[attr] + unit = size[-2:] + if unit.isalpha(): + size = size[:-2] + else: + unit = 'px' + try: + size = float(size) + except ValueError, e: + self.document.reporter.warning( + 'Invalid %s for image: "%s"' % ( + attr, node.attributes[attr])) + size = [size, unit] + return size + + def get_image_scale(self, node): + if 'scale' in node.attributes: + try: + scale = int(node.attributes['scale']) + if scale < 1: # or scale > 100: + self.document.reporter.warning( + 'scale out of range (%s), using 1.' % (scale, )) + scale = 1 + scale = scale * 0.01 + except ValueError, e: + self.document.reporter.warning( + 'Invalid scale for image: "%s"' % ( + node.attributes['scale'], )) + else: + scale = 1.0 + return scale + + def get_image_scaled_width_height(self, node, source): + scale = self.get_image_scale(node) + width = self.get_image_width_height(node, 'width') + height = self.get_image_width_height(node, 'height') + + dpi = (72, 72) + if Image is not None and source in self.image_dict: + filename, destination = self.image_dict[source] + imageobj = Image.open(filename, 'r') + dpi = imageobj.info.get('dpi', dpi) + # dpi information can be (xdpi, ydpi) or xydpi + try: iter(dpi) + except: dpi = (dpi, dpi) + else: + imageobj = None + + if width is None or height is None: + if imageobj is None: + raise RuntimeError( + 'image size not fully specified and PIL not installed') + if width is None: width = [imageobj.size[0], 'px'] + if height is None: height = [imageobj.size[1], 'px'] + + width[0] *= scale + height[0] *= scale + if width[1] == 'px': width = [width[0] / dpi[0], 'in'] + if height[1] == 'px': height = [height[0] / dpi[1], 'in'] + + width[0] = str(width[0]) + height[0] = str(height[0]) + return ''.join(width), ''.join(height) + + def generate_figure(self, node, source, destination, current_element): + caption = None + width, height = self.get_image_scaled_width_height(node, source) + for node1 in node.parent.children: + if node1.tagname == 'caption': + caption = node1.astext() + self.image_style_count += 1 + # + # Add the style for the caption. + if caption is not None: + attrib = { + 'style:class': 'extra', + 'style:family': 'paragraph', + 'style:name': 'Caption', + 'style:parent-style-name': 'Standard', + } + el1 = SubElement(self.automatic_styles, 'style:style', + attrib=attrib, nsdict=SNSD) + attrib = { + 'fo:margin-bottom': '0.0835in', + 'fo:margin-top': '0.0835in', + 'text:line-number': '0', + 'text:number-lines': 'false', + } + el2 = SubElement(el1, 'style:paragraph-properties', + attrib=attrib, nsdict=SNSD) + attrib = { + 'fo:font-size': '12pt', + 'fo:font-style': 'italic', + 'style:font-name': 'Times', + 'style:font-name-complex': 'Lucidasans1', + 'style:font-size-asian': '12pt', + 'style:font-size-complex': '12pt', + 'style:font-style-asian': 'italic', + 'style:font-style-complex': 'italic', + } + el2 = SubElement(el1, 'style:text-properties', + attrib=attrib, nsdict=SNSD) + style_name = 'rstframestyle%d' % self.image_style_count + # Add the styles + attrib = { + 'style:name': style_name, + 'style:family': 'graphic', + 'style:parent-style-name': self.rststyle('figureframe'), + } + el1 = SubElement(self.automatic_styles, + 'style:style', attrib=attrib, nsdict=SNSD) + halign = 'center' + valign = 'top' + if 'align' in node.attributes: + align = node.attributes['align'].split() + for val in align: + if val in ('left', 'center', 'right'): + halign = val + elif val in ('top', 'middle', 'bottom'): + valign = val + attrib = {} + wrap = False + classes = node.parent.attributes.get('classes') + if classes and 'wrap' in classes: + wrap = True + if wrap: + attrib['style:wrap'] = 'dynamic' + else: + attrib['style:wrap'] = 'none' + el2 = SubElement(el1, + 'style:graphic-properties', attrib=attrib, nsdict=SNSD) + attrib = { + 'draw:style-name': style_name, + 'draw:name': 'Frame1', + 'text:anchor-type': 'paragraph', + 'draw:z-index': '0', + } + attrib['svg:width'] = width + # dbg + #attrib['svg:height'] = height + el3 = SubElement(current_element, 'draw:frame', attrib=attrib) + attrib = {} + el4 = SubElement(el3, 'draw:text-box', attrib=attrib) + attrib = { + 'text:style-name': self.rststyle('caption'), + } + el5 = SubElement(el4, 'text:p', attrib=attrib) + return el3, el4, el5, caption + + def generate_image(self, node, source, destination, current_element, + frame_attrs=None): + width, height = self.get_image_scaled_width_height(node, source) + self.image_style_count += 1 + style_name = 'rstframestyle%d' % self.image_style_count + # Add the style. + attrib = { + 'style:name': style_name, + 'style:family': 'graphic', + 'style:parent-style-name': self.rststyle('image'), + } + el1 = SubElement(self.automatic_styles, + 'style:style', attrib=attrib, nsdict=SNSD) + halign = None + valign = None + if 'align' in node.attributes: + align = node.attributes['align'].split() + for val in align: + if val in ('left', 'center', 'right'): + halign = val + elif val in ('top', 'middle', 'bottom'): + valign = val + if frame_attrs is None: + attrib = { + 'style:vertical-pos': 'top', + 'style:vertical-rel': 'paragraph', + 'style:horizontal-rel': 'paragraph', + 'style:mirror': 'none', + 'fo:clip': 'rect(0cm 0cm 0cm 0cm)', + 'draw:luminance': '0%', + 'draw:contrast': '0%', + 'draw:red': '0%', + 'draw:green': '0%', + 'draw:blue': '0%', + 'draw:gamma': '100%', + 'draw:color-inversion': 'false', + 'draw:image-opacity': '100%', + 'draw:color-mode': 'standard', + } + else: + attrib = frame_attrs + if halign is not None: + attrib['style:horizontal-pos'] = halign + if valign is not None: + attrib['style:vertical-pos'] = valign + # If there is a classes/wrap directive or we are + # inside a table, add a no-wrap style. + wrap = False + classes = node.attributes.get('classes') + if classes and 'wrap' in classes: + wrap = True + if wrap: + attrib['style:wrap'] = 'dynamic' + else: + attrib['style:wrap'] = 'none' + # If we are inside a table, add a no-wrap style. + if self.is_in_table(node): + attrib['style:wrap'] = 'none' + el2 = SubElement(el1, + 'style:graphic-properties', attrib=attrib, nsdict=SNSD) + # Add the content. + #el = SubElement(current_element, 'text:p', + # attrib={'text:style-name': self.rststyle('textbody')}) + attrib={ + 'draw:style-name': style_name, + 'draw:name': 'graphics2', + 'draw:z-index': '1', + } + if isinstance(node.parent, nodes.TextElement): + attrib['text:anchor-type'] = 'as-char' #vds + else: + attrib['text:anchor-type'] = 'paragraph' + attrib['svg:width'] = width + attrib['svg:height'] = height + el1 = SubElement(current_element, 'draw:frame', attrib=attrib) + el2 = SubElement(el1, 'draw:image', attrib={ + 'xlink:href': '%s' % (destination, ), + 'xlink:type': 'simple', + 'xlink:show': 'embed', + 'xlink:actuate': 'onLoad', + }) + return el1, width + + def is_in_table(self, node): + node1 = node.parent + while node1: + if isinstance(node1, docutils.nodes.entry): + return True + node1 = node1.parent + return False + + def visit_legend(self, node): + if isinstance(node.parent, docutils.nodes.figure): + el1 = self.current_element[-1] + el1 = el1[0][0] + self.current_element = el1 + self.paragraph_style_stack.append(self.rststyle('legend')) + + def depart_legend(self, node): + if isinstance(node.parent, docutils.nodes.figure): + self.paragraph_style_stack.pop() + self.set_to_parent() + self.set_to_parent() + self.set_to_parent() + + def visit_line_block(self, node): + self.line_indent_level += 1 + self.line_block_level += 1 + + def depart_line_block(self, node): + self.line_indent_level -= 1 + self.line_block_level -= 1 + + def visit_line(self, node): + style = 'lineblock%d' % self.line_indent_level + el1 = SubElement(self.current_element, 'text:p', attrib={ + 'text:style-name': self.rststyle(style), + }) + self.current_element = el1 + + def depart_line(self, node): + self.set_to_parent() + + def visit_literal(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('inlineliteral')}) + self.set_current_element(el) + + def depart_literal(self, node): + self.set_to_parent() + + def visit_inline(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + inline_style = styles[0] + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle(inline_style)}) + self.set_current_element(el) + + def depart_inline(self, node): + self.set_to_parent() + + def _calculate_code_block_padding(self, line): + count = 0 + matchobj = SPACES_PATTERN.match(line) + if matchobj: + pad = matchobj.group() + count = len(pad) + else: + matchobj = TABS_PATTERN.match(line) + if matchobj: + pad = matchobj.group() + count = len(pad) * 8 + return count + + def _add_syntax_highlighting(self, insource, language): + lexer = pygments.lexers.get_lexer_by_name(language, stripall=True) + if language in ('latex', 'tex'): + fmtr = OdtPygmentsLaTeXFormatter(lambda name, parameters=(): + self.rststyle(name, parameters), + escape_function=escape_cdata) + else: + fmtr = OdtPygmentsProgFormatter(lambda name, parameters=(): + self.rststyle(name, parameters), + escape_function=escape_cdata) + outsource = pygments.highlight(insource, lexer, fmtr) + return outsource + + def fill_line(self, line): + line = FILL_PAT1.sub(self.fill_func1, line) + line = FILL_PAT2.sub(self.fill_func2, line) + return line + + def fill_func1(self, matchobj): + spaces = matchobj.group(0) + repl = '' % (len(spaces), ) + return repl + + def fill_func2(self, matchobj): + spaces = matchobj.group(0) + repl = ' ' % (len(spaces) - 1, ) + return repl + + def visit_literal_block(self, node): + wrapper1 = '%%s' % ( + self.rststyle('codeblock'), ) + source = node.astext() + if (pygments and + self.settings.add_syntax_highlighting + #and + #node.get('hilight', False) + ): + language = node.get('language', 'python') + source = self._add_syntax_highlighting(source, language) + else: + source = escape_cdata(source) + lines = source.split('\n') + lines1 = [''] + + my_lines = [] + for my_line in lines: + my_line = self.fill_line(my_line) + my_line = my_line.replace(" ", "\n") + my_lines.append(my_line) + my_lines_str = ''.join(my_lines) + my_lines_str2 = wrapper1 % (my_lines_str, ) + lines1.append(my_lines_str2) + lines1.append('') + s1 = ''.join(lines1) + if WhichElementTree != "lxml": + s1 = s1.encode("utf-8") + el1 = etree.fromstring(s1) + children = el1.getchildren() + for child in children: + self.current_element.append(child) + + def depart_literal_block(self, node): + pass + + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + def visit_meta(self, node): + name = node.attributes.get('name') + content = node.attributes.get('content') + if name is not None and content is not None: + self.meta_dict[name] = content + + def depart_meta(self, node): + pass + + def visit_option_list(self, node): + table_name = 'tableoption' + # + # Generate automatic styles + if not self.optiontablestyles_generated: + self.optiontablestyles_generated = True + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle(table_name), + 'style:family': 'table'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-properties', attrib={ + 'style:width': '17.59cm', + 'table:align': 'left', + 'style:shadow': 'none'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle('%s.%%c' % table_name, ( 'A', )), + 'style:family': 'table-column'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-column-properties', attrib={ + 'style:column-width': '4.999cm'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle('%s.%%c' % table_name, ( 'B', )), + 'style:family': 'table-column'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-column-properties', attrib={ + 'style:column-width': '12.587cm'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 1, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:background-color': 'transparent', + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': 'none', + 'fo:border-top': '0.035cm solid #000000', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + el2 = SubElement(el1, 'style:background-image', nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 1, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border': '0.035cm solid #000000'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 2, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': 'none', + 'fo:border-top': 'none', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 2, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': '0.035cm solid #000000', + 'fo:border-top': 'none', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + # + # Generate table data + el = self.append_child('table:table', attrib={ + 'table:name': self.rststyle(table_name), + 'table:style-name': self.rststyle(table_name), + }) + el1 = SubElement(el, 'table:table-column', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c' % table_name, ( 'A', ))}) + el1 = SubElement(el, 'table:table-column', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c' % table_name, ( 'B', ))}) + el1 = SubElement(el, 'table:table-header-rows') + el2 = SubElement(el1, 'table:table-row') + el3 = SubElement(el2, 'table:table-cell', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 1, )), + 'office:value-type': 'string'}) + el4 = SubElement(el3, 'text:p', attrib={ + 'text:style-name': 'Table_20_Heading'}) + el4.text= 'Option' + el3 = SubElement(el2, 'table:table-cell', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 1, )), + 'office:value-type': 'string'}) + el4 = SubElement(el3, 'text:p', attrib={ + 'text:style-name': 'Table_20_Heading'}) + el4.text= 'Description' + self.set_current_element(el) + + def depart_option_list(self, node): + self.set_to_parent() + + def visit_option_list_item(self, node): + el = self.append_child('table:table-row') + self.set_current_element(el) + + def depart_option_list_item(self, node): + self.set_to_parent() + + def visit_option_group(self, node): + el = self.append_child('table:table-cell', attrib={ + 'table:style-name': 'Table%d.A2' % self.table_count, + 'office:value-type': 'string', + }) + self.set_current_element(el) + + def depart_option_group(self, node): + self.set_to_parent() + + def visit_option(self, node): + el = self.append_child('text:p', attrib={ + 'text:style-name': 'Table_20_Contents'}) + el.text = node.astext() + + def depart_option(self, node): + pass + + def visit_option_string(self, node): + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + pass + + def depart_option_argument(self, node): + pass + + def visit_description(self, node): + el = self.append_child('table:table-cell', attrib={ + 'table:style-name': 'Table%d.B2' % self.table_count, + 'office:value-type': 'string', + }) + el1 = SubElement(el, 'text:p', attrib={ + 'text:style-name': 'Table_20_Contents'}) + el1.text = node.astext() + raise nodes.SkipChildren() + + def depart_description(self, node): + pass + + def visit_paragraph(self, node): + self.in_paragraph = True + if self.in_header: + el = self.append_p('header') + elif self.in_footer: + el = self.append_p('footer') + else: + style_name = self.paragraph_style_stack[-1] + el = self.append_child('text:p', + attrib={'text:style-name': style_name}) + self.append_pending_ids(el) + self.set_current_element(el) + + def depart_paragraph(self, node): + self.in_paragraph = False + self.set_to_parent() + if self.in_header: + self.header_content.append( + self.current_element.getchildren()[-1]) + self.current_element.remove( + self.current_element.getchildren()[-1]) + elif self.in_footer: + self.footer_content.append( + self.current_element.getchildren()[-1]) + self.current_element.remove( + self.current_element.getchildren()[-1]) + + def visit_problematic(self, node): + pass + + def depart_problematic(self, node): + pass + + def visit_raw(self, node): + if 'format' in node.attributes: + formats = node.attributes['format'] + formatlist = formats.split() + if 'odt' in formatlist: + rawstr = node.astext() + attrstr = ' '.join(['%s="%s"' % (k, v, ) + for k,v in CONTENT_NAMESPACE_ATTRIB.items()]) + contentstr = '%s' % (attrstr, rawstr, ) + if WhichElementTree != "lxml": + contentstr = contentstr.encode("utf-8") + content = etree.fromstring(contentstr) + elements = content.getchildren() + if len(elements) > 0: + el1 = elements[0] + if self.in_header: + pass + elif self.in_footer: + pass + else: + self.current_element.append(el1) + raise nodes.SkipChildren() + + def depart_raw(self, node): + if self.in_header: + pass + elif self.in_footer: + pass + else: + pass + + def visit_reference(self, node): + text = node.astext() + if self.settings.create_links: + if node.has_key('refuri'): + href = node['refuri'] + if ( self.settings.cloak_email_addresses + and href.startswith('mailto:')): + href = self.cloak_mailto(href) + el = self.append_child('text:a', attrib={ + 'xlink:href': '%s' % href, + 'xlink:type': 'simple', + }) + self.set_current_element(el) + elif node.has_key('refid'): + if self.settings.create_links: + href = node['refid'] + el = self.append_child('text:reference-ref', attrib={ + 'text:ref-name': '%s' % href, + 'text:reference-format': 'text', + }) + else: + self.document.reporter.warning( + 'References must have "refuri" or "refid" attribute.') + if (self.in_table_of_contents and + len(node.children) >= 1 and + isinstance(node.children[0], docutils.nodes.generated)): + node.remove(node.children[0]) + + def depart_reference(self, node): + if self.settings.create_links: + if node.has_key('refuri'): + self.set_to_parent() + + def visit_rubric(self, node): + style_name = self.rststyle('rubric') + classes = node.get('classes') + if classes: + class1 = classes[0] + if class1: + style_name = class1 + el = SubElement(self.current_element, 'text:h', attrib = { + #'text:outline-level': '%d' % section_level, + #'text:style-name': 'Heading_20_%d' % section_level, + 'text:style-name': style_name, + }) + text = node.astext() + el.text = self.encode(text) + + def depart_rubric(self, node): + pass + + def visit_section(self, node, move_ids=1): + self.section_level += 1 + self.section_count += 1 + if self.settings.create_sections: + el = self.append_child('text:section', attrib={ + 'text:name': 'Section%d' % self.section_count, + 'text:style-name': 'Sect%d' % self.section_level, + }) + self.set_current_element(el) + + def depart_section(self, node): + self.section_level -= 1 + if self.settings.create_sections: + self.set_to_parent() + + def visit_strong(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + self.set_current_element(el) + + def depart_strong(self, node): + self.set_to_parent() + + def visit_substitution_definition(self, node): + raise nodes.SkipChildren() + + def depart_substitution_definition(self, node): + pass + + def visit_system_message(self, node): + pass + + def depart_system_message(self, node): + pass + + def get_table_style(self, node): + table_style = None + table_name = None + use_predefined_table_style = False + str_classes = node.get('classes') + if str_classes is not None: + for str_class in str_classes: + if str_class.startswith(TABLESTYLEPREFIX): + table_name = str_class + use_predefined_table_style = True + break + if table_name is not None: + table_style = self.table_styles.get(table_name) + if table_style is None: + # If we can't find the table style, issue warning + # and use the default table style. + self.document.reporter.warning( + 'Can\'t find table style "%s". Using default.' % ( + table_name, )) + table_name = TABLENAMEDEFAULT + table_style = self.table_styles.get(table_name) + if table_style is None: + # If we can't find the default table style, issue a warning + # and use a built-in default style. + self.document.reporter.warning( + 'Can\'t find default table style "%s". Using built-in default.' % ( + table_name, )) + table_style = BUILTIN_DEFAULT_TABLE_STYLE + else: + table_name = TABLENAMEDEFAULT + table_style = self.table_styles.get(table_name) + if table_style is None: + # If we can't find the default table style, issue a warning + # and use a built-in default style. + self.document.reporter.warning( + 'Can\'t find default table style "%s". Using built-in default.' % ( + table_name, )) + table_style = BUILTIN_DEFAULT_TABLE_STYLE + return table_style + + def visit_table(self, node): + self.table_count += 1 + table_style = self.get_table_style(node) + table_name = '%s%%d' % TABLESTYLEPREFIX + el1 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + 'style:family': 'table', + }, nsdict=SNSD) + if table_style.backgroundcolor is None: + el1_1 = SubElement(el1, 'style:table-properties', attrib={ + #'style:width': '17.59cm', + 'table:align': 'margins', + 'fo:margin-top': '0in', + 'fo:margin-bottom': '0.10in', + }, nsdict=SNSD) + else: + el1_1 = SubElement(el1, 'style:table-properties', attrib={ + #'style:width': '17.59cm', + 'table:align': 'margins', + 'fo:margin-top': '0in', + 'fo:margin-bottom': '0.10in', + 'fo:background-color': table_style.backgroundcolor, + }, nsdict=SNSD) + # We use a single cell style for all cells in this table. + # That's probably not correct, but seems to work. + el2 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( self.table_count, 'A', 1, )), + 'style:family': 'table-cell', + }, nsdict=SNSD) + thickness = self.settings.table_border_thickness + if thickness is None: + line_style1 = table_style.border + else: + line_style1 = '0.%03dcm solid #000000' % (thickness, ) + el2_1 = SubElement(el2, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.049cm', + 'fo:border-left': line_style1, + 'fo:border-right': line_style1, + 'fo:border-top': line_style1, + 'fo:border-bottom': line_style1, + }, nsdict=SNSD) + title = None + for child in node.children: + if child.tagname == 'title': + title = child.astext() + break + if title is not None: + el3 = self.append_p('table-title', title) + else: + pass + el4 = SubElement(self.current_element, 'table:table', attrib={ + 'table:name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + 'table:style-name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + }) + self.set_current_element(el4) + self.current_table_style = el1 + self.table_width = 0 + + def depart_table(self, node): + attribkey = add_ns('style:width', nsdict=SNSD) + attribval = '%dcm' % self.table_width + self.current_table_style.attrib[attribkey] = attribval + self.set_to_parent() + + def visit_tgroup(self, node): + self.column_count = ord('A') - 1 + + def depart_tgroup(self, node): + pass + + def visit_colspec(self, node): + self.column_count += 1 + colspec_name = self.rststyle( + '%s%%d.%%s' % TABLESTYLEPREFIX, + (self.table_count, chr(self.column_count), ) + ) + colwidth = node['colwidth'] + el1 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': colspec_name, + 'style:family': 'table-column', + }, nsdict=SNSD) + el1_1 = SubElement(el1, 'style:table-column-properties', attrib={ + 'style:column-width': '%dcm' % colwidth }, nsdict=SNSD) + el2 = self.append_child('table:table-column', attrib={ + 'table:style-name': colspec_name, + }) + self.table_width += colwidth + + def depart_colspec(self, node): + pass + + def visit_thead(self, node): + el = self.append_child('table:table-header-rows') + self.set_current_element(el) + self.in_thead = True + self.paragraph_style_stack.append('Table_20_Heading') + + def depart_thead(self, node): + self.set_to_parent() + self.in_thead = False + self.paragraph_style_stack.pop() + + def visit_row(self, node): + self.column_count = ord('A') - 1 + el = self.append_child('table:table-row') + self.set_current_element(el) + + def depart_row(self, node): + self.set_to_parent() + + def visit_entry(self, node): + self.column_count += 1 + cellspec_name = self.rststyle( + '%s%%d.%%c%%d' % TABLESTYLEPREFIX, + (self.table_count, 'A', 1, ) + ) + attrib={ + 'table:style-name': cellspec_name, + 'office:value-type': 'string', + } + morecols = node.get('morecols', 0) + if morecols > 0: + attrib['table:number-columns-spanned'] = '%d' % (morecols + 1,) + self.column_count += morecols + morerows = node.get('morerows', 0) + if morerows > 0: + attrib['table:number-rows-spanned'] = '%d' % (morerows + 1,) + el1 = self.append_child('table:table-cell', attrib=attrib) + self.set_current_element(el1) + + def depart_entry(self, node): + self.set_to_parent() + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_target(self, node): + # + # I don't know how to implement targets in ODF. + # How do we create a target in oowriter? A cross-reference? + if not (node.has_key('refuri') or node.has_key('refid') + or node.has_key('refname')): + pass + else: + pass + + def depart_target(self, node): + pass + + def visit_title(self, node, move_ids=1, title_type='title'): + if isinstance(node.parent, docutils.nodes.section): + section_level = self.section_level + if section_level > 7: + self.document.reporter.warning( + 'Heading/section levels greater than 7 not supported.') + self.document.reporter.warning( + ' Reducing to heading level 7 for heading: "%s"' % ( + node.astext(), )) + section_level = 7 + el1 = self.append_child('text:h', attrib = { + 'text:outline-level': '%d' % section_level, + #'text:style-name': 'Heading_20_%d' % section_level, + 'text:style-name': self.rststyle( + 'heading%d', (section_level, )), + }) + self.append_pending_ids(el1) + self.set_current_element(el1) + elif isinstance(node.parent, docutils.nodes.document): + # text = self.settings.title + #else: + # text = node.astext() + el1 = SubElement(self.current_element, 'text:p', attrib = { + 'text:style-name': self.rststyle(title_type), + }) + self.append_pending_ids(el1) + text = node.astext() + self.title = text + self.found_doc_title = True + self.set_current_element(el1) + + def depart_title(self, node): + if (isinstance(node.parent, docutils.nodes.section) or + isinstance(node.parent, docutils.nodes.document)): + self.set_to_parent() + + def visit_subtitle(self, node, move_ids=1): + self.visit_title(node, move_ids, title_type='subtitle') + + def depart_subtitle(self, node): + self.depart_title(node) + + def visit_title_reference(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': self.rststyle('quotation')}) + el.text = self.encode(node.astext()) + raise nodes.SkipChildren() + + def depart_title_reference(self, node): + pass + + def generate_table_of_content_entry_template(self, el1): + for idx in range(1, 11): + el2 = SubElement(el1, + 'text:table-of-content-entry-template', + attrib={ + 'text:outline-level': "%d" % (idx, ), + 'text:style-name': self.rststyle('contents-%d' % (idx, )), + }) + el3 = SubElement(el2, 'text:index-entry-chapter') + el3 = SubElement(el2, 'text:index-entry-text') + el3 = SubElement(el2, 'text:index-entry-tab-stop', attrib={ + 'style:leader-char': ".", + 'style:type': "right", + }) + el3 = SubElement(el2, 'text:index-entry-page-number') + + def visit_topic(self, node): + if 'classes' in node.attributes: + if 'contents' in node.attributes['classes']: + if self.settings.generate_oowriter_toc: + el1 = self.append_child('text:table-of-content', attrib={ + 'text:name': 'Table of Contents1', + 'text:protected': 'true', + 'text:style-name': 'Sect1', + }) + el2 = SubElement(el1, + 'text:table-of-content-source', + attrib={ + 'text:outline-level': '10', + }) + el3 =SubElement(el2, 'text:index-title-template', attrib={ + 'text:style-name': 'Contents_20_Heading', + }) + el3.text = 'Table of Contents' + self.generate_table_of_content_entry_template(el2) + el4 = SubElement(el1, 'text:index-body') + el5 = SubElement(el4, 'text:index-title') + el6 = SubElement(el5, 'text:p', attrib={ + 'text:style-name': self.rststyle('contents-heading'), + }) + el6.text = 'Table of Contents' + self.save_current_element = self.current_element + self.table_of_content_index_body = el4 + self.set_current_element(el4) + else: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = 'Contents' + self.in_table_of_contents = True + elif 'abstract' in node.attributes['classes']: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = 'Abstract' + + def depart_topic(self, node): + if 'classes' in node.attributes: + if 'contents' in node.attributes['classes']: + if self.settings.generate_oowriter_toc: + self.update_toc_page_numbers( + self.table_of_content_index_body) + self.set_current_element(self.save_current_element) + else: + el = self.append_p('horizontalline') + self.in_table_of_contents = False + + def update_toc_page_numbers(self, el): + collection = [] + self.update_toc_collect(el, 0, collection) + self.update_toc_add_numbers(collection) + + def update_toc_collect(self, el, level, collection): + collection.append((level, el)) + level += 1 + for child_el in el.getchildren(): + if child_el.tag != 'text:index-body': + self.update_toc_collect(child_el, level, collection) + + def update_toc_add_numbers(self, collection): + for level, el1 in collection: + if (el1.tag == 'text:p' and + el1.text != 'Table of Contents'): + el2 = SubElement(el1, 'text:tab') + el2.tail = '9999' + + + def visit_transition(self, node): + el = self.append_p('horizontalline') + + def depart_transition(self, node): + pass + + # + # Admonitions + # + def visit_warning(self, node): + self.generate_admonition(node, 'warning') + + def depart_warning(self, node): + self.paragraph_style_stack.pop() + + def visit_attention(self, node): + self.generate_admonition(node, 'attention') + + depart_attention = depart_warning + + def visit_caution(self, node): + self.generate_admonition(node, 'caution') + + depart_caution = depart_warning + + def visit_danger(self, node): + self.generate_admonition(node, 'danger') + + depart_danger = depart_warning + + def visit_error(self, node): + self.generate_admonition(node, 'error') + + depart_error = depart_warning + + def visit_hint(self, node): + self.generate_admonition(node, 'hint') + + depart_hint = depart_warning + + def visit_important(self, node): + self.generate_admonition(node, 'important') + + depart_important = depart_warning + + def visit_note(self, node): + self.generate_admonition(node, 'note') + + depart_note = depart_warning + + def visit_tip(self, node): + self.generate_admonition(node, 'tip') + + depart_tip = depart_warning + + def visit_admonition(self, node): + title = None + for child in node.children: + if child.tagname == 'title': + title = child.astext() + if title is None: + classes1 = node.get('classes') + if classes1: + title = classes1[0] + self.generate_admonition(node, 'generic', title) + + depart_admonition = depart_warning + + def generate_admonition(self, node, label, title=None): + el1 = SubElement(self.current_element, 'text:p', attrib = { + 'text:style-name': self.rststyle('admon-%s-hdr', ( label, )), + }) + if title: + el1.text = title + else: + el1.text = '%s!' % (label.capitalize(), ) + s1 = self.rststyle('admon-%s-body', ( label, )) + self.paragraph_style_stack.append(s1) + + # + # Roles (e.g. subscript, superscript, strong, ... + # + def visit_subscript(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': 'rststyle-subscript', + }) + self.set_current_element(el) + + def depart_subscript(self, node): + self.set_to_parent() + + def visit_superscript(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': 'rststyle-superscript', + }) + self.set_current_element(el) + + def depart_superscript(self, node): + self.set_to_parent() + + +# Use an own reader to modify transformations done. +class Reader(standalone.Reader): + + def get_transforms(self): + default = standalone.Reader.get_transforms(self) + if self.settings.create_links: + return default + return [ i + for i in default + if i is not references.DanglingReferences ] diff --git a/python/helpers/docutils/writers/odf_odt/pygmentsformatter.py b/python/helpers/docutils/writers/odf_odt/pygmentsformatter.py new file mode 100644 index 000000000000..e8ce8279e5c0 --- /dev/null +++ b/python/helpers/docutils/writers/odf_odt/pygmentsformatter.py @@ -0,0 +1,109 @@ +# $Id: pygmentsformatter.py 5853 2009-01-19 21:02:02Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" + +Additional support for Pygments formatter. + +""" + + +import pygments +import pygments.formatter + + +class OdtPygmentsFormatter(pygments.formatter.Formatter): + def __init__(self, rststyle_function, escape_function): + pygments.formatter.Formatter.__init__(self) + self.rststyle_function = rststyle_function + self.escape_function = escape_function + + def rststyle(self, name, parameters=( )): + return self.rststyle_function(name, parameters) + + +class OdtPygmentsProgFormatter(OdtPygmentsFormatter): + def format(self, tokensource, outfile): + tokenclass = pygments.token.Token + for ttype, value in tokensource: + value = self.escape_function(value) + if ttype == tokenclass.Keyword: + s2 = self.rststyle('codeblock-keyword') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Literal.String: + s2 = self.rststyle('codeblock-string') + s1 = '%s' % \ + (s2, value, ) + elif ttype in ( + tokenclass.Literal.Number.Integer, + tokenclass.Literal.Number.Integer.Long, + tokenclass.Literal.Number.Float, + tokenclass.Literal.Number.Hex, + tokenclass.Literal.Number.Oct, + tokenclass.Literal.Number, + ): + s2 = self.rststyle('codeblock-number') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Operator: + s2 = self.rststyle('codeblock-operator') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Comment: + s2 = self.rststyle('codeblock-comment') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Class: + s2 = self.rststyle('codeblock-classname') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Function: + s2 = self.rststyle('codeblock-functionname') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name: + s2 = self.rststyle('codeblock-name') + s1 = '%s' % \ + (s2, value, ) + else: + s1 = value + outfile.write(s1) + + +class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter): + def format(self, tokensource, outfile): + tokenclass = pygments.token.Token + for ttype, value in tokensource: + value = self.escape_function(value) + if ttype == tokenclass.Keyword: + s2 = self.rststyle('codeblock-keyword') + s1 = '%s' % \ + (s2, value, ) + elif ttype in (tokenclass.Literal.String, + tokenclass.Literal.String.Backtick, + ): + s2 = self.rststyle('codeblock-string') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Attribute: + s2 = self.rststyle('codeblock-operator') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Comment: + if value[-1] == '\n': + s2 = self.rststyle('codeblock-comment') + s1 = '%s\n' % \ + (s2, value[:-1], ) + else: + s2 = self.rststyle('codeblock-comment') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Builtin: + s2 = self.rststyle('codeblock-name') + s1 = '%s' % \ + (s2, value, ) + else: + s1 = value + outfile.write(s1) diff --git a/python/helpers/docutils/writers/odf_odt/styles.odt b/python/helpers/docutils/writers/odf_odt/styles.odt new file mode 100644 index 000000000000..f217bb903272 Binary files /dev/null and b/python/helpers/docutils/writers/odf_odt/styles.odt differ diff --git a/python/helpers/docutils/writers/pep_html/__init__.py b/python/helpers/docutils/writers/pep_html/__init__.py new file mode 100644 index 000000000000..503fa17bfe6f --- /dev/null +++ b/python/helpers/docutils/writers/pep_html/__init__.py @@ -0,0 +1,105 @@ +# $Id: __init__.py 6328 2010-05-23 21:20:29Z gbrandl $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +PEP HTML Writer. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import os +import os.path +import codecs +import docutils +from docutils import frontend, nodes, utils, writers +from docutils.writers import html4css1 + + +class Writer(html4css1.Writer): + + default_stylesheet = 'pep.css' + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = html4css1.Writer.settings_spec + ( + 'PEP/HTML-Specific Options', + 'For the PEP/HTML writer, the default value for the --stylesheet-path ' + 'option is "%s", and the default value for --template is "%s". ' + 'See HTML-Specific Options above.' + % (default_stylesheet_path, default_template_path), + (('Python\'s home URL. Default is "http://www.python.org".', + ['--python-home'], + {'default': 'http://www.python.org', 'metavar': ''}), + ('Home URL prefix for PEPs. Default is "." (current directory).', + ['--pep-home'], + {'default': '.', 'metavar': ''}), + # For testing. + (frontend.SUPPRESS_HELP, + ['--no-random'], + {'action': 'store_true', 'validator': frontend.validate_boolean}),)) + + settings_default_overrides = {'stylesheet_path': default_stylesheet_path, + 'template': default_template_path,} + + relative_path_settings = (html4css1.Writer.relative_path_settings + + ('template',)) + + config_section = 'pep_html writer' + config_section_dependencies = ('writers', 'html4css1 writer') + + def __init__(self): + html4css1.Writer.__init__(self) + self.translator_class = HTMLTranslator + + def interpolation_dict(self): + subs = html4css1.Writer.interpolation_dict(self) + settings = self.document.settings + pyhome = settings.python_home + subs['pyhome'] = pyhome + subs['pephome'] = settings.pep_home + if pyhome == '..': + subs['pepindex'] = '.' + else: + subs['pepindex'] = pyhome + '/dev/peps' + index = self.document.first_child_matching_class(nodes.field_list) + header = self.document[index] + self.pepnum = header[0][1].astext() + subs['pep'] = self.pepnum + if settings.no_random: + subs['banner'] = 0 + else: + import random + subs['banner'] = random.randrange(64) + try: + subs['pepnum'] = '%04i' % int(self.pepnum) + except ValueError: + subs['pepnum'] = self.pepnum + self.title = header[1][1].astext() + subs['title'] = self.title + subs['body'] = ''.join( + self.body_pre_docinfo + self.docinfo + self.body) + return subs + + def assemble_parts(self): + html4css1.Writer.assemble_parts(self) + self.parts['title'] = [self.title] + self.parts['pepnum'] = self.pepnum + + +class HTMLTranslator(html4css1.HTMLTranslator): + + def depart_field_list(self, node): + html4css1.HTMLTranslator.depart_field_list(self, node) + if 'rfc2822' in node['classes']: + self.body.append('
\n') diff --git a/python/helpers/docutils/writers/pep_html/pep.css b/python/helpers/docutils/writers/pep_html/pep.css new file mode 100644 index 000000000000..5d8c0409eb8b --- /dev/null +++ b/python/helpers/docutils/writers/pep_html/pep.css @@ -0,0 +1,344 @@ +/* +:Author: David Goodger +:Contact: goodger@python.org +:date: $Date: 2006-05-21 16:44:42 -0400 (Sun, 21 May 2006) $ +:version: $Revision: 4564 $ +:copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the PEP HTML output of Docutils. +*/ + +/* "! important" is used here to override other ``margin-top`` and + ``margin-bottom`` styles that are later in the stylesheet or + more specific. See http://www.w3.org/TR/CSS1#the-cascade */ +.first { + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +.navigation { + width: 100% ; + background: #99ccff ; + margin-top: 0px ; + margin-bottom: 0px } + +.navigation .navicon { + width: 150px ; + height: 35px } + +.navigation .textlinks { + padding-left: 1em ; + text-align: left } + +.navigation td, .navigation th { + padding-left: 0em ; + padding-right: 0em ; + vertical-align: middle } + +.rfc2822 { + margin-top: 0.5em ; + margin-left: 0.5em ; + margin-right: 0.5em ; + margin-bottom: 0em } + +.rfc2822 td { + text-align: left } + +.rfc2822 th.field-name { + text-align: right ; + font-family: sans-serif ; + padding-right: 0.5em ; + font-weight: bold ; + margin-bottom: 0em } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +body { + margin: 0px ; + margin-bottom: 1em ; + padding: 0px } + +dl.docutils dd { + margin-bottom: 0.5em } + +div.section { + margin-left: 1em ; + margin-right: 1em ; + margin-bottom: 1.5em } + +div.section div.section { + margin-left: 0em ; + margin-right: 0em ; + margin-top: 1.5em } + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.footer { + margin-left: 1em ; + margin-right: 1em } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin-left: 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1 { + font-family: sans-serif ; + font-size: large } + +h2 { + font-family: sans-serif ; + font-size: medium } + +h3 { + font-family: sans-serif ; + font-size: small } + +h4 { + font-family: sans-serif ; + font-style: italic ; + font-size: small } + +h5 { + font-family: sans-serif; + font-size: x-small } + +h6 { + font-family: sans-serif; + font-style: italic ; + font-size: x-small } + +hr.docutils { + width: 75% } + +img.align-left { + clear: left } + +img.align-right { + clear: right } + +img.borderless { + border: 0 } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-family: sans-serif ; + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.option-argument { + font-style: italic } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +td.num { + text-align: right } + +th.field-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } diff --git a/python/helpers/docutils/writers/pep_html/template.txt b/python/helpers/docutils/writers/pep_html/template.txt new file mode 100644 index 000000000000..62c07a87f1f1 --- /dev/null +++ b/python/helpers/docutils/writers/pep_html/template.txt @@ -0,0 +1,29 @@ + + + + + + + + PEP %(pep)s -- %(title)s + %(stylesheet)s + + + + + +
+%(body)s +%(body_suffix)s diff --git a/python/helpers/docutils/writers/pseudoxml.py b/python/helpers/docutils/writers/pseudoxml.py new file mode 100644 index 000000000000..547adbf6fdf6 --- /dev/null +++ b/python/helpers/docutils/writers/pseudoxml.py @@ -0,0 +1,31 @@ +# $Id: pseudoxml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Simple internal document tree Writer, writes indented pseudo-XML. +""" + +__docformat__ = 'reStructuredText' + + +from docutils import writers + + +class Writer(writers.Writer): + + supported = ('pprint', 'pformat', 'pseudoxml') + """Formats this writer supports.""" + + config_section = 'pseudoxml writer' + config_section_dependencies = ('writers',) + + output = None + """Final translated form of `document`.""" + + def translate(self): + self.output = self.document.pformat() + + def supports(self, format): + """This writer supports all format-specific elements.""" + return 1 diff --git a/python/helpers/docutils/writers/s5_html/__init__.py b/python/helpers/docutils/writers/s5_html/__init__.py new file mode 100644 index 000000000000..12647bc78353 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/__init__.py @@ -0,0 +1,340 @@ +# $Id: __init__.py 5889 2009-04-01 20:00:21Z gbrandl $ +# Authors: Chris Liechti ; +# David Goodger +# Copyright: This module has been placed in the public domain. + +""" +S5/HTML Slideshow Writer. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import os +import re +import docutils +from docutils import frontend, nodes, utils +from docutils.writers import html4css1 +from docutils.parsers.rst import directives +from docutils._compat import b + +themes_dir_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), 'themes')) + +def find_theme(name): + # Where else to look for a theme? + # Check working dir? Destination dir? Config dir? Plugins dir? + path = os.path.join(themes_dir_path, name) + if not os.path.isdir(path): + raise docutils.ApplicationError( + 'Theme directory not found: %r (path: %r)' % (name, path)) + return path + + +class Writer(html4css1.Writer): + + settings_spec = html4css1.Writer.settings_spec + ( + 'S5 Slideshow Specific Options', + 'For the S5/HTML writer, the --no-toc-backlinks option ' + '(defined in General Docutils Options above) is the default, ' + 'and should not be changed.', + (('Specify an installed S5 theme by name. Overrides --theme-url. ' + 'The default theme name is "default". The theme files will be ' + 'copied into a "ui/" directory, in the same directory as the ' + 'destination file (output HTML). Note that existing theme files ' + 'will not be overwritten (unless --overwrite-theme-files is used).', + ['--theme'], + {'default': 'default', 'metavar': '', + 'overrides': 'theme_url'}), + ('Specify an S5 theme URL. The destination file (output HTML) will ' + 'link to this theme; nothing will be copied. Overrides --theme.', + ['--theme-url'], + {'metavar': '', 'overrides': 'theme'}), + ('Allow existing theme files in the ``ui/`` directory to be ' + 'overwritten. The default is not to overwrite theme files.', + ['--overwrite-theme-files'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Keep existing theme files in the ``ui/`` directory; do not ' + 'overwrite any. This is the default.', + ['--keep-theme-files'], + {'dest': 'overwrite_theme_files', 'action': 'store_false'}), + ('Set the initial view mode to "slideshow" [default] or "outline".', + ['--view-mode'], + {'choices': ['slideshow', 'outline'], 'default': 'slideshow', + 'metavar': ''}), + ('Normally hide the presentation controls in slideshow mode. ' + 'This is the default.', + ['--hidden-controls'], + {'action': 'store_true', 'default': True, + 'validator': frontend.validate_boolean}), + ('Always show the presentation controls in slideshow mode. ' + 'The default is to hide the controls.', + ['--visible-controls'], + {'dest': 'hidden_controls', 'action': 'store_false'}), + ('Enable the current slide indicator ("1 / 15"). ' + 'The default is to disable it.', + ['--current-slide'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Disable the current slide indicator. This is the default.', + ['--no-current-slide'], + {'dest': 'current_slide', 'action': 'store_false'}),)) + + settings_default_overrides = {'toc_backlinks': 0} + + config_section = 's5_html writer' + config_section_dependencies = ('writers', 'html4css1 writer') + + def __init__(self): + html4css1.Writer.__init__(self) + self.translator_class = S5HTMLTranslator + + +class S5HTMLTranslator(html4css1.HTMLTranslator): + + s5_stylesheet_template = """\ + + + + + + + + +\n""" + # The script element must go in front of the link elements to + # avoid a flash of unstyled content (FOUC), reproducible with + # Firefox. + + disable_current_slide = """ +\n""" + + layout_template = """\ +
+
+
+ + +
\n""" +#
+#
+#
+#
+ + default_theme = 'default' + """Name of the default theme.""" + + base_theme_file = '__base__' + """Name of the file containing the name of the base theme.""" + + direct_theme_files = ( + 'slides.css', 'outline.css', 'print.css', 'opera.css', 'slides.js') + """Names of theme files directly linked to in the output HTML""" + + indirect_theme_files = ( + 's5-core.css', 'framing.css', 'pretty.css', 'blank.gif', 'iepngfix.htc') + """Names of files used indirectly; imported or used by files in + `direct_theme_files`.""" + + required_theme_files = indirect_theme_files + direct_theme_files + """Names of mandatory theme files.""" + + def __init__(self, *args): + html4css1.HTMLTranslator.__init__(self, *args) + #insert S5-specific stylesheet and script stuff: + self.theme_file_path = None + self.setup_theme() + view_mode = self.document.settings.view_mode + control_visibility = ('visible', 'hidden')[self.document.settings + .hidden_controls] + self.stylesheet.append(self.s5_stylesheet_template + % {'path': self.theme_file_path, + 'view_mode': view_mode, + 'control_visibility': control_visibility}) + if not self.document.settings.current_slide: + self.stylesheet.append(self.disable_current_slide) + self.add_meta('\n') + self.s5_footer = [] + self.s5_header = [] + self.section_count = 0 + self.theme_files_copied = None + + def setup_theme(self): + if self.document.settings.theme: + self.copy_theme() + elif self.document.settings.theme_url: + self.theme_file_path = self.document.settings.theme_url + else: + raise docutils.ApplicationError( + 'No theme specified for S5/HTML writer.') + + def copy_theme(self): + """ + Locate & copy theme files. + + A theme may be explicitly based on another theme via a '__base__' + file. The default base theme is 'default'. Files are accumulated + from the specified theme, any base themes, and 'default'. + """ + settings = self.document.settings + path = find_theme(settings.theme) + theme_paths = [path] + self.theme_files_copied = {} + required_files_copied = {} + # This is a link (URL) in HTML, so we use "/", not os.sep: + self.theme_file_path = '%s/%s' % ('ui', settings.theme) + if settings._destination: + dest = os.path.join( + os.path.dirname(settings._destination), 'ui', settings.theme) + if not os.path.isdir(dest): + os.makedirs(dest) + else: + # no destination, so we can't copy the theme + return + default = 0 + while path: + for f in os.listdir(path): # copy all files from each theme + if f == self.base_theme_file: + continue # ... except the "__base__" file + if ( self.copy_file(f, path, dest) + and f in self.required_theme_files): + required_files_copied[f] = 1 + if default: + break # "default" theme has no base theme + # Find the "__base__" file in theme directory: + base_theme_file = os.path.join(path, self.base_theme_file) + # If it exists, read it and record the theme path: + if os.path.isfile(base_theme_file): + lines = open(base_theme_file).readlines() + for line in lines: + line = line.strip() + if line and not line.startswith('#'): + path = find_theme(line) + if path in theme_paths: # check for duplicates (cycles) + path = None # if found, use default base + else: + theme_paths.append(path) + break + else: # no theme name found + path = None # use default base + else: # no base theme file found + path = None # use default base + if not path: + path = find_theme(self.default_theme) + theme_paths.append(path) + default = 1 + if len(required_files_copied) != len(self.required_theme_files): + # Some required files weren't found & couldn't be copied. + required = list(self.required_theme_files) + for f in required_files_copied.keys(): + required.remove(f) + raise docutils.ApplicationError( + 'Theme files not found: %s' + % ', '.join(['%r' % f for f in required])) + + files_to_skip_pattern = re.compile(r'~$|\.bak$|#$|\.cvsignore$') + + def copy_file(self, name, source_dir, dest_dir): + """ + Copy file `name` from `source_dir` to `dest_dir`. + Return 1 if the file exists in either `source_dir` or `dest_dir`. + """ + source = os.path.join(source_dir, name) + dest = os.path.join(dest_dir, name) + if dest in self.theme_files_copied: + return 1 + else: + self.theme_files_copied[dest] = 1 + if os.path.isfile(source): + if self.files_to_skip_pattern.search(source): + return None + settings = self.document.settings + if os.path.exists(dest) and not settings.overwrite_theme_files: + settings.record_dependencies.add(dest) + else: + src_file = open(source, 'rb') + src_data = src_file.read() + src_file.close() + dest_file = open(dest, 'wb') + dest_dir = dest_dir.replace(os.sep, '/') + dest_file.write(src_data.replace( + b('ui/default'), + dest_dir[dest_dir.rfind('ui/'):].encode( + sys.getfilesystemencoding()))) + dest_file.close() + settings.record_dependencies.add(source) + return 1 + if os.path.isfile(dest): + return 1 + + def depart_document(self, node): + header = ''.join(self.s5_header) + footer = ''.join(self.s5_footer) + title = ''.join(self.html_title).replace('

', '

') + layout = self.layout_template % {'header': header, + 'title': title, + 'footer': footer} + self.fragment.extend(self.body) + self.body_prefix.extend(layout) + self.body_prefix.append('
\n') + self.body_prefix.append( + self.starttag({'classes': ['slide'], 'ids': ['slide0']}, 'div')) + if not self.section_count: + self.body.append('
\n') + self.body_suffix.insert(0, '

\n') + # skip content-type meta tag with interpolated charset value: + self.html_head.extend(self.head[1:]) + self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo + + self.docinfo + self.body + + self.body_suffix[:-1]) + + def depart_footer(self, node): + start = self.context.pop() + self.s5_footer.append('

') + self.s5_footer.extend(self.body[start:]) + self.s5_footer.append('

') + del self.body[start:] + + def depart_header(self, node): + start = self.context.pop() + header = ['\n') + del self.body[start:] + self.s5_header.extend(header) + + def visit_section(self, node): + if not self.section_count: + self.body.append('\n\n') + self.section_count += 1 + self.section_level += 1 + if self.section_level > 1: + # dummy for matching div's + self.body.append(self.starttag(node, 'div', CLASS='section')) + else: + self.body.append(self.starttag(node, 'div', CLASS='slide')) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.section): + level = self.section_level + self.initial_header_level - 1 + if level == 1: + level = 2 + tag = 'h%s' % level + self.body.append(self.starttag(node, tag, '')) + self.context.append('\n' % tag) + else: + html4css1.HTMLTranslator.visit_subtitle(self, node) + + def visit_title(self, node): + html4css1.HTMLTranslator.visit_title(self, node) diff --git a/python/helpers/docutils/writers/s5_html/themes/README.txt b/python/helpers/docutils/writers/s5_html/themes/README.txt new file mode 100644 index 000000000000..2e01b51ee78a --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/README.txt @@ -0,0 +1,6 @@ +Except where otherwise noted (default/iepngfix.htc), all files in this +directory have been released into the Public Domain. + +These files are based on files from S5 1.1, released into the Public +Domain by Eric Meyer. For further details, please see +http://www.meyerweb.com/eric/tools/s5/credits.html. diff --git a/python/helpers/docutils/writers/s5_html/themes/big-black/__base__ b/python/helpers/docutils/writers/s5_html/themes/big-black/__base__ new file mode 100644 index 000000000000..f08be9ad5989 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/big-black/__base__ @@ -0,0 +1,2 @@ +# base theme of this theme: +big-white diff --git a/python/helpers/docutils/writers/s5_html/themes/big-black/framing.css b/python/helpers/docutils/writers/s5_html/themes/big-black/framing.css new file mode 100644 index 000000000000..5a31113fb4eb --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/big-black/framing.css @@ -0,0 +1,25 @@ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {top: 0; z-index: 1;} +div#footer {display:none;} +.slide {top: 0; width: 92%; padding: 0.1em 4% 4%; z-index: 2;} +/* list-style: none;} */ +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/python/helpers/docutils/writers/s5_html/themes/big-black/pretty.css b/python/helpers/docutils/writers/s5_html/themes/big-black/pretty.css new file mode 100644 index 000000000000..82bcc9dc432d --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/big-black/pretty.css @@ -0,0 +1,109 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: black; color: white;} +:link, :visited {text-decoration: none; color: cyan;} +#controls :active {color: #888 !important;} +#controls :focus {outline: 1px dotted #CCC;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +.slide {font-size: 3em; font-family: sans-serif; font-weight: bold;} +.slide h1 {padding-top: 0; z-index: 1; margin: 0; font-size: 120%;} +.slide h2 {font-size: 110%;} +.slide h3 {font-size: 105%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + border: none; color: #888; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: black; color: #CCC;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #AAA; + font-family: sans-serif; font-weight: bold;} + +#slide0 h1 {position: static; margin: 0 0 0.5em; padding-top: 0.3em; top: 0; + font-size: 150%; white-space: normal; background: transparent;} +#slide0 h2 {font: 110%; font-style: italic; color: gray;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #FCC;} + +.incremental, .incremental *, .incremental *:after { + color: black; visibility: visible; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: lime;} + +.slide-display {display: inline ! important;} + +.huge {font-size: 150%;} +.big {font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 0.5em 0 0.5em 1em;} + +div.sidebar {background-color: black;} + +pre.literal-block, pre.doctest-block {background-color: black;} + +tt.docutils {background-color: black;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff --git a/python/helpers/docutils/writers/s5_html/themes/big-white/framing.css b/python/helpers/docutils/writers/s5_html/themes/big-white/framing.css new file mode 100644 index 000000000000..cd343432b367 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/big-white/framing.css @@ -0,0 +1,24 @@ +/* This file has been placed in the public domain. */ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#footer {display:none;} +.slide {top: 0; width: 92%; padding: 0.25em 4% 4%; z-index: 2;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/python/helpers/docutils/writers/s5_html/themes/big-white/pretty.css b/python/helpers/docutils/writers/s5_html/themes/big-white/pretty.css new file mode 100644 index 000000000000..c5e2fcf97a63 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/big-white/pretty.css @@ -0,0 +1,107 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: white; color: black;} +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +.slide {font-size: 3em; font-family: sans-serif; font-weight: bold;} +.slide h1 {padding-top: 0; z-index: 1; margin: 0; font-size: 120%;} +.slide h2 {font-size: 110%;} +.slide h3 {font-size: 105%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + border: none; color: #005; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #444; + font-family: sans-serif; font-weight: bold;} + +#slide0 h1 {position: static; margin: 0 0 0.5em; padding-top: 0.3em; top: 0; + font-size: 150%; white-space: normal; background: transparent;} +#slide0 h2 {font: 110%; font-style: italic; color: gray;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after { + color: white; visibility: visible; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: green;} + +.slide-display {display: inline ! important;} + +.huge {font-size: 150%;} +.big {font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 0.5em 0 0.5em 1em;} + +pre.literal-block, pre.doctest-block {background-color: white;} + +tt.docutils {background-color: white;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff --git a/python/helpers/docutils/writers/s5_html/themes/default/blank.gif b/python/helpers/docutils/writers/s5_html/themes/default/blank.gif new file mode 100644 index 000000000000..75b945d25538 Binary files /dev/null and b/python/helpers/docutils/writers/s5_html/themes/default/blank.gif differ diff --git a/python/helpers/docutils/writers/s5_html/themes/default/framing.css b/python/helpers/docutils/writers/s5_html/themes/default/framing.css new file mode 100644 index 000000000000..c4727f303c32 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/framing.css @@ -0,0 +1,25 @@ +/* This file has been placed in the public domain. */ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {position: fixed; top: 0; height: 3em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +.slide {top: 0; width: 92%; padding: 2.5em 4% 4%; z-index: 2;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff --git a/python/helpers/docutils/writers/s5_html/themes/default/iepngfix.htc b/python/helpers/docutils/writers/s5_html/themes/default/iepngfix.htc new file mode 100644 index 000000000000..9f3d628b5da9 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/iepngfix.htc @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff --git a/python/helpers/docutils/writers/s5_html/themes/default/opera.css b/python/helpers/docutils/writers/s5_html/themes/default/opera.css new file mode 100644 index 000000000000..c9d1148bed27 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/opera.css @@ -0,0 +1,8 @@ +/* This file has been placed in the public domain. */ +/* DO NOT CHANGE THESE unless you really want to break Opera Show */ +.slide { + visibility: visible !important; + position: static !important; + page-break-before: always; +} +#slide0 {page-break-before: avoid;} diff --git a/python/helpers/docutils/writers/s5_html/themes/default/outline.css b/python/helpers/docutils/writers/s5_html/themes/default/outline.css new file mode 100644 index 000000000000..fa767e227e63 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/outline.css @@ -0,0 +1,16 @@ +/* This file has been placed in the public domain. */ +/* Don't change this unless you want the layout stuff to show up in the + outline view! */ + +.layout div, #footer *, #controlForm * {display: none;} +#footer, #controls, #controlForm, #navLinks, #toggle { + display: block; visibility: visible; margin: 0; padding: 0;} +#toggle {float: right; padding: 0.5em;} +html>body #toggle {position: fixed; top: 0; right: 0;} + +/* making the outline look pretty-ish */ + +#slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} +#toggle {border: 1px solid; border-width: 0 0 1px 1px; background: #FFF;} + +.outline {display: inline ! important;} diff --git a/python/helpers/docutils/writers/s5_html/themes/default/pretty.css b/python/helpers/docutils/writers/s5_html/themes/default/pretty.css new file mode 100644 index 000000000000..1cede72d4a87 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/pretty.css @@ -0,0 +1,120 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: white; color: black;} +/* Replace the background style above with the style below (and again for + div#header) for a graphic: */ +/* background: white url(bodybg.gif) -16px 0 no-repeat; */ +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} +h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +div#header, div#footer {background: #005; color: #AAB; font-family: sans-serif;} +/* background: #005 url(bodybg.gif) -16px 0 no-repeat; */ +div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} +#footer h1 {display: block; padding: 0 1em;} +#footer h2 {display: block; padding: 0.8em 1em 0;} + +.slide {font-size: 1.2em;} +.slide h1 {position: absolute; top: 0.45em; z-index: 1; + margin: 0; padding-left: 0.7em; white-space: nowrap; + font: bold 150% sans-serif; color: #DDE; background: #005;} +.slide h2 {font: bold 120%/1em sans-serif; padding-top: 0.5em;} +.slide h3 {font: bold 100% sans-serif; padding-top: 0.5em;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + background: #005; border: none; color: #779; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #449; + font-family: sans-serif; font-weight: bold;} + +#slide0 {padding-top: 1.5em} +#slide0 h1 {position: static; margin: 1em 0 0; padding: 0; color: #000; + font: bold 2em sans-serif; white-space: normal; background: transparent;} +#slide0 h2 {font: bold italic 1em sans-serif; margin: 0.25em;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after {visibility: visible; + color: white; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: green;} + +.slide-display {display: inline ! important;} + +.huge {font-family: sans-serif; font-weight: bold; font-size: 150%;} +.big {font-family: sans-serif; font-weight: bold; font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 1em 0 0.5em 2em;} + +pre.literal-block, pre.doctest-block {background-color: white;} + +tt.docutils {background-color: white;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff --git a/python/helpers/docutils/writers/s5_html/themes/default/print.css b/python/helpers/docutils/writers/s5_html/themes/default/print.css new file mode 100644 index 000000000000..9d057cc8c17c --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/print.css @@ -0,0 +1,24 @@ +/* This file has been placed in the public domain. */ +/* The following rule is necessary to have all slides appear in print! + DO NOT REMOVE IT! */ +.slide, ul {page-break-inside: avoid; visibility: visible !important;} +h1 {page-break-after: avoid;} + +body {font-size: 12pt; background: white;} +* {color: black;} + +#slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;} +#slide0 h3 {margin: 0; padding: 0;} +#slide0 h4 {margin: 0 0 0.5em; padding: 0;} +#slide0 {margin-bottom: 3em;} + +#header {display: none;} +#footer h1 {margin: 0; border-bottom: 1px solid; color: gray; + font-style: italic;} +#footer h2, #controls {display: none;} + +.print {display: inline ! important;} + +/* The following rule keeps the layout stuff out of print. + Remove at your own risk! */ +.layout, .layout * {display: none !important;} diff --git a/python/helpers/docutils/writers/s5_html/themes/default/s5-core.css b/python/helpers/docutils/writers/s5_html/themes/default/s5-core.css new file mode 100644 index 000000000000..6965f5e8f877 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/s5-core.css @@ -0,0 +1,11 @@ +/* This file has been placed in the public domain. */ +/* Do not edit or override these styles! + The system will likely break if you do. */ + +div#header, div#footer, div#controls, .slide {position: absolute;} +html>body div#header, html>body div#footer, + html>body div#controls, html>body .slide {position: fixed;} +.handout {display: none;} +.layout {display: block;} +.slide, .hideme, .incremental {visibility: hidden;} +#slide0 {visibility: visible;} diff --git a/python/helpers/docutils/writers/s5_html/themes/default/slides.css b/python/helpers/docutils/writers/s5_html/themes/default/slides.css new file mode 100644 index 000000000000..82bdc0ee0830 --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/slides.css @@ -0,0 +1,10 @@ +/* This file has been placed in the public domain. */ + +/* required to make the slide show run at all */ +@import url(s5-core.css); + +/* sets basic placement and size of slide components */ +@import url(framing.css); + +/* styles that make the slides look good */ +@import url(pretty.css); diff --git a/python/helpers/docutils/writers/s5_html/themes/default/slides.js b/python/helpers/docutils/writers/s5_html/themes/default/slides.js new file mode 100644 index 000000000000..81e04e5d442f --- /dev/null +++ b/python/helpers/docutils/writers/s5_html/themes/default/slides.js @@ -0,0 +1,558 @@ +// S5 v1.1 slides.js -- released into the Public Domain +// Modified for Docutils (http://docutils.sf.net) by David Goodger +// +// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for +// information about all the wonderful and talented contributors to this code! + +var undef; +var slideCSS = ''; +var snum = 0; +var smax = 1; +var slideIDs = new Array(); +var incpos = 0; +var number = undef; +var s5mode = true; +var defaultView = 'slideshow'; +var controlVis = 'visible'; + +var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0; +var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; +var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; + +function hasClass(object, className) { + if (!object.className) return false; + return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); +} + +function hasValue(object, value) { + if (!object) return false; + return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); +} + +function removeClass(object,className) { + if (!object) return; + object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); +} + +function addClass(object,className) { + if (!object || hasClass(object, className)) return; + if (object.className) { + object.className += ' '+className; + } else { + object.className = className; + } +} + +function GetElementsWithClassName(elementName,className) { + var allElements = document.getElementsByTagName(elementName); + var elemColl = new Array(); + for (var i = 0; i< allElements.length; i++) { + if (hasClass(allElements[i], className)) { + elemColl[elemColl.length] = allElements[i]; + } + } + return elemColl; +} + +function isParentOrSelf(element, id) { + if (element == null || element.nodeName=='BODY') return false; + else if (element.id == id) return true; + else return isParentOrSelf(element.parentNode, id); +} + +function nodeValue(node) { + var result = ""; + if (node.nodeType == 1) { + var children = node.childNodes; + for (var i = 0; i < children.length; ++i) { + result += nodeValue(children[i]); + } + } + else if (node.nodeType == 3) { + result = node.nodeValue; + } + return(result); +} + +function slideLabel() { + var slideColl = GetElementsWithClassName('*','slide'); + var list = document.getElementById('jumplist'); + smax = slideColl.length; + for (var n = 0; n < smax; n++) { + var obj = slideColl[n]; + + var did = 'slide' + n.toString(); + if (obj.getAttribute('id')) { + slideIDs[n] = obj.getAttribute('id'); + } + else { + obj.setAttribute('id',did); + slideIDs[n] = did; + } + if (isOp) continue; + + var otext = ''; + var menu = obj.firstChild; + if (!menu) continue; // to cope with empty slides + while (menu && menu.nodeType == 3) { + menu = menu.nextSibling; + } + if (!menu) continue; // to cope with slides with only text nodes + + var menunodes = menu.childNodes; + for (var o = 0; o < menunodes.length; o++) { + otext += nodeValue(menunodes[o]); + } + list.options[list.length] = new Option(n + ' : ' + otext, n); + } +} + +function currentSlide() { + var cs; + var footer_nodes; + var vis = 'visible'; + if (document.getElementById) { + cs = document.getElementById('currentSlide'); + footer_nodes = document.getElementById('footer').childNodes; + } else { + cs = document.currentSlide; + footer = document.footer.childNodes; + } + cs.innerHTML = '' + snum + '<\/span> ' + + '\/<\/span> ' + + '' + (smax-1) + '<\/span>'; + if (snum == 0) { + vis = 'hidden'; + } + cs.style.visibility = vis; + for (var i = 0; i < footer_nodes.length; i++) { + if (footer_nodes[i].nodeType == 1) { + footer_nodes[i].style.visibility = vis; + } + } +} + +function go(step) { + if (document.getElementById('slideProj').disabled || step == 0) return; + var jl = document.getElementById('jumplist'); + var cid = slideIDs[snum]; + var ce = document.getElementById(cid); + if (incrementals[snum].length > 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + removeClass(incrementals[snum][i], 'current'); + removeClass(incrementals[snum][i], 'incremental'); + } + } + if (step != 'j') { + snum += step; + lmax = smax - 1; + if (snum > lmax) snum = lmax; + if (snum < 0) snum = 0; + } else + snum = parseInt(jl.value); + var nid = slideIDs[snum]; + var ne = document.getElementById(nid); + if (!ne) { + ne = document.getElementById(slideIDs[0]); + snum = 0; + } + if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} + if (incrementals[snum].length > 0 && incpos == 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + if (hasClass(incrementals[snum][i], 'current')) + incpos = i + 1; + else + addClass(incrementals[snum][i], 'incremental'); + } + } + if (incrementals[snum].length > 0 && incpos > 0) + addClass(incrementals[snum][incpos - 1], 'current'); + ce.style.visibility = 'hidden'; + ne.style.visibility = 'visible'; + jl.selectedIndex = snum; + currentSlide(); + number = 0; +} + +function goTo(target) { + if (target >= smax || target == snum) return; + go(target - snum); +} + +function subgo(step) { + if (step > 0) { + removeClass(incrementals[snum][incpos - 1],'current'); + removeClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos],'current'); + incpos++; + } else { + incpos--; + removeClass(incrementals[snum][incpos],'current'); + addClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos - 1],'current'); + } +} + +function toggle() { + var slideColl = GetElementsWithClassName('*','slide'); + var slides = document.getElementById('slideProj'); + var outline = document.getElementById('outlineStyle'); + if (!slides.disabled) { + slides.disabled = true; + outline.disabled = false; + s5mode = false; + fontSize('1em'); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'visible'; + } + } else { + slides.disabled = false; + outline.disabled = true; + s5mode = true; + fontScale(); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'hidden'; + } + slideColl[snum].style.visibility = 'visible'; + } +} + +function showHide(action) { + var obj = GetElementsWithClassName('*','hideme')[0]; + switch (action) { + case 's': obj.style.visibility = 'visible'; break; + case 'h': obj.style.visibility = 'hidden'; break; + case 'k': + if (obj.style.visibility != 'visible') { + obj.style.visibility = 'visible'; + } else { + obj.style.visibility = 'hidden'; + } + break; + } +} + +// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) +function keys(key) { + if (!key) { + key = event; + key.which = key.keyCode; + } + if (key.which == 84) { + toggle(); + return; + } + if (s5mode) { + switch (key.which) { + case 10: // return + case 13: // enter + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + if(number != undef) { + goTo(number); + break; + } + case 32: // spacebar + case 34: // page down + case 39: // rightkey + case 40: // downkey + if(number != undef) { + go(number); + } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + break; + case 33: // page up + case 37: // leftkey + case 38: // upkey + if(number != undef) { + go(-1 * number); + } else if (!incrementals[snum] || incpos <= 0) { + go(-1); + } else { + subgo(-1); + } + break; + case 36: // home + goTo(0); + break; + case 35: // end + goTo(smax-1); + break; + case 67: // c + showHide('k'); + break; + } + if (key.which < 48 || key.which > 57) { + number = undef; + } else { + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + number = (((number != undef) ? number : 0) * 10) + (key.which - 48); + } + } + return false; +} + +function clicker(e) { + number = undef; + var target; + if (window.event) { + target = window.event.srcElement; + e = window.event; + } else target = e.target; + if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true; + if (!e.which || e.which == 1) { + if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + } +} + +function findSlide(hash) { + var target = document.getElementById(hash); + if (target) { + for (var i = 0; i < slideIDs.length; i++) { + if (target.id == slideIDs[i]) return i; + } + } + return null; +} + +function slideJump() { + if (window.location.hash == null || window.location.hash == '') { + currentSlide(); + return; + } + if (window.location.hash == null) return; + var dest = null; + dest = findSlide(window.location.hash.slice(1)); + if (dest == null) { + dest = 0; + } + go(dest - snum); +} + +function fixLinks() { + var thisUri = window.location.href; + thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); + var aelements = document.getElementsByTagName('A'); + for (var i = 0; i < aelements.length; i++) { + var a = aelements[i].href; + var slideID = a.match('\#.+'); + if ((slideID) && (slideID[0].slice(0,1) == '#')) { + var dest = findSlide(slideID[0].slice(1)); + if (dest != null) { + if (aelements[i].addEventListener) { + aelements[i].addEventListener("click", new Function("e", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "if (e.preventDefault) e.preventDefault();"), true); + } else if (aelements[i].attachEvent) { + aelements[i].attachEvent("onclick", new Function("", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "event.returnValue = false;")); + } + } + } + } +} + +function externalLinks() { + if (!document.getElementsByTagName) return; + var anchors = document.getElementsByTagName('a'); + for (var i=0; i' + + '