Source code for udapi.block.write.tikz

"""Tikz class is a writer for LaTeX with tikz-dependency."""
import logging

from udapi.core.basewriter import BaseWriter


[docs] class Tikz(BaseWriter): r"""A writer of files in the LaTeX with tikz-dependency format. Usage:: udapy write.Tikz < my.conllu > my.tex # or for 2D tree-like rendering udapy write.Tikz as_tree=1 < my.conllu > my.tex pdflatex my.tex xdg-open my.pdf Long sentences may result in too large pictures. You can tune the width (in addition to changing fontsize or using minipage and rescaling) with ``\begin{deptext}[column sep=0.2cm]`` or individually for each word: ``My \&[.5cm] dog \& etc.`` By default, the height of the horizontal segment of a dependency edge is proportional to the distance between the linked words. You can tune the height with: ``\depedge[edge unit distance=1.5ex]{9}{1}{deprel}`` See `tikz-dependency documentation <http://mirrors.ctan.org/graphics/pgf/contrib/tikz-dependency/tikz-dependency-doc.pdf>`_ for details. With ``as_tree=1``, there are two options how to visualize deprels: either as labels positioned on the edges by uncommenting the relevant style definition, or by adding ``deprel`` to the list of attributes, so deprels are above/below the words. The latter is the default because the edge labels need manual tweaks to prevent overlapping. Alternatives: * use `write.TextModeTrees` and include it in verbatim environment in LaTeX. * use `write.Html`, press "Save as SVG" button, convert to pdf and include in LaTeX. """ def __init__(self, print_sent_id=True, print_text=True, print_preambule=True, attributes=None, as_tree=False, comment_attribute=None, enhanced=False, **kwargs): """Create the Tikz block object. Args: print_sent_id: print sent_id (`tree.address()`) as a LaTeX comment (default=True) print_text: print sentence text (`tree.get_sentence()`) as a LaTeX comment (default=True) print_preambule: surround each document with LaTeX preambule (`documentclass` etc) and `end{document}` (default=True) attributes: comma-separated list of node attributes to print (each on a separate line). as_tree: boolean - should print it as a 2D tree? comment_attribute: which attribute to print as a string under each graph (e.g. text_en) enhanced: boolean - print the enhanced graph below the sentence, too? """ super().__init__(**kwargs) self.print_sent_id = print_sent_id self.print_text = print_text self.print_preambule = print_preambule if attributes is not None: self.node_attributes = attributes.split(',') elif as_tree: self.node_attributes = 'form,upos,deprel'.split(',') else: self.node_attributes = 'form,upos'.split(',') self.as_tree = as_tree self.comment_attribute = comment_attribute if as_tree and enhanced: raise ValueError("The enhanced graph cannot be printed as a tree") self.enhanced = enhanced
[docs] def before_process_document(self, doc): super().before_process_document(doc) if self.print_preambule: print(r'\documentclass[multi=dependency]{standalone}') print(r'\usepackage[T1]{fontenc}') print(r'\usepackage[utf8]{inputenc}') print(r'\usepackage{tikz-dependency}') if self.as_tree: print(r'\tikzset{depedge/.style = {blue,thick}, %,<-') print(r' deplabel/.style = {opacity=0, %black, fill opacity=0.9, text opacity=1,') print(r' % yshift=4pt, pos=0.1, inner sep=0, fill=white, font={\scriptsize}') print(r' },') print(r' depnode/.style = {draw,circle,fill,blue,inner sep=1.5pt},') print(r' depguide/.style = {dashed,gray},') print(r'}') print(r'\newlength{\deplevel}\setlength{\deplevel}{8mm}') print(r'\newlength{\depskip}\setlength{\depskip}{4mm}') print(r'\newcommand{\deptrans}[1]{\node (t) at (\matrixref.south)[yshift=-1mm]' " {``#1''};}") print(r'\begin{document}')
[docs] def after_process_document(self, doc): if self.print_preambule: print(r'\end{document}') logging.info('Use pdflatex to compile the output') super().after_process_document(doc)
def _tex_escape(self, string): return string.replace('_', r'\_').replace('$', '\$').replace('[', '$[$').replace(']', '$]$')
[docs] def process_tree(self, tree): print(r'\begin{dependency}') print(r'\begin{deptext}') nodes = tree.descendants if self.print_sent_id: print('% sent_id = ' + tree.address()) if self.print_text: print("% text = " + tree.get_sentence()) comment = tree.comment if comment: comment = comment.rstrip() print('%' + comment.replace('\n', '\n%')) lines = ['' for _ in self.node_attributes] for node in nodes: values = [self._tex_escape(v) for v in node.get_attrs(self.node_attributes)] max_len = max(len(value) for value in values) for index, value in enumerate(values): if node.ord > 1: lines[index] += r' \& ' lines[index] += value.ljust(max_len) for line in lines: print(line + r' \\') print(r'\end{deptext}') if self.as_tree: depths = [n._get_attr('depth') for n in nodes] max_depth = max(depths) for node in nodes: print(r'\node (w%d) [yshift=\depskip+%s\deplevel,depnode] at (\wordref{1}{%d}) {};' % (node.ord, max_depth - depths[node.ord - 1], node.ord)) for node in nodes: print(r'\draw[depguide] (w%d)--(\wordref{1}{%d});' % (node.ord, node.ord), end='') if node.parent.is_root(): print('') else: print(r' \draw[depedge] (w%d)--node[deplabel] {%s} (w%d);' % (node.ord, node.deprel, node.parent.ord)) else: for node in nodes: if node.parent.is_root(): print(r'\deproot{%d}{root}' % node.ord) else: print(r'\depedge{%d}{%d}{%s}' % (node.parent.ord, node.ord, node.deprel)) if self.enhanced: for dep in node.deps: if dep['parent'].is_root(): print(r'\deproot[edge below]{%d}{root}' % node.ord) else: print(r'\depedge[edge below]{%d}{%d}{%s}' % (dep['parent'].ord, node.ord, dep['deprel'])) if self.comment_attribute and tree.comment: start_pos = tree.comment.find(self.comment_attribute + ' = ') if start_pos != -1: start_pos += len(self.comment_attribute) + 3 end_pos = tree.comment.find('\n', start_pos) print(r'\deptrans{' + tree.comment[start_pos:end_pos]) print(r'\end{dependency}') print('') # empty line marks a new paragraph in LaTeX, but multi=dependency causes newpage