def depart_field(self, node): '''Simply calls the super method and then simply removes the just rendered field if it should be ignored ''' # call super and reset flags (removing field if in ignore list) LT.depart_field(self, node) # process fbib field if something has to be done: field_name = self._current_field_name__.lower() field_value = self._current_field_body__ # is a particular field which has to be rendered somewhere else? if field_name in ("author", "authors"): auth, auth_affil, affil = parse_authors(field_value) envdict = self.builder.app.env.metadata[ self.builder.app.config.master_doc] envdict['authorsWithAffiliations'] = auth_affil envdict['affiliations'] = affil self.elements.update({'author': auth}) elif field_name == "revision": self.elements.update({'release': field_value}) elif field_name == "citations" or field_name == 'citation': envdict = self.builder.app.env.metadata[ self.builder.app.config.master_doc] envdict['citations'] = field_value elif field_name == "abstract": self._abstract_text_reminder__ = field_value # remove field from built document if it has to be ignored if field_name in self._ignored_fieldnames__ and \ self._current_field_start__ > -1: self.body = self.body[:self._current_field_start__] # reset values: self._current_field_name__ = self._current_field_body__ = '' self._current_field_start__ = -1
def depart_field(self, node): '''Simply calls the super method and then simply removes the just rendered field if it should be ignored ''' # call super and reset flags (removing field if in ignore list) LT.depart_field(self, node) # process fbib field if something has to be done: field_name = self._current_field_name__.lower() field_value = self._current_field_body__ # is a particular field which has to be rendered somewhere else? if field_name in ("author", "authors"): auth, auth_affil, affil = parse_authors(field_value) envdict = self.builder.app.env.metadata[self.builder.app.config.master_doc] envdict['authorsWithAffiliations'] = auth_affil envdict['affiliations'] = affil self.elements.update({'author': auth}) elif field_name == "revision": self.elements.update({'release': field_value}) elif field_name == "citations" or field_name == 'citation': envdict = self.builder.app.env.metadata[self.builder.app.config.master_doc] envdict['citations'] = field_value elif field_name == "abstract": self._abstract_text_reminder__ = field_value # remove field from built document if it has to be ignored if field_name in self._ignored_fieldnames__ and \ self._current_field_start__ > -1: self.body = self.body[:self._current_field_start__] # reset values: self._current_field_name__ = self._current_field_body__ ='' self._current_field_start__ = -1
def visit_field(self, node): '''Simply stores informations here about the current field in order to remove it later in `depart_field`, if the current field has not to be rendered, and then calls the super method ''' self._current_field_name__ = str(node.children[0].children[0]) self._current_field_start__ = len(self.body) LT.visit_field(self, node)
def depart_field_list(self, node): '''Calls the super method nd then, if a bib.field named abstract has been found, puts the abstract by appending latex's '\begin{abstract}' (... and so on) to `self.body`''' LT.depart_field_list(self, node) if self._abstract_text_reminder__ is not None: self.body.extend([r'\begin{abstract}', '\n', self._abstract_text_reminder__, '\n', r'\end{abstract}']) self._abstract_text_reminder__ = None
def __init__(self, document, builder): LT.__init__(self, document, builder) # define our variables: self._abstract_text_reminder__ = None self._current_field_start__ = -1 self._current_fieldbody_start__ = -1 self._current_field_name__ = '' self._current_field_body__ = ''
def _depart_issue_node_latex(translator: LaTeXTranslator, node: IssueNode): """ Depart an :class:`~.IssueNode`. :param translator: :param node: The node being visited. """ translator.depart_reference(node)
def _depart_github_object_link_node_latex(translator: LaTeXTranslator, node: GitHubObjectLinkNode): """ Depart an :class:`~.GitHubObjectLinkNode`. :param translator: :param node: The node being visited. """ translator.depart_reference(node)
def depart_field_list(self, node): '''Calls the super method nd then, if a bib.field named abstract has been found, puts the abstract by appending latex's '\begin{abstract}' (... and so on) to `self.body`''' LT.depart_field_list(self, node) if self._abstract_text_reminder__ is not None: self.body.extend([ r'\begin{abstract}', '\n', self._abstract_text_reminder__, '\n', r'\end{abstract}' ]) self._abstract_text_reminder__ = None
def _visit_github_object_link_node_latex(translator: LaTeXTranslator, node: GitHubObjectLinkNode): """ Visit a :class:`~.GitHubObjectLinkNode`. :param translator: :param node: The node being visited. """ node.children = node.children[:1] translator.visit_reference(node)
def visit_seealso(translator: LaTeXTranslator, node: addnodes.seealso) -> None: """ Visit an :class:`addnodes.seealso`` node. :param translator: :param node: """ if len(node) > 1: LaTeXTranslator.visit_seealso(translator, node) else: translator.body.append('\n\n\\sphinxstrong{%s:} ' % admonitionlabels["seealso"])
def depart_title(translator: LaTeXTranslator, node: nodes.title) -> None: """ Depart a :class:`docutils.nodes.title` node. :param translator: :param node: The node itself. """ translator.in_title = 0 if isinstance(node.parent, nodes.table): translator.table.caption = translator.popbody() else: translator.body.append(translator.context.pop())
def __init__(self, builder, document): if not hasattr(builder, 'config'): raise TypeError("Unexpected type for builder {0}".format( type(builder))) LaTeXTranslator.__init__(self, document, builder) newlines = builder.config.text_newlines if newlines == 'windows': self.nl = '\r\n' elif newlines == 'native': self.nl = os.linesep else: self.nl = '\n'
def _visit_issue_node_latex(translator: LaTeXTranslator, node: IssueNode): """ Visit an :class:`~.IssueNode`. If the node points to a valid issue / pull request, add a tooltip giving the title of the issue / pull request and a hyperlink to the page on GitHub. :param translator: :param node: The node being visited. """ node.children = node.children[:1] translator.visit_reference(node)
def depart_table(self, node): """ Horrible hack which removes the TERRIBLE (and HARD CODED) frame in the bottom of longtable saying "continued on next page". WHY Sphinx does that? WHY??!!!?? """ # get the current body length. Tables in sphinx writer superclass work weirdly. # The real body is allocated as last element of self.bodystack. self.body at this point # is the table body (which we are about to include in self.,body in the method # LT.depart_table) strt = len(self.bodystack[-1]) LT.depart_table(self, node) if self.body[-1].strip() == "\\end{longtable}": for i in xrange(strt, len(self.body)): if self.body[i].strip() == '\\endlastfoot': break elif self.body[i].startswith(r'\hline \multicolumn{'): self.body[i] = self.body[i].replace("\\hline", "").replace("{|r|}", "{r}")
def astext(self): # build a dict of bibliographic fields, and inject them as newcommand # in the latex header # for commands starting with doi-, build also the citation command with suffix "Citation" # The final string should be unicode, as jinja (used by sphinx later) expects unicodes # So perfect, let's work with unicode. But we make use of # "%s%s" % (A, B) and "{}{}".format(A, B) here, and "%s%s" are bytes in py2 and str in py3 # So we should convert them as well to unicode # All in all, we use the touni function which is py2 and py3 compatible LATEX_COMMAND = touni("\\rst{}{}") # value should be already formatted for latex.The only thing is that we want to # preserve also rst newlines so we replace \n with \\ (which becomes \\\\ after # escaping, plus a final \n to preserve "visually" the newlines in latex, although it # won't be rendered in latex output): NEWLINE = touni("\n") LATEX_NEWLINE = touni('\\\\\n') latexcommands = {} data = self.builder.app.env.metadata[ self.builder.app.config.master_doc] for name, value in data.iteritems(): name, value = touni(name), touni(value) latex_command_name = LATEX_COMMAND.format(name[0].upper(), name[1:]) try: value = value.replace(NEWLINE, LATEX_NEWLINE) except AttributeError: # not a string? stringify it: value = touni(str(value)) latexcommands[latex_command_name] = value # Convert the dict latexcommands into a latex string, newline-separated LATEX_NEWCOMMAND = touni("\\newcommand{%s}{%s}") newcommands = NEWLINE.join(LATEX_NEWCOMMAND % (n, v) for n, v in latexcommands.iteritems()) preamble_wrapper = \ touni("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" "%% Newcommands converted from rst fields before the title:\n" "{}\n" "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" "%% Content of latex_elements['preamble'] in conf.py (if defined):\n" "%(preamble)s\n" "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ).format(newcommands) # merge existing preamble: preamble = preamble_wrapper % { touni('preamble'): touni(self.elements.get("preamble", "")) } # update object elements: self.elements.update({ 'preamble': preamble, }) return LT.astext(self)
def __init__(self, document, builder): """ Set up Beamer elements. """ LaTeXTranslator.__init__(self, document, builder) self.elements["maketitle"] = r"\maketitle" self.elements["makeindex"] = "" self.elements["printindex"] = "" self.elements["tableofcontents"] = "" self.elements["wrapperclass"] = "beamer" self.elements["beamer_theme"] = builder.config_options["beamer_theme"] self.section_levels = [ r"\begin{frame}", r"\subsection", r"\subsubsection", ] self.elements["allowframebreaks"] = \ builder.config_options["allowframebreaks"]
def depart_table(self, node): """ Horrible hack which removes the TERRIBLE (and HARD CODED) frame in the bottom of longtable saying "continued on next page". WHY Sphinx does that? WHY??!!!?? """ # get the current body length. Tables in sphinx writer superclass work weirdly. # The real body is allocated as last element of self.bodystack. self.body at this point # is the table body (which we are about to include in self.,body in the method # LT.depart_table) strt = len(self.bodystack[-1]) LT.depart_table(self, node) if self.body[-1].strip() == "\\end{longtable}": for i in xrange(strt, len(self.body)): if self.body[i].strip() == '\\endlastfoot': break elif self.body[i].startswith(r'\hline \multicolumn{'): self.body[i] = self.body[i].replace("\\hline", "").replace( "{|r|}", "{r}")
def visit_desc(translator: LaTeXTranslator, node: addnodes.desc) -> None: """ Visit an :class:`addnodes.desc` node and add a custom table of contents label for the item, if required. :param translator: :param node: """ if node["domain"] == "py": translator.body.append(r"\needspace{5\baselineskip}") if "sphinxcontrib.toctree_plus" in translator.config.extensions: # 3rd party from sphinxcontrib import toctree_plus # nodep toctree_plus.visit_desc(translator, node) else: LaTeXTranslator.visit_desc(translator, node)
def visit_literal_block(self, node): code = node.astext().rstrip('\n') self.builder.info('node=%s' % node) envname = node.get('classes', None) if not envname: return LaTeXTranslator.visit_literal_block(self, node) envname = envname[0] self.body.append('\n\\begin{%s}\n%s\n\\end{%s}\n' % (envname, code, envname)) raise nodes.SkipNode
def depart_literal_block(self, node): code = self.verbatim.rstrip('\n') self.builder.info('node=%s' % node) envname = node.get('classes', None) if not envname: return LaTeXTranslator.depart_literal_block(self, node) envname = envname[0] self.body.append('\n\\begin{%s}\n%s\n\\end{%s}\n' % (envname, code, envname)) self.verbatim = None
def __init__(self, document, builder): LaTeXTranslator.__init__(self, document, builder) self.elements["passoptionstopackages"] += "\n\\usepackage{xr}" self.elements["passoptionstopackages"] += "\n\\usepackage{xr-hyper}" self.settings = document.settings lines = [] variables = self.settings.multilatex_variables for name, value in variables.items(): lines.append("\\newcommand{{\\{0}}}{{{1}}}".format(name, value)) for node in self.settings.multilatex_all_latex_document_nodes: filename = node["multilatex-filename"] if filename != self.settings.multilatex_output_filename: lines.append("\\externaldocument{{{0}}}".format(filename)) self.elements["preamble"] += "\n" + "\n".join(lines)
def __init__(self, document, builder): LaTeXTranslator.__init__(self, document, builder) self.elements["passoptionstopackages"] += "\n\\usepackage{xr}" self.elements["passoptionstopackages"] += "\n\\usepackage{xr-hyper}" self.settings = document.settings lines = [] variables = self.settings.multilatex_variables for name, value in variables.items(): lines.append("\\newcommand{{\\{0}}}{{{1}}}" .format(name, value)) for node in self.settings.multilatex_all_latex_document_nodes: filename = node["multilatex-filename"] if filename != self.settings.multilatex_output_filename: lines.append("\\externaldocument{{{0}}}".format(filename)) self.elements["preamble"] += "\n" + "\n".join(lines)
def astext(self): # build a dict of bibliographic fields, and inject them as newcommand # in the latex header # for commands starting with doi-, build also the citation command with suffix "Citation" # The final string should be unicode, as jinja (used by sphinx later) expects unicodes # So perfect, let's work with unicode. But we make use of # "%s%s" % (A, B) and "{}{}".format(A, B) here, and "%s%s" are bytes in py2 and str in py3 # So we should convert them as well to unicode # All in all, we use the touni function which is py2 and py3 compatible LATEX_COMMAND = touni("\\rst{}{}") # value should be already formatted for latex.The only thing is that we want to # preserve also rst newlines so we replace \n with \\ (which becomes \\\\ after # escaping, plus a final \n to preserve "visually" the newlines in latex, although it # won't be rendered in latex output): NEWLINE = touni("\n") LATEX_NEWLINE = touni('\\\\\n') latexcommands = {} data = self.builder.app.env.metadata[self.builder.app.config.master_doc] for name, value in data.iteritems(): name, value = touni(name), touni(value) latex_command_name = LATEX_COMMAND.format(name[0].upper(), name[1:]) try: value = value.replace(NEWLINE, LATEX_NEWLINE) except AttributeError: # not a string? stringify it: value = touni(str(value)) latexcommands[latex_command_name] = value # Convert the dict latexcommands into a latex string, newline-separated LATEX_NEWCOMMAND = touni("\\newcommand{%s}{%s}") newcommands = NEWLINE.join(LATEX_NEWCOMMAND % (n, v) for n, v in latexcommands.iteritems()) preamble_wrapper = \ touni("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" "%% Newcommands converted from rst fields before the title:\n" "{}\n" "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" "%% Content of latex_elements['preamble'] in conf.py (if defined):\n" "%(preamble)s\n" "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ).format(newcommands) # merge existing preamble: preamble = preamble_wrapper % {touni('preamble'): touni(self.elements.get("preamble", ""))} # update object elements: self.elements.update({ 'preamble': preamble, }) return LT.astext(self)
def visit_reference(self, node): uri = node.get("refuri", "") match = self.inter_doc_re.match(uri) if match: target_docname = match.group("docname") short_uri = match.group("short_uri") for latex_document in \ self.settings.multilatex_all_latex_document_nodes: docnames = latex_document["multilatex-docnames"] if target_docname in docnames: target_latex_document = latex_document break else: target_latex_document = None if target_latex_document: options = target_latex_document["multilatex-options"] parameters = { "ref": short_uri[1:].replace("#", ":"), "title": options.get("title", "unknown external document"), } prefix = self.exdoc_ref_prefix.format(**parameters) suffix = self.exdoc_ref_suffix.format(**parameters) else: prefix = self.unknown_exdoc_ref_prefix suffix = self.unknown_exdoc_ref_suffix self.body.append(prefix) self.context.append(suffix) node["refuri"] = short_uri try: LaTeXTranslator.visit_reference(self, node) finally: node["refuri"] = uri else: self.context.append("") LaTeXTranslator.visit_reference(self, node)
def __init__(self, document, builder): LaTeXTranslator.__init__(self, document, builder)
def depart_reference(self, node): LaTeXTranslator.depart_reference(self, node) self.body.append(self.context.pop())
def depart_field_body(self, node): '''Simply calls the super method and then stores the rendered field body for later use''' LT.depart_field_body(self, node) self._current_field_body__ = "".join(self.body[self._current_fieldbody_start__:]).strip()
def __init__(self, document, builder): LaTeXTranslator.__init__(self, document, builder) # flag whether caption is under container[litelal_block=True] node self.in_container_literal_block = 0
def visit_title(translator: LaTeXTranslator, node: nodes.title) -> None: """ Visit a :class:`docutils.nodes.title` node. :param translator: :param node: The node itself. """ if isinstance(node.parent, addnodes.seealso): # the environment already handles this raise nodes.SkipNode elif isinstance(node.parent, nodes.section): if translator.this_is_the_title: if len(node.children) != 1 and not isinstance( node.children[0], nodes.Text): logger.warning(__("document title is not a single Text node"), location=node) if not translator.elements["title"]: # text needs to be escaped since it is inserted into # the output literally translator.elements["title"] = translator.escape(node.astext()) translator.this_is_the_title = 0 raise nodes.SkipNode else: short = '' if node.traverse(nodes.image): short = f"[{translator.escape(' '.join(clean_astext(node).split()))}]" try: translator.body.append( fr'\{translator.sectionnames[translator.sectionlevel]}{short}{{' ) except IndexError: # just use "subparagraph", it's not numbered anyway translator.body.append( fr'\{translator.sectionnames[-1]}{short}{{') # breakpoint() translator.context.append( f'}}\n{translator.hypertarget_to(node.parent)}') elif isinstance(node.parent, nodes.topic): translator.body.append(r'\sphinxstyletopictitle{') translator.context.append('}\n') elif isinstance(node.parent, nodes.sidebar): translator.body.append(r'\sphinxstylesidebartitle{') translator.context.append('}\n') elif isinstance(node.parent, nodes.Admonition): translator.body.append('{') translator.context.append('}\n') elif isinstance(node.parent, nodes.table): # Redirect body output until title is finished. translator.pushbody([]) else: logger.warning( __("encountered title node not in section, topic, table, admonition or sidebar" ), location=node, ) translator.body.append("\\sphinxstyleothertitle{") translator.context.append('}\n') translator.in_title = 1
def depart_field_body(self, node): '''Simply calls the super method and then stores the rendered field body for later use''' LT.depart_field_body(self, node) self._current_field_body__ = "".join( self.body[self._current_fieldbody_start__:]).strip()
def visit_field_body(self, node): '''Stores the length of self.body in order to retrieve the rendered field body, if needed for processing it, and then calls the super method''' # set flags and call super self._current_fieldbody_start__ = len(self.body) LT.visit_field_body(self, node)
def visit_document(self, node): LaTeXTranslator.visit_document(self, node)
def visit_desc(translator: LaTeXTranslator, node: addnodes.desc) -> None: translator.body.append('\n\n\\vspace{5px}') LaTeXTranslator.visit_desc(translator, node)