Belle II Software  release-05-02-19
b2latex.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 import subprocess
5 
6 
7 class LatexObject(object):
8  """
9  Common base class of all Latex Wrapper objects
10  """
11 
12  def __init__(self):
13  """
14  Constructor, initialize output with empty string
15  """
16 
17  self.output = ''
18 
19  def __str__(self):
20  """
21  Transform object to string, in this case, just returns .the generated latex-code
22  """
23  return self.output
24 
25  def add(self):
26  """
27  Add latex-code to the output string.
28  This method is usually overriden in the subclasses
29  """
30  return self
31 
32  def finish(self):
33  """
34  Finishes the generation of latex-code.
35  E.g. adds end latex-commands
36  This method is usually overriden in the subclasses
37  """
38  return self
39 
40  def save(self, filename, compile=False):
41  """
42  Saves the latex-code into a file, adds preamble and end of document,
43  and compiles the code if requested.
44  @param filename latex-code is stored in this file, should end on .tex
45  @param compile compile the .tex file using pdflatex into a .pdf file
46  """
47  output = r"""
48  \documentclass[10pt,a4paper]{article}
49  \usepackage[latin1]{inputenc}
50  \usepackage[T1]{fontenc}
51  \usepackage{amsmath}
52  \usepackage{amsfonts}
53  \usepackage{amssymb}
54  \usepackage{graphicx}
55  \usepackage{caption}
56  \usepackage{lmodern}
57  \usepackage{placeins}
58  \usepackage{multicol}
59  \usepackage{tikz}
60  \usetikzlibrary{shapes.arrows,chains, positioning}
61  \usepackage{booktabs} %professional tables
62  \usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry}
63  \usepackage{microtype} %optimises spacing, needs to go after fonts
64  \usepackage{hyperref} %adds links (also in TOC), should be loaded at the very end
65  \usepackage{longtable}
66  \usepackage{color}
67  \usepackage{listings}
68 
69  \definecolor{gray}{rgb}{0.4,0.4,0.4}
70  \definecolor{darkblue}{rgb}{0.0,0.0,0.6}
71  \definecolor{cyan}{rgb}{0.0,0.6,0.6}
72 
73  \lstset{
74  basicstyle=\ttfamily\scriptsize,
75  columns=fullflexible,
76  showstringspaces=false,
77  commentstyle=\color{gray}\upshape
78  }
79 
80  \lstdefinelanguage{XML}
81  {
82  morestring=[b]",
83  morestring=[s]{>}{<},
84  morecomment=[s]{<?}{?>},
85  stringstyle=\color{black},
86  identifierstyle=\color{darkblue},
87  keywordstyle=\color{cyan},
88  morekeywords={xmlns,version,type}% list your attributes here
89  }
90 
91  \usepackage[load-configurations=abbreviations]{siunitx}
92  \makeatletter
93  % In newer versions of latex there is a problem with the calc package and tikz
94  % http://tex.stackexchange.com/questions/289551/how-to-resolve-conflict-between-versions-of-texlive-and-pgf
95  \def\pgfmathparse@#1{%
96  % Stuff for calc compatiability.
97  \let\real=\pgfmath@calc@real
98  \let\minof=\pgfmath@calc@minof
99  \let\maxof=\pgfmath@calc@maxof
100  \let\ratio=\pgfmath@calc@ratio
101  \let\widthof=\pgfmath@calc@widthof
102  \let\heightof=\pgfmath@calc@heightof
103  \let\depthof=\pgfmath@calc@depthof
104  % No (math) units yet.
105  \global\pgfmathunitsdeclaredfalse
106  \global\pgfmathmathunitsdeclaredfalse
107  % Expand expression so any reamining CSs are registers
108  % or box dimensions (i.e. |\wd|, |\ht|, |\dp|).
109  \edef\pgfmath@expression{#1}%
110  %
111  \expandafter\pgfmathparse@trynumber@loop\pgfmath@expression\pgfmath@parse@stop
112  %
113  % this here is the _real_ parser. it is invoked by
114  % \pgfmathparse@trynumber@loop if that says "this is no number"
115  %\pgfmathparse@@\pgfmath@parse@stop%
116  }
117  \makeatother
118  \begin{document}
119  """
120  output += self.output
121  output += r"\end{document}"
122 
123  with open(filename, 'w') as f:
124  f.write(output)
125  if compile:
126  for i in range(0, 2):
127  ret = subprocess.call(['pdflatex', '-halt-on-error', '-interaction=nonstopmode', filename])
128  if ret != 0:
129  raise RuntimeError("pdflatex failed to create FEI summary PDF, please check.")
130  return self
131 
132 
134  """
135  Convinience class implementing += operator, can be used instead of raw LatexObject to collect
136  all the latex code in your project which should go into a common file.
137  """
138 
139  def add(self, text=''):
140  """
141  Adds an object to the output
142  @param text string or object with implicit string conversion (like LatexObject)
143  """
144  self.output += str(text)
145  return self
146 
147  def __iadd__(self, text):
148  """
149  Adds an object to the output
150  @param text string or object with implicit string conversion (like LatexObject)
151  """
152  self.add(text)
153  return self
154 
155 
157  """
158  Used for wrapping conventionel text into latex-code.
159  Has to possibility to handle python-style format-placeholders
160  """
161 
162  def __init__(self, text=''):
163  """
164  Calls super-class initialize and adds initial text to output
165  @param text intial text, usually you want to give a raw string r"some text"
166  """
167  super(String, self).__init__()
168 
169  self.output += str(text)
170 
171  def add(self, text=''):
172  """
173  Adds an object to the output, can contain python-placeholders
174  @param text string or object with implicit string conversion (like LatexObject)
175  """
176  self.output += str(text)
177  return self
178 
179  def finish(self, **kwargs):
180  """
181  Finish the generation of the string by replacing possible placehholders with the given dictionary
182  @param kwargs dictionary used to replace placeholders
183  """
184 
185  self.output = self.output.format(**kwargs) + '\n'
186  return self
187 
188 
190  """
191  Used for wrapping code in a listing environment
192  """
193 
194  def __init__(self, language='XML'):
195  """
196  Calls super-class initialize and adds initial text to output
197  @param text intial text, usually you want to give a raw string r"some text"
198  """
199  super(Listing, self).__init__()
200 
201  self.output += r'\lstset{language=' + language + '}\n'
202  self.output += r'\begin{lstlisting}[breaklines=true]' + '\n'
203 
204  def add(self, text=''):
205  """
206  Adds code to the output
207  @param code which is wrapped in the listing environment
208  """
209  self.output += str(text)
210  return self
211 
212  def finish(self, **kwargs):
213  """
214  Finish the generation of the lsiting environment
215  """
216 
217  self.output += r'\end{lstlisting}'
218  return self
219 
220 
222  """
223  Defines the colourlist latex-command, which draws a bargraph with relative
224  fractions indicated by colours using tikz.
225  After including this object in you latex code the command \\bargraph is available.
226  You should include only one of these objects in your latex code.
227  """
228 
229 
230  colours = ["red", "green", "blue", "orange", "cyan", "purple"]
231 
232  def __init__(self):
233  """
234  Calls super-class init, adds definition of colourlist to latex code.
235  """
236  super(DefineColourList, self).__init__()
237  self.output += r"\def\colourlist{{" + ', '.join('"%s"' % (c) for c in self.colours) + r"}}" + '\n'
238  self.output += r"""
239  \tikzset{nodeStyle/.style={text height=\heightof{A},text depth=\depthof{g}, inner sep = 0pt, node distance = -0.15mm}}
240  \newcount\colourindex \colourindex=-1
241  \newcommand{\plotbar}[1]{
242  \begin{tikzpicture}[start chain=going right, nodes = {font=\sffamily}]
243  \global\colourindex=-1
244  \foreach \percent/\name in {
245  #1
246  } {
247  \ifx\percent\empty\else % If \percent is empty, do nothing
248  \global\advance\colourindex by 1
249  \ifnum"""
250  self.output += str(len(self.colours) - 1)
251  self.output += r"""5<\colourindex %back to first colour if we run out
252  \global\colourindex=0
253  \fi
254  \pgfmathparse{\colourlist[\the\colourindex]} % Get color from cycle list
255  \edef\color{\pgfmathresult} % and store as \color
256  \node[nodeStyle, draw, on chain, fill={\color!40}, minimum width=\percent*1.0, minimum height=12] {\name};
257  \fi
258  };
259  \end{tikzpicture}
260  }
261  """
262 
263 
265  """
266  Adds a new section to your latex code with some additional commands
267  to force a pagebreak and add a barrier for figure objects.
268  """
269 
270  def __init__(self, name):
271  """
272  Calls super-class init and adds necessary latex commands to output.
273  """
274  super(Section, self).__init__()
275  self.output += r"\raggedbottom" + '\n'
276  self.output += r"\pagebreak[0]" + '\n'
277  self.output += r"\FloatBarrier" + '\n'
278  self.output += r"\section{" + str(name) + r"}" + '\n'
279 
280 
282  """
283  Adds a new subsection to your latex code.
284  """
285 
286  def __init__(self, name):
287  """
288  Calls super-class init and adds necessary latex commands to output.
289  """
290  super(SubSection, self).__init__()
291  self.output += r"\subsection{" + str(name) + r"}" + '\n'
292 
293 
295  """
296  Adds a new subsubsection to your latex code.
297  """
298 
299  def __init__(self, name):
300  """
301  Calls super-class init and adds necessary latex commands to output.
302  """
303  super(SubSubSection, self).__init__()
304  self.output += r"\subsubsection{" + str(name) + r"}" + '\n'
305 
306 
308  """
309  Includes a series of image files into your latex code and centers them.
310  """
311 
312  def __init__(self):
313  """
314  Calls super-class init and begins centered environment.
315  """
316  super(Graphics, self).__init__()
317  self.output += r"\begin{center}" + '\n'
318 
319  def add(self, filename, width=0.7):
320  """
321  Include a image file.
322  @param filename containing the image
323  @param width texwidth argument of includegraphics
324  """
325  self.output += r"\includegraphics[width=" + str(width) + r"\textwidth]"
326  self.output += r"{" + str(filename) + r"}" + '\n'
327  return self
328 
329  def finish(self):
330  """
331  Ends centered environment
332  """
333  self.output += r"\end{center}" + '\n'
334  return self
335 
336 
338  """
339  Creates a itemized list in latex.
340  """
341 
342  def __init__(self):
343  """
344  Calls super-class init and begins itemize
345  """
346  super(Itemize, self).__init__()
347 
348  self.amount = 0
349  self.output += r"\begin{itemize}"
350 
351  def add(self, item):
352  """
353  Adds another item.
354  @param item string or object with implicit string conversion used as item
355  """
356  self.amount += 1
357  self.output += r"\item " + str(item) + '\n'
358  return self
359 
360  def finish(self):
361  """
362  Finishes the generation of latex-code.
363  """
364  if self.amount == 0:
365  return ''
366  self.output += r"\end{itemize}"
367  return self
368 
369 
371  """
372  Creates a longtable in latex. A longtable can span multiple pages
373  and is automatically wrapped.
374  """
375 
376  def __init__(self, columnspecs, caption, format_string, head):
377  """
378  Calls super-class init, begins centered environment and longtable environment.
379  Defines caption and head of the table.
380  @param columnspecs of the longtable, something like:
381  rclp{7cm} 4 columns, right-center-left aligned and one paragraph column with a width of 7cm
382  @param caption string or object with implicit string conversion used as caption.
383  @param format_string python-style format-string used to generate a new row out of a given dictionary.
384  @param head of the table
385  """
386  super(LongTable, self).__init__()
387  self.output += r"\begin{center}" + '\n'
388  self.output += r"\begin{longtable}{" + str(columnspecs) + r"}" + '\n'
389  self.output += r"\caption{" + str(caption) + r"}\\" + '\n'
390  self.output += r"\toprule" + '\n'
391  self.output += head + r"\\" + '\n'
392  self.output += r"\midrule" + '\n'
393 
394  self.format_string = format_string
395 
396  def add(self, *args, **kwargs):
397  """
398  Add a new row to the longtable by generating the row using the format_string given in init
399  and the provided dictionary.
400  @param args positional arguments used to generate the row using the python-style format string.
401  @param kwargs dictionary used to generate the row using the python-style format-string.
402  """
403  self.output += self.format_string.format(*args, **kwargs) + r"\\" + '\n'
404  return self
405 
406  def finish(self, tail=''):
407  """
408  Adds optional tail of the table, ends longtable and centered environment.
409  @param tail optional tail, like head but at the bottom of the table.
410  """
411  self.output += r"\bottomrule" + '\n'
412  if str(tail) != '':
413  self.output += str(tail) + r"\\" + '\n'
414  self.output += r"\bottomrule" + '\n'
415  self.output += r"\end{longtable}" + '\n'
416  self.output += r"\end{center}" + '\n'
417  return self
418 
419 
421  """
422  Creates a latex title-page and optionally abstract and table-of-contents.
423  You should include only one of these objects in your latex code.
424  """
425 
426  def __init__(self, title, authors, abstract, add_table_of_contents=True, clearpage=True):
427  """
428  Sets author, date, title property, calls maketitle, optionalla adds abstract and table-of-contents.
429  @param title of the latex file.
430  @param authors of the latex file, so the person who write the corresponding python-code with this framework :-)
431  @param abstract optional abstract placed on the title-page.
432  @param add_table_of_contents bool indicating of table-of-contents should be included.
433  """
434  super(TitlePage, self).__init__()
435  self.output += r"\author{"
436  for author in authors:
437  self.output += author + r"\\"
438  self.output += r"}" + '\n'
439  self.output += r"\date{\today}" + '\n'
440  self.output += r"\title{" + title + r"}" + '\n'
441  self.output += r"\maketitle" + '\n'
442  if abstract:
443  self.output += r"\begin{abstract}" + '\n'
444  self.output += abstract
445  self.output += '\n' + r'\end{abstract}' + '\n'
446 
447  if clearpage:
448  self.output += r"\clearpage" + '\n'
449  if add_table_of_contents:
450  self.output += r"\tableofcontents" + '\n'
451  self.output += r"\FloatBarrier" + '\n'
452  if clearpage:
453  self.output += r"\clearpage" + '\n'
454 
455 
456 if __name__ == '__main__':
457  import random
458  import subprocess
459 
460  o = LatexFile()
461  o += TitlePage(title='Automatic Latex Code Example',
462  authors=['Thomas Keck'],
463  abstract='This is an example for automatic latex code generation in basf2.',
464  add_table_of_contents=True).finish()
465 
466  o += Section("LongTable").finish()
467  o += String(r"In the following subsection there's:")
468  o += Itemize().add("a really long table").add("a coloured table").add("nothing else").finish()
469  o += SubSection("A really long table")
470 
471  table = LongTable(r"lr", "A long table", "{name} & {value:.2f}", r"Name & Value in $\mathrm{cm}$")
472  for i in range(60):
473  table.add(name='test' + str(i), value=random.gauss(0.0, 1.0))
474  o += table.finish()
475 
476  o += SubSection("A table with color and tail")
477  colour_list = DefineColourList()
478  o += colour_list.finish()
479 
480  table = LongTable(columnspecs=r"lcrrr",
481  caption="A coloured table for " +
482  ', '.join(('\\textcolor{%s}{%s}' % (c, m) for c, m in zip(colour_list.colours[:3], "RGB"))),
483  format_string="{name} & {bargraph} & {r:.2f} & {g:.2f} & {b:.2f}",
484  head=r"Name & Relative fractions & R - Value & G - Value & B - Value")
485  sr = sg = sb = 0
486  for i in range(10):
487  r = random.uniform(0.1, 0.9)
488  g = random.uniform(0.1, 0.9)
489  b = random.uniform(0.1, 0.9)
490  n = (r + g + b) / 100.0
491  sr += r
492  sg += g
493  sb += b
494  table.add(name='test' + str(i), bargraph=r'\plotbar{ %g/, %g/, %g/,}' % (r / n, g / n, b / n), r=r, g=g, b=b)
495  n = (sr + sg + sb) / 100.0
496  o += table.finish(tail=r"Total & \plotbar{ %g/, %g/, %g/,} & %g & %g & %g" % (sr / n, sg / n, sb / n, sr, sg, sb))
497 
498  o += Section("Graphic section")
499  graphics = Graphics()
500  images = subprocess.check_output("locate .png", shell=True).split('\n')[:-1]
501  for i in range(4):
502  image = random.choice(images)
503  graphics.add(image, width=0.49)
504  o += graphics.finish()
505 
506  from . import format
507  o += Section("Format section e.g. " + format.decayDescriptor("B+ ==> mu+ nu gamma"))
508  o += r"Some important channels in B physics"
509  items = Itemize()
510  items.add(format.decayDescriptor("B+ ==> mu+ nu gamma"))
511  items.add(format.decayDescriptor("B0 ==> J/Psi K_S0"))
512  items.add(format.decayDescriptor("B+ ==> tau+ nu"))
513  o += items.finish()
514 
515  o += r"Use format.variable to add hyphenations to long variable names, so latex can do line breaks "
516  o += format.variable("daughterProductOf(extraInfo(SignalProbability))")
517  o += r" . Use format.string for stuff that contains special latex characters like " + format.string(r"_ ^ \ ")
518  o += r" . Use format.duration for durations " + format.duration(20000) + " " + format.duration(0.00010)
519 
520  o.finish()
521  o.save("test.tex", compile=True)
B2Tools.b2latex.LatexObject.output
output
Stores the outputted latex-code.
Definition: b2latex.py:17
B2Tools.b2latex.DefineColourList.colours
list colours
6 default colours used for the bargraph
Definition: b2latex.py:230
B2Tools.b2latex.Section.__init__
def __init__(self, name)
Definition: b2latex.py:270
B2Tools.b2latex.Section
Definition: b2latex.py:264
B2Tools.b2latex.Listing.finish
def finish(self, **kwargs)
Definition: b2latex.py:212
B2Tools.b2latex.Itemize.amount
amount
number of items
Definition: b2latex.py:348
B2Tools.b2latex.SubSubSection.__init__
def __init__(self, name)
Definition: b2latex.py:299
B2Tools.b2latex.LongTable
Definition: b2latex.py:370
B2Tools.b2latex.String.finish
def finish(self, **kwargs)
Definition: b2latex.py:179
B2Tools.b2latex.LatexObject.__str__
def __str__(self)
Definition: b2latex.py:19
B2Tools.b2latex.LatexObject.add
def add(self)
Definition: b2latex.py:25
B2Tools.b2latex.LongTable.add
def add(self, *args, **kwargs)
Definition: b2latex.py:396
B2Tools.b2latex.SubSection.__init__
def __init__(self, name)
Definition: b2latex.py:286
B2Tools.b2latex.String
Definition: b2latex.py:156
B2Tools.b2latex.Graphics.add
def add(self, filename, width=0.7)
Definition: b2latex.py:319
B2Tools.b2latex.LongTable.__init__
def __init__(self, columnspecs, caption, format_string, head)
Definition: b2latex.py:376
B2Tools.b2latex.LatexFile
Definition: b2latex.py:133
B2Tools.b2latex.String.add
def add(self, text='')
Definition: b2latex.py:171
B2Tools.b2latex.LatexFile.__iadd__
def __iadd__(self, text)
Definition: b2latex.py:147
B2Tools.b2latex.DefineColourList.__init__
def __init__(self)
Definition: b2latex.py:232
B2Tools.b2latex.String.__init__
def __init__(self, text='')
Definition: b2latex.py:162
B2Tools.b2latex.DefineColourList
Definition: b2latex.py:221
B2Tools.b2latex.Graphics
Definition: b2latex.py:307
B2Tools.b2latex.LatexObject.save
def save(self, filename, compile=False)
Definition: b2latex.py:40
B2Tools.b2latex.LatexObject.__init__
def __init__(self)
Definition: b2latex.py:12
B2Tools.b2latex.LongTable.format_string
format_string
python-style format-string used to generate a new row out of a given dictionary.
Definition: b2latex.py:394
B2Tools.b2latex.LatexObject.finish
def finish(self)
Definition: b2latex.py:32
B2Tools.b2latex.Itemize
Definition: b2latex.py:337
B2Tools.b2latex.Itemize.__init__
def __init__(self)
Definition: b2latex.py:342
B2Tools.b2latex.LatexObject
Definition: b2latex.py:7
B2Tools.b2latex.Listing.__init__
def __init__(self, language='XML')
Definition: b2latex.py:194
B2Tools.b2latex.Graphics.__init__
def __init__(self)
Definition: b2latex.py:312
B2Tools.b2latex.Graphics.finish
def finish(self)
Definition: b2latex.py:329
B2Tools.b2latex.TitlePage.__init__
def __init__(self, title, authors, abstract, add_table_of_contents=True, clearpage=True)
Definition: b2latex.py:426
B2Tools.b2latex.SubSubSection
Definition: b2latex.py:294
B2Tools.b2latex.Itemize.finish
def finish(self)
Definition: b2latex.py:360
B2Tools.b2latex.TitlePage
Definition: b2latex.py:420
B2Tools.b2latex.LongTable.finish
def finish(self, tail='')
Definition: b2latex.py:406
B2Tools.b2latex.SubSection
Definition: b2latex.py:281
B2Tools.b2latex.Listing.add
def add(self, text='')
Definition: b2latex.py:204
B2Tools.b2latex.LatexFile.add
def add(self, text='')
Definition: b2latex.py:139
B2Tools.b2latex.Itemize.add
def add(self, item)
Definition: b2latex.py:351
B2Tools.b2latex.Listing
Definition: b2latex.py:189