📄 __init__.py
字号:
# Author: Felix Wiemann# Contact: Felix_Wiemann@ososo.de# Revision: $Revision: 4242 $# Date: $Date: 2006-01-06 00:28:53 +0100 (Fri, 06 Jan 2006) $# 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 reimport os.pathfrom types import ListTypeimport docutilsfrom docutils import nodes, writers, utilsfrom docutils.writers.newlatex2e import unicode_mapfrom docutils.transforms import writer_auxclass 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. ' 'You must specify the location of the tools/stylesheets/latex.tex ' 'stylesheet file contained in the Docutils distribution tarball to ' 'make the LaTeX output work.', (('Specify a stylesheet file. The path is used verbatim to include ' 'the file. Overrides --stylesheet-path.', ['--stylesheet'], {'default': '', 'metavar': '<file>', 'overrides': 'stylesheet_path'}), ('Specify a stylesheet file, relative to the current working ' 'directory. Overrides --stylesheet. Default: "%s"' % default_stylesheet_path, ['--stylesheet-path'], {'metavar': '<file>', 'overrides': 'stylesheet', 'default': default_stylesheet_path}), ('Specify a user stylesheet file. See --stylesheet.', ['--user-stylesheet'], {'default': '', 'metavar': '<file>', 'overrides': 'user_stylesheet_path'}), ('Specify a user stylesheet file. See --stylesheet-path.', ['--user-stylesheet-path'], {'metavar': '<file>', 'overrides': 'user_stylesheet'}) ),) settings_defaults = { # Many Unicode characters are provided by unicode_map.py. 'output_encoding': 'ascii', '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] 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.bodyclass LaTeXException(Exception): """ Exception base class to for exceptions which influence the automatic generation of LaTeX code. """class SkipAttrParentLaTeX(LaTeXException): """ Do not generate ``\Dattr`` and ``\renewcommand{\Dparent}{...}`` for this node. To be raised from ``before_...`` methods. """class SkipParentLaTeX(LaTeXException): """ Do not generate ``\renewcommand{\DNparent}{...}`` 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) self.write_header() def write_header(self): a = self.header.append a('%% Generated by Docutils %s <http://docutils.sourceforge.net>.' % docutils.__version__) a('') a('% Docutils settings:') lang = self.settings.language_code or '' a(r'\providecommand{\Dlanguageiso}{%s}' % lang) a(r'\providecommand{\Dlanguagebabel}{%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:') a(r'\providecommand{\Dsetattr}[2]{}') a(r'\providecommand{\Dparent}{} % variable') a(r'\providecommand{\Dattr}[5]{#5}') a(r'\providecommand{\Dattrlen}{} % variable') a(r'\providecommand{\Dtitleastext}{x} % variable') a(r'\providecommand{\Dsinglebackref}{} % variable') a(r'\providecommand{\Dmultiplebackrefs}{} % variable') a(r'\providecommand{\Dparagraphindented}{false} % variable') 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'{\Dtextunderscore}', } character_map.update(unicode_map) #character_map.update(special_map) # `att_map` is for encoding attributes. According to # <http://www-h.eng.cam.ac.uk/help/tpl/textprocessing/teTeX/latex/latex2e-html/ltx-164.html>, # 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'\Drelativeunit' elif unit in ('', 'px'): # If \Dpixelunit is "pt", this gives the same notion # of pixels as graphicx. value = str(float(value) * 0.75) unit = '\Dpixelunit' 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, for some not-so-obvious # reason, 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'{\Dtextliteralsinglequote}') 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'{\Dtextleftdblquote}' or r'{\Dtextrightdblquote}') 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{\Dparagraphindented}{%s}' % (self.is_indented(node) and 'true' or 'false')) def before_title(self, node): self.append(r'\renewcommand{\Dtitleastext}{%s}' % self.encode(node.astext())) self.append(r'\renewcommand{\Dhassubtitle}{%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()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -