From: Leandro Lucarella Date: Thu, 11 Nov 2010 03:02:10 +0000 (-0300) Subject: Versión inicial de la presentación X-Git-Tag: defensa-tesis~31 X-Git-Url: https://git.llucax.com/z.facultad/75.00/presentacion.git/commitdiff_plain/39db1a27fc811db16e2fc51cf39ab3771a7c23a3 Versión inicial de la presentación Presentación escrita en reStructuredText, hecha con LaTeX-beamer usando rst2beamer (incluído en el repo porque está parchado). Por ahora tengo un lindo Makefile y la Introducción y Conclusión más o menos listas; y Recolección de Basura en D apenas empezada. --- 39db1a27fc811db16e2fc51cf39ab3771a7c23a3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7eae00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.tmp/* +presentacion.pdf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b872447 --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ + +O := .tmp + +R2B := ./rst2beamer.py +R2BTHEME := Darmstadt +R2BFLAGS := --halt=2 --lang es --codeblocks-use-pygments \ + --input-encoding=utf-8 --output-encoding=utf-8 \ + --overlaybullets= \ + --theme $(R2BTHEME) +R2BFILTER := sed '/\\usepackage\[scaled=\.90\]{helvet}/d' + +DOT := dot +DOTFLAGS := + +AAFIG := aafigure +AAFIGFLAGS := --proportional + +PDFLATEX := pdflatex +PDFLATEXFLAGS := -halt-on-error -file-line-error + +imgs := $O/img/mark-sweep-0.pdf \ + $(patsubst %.dot,$O/%.pdf,$(wildcard img/mark-sweep-*.dot)) \ + $O/img/heap.pdf + +presentacion.pdf: $O/presentacion.tex $(imgs) + @echo "$(PDFLATEX) $< > $@" + @cd $O && $(PDFLATEX) $(PDFLATEXFLAGS) $( $@.log + @cd $O && $(PDFLATEX) $(PDFLATEXFLAGS) $(> $@.log + @mv $O/$@ $@ + +$O/presentacion.tex: presentacion.rst $(R2B) + @echo "$(R2B) $< > $@" + @$(R2B) $(R2BFLAGS) $< | $(R2BFILTER) > $@ + +$O/img/%.pdf: img/%.dot + @echo "$(DOT) $< > $@" + @$(DOT) $(DOTFLAGS) -Tpdf -o $@ $< + @#pdftops mark-sweep-$i.pdf && ps2pdf14 mark-sweep-$i.ps && rm mark-sweep-$i.ps + +$O/img/%.pdf: img/%.aafig + @echo "$(AAFIG) $< > $@" + @$(AAFIG) $(AAFIGFLAGS) -t pdf -o $@ $< + +$O/img/%.pdf: img/%.pdf + @echo "cp $< > $@" + @cp $< $@ + +$O/heap.pdf: AAFIGFLAGS += -s 1.4 -a 0.8 + +.PHONY: clean +clean: + $(RM) -r $O + +.PHONY: clean-all +clean-all: clean + $(RM) presentacion.pdf + +__dummy := $(shell mkdir -p $O/img) diff --git a/img/heap.aafig b/img/heap.aafig new file mode 100644 index 0000000..46ae265 --- /dev/null +++ b/img/heap.aafig @@ -0,0 +1,47 @@ + ++----------------------------------------------------------------------+ +| Heap | ++======================================================================+ +| "Pool 0" "Pool 1" "Pool 2" "Pool 3" ... "Pool N" | +| +----------+ +----------+ +----------+ +----------+ +----------+ | +| | Página 0 | | Página 0 | | Página 0 | | Página 0 | ... | Página 0 | | +| | (8x512) | | (4x1024) | | (64x64) | | (2x2048) | ... | (1x4096) | | +| |+--------+| |+--------+| |+--------+| |+--------+| |+--------+| | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| || Bloque || ||qqqqqqqq|| || || || || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| |+--------+| ||qqqqqqqq|| || Bloque || || || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| || Bloque || ||qqqqqqqq|| || || || || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| |+--------+| ||qqqqqqqq|| |+--------+| || Bloque || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| || Bloque || ||qqqqqqqq|| || || || || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| |+--------+| ||qqqqqqqq|| || Bloque || || || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| || Bloque || ||qqqqqqqq|| || || || || | +| || Bloque || || || ||qqqqqqqq|| || || || || | +| |+--------+| |+--------+| |+--------+| |+--------+| |+--------+| | +| | Página 1 | | Página 1 | +----------+ | Página 1 | ... | Página 1 | | +| | (16x256) | | (8x512) | | (32x128) | ... | (1x4096) | | +| |+--------+| |+--------+| |+--------+| |+--------+| | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || Bloque || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| ||nnnnnnnn|| || || | +| |+--------+| || Bloque || ||nnnnnnnn|| || || | +| |+--------+| |+--------+| |+--------+| ... |+--------+| | +| +----------+ +----------+ +----------+ +----------+ | ++----------------------------------------------------------------------+ + diff --git a/img/mark-sweep-0.pdf b/img/mark-sweep-0.pdf new file mode 100644 index 0000000..5b01dcc Binary files /dev/null and b/img/mark-sweep-0.pdf differ diff --git a/img/mark-sweep-1.dot b/img/mark-sweep-1.dot new file mode 100644 index 0000000..a911a45 --- /dev/null +++ b/img/mark-sweep-1.dot @@ -0,0 +1,29 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0\n*| r1", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + }; + + root:r0 -> h1; + h1 -> h2 -> h5 -> h1; + root:r1 -> h6 -> h2; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-2.dot b/img/mark-sweep-2.dot new file mode 100644 index 0000000..da48259 --- /dev/null +++ b/img/mark-sweep-2.dot @@ -0,0 +1,30 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0\n*| r1", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; + }; + + root:r0 -> h1 [ style = bold, color = black ]; + h1 -> h2 -> h5 -> h1; + root:r1 -> h6 -> h2; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-3.dot b/img/mark-sweep-3.dot new file mode 100644 index 0000000..197bb2d --- /dev/null +++ b/img/mark-sweep-3.dot @@ -0,0 +1,31 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0 ]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0\n*| r1", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; h2; + }; + + root:r0 -> h1 [ color = gray10 ]; + h1 -> h2 [ style = bold, color = black ]; + h2 -> h5 -> h1; + root:r1 -> h6 -> h2; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-4.dot b/img/mark-sweep-4.dot new file mode 100644 index 0000000..345b5c9 --- /dev/null +++ b/img/mark-sweep-4.dot @@ -0,0 +1,32 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0 ]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0\n*| r1", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; h2; h5; + }; + + root:r0 -> h1 [ color = gray10 ]; + h1 -> h2 [ color = gray10 ]; + h2 -> h5 [ style = bold, color = black ]; + h5 -> h1; + root:r1 -> h6 -> h2; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-5.dot b/img/mark-sweep-5.dot new file mode 100644 index 0000000..e49accb --- /dev/null +++ b/img/mark-sweep-5.dot @@ -0,0 +1,32 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0 ]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0\n*| r1", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; h2; h5; + }; + + root:r0 -> h1 [ color = gray10 ]; + h1 -> h2 [ color = gray10 ]; + h2 -> h5 [ color = gray10 ]; + h5 -> h1 [ style = bold, color = black ]; + root:r1 -> h6 -> h2; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-6.dot b/img/mark-sweep-6.dot new file mode 100644 index 0000000..b7e7c83 --- /dev/null +++ b/img/mark-sweep-6.dot @@ -0,0 +1,33 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0 ]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0| r1\n*", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; h2; h5; h6; + }; + + root:r0 -> h1 [ color = gray10 ]; + h1 -> h2 [ color = gray10 ]; + h2 -> h5 [ color = gray10 ]; + h5 -> h1 [ color = gray10 ]; + root:r1 -> h6 [ style = bold, color = black ]; + h6 -> h2; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-7.dot b/img/mark-sweep-7.dot new file mode 100644 index 0000000..429b6c3 --- /dev/null +++ b/img/mark-sweep-7.dot @@ -0,0 +1,33 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0 ]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0| r1\n*", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; h2; h5; h6; + }; + + root:r0 -> h1 [ color = gray10 ]; + h1 -> h2 [ color = gray10 ]; + h2 -> h5 [ color = gray10 ]; + h5 -> h1 [ color = gray10 ]; + root:r1 -> h6 [ color = gray10 ]; + h6 -> h2 [ style = bold, color = black ]; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/img/mark-sweep-8.dot b/img/mark-sweep-8.dot new file mode 100644 index 0000000..08dd25e --- /dev/null +++ b/img/mark-sweep-8.dot @@ -0,0 +1,33 @@ + +digraph { + margin = 0; + ratio = fill; + rankdir = LR; + node [ shape = record, width = 0, height = 0 ]; + edge [ color = gray40 ]; + + subgraph cluster_all { + + root [ + label = "root\nset| r0| r1\n*", + style = filled, + fillcolor = gray96, + ]; + + subgraph marked { + node [ style = filled, fillcolor = gray25, fontcolor = white ]; + h1; h2; h5; h6; + }; + + root:r0 -> h1 [ color = gray10 ]; + h1 -> h2 [ color = gray10 ]; + h2 -> h5 [ color = gray10 ]; + h5 -> h1 [ color = gray10 ]; + root:r1 -> h6 [ color = gray10 ]; + h6 -> h2 [ color = gray10 ]; + h4 -> h3; + h3 -> h5; + + } +} + diff --git a/presentacion.rst b/presentacion.rst new file mode 100644 index 0000000..41a6e61 --- /dev/null +++ b/presentacion.rst @@ -0,0 +1,332 @@ + +========================== +Recolección de Basura en D +========================== + +:Autor: Leandro Lucarella +:Fecha: Diciembre 2010 +:Organización: FIUBA + + +Introducción +============================================================================== + +Presentación +-------------------------------------------------- + +Motivación +~~~~~~~~~~ +* Recolección de basura +* Lenguaje de programación **D** +* Utilidad → Software Libre → Contribución + + +Recolección de Basura +-------------------------------------------------- + +Introducción +~~~~~~~~~~~~ +¿Qué? + +* Administración automática de memoria + +¿Para qué? + +* Simplificar interfaces +* Mejorar eficiencia (**!**) +* Evitar errores de memoria + + * *Dangling pointers* + * *Memory leaks* + * *Double free* + +¿Cómo? + +Algoritmos clásicos +~~~~~~~~~~~~~~~~~~~ +* Conteo de referencias +* **Marcado y barrido** +* Copia de semi-espacio + +.. raw:: latex + + \multiinclude[format=pdf,graphics={height=4.5cm}]{img/mark-sweep} + +.. dummy: para que ande bien el raw de arriba + +Estado del arte +~~~~~~~~~~~~~~~ +* Medio siglo de investigación y desarrollo (3000+ publicaciones) +* Objetivo + + * ↓ Tiempo total de ejecución + * ↓ Cantidad de recolecciones + * ↓ Tiempo de recolección + * ↓ **Tiempo (máximo) de pausa** + +* Técnicas + + * Particiones + * **Concurrencia** + * Organización de memoria + * **Precisión** + * Análisis estático + + +El lenguaje de programación D +-------------------------------------------------- + +Características generales +~~~~~~~~~~~~~~~~~~~~~~~~~ +* Sintaxis tipo C/C++ +* Compilado +* Sistema de tipos estático +* Multi-paradigma + +Paradigmas +~~~~~~~~~~ +* Programación de bajo nivel (*system-programming*) ← C/C++ + + * ``asm`` + * ``union`` + * ``extern (C)`` + * ``malloc()`` + +* Programación de alto nivel ← Python/Ruby/Perl + + * *GC* + * ``T[]``, ``T[K]`` + +* Orientación a objetos ← Java + + * ``~this()`` + +* Programación genérica y meta-programación ← C++ +* Programación por contratos ← Eiffel + +.. r2b-note:: + + * Programación de bajo nivel (*system-programming*) ← C/C++ + + * **asm**, ``goto``, **align**, ``struct``, **union**, **link-compitble con C**, + **malloc** + + * Programación de alto nivel ← Python/Ruby/Perl + + * **GC**, module/import, ``delegate``, ``lazy``, *strings*, **arreglos dinámicos + y asociativos**, ddoc, inferencia de tipos (ltd), ``foreach`` + + * Orientación a objetos ← Java + + * Más Java que C++: semántica de referencia, métodos siempre virtuales, herencia + simple + interfaces, clases anidadas, **destructores**... + Properties + + * Programación genérica y meta-programación ← C++ + + * ``static if``, ``typeof``, (*variadic*) ``tamplate``, *CTFE*, (*string*) + ``mixin``\ s, expresiones ``is`` + + * Programación por contratos ← Eiffel + + * Excepciones, ``assert``, pre/post condiciones, ``invariant``, ``unittest``, + ``scope``, inicialización garantizada, *RAII*, ``synchronized`` + + + +Recolección de Basura en D +============================================================================== + +Requerimientos +-------------------------------------------------- + +Según paradigma +~~~~~~~~~~~~~~~ +* Programación de bajo nivel + + * ``asm`` + * ``union`` + * ``extern (C)`` + * ``malloc()`` + + → Conservativo + Manipulación de *root set* + +* Programación de alto nivel ← Python/Ruby/Perl + + * ``T[]``, ``T[K]`` + + → Punteros interiores + +* Orientación a objetos ← Java + + * ``~this()`` + + → Finalización + + +Implementación Actual +-------------------------------------------------- + +Organización del heap +~~~~~~~~~~~~~~~~~~~~~ +.. image:: img/heap.pdf + :height: 7cm + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Lo Bueno, lo Malo y lo Feo +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + + +Modificaciones Propuestas +============================================================================== + +Precisión +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Concurrencia +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Optimizaciones +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + + +Resultados +============================================================================== + +Banco de Pruebas +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Tiempo de Stop-The-World +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Tiempo de Pausa Real +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Tiempo de Ejecución +-------------------------------------------------- + +Diapositiva 1 +~~~~~~~~~~~~~ +Diapositiva 1 + +Diapositiva 2 +~~~~~~~~~~~~~ +Diapositiva 2 + + +Conclusión +============================================================================== + +Conclusión +-------------------------------------------------- + +Resumen +~~~~~~~ +* Recolección de basura → Inagotable +* D → Multi-paradigma → Desafío +* Recolección de basura en D → Fértil +* Mejoras propuestas → Acierto +* Resultados → Esperados + Inesperados + +Problemas, limitaciones y Puntos Pendientes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Predicción de *early collection* +* Explosión de uso de memoria con *eager allocation* +* Experimentar con ``clone(2)`` +* Eficiencia de marcado + +Trabajos Relacionados +~~~~~~~~~~~~~~~~~~~~~ +* *Memory Management in the D Programming Language* + + Vladimir Panteleev. Proyecto de licenciatura, Universitatea Tehnică + a Moldovei, 2009. + +* *Integrate Precise Heap Scanning Into the GC* + + David Simcha (GC + diseño) + Vincent Lang (compilador). No formal, *bug + report*, 2009-2010. + +Trabajos Futuros +~~~~~~~~~~~~~~~~ +* Organización de memoria +* Barrido +* Precisión +* Concurrencia → *Lock* **global** +* Movimiento + +Preguntas +~~~~~~~~~ +¿? + +Fin +~~~ +¡Gracias! + +.. vim: set et sw=4 sts=4 spell spelllang=es : diff --git a/rst2beamer.py b/rst2beamer.py new file mode 100755 index 0000000..e764602 --- /dev/null +++ b/rst2beamer.py @@ -0,0 +1,1323 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +A docutils script converting restructured text into Beamer-flavoured LaTeX. + +Beamer is a LaTeX document class for presentations. Via this script, ReST can +be used to prepare slides. It can be called:: + + rst2beamer.py infile.txt > outfile.tex + +where ``infile.txt`` contains the rst and ``outfile.tex`` contains the +Beamer-flavoured LaTeX. + +See for more details. + +""" +# TODO: modifications for handout sections? +# TOOD: sections and subsections? +# TODO: convert document metadata to front page fields? +# TODO: toc-conversion? +# TODO: fix descriptions +# TODO: 'r2b' or 'beamer' as identifying prefix? + + +# This file has been modified by Ryan Krauss starting on 2009-03-25. +# Please contact him if it is broken: ryanwkrauss@gmail.com + +__docformat__ = 'restructuredtext en' +__author__ = "Ryan Krauss & Paul-Michael Agapow " +__version__ = "0.6.6" + + +### IMPORTS ### + +import re +import pdb + +try: + locale.setlocale (locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers.latex2e import Writer as Latex2eWriter +from docutils.writers.latex2e import LaTeXTranslator, DocumentClass +from docutils import nodes +from docutils.nodes import fully_normalize_name as normalize_name +from docutils.parsers.rst import directives, Directive +from docutils import frontend +from docutils.writers.latex2e import PreambleCmds +## CONSTANTS & DEFINES ### + +SHOWNOTES_FALSE = 'false' +SHOWNOTES_TRUE = 'true' +SHOWNOTES_ONLY = 'only' +SHOWNOTES_LEFT = 'left' +SHOWNOTES_RIGHT = 'right' +SHOWNOTES_TOP = 'top' +SHOWNOTES_BOTTOM = 'bottom' + +SHOWNOTES_OPTIONS = [ + SHOWNOTES_FALSE, + SHOWNOTES_TRUE, + SHOWNOTES_ONLY, + SHOWNOTES_LEFT, + SHOWNOTES_RIGHT, + SHOWNOTES_TOP, + SHOWNOTES_BOTTOM, +] + +HILITE_OPTIONS = { + 'python': 'python', + 'guess': 'guess', + 'c++': 'cpp', +} + +BEAMER_SPEC = ( + 'Beamer options', + 'These are derived almost entirely from the LaTeX2e options', + tuple ( + [ + ( + 'Specify theme.', + ['--theme'], + {'default': 'Warsaw', } + ), + ( + 'Overlay bulleted items. Put [<+-| alert@+>] at the end of ' + '\\begin{itemize} so that Beamer creats an overlay for each ' + 'bulleted item and the presentation reveals one bullet at a time', + ['--overlaybullets'], + {'default': True, } + ), + ( + 'Default for whether or not to pass the fragile option to ' + 'the beamber frames (slides).', + ['--fragile-default'], + {'default': True, } + ), + + ( + 'Center figures. All includegraphics statements will be put ' + 'inside center environments.', + ['--centerfigs'], + {'default': True, } + ), + ( + # TODO: this doesn't seem to do anything ... + 'Specify document options. Multiple options can be given, ' + 'separated by commas. Default is "10pt,a4paper".', + ['--documentoptions'], + {'default': '', } + ), +## ( +## 'Attach author and date to the document title.', +## ['--use-latex-docinfo'], +## {'default': 1, 'action': 'store_true', +## 'validator': frontend.validate_boolean} +## ), + ( + "Print embedded notes along with the slides. Possible " + "arguments include 'false' (don't show), 'only' (show " + "only notes), 'left', 'right', 'top', 'bottom' (show in " + "relation to the annotated slide).", + ['--shownotes'], + { + 'action': "store", + 'type': 'choice', + 'dest': 'shownotes', + 'choices': SHOWNOTES_OPTIONS, + 'default': SHOWNOTES_FALSE, + } + ), + # should the pygments highlighter be used for codeblocks? + ( + "Use the Pygments syntax highlighter to color blocks of " + "code. Otherwise, they will be typeset as simple literal " + "text. Obviously Pygments must be installed or an error. " + "will be raised. ", + ['--codeblocks-use-pygments'], + { + 'action': "store_true", + 'dest': 'cb_use_pygments', + 'default': False, + } + ), + # replace tabs inside codeblocks? + ( + "Replace the leading tabs in codeblocks with spaces.", + ['--codeblocks-replace-tabs'], + { + 'action': 'store', + 'type': int, + 'dest': 'cb_replace_tabs', + 'default': 0, + } + ), + # what language the codeblock is if not specified + ( + "The default language to hilight code blocks as. ", + ['--codeblocks-default-language'], + { + 'action': 'store', + 'type': 'choice', + 'dest': 'cb_default_lang', + 'choices': HILITE_OPTIONS.values(), + 'default': 'guess', + } + ), + ] + list (Latex2eWriter.settings_spec[2][2:]) + ), +) + +BEAMER_DEFAULTS = { + 'use_latex_toc': True, + 'output_encoding': 'latin-1', + 'documentclass': 'beamer', + 'documentoptions': 't',#text is at the top of each slide rather than centered. Changing to 'c' centers the text on each slide (vertically) +} + +BEAMER_DEFAULT_OVERRIDES = {'use_latex_docinfo': 1} + + +bool_strs = ['false','true','0','1'] +bool_vals = [False, True, False, True] +bool_dict = dict (zip (bool_strs, bool_vals)) + +PreambleCmds.documenttitle = r""" +%% Document title +\title[%s]{%s} +\author[%s]{%s} +\date{%s} +\maketitle +""" + +docinfo_w_institute = r""" +%% Document title +\title[%s]{%s} +\author[%s]{%s} +\date{%s} +\institute{%s} +\maketitle +""" + +### IMPLEMENTATION ### + +### UTILS + +LEADING_SPACE_RE = re.compile ('^ +') + +def adjust_indent_spaces (strn, orig_width=8, new_width=3): + """ + Adjust the leading space on a string so as to change the indent width. + + :Parameters: + strn + The source string to change. + orig_width + The expected width for an indent in the source string. + new_width + The new width to make an ident. + + :Returns: + The original string re-indented. + + That takes strings that may be indented by a set number of spaces (or its + multiple) and adjusts the indent for a new number of spaces. So if the + expected indent width is 8 and the desired ident width is 3, a string has + been indented by 16 spaces, will be changed to have a indent of 6. + + For example:: + + >>> adjust_indent_spaces (' foo') + ' foo' + >>> adjust_indent_spaces (' foo', orig_width=2, new_width=1) + ' foo' + + This is useful where meaningful indent must be preserved (i.e. passed + through) ReST, especially tabs when used in the literal environments. ReST + transforms tabs-as-indents to 8 spaces, which leads to excessively spaced + out text. This function can be used to adjust the indent step to a + reasonable size. + + .. note:: + + Excess spaces (those above and beyond a multiple of the original + indent width) will be preserved. Only indenting spaces will be + handled. Mixing tabs and spaces is - as always - a bad idea. + + """ + ## Preconditions & preparation: + assert (1 <= orig_width) + assert (0 <= new_width) + if (orig_width == new_width): + return strn + ## Main: + match = LEADING_SPACE_RE.match (strn) + if (match): + indent_len = match.end() - match.start() + indent_cnt = indent_len / orig_width + indent_depth = indent_cnt * orig_width + strn = ' ' * indent_cnt * new_width + strn[indent_depth:] + return strn + + +def index (seq, f, fail=None): + """ + Return the index of the first item in seq where f(item) is True. + + :Parameters: + seq + A sequence or iterable + f + A boolean function an element of `seq`, e.g. `lambda x: x==4` + fail + The value to return if no item is found in seq. + + While this could be written in a neater fashion in Python 2.6, this method + maintains compatiability with earlier version. + """ + for index in (i for i in xrange (len (seq)) if f (seq[i])): + return index + return fail + + +def node_has_class (node, classes): + """ + Does the node have one of these classes? + + :Parameters: + node + A docutils node + class + A class name or list of class names. + + :Returns: + A boolean indicating membership. + + A convenience function, largely for testing for the special class names + in containers. + """ + ## Preconditions & preparation: + # wrap single name in list + if (not (issubclass (type (classes), list))): + classes = [classes] + ## Main: + for cname in classes: + if cname in node['classes']: + return True + return False + + +def node_lang_class (node): + """ + Extract a language specification from a node class names. + + :Parameters: + node + A docutils node + + :Returns: + A string giving a language abbreviation (e.g. 'py') or None if no + langauge is found. + + Some sourcecode containers can pass a (programming) language specification + by passing it via a classname like 'lang-py'. This function searches a + nodes classnames for those starting with 'lang-' and returns the trailing + portion. Note that if more than one classname matches, only the first is + seen. + """ + ## Main: + for cname in node['classes']: + if (cname.startswith ('lang-')): + return cname[5:] + return None + + +def wrap_children_in_columns (par_node, children, width=None): + """ + Replace this node's children with columns containing the passed children. + + :Parameters: + par_node + The node whose children are to be replaced. + children + The new child nodes, to be wrapped in columns and added to the + parent. + width + The width to be assigned to the columns. + + In constructing columns for beamer using either 'simplecolumns' approach, + we have to wrap the original elements in column nodes, giving them an + appropriate width. Note that this mutates parent node. + """ + ## Preconditions & preparation: + # TODO: check for children and raise error if not? + width = width or 0.90 + ## Main: + # calc width of child columns + child_cnt = len (children) + col_width = width / child_cnt + # set each element of content in a column and add to column set + new_children = [] + for child in children: + col = column() + col.width = col_width + col.append (child) + new_children.append (col) + par_node.children = new_children + + +def has_sub_sections (node): + """Test whether or not a section node has children with the + tagname section. The function is going to be used to assess + whether or not a certain section is the lowest level. Sections + that have not sub-sections (i.e. no children with the tagname + section) are assumed to be Beamer slides""" + for child in node.children: + if child.tagname == 'section': + return True + return False + + +def string_to_bool (stringin, default=True): + """ + Turn a commandline arguement string into a boolean value. + """ + if type (stringin) == bool: + return stringin + temp = stringin.lower() + if temp not in bool_strs: + return default + else: + return bool_dict[temp] + + +def highlight_code (text, lang): + """ + Syntax-highlight source code using Pygments. + + :Parameters: + text + The code to be formatted. + lang + The language of the source code. + + :Returns: + A LaTeX formatted representation of the source code. + + """ + ## Preconditions & preparation: + from pygments import highlight + from pygments.formatters import LatexFormatter + ## Main: + lexer = get_lexer (text, lang) + lexer.add_filter('whitespace', tabsize=3, tabs=' ') + return highlight (text, lexer, LatexFormatter(tabsize=3)) + + +def get_lexer (text, lang): + """ + Return the Pygments lexer for parsing this sourcecode. + + :Parameters: + text + The sourcecode to be lexed for highlighting. This is analysed if + the language is 'guess'. + lang + An abbreviation for the programming langauge of the code. Can be + any 'name' accepted by Pygments, including 'none' (plain text) or + 'guess' (analyse the passed code for clues). + + :Returns: + A Pygments lexer. + + """ + # TODO: what if source has errors? + ## Preconditions & preparation: + from pygments.lexers import (get_lexer_by_name, TextLexer, guess_lexer) + ## Main: + if lang == 'guess': + try: + return guess_lexer (text) + except Exception: + return None + elif lang == 'none': + return TextLexer + else: + return get_lexer_by_name (lang) + + + +### NODES ### +# Special nodes for marking up beamer layout + +class columnset (nodes.container): + """ + A group of columns to display on one slide. + + Named as per docutils standards. + """ + # NOTE: a simple container, has no attributes. + + +class column (nodes.container): + """ + A single column, grouping content. + + Named as per docutils standards. + """ + # TODO: should really init width in a c'tor + +class beamer_note (nodes.container): + """ + Annotations for a beamer presentation. + + Named as per docutils standards and to distinguish it from core docutils + node type. + """ + pass + + +### DIRECTIVES + +class CodeBlockDirective (Directive): + """ + Directive for a code block with special highlighting or line numbering + settings. + + Unabashedly borrowed from the Sphinx source. + """ + has_content = True + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = False + option_spec = { + 'linenos': directives.flag, + } + + def run (self): + # extract langauge from block or commandline + # we allow the langauge specification to be optional + try: + language = self.arguments[0] + except IndexError: + language = 'guess' + code = u'\n'.join (self.content) + literal = nodes.literal_block (code, code) + literal['classes'].append ('code-block') + literal['language'] = language + literal['linenos'] = 'linenos' in self.options + return [literal] + +for name in ['code-block', 'sourcecode']: + directives.register_directive (name, CodeBlockDirective) + + +class SimpleColsDirective (Directive): + """ + A directive that wraps all contained nodes in beamer columns. + + Accept 'width' as an optional argument for total width of contained + columns. + """ + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + has_content = True + option_spec = {'width': float} + + def run (self): + ## Preconditions: + self.assert_has_content() + # get width + width = self.options.get ('width', 0.9) + if (width <= 0.0) or (1.0 < width): + raise self.error ("columnset width '%f' must be between 0.0 and 1.0" % width) + ## Main: + # parse content of columnset + dummy = nodes.Element() + self.state.nested_parse (self.content, self.content_offset, + dummy) + # make columnset + text = '\n'.join (self.content) + cset = columnset (text) + # wrap children in columns & set widths + wrap_children_in_columns (cset, dummy.children, width) + ## Postconditions & return: + return [cset] + +for name in ['r2b-simplecolumns', 'r2b_simplecolumns']: + directives.register_directive (name, SimpleColsDirective) + + +class ColumnSetDirective (Directive): + """ + A directive that encloses explicit columns in a 'columns' environment. + + Within this, columns are explcitly set with the column directive. There is + a single optional argument 'width' to determine the total width of + columns on the page, expressed as a fraction of textwidth. If no width is + given, it defaults to 0.90. + + Contained columns may have an assigned width. If not, the remaining width + is divided amongst them. Contained columns can 'overassign' width, + provided all column widths are defined. + + """ + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + has_content = True + option_spec = {'width': float} + + def run (self): + ## Preconditions: + self.assert_has_content() + # get and check width of column set + width = self.options.get ('width', 0.9) + if ((width <= 0.0) or (1.0 < width)): + raise self.error ( \ + "columnset width '%f' must be between 0.0 and 1.0" % width) + ## Main: + # make columnset + text = '\n'.join (self.content) + cset = columnset (text) + # parse content of columnset + self.state.nested_parse (self.content, self.content_offset, cset) + # survey widths + used_width = 0.0 + unsized_cols = [] + for child in cset: + child_width = getattr (child, 'width', None) + if (child_width): + used_width += child_width + else: + unsized_cols.append (child) + + if (1.0 < used_width): + raise self.error ( \ + "cumulative column width '%f' exceeds 1.0" % used_width) + # set unsized widths + if (unsized_cols): + excess_width = width - used_width + if (excess_width <= 0.0): + raise self.error ( \ + "no room for unsized columns '%f'" % excess_width) + col_width = excess_width / len (unsized_cols) + for child in unsized_cols: + child.width = col_width + elif (width < used_width): + # TODO: should post a warning? + pass + ## Postconditions & return: + return [cset] + +for name in ['r2b-columnset', 'r2b_columnset']: + directives.register_directive (name, ColumnSetDirective) + + +class ColumnDirective (Directive): + """ + A directive to explicitly create an individual column. + + This can only be used within the columnset directive. It can takes a + single optional argument 'width' to determine the column width on page. + If no width is given, it is recorded as None and should be later assigned + by the enclosing columnset. + """ + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + has_content = True + option_spec = {'width': float} + + def run (self): + ## Preconditions: + self.assert_has_content() + # get width + width = self.options.get ('width', None) + if (width is not None): + if (width <= 0.0) or (1.0 < width): + raise self.error ("columnset width '%f' must be between 0.0 and 1.0" % width) + ## Main: + # make columnset + text = '\n'.join (self.content) + col = column (text) + col.width = width + # parse content of column + self.state.nested_parse (self.content, self.content_offset, col) + # adjust widths + ## Postconditions & return: + return [col] + +for name in ['r2b-column', 'r2b_column']: + directives.register_directive (name, ColumnDirective) + + +class NoteDirective (Directive): + """ + A directive to include notes within a beamer presentation. + + """ + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + has_content = True + option_spec = {} + + def run (self): + ## Preconditions: + self.assert_has_content() + ## Main: + ## Preconditions: + # make columnset + text = '\n'.join (self.content) + note_node = beamer_note (text) + # parse content of note + self.state.nested_parse (self.content, self.content_offset, note_node) + ## Postconditions & return: + return [note_node] + +for name in ['r2b-note', 'r2b_note']: + directives.register_directive (name, NoteDirective) + + +class beamer_section (Directive): + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + has_content = True + + def run (self): + title = self.arguments[0] + + section_text = '\\section{%s}' % title + text_node = nodes.Text (title) + text_nodes = [text_node] + title_node = nodes.title (title, '', *text_nodes) + name = normalize_name (title_node.astext()) + + section_node = nodes.section(rawsource=self.block_text) + section_node['names'].append(name) + section_node += title_node + messages = [] + title_messages = [] + section_node += messages + section_node += title_messages + section_node.tagname = 'beamer_section' + return [section_node] + +for name in ['beamer_section', 'r2b-section', 'r2b_section']: + directives.register_directive (name, beamer_section) + + +### WRITER + +class BeamerTranslator (LaTeXTranslator): + """ + A converter for docutils elements to beamer-flavoured latex. + """ + + def __init__ (self, document): + LaTeXTranslator.__init__ (self, document) + + self.organization = None#used for Beamer title and possibly + #header/footer. Set from docinfo + # record the the settings for codeblocks + self.cb_use_pygments = document.settings.cb_use_pygments + self.cb_replace_tabs = document.settings.cb_replace_tabs + self.cb_default_lang = document.settings.cb_default_lang + + self.head_prefix = [x for x in self.head_prefix + if ('{typearea}' not in x)] + #hyperref_posn = [i for i in range (len (self.head_prefix)) + # if ('{hyperref}' in self.head_prefix[i])] + hyperref_posn = index (self.head_prefix, + lambda x: '{hyperref}\n' in x) + if (hyperref_posn is None): + self.head_prefix.extend ([ + '\\usepackage{hyperref}\n' + ]) + + #self.head_prefix[hyperref_posn[0]] = '\\usepackage{hyperref}\n' + self.head_prefix.extend ([ + '\\definecolor{rrblitbackground}{rgb}{0.55, 0.3, 0.1}\n', + '\\newenvironment{rtbliteral}{\n', + '\\begin{ttfamily}\n', + '\\color{rrblitbackground}\n', + '}{\n', + '\\end{ttfamily}\n', + '}\n', + ]) + + if (self.cb_use_pygments): + #from pygments.formatters import LatexFormatter + #fmtr = LatexFormatter() + self.head_prefix.extend ([ + '\\usepackage{fancyvrb}\n', + '\\usepackage{color}\n', + #LatexFormatter().get_style_defs(), + ]) + + # set appropriate header options for theming + theme = document.settings.theme + if theme: + self.head_prefix.append ('\\usetheme{%s}\n' % theme) + + # set appropriate header options for note display + shownotes = document.settings.shownotes + if shownotes == SHOWNOTES_TRUE: + shownotes = SHOWNOTES_RIGHT + use_pgfpages = True + if (shownotes == SHOWNOTES_FALSE): + option_str = 'hide notes' + use_pgfpages = False + elif (shownotes == SHOWNOTES_ONLY): + option_str = 'show only notes' + else: + if (shownotes == SHOWNOTES_LEFT): + notes_posn = 'left' + elif (shownotes in SHOWNOTES_RIGHT): + notes_posn = 'right' + elif (shownotes == SHOWNOTES_TOP): + notes_posn = 'top' + elif (shownotes == SHOWNOTES_BOTTOM): + notes_posn = 'bottom' + else: + # TODO: better error handling + assert False, "unrecognised option for shownotes '%s'" % shownotes + option_str = 'show notes on second screen=%s' % notes_posn + if use_pgfpages: + self.head_prefix.append ('\\usepackage{pgfpages}\n') + self.head_prefix.append ('\\setbeameroption{%s}\n' % option_str) + self.head_prefix.append ('\\usepackage{xmpmulti}\n') + + if (self.cb_use_pygments): + from pygments.formatters import LatexFormatter + fmtr = LatexFormatter() + self.head_prefix.extend ([ + LatexFormatter().get_style_defs(), + ]) + + self.overlay_bullets = string_to_bool (document.settings.overlaybullets, False) + self.fragile_default = string_to_bool (document.settings.fragile_default, True) + #using a False default because + #True is the actual default. If you are trying to pass in a value + #and I can't determine what you really meant, I am assuming you + #want something other than the actual default. + self.centerfigs = string_to_bool(document.settings.centerfigs, False)#same reasoning as above + self.in_columnset = False + self.in_column = False + self.in_note = False + self.frame_level = 0 + + # this fixes the hardcoded section titles in docutils 0.4 + self.d_class = DocumentClass ('article') + + + 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 + shorttitle = ''.join(self.title) + shortauthor = ''.join(self.pdfauthor) + + if self.subtitle: + title += [r'\\ % subtitle', + r'\large{%s}' % ''.join(self.subtitle) + ] + self.subtitle_labels + docinfo_list = [shorttitle, + '%\n '.join(title), + shortauthor, + ' \\and\n'.join(authors), + ', '.join(self.date)] + if self.organization is None: + docinfo_str = PreambleCmds.documenttitle % tuple(docinfo_list) + else: + docinfo_list.append(self.organization) + docinfo_str = docinfo_w_institute % tuple(docinfo_list) + self.body_pre_docinfo.append(docinfo_str) + # 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)0: + ## options = '[%s]' % (','.join(include_graphics_options)) + ## self.out.append( '\\includegraphics%s{%s}' % ( + ## options, attrs['uri'] ) ) + ## self.out.extend( post ) + + + ## def depart_image(self, node): + ## #This goes with the old approach above + ## if self.centerfigs: + ## self.out.append('\\end{center}\n') + + + ## def visit_Text (self, node): + ## self.out.append(self.encode(node.astext())) + + def depart_Text(self, node): + pass + + + def node_fragile_check(self, node): + """Check whether or not a slide should be marked as fragile. + If the slide has class attributes of fragile or notfragile, + then the document default is overriden.""" + if 'notfragile' in node.attributes['classes']: + return False + elif 'fragile' in node.attributes['classes']: + return True + else: + return self.fragile_default + + + def begin_frametag (self, node): + bf_str = '\n\\begin{frame}' + if self.node_fragile_check(node): + bf_str += '[fragile]' + bf_str += '\n' + return bf_str + + + def end_frametag (self): + return '\\end{frame}\n' + + def visit_section (self, node): + if has_sub_sections (node): + temp = self.section_level + 1 + if temp > self.frame_level: + self.frame_level = temp + else: + self.out.append (self.begin_frametag(node)) + LaTeXTranslator.visit_section (self, node) + + + def bookmark (self, node): + """I think beamer alread handles bookmarks well, so I + don't want duplicates.""" + return '' + + def depart_section (self, node): + # Remove counter for potential subsections: + LaTeXTranslator.depart_section (self, node) + if (self.section_level == self.frame_level):#0 + self.out.append (self.end_frametag()) + + + def visit_title (self, node): + if node.astext() == 'dummy': + raise nodes.SkipNode + if (self.section_level == self.frame_level+1):#1 + self.out.append ('\\frametitle{%s}\n\n' % \ + self.encode(node.astext())) + raise nodes.SkipNode + else: + LaTeXTranslator.visit_title (self, node) + + def depart_title (self, node): + if (self.section_level != self.frame_level+1):#1 + LaTeXTranslator.depart_title (self, node) + + + def visit_literal_block (self, node): + # FIX: the purpose of this method is unclear, but it causes parsed + # literals in docutils 0.6 to lose indenting. Thus we've solve the + # problem be just getting rid of it. [PMA 20091020] + # TODO: replace leading tabs like in codeblocks? + if (node_has_class (node, 'code-block') and self.cb_use_pygments): + self.visit_codeblock (node) + else: + self.out.append ('\\setbeamerfont{quote}{parent={}}\n') + LaTeXTranslator.visit_literal_block (self, node) + + def depart_literal_block (self, node): + # FIX: see `visit_literal_block` + if (node_has_class (node, 'code-block') and self.cb_use_pygments): + self.visit_codeblock (node) + else: + LaTeXTranslator.depart_literal_block (self, node) + self.out.append ( '\\setbeamerfont{quote}{parent=quotation}\n' ) + + def visit_codeblock (self, node): + # was langauge argument defined on node? + lang = node.get ('language', None) + # otherwise, was it defined in node classes? + if (lang is None): + lang = node_lang_class (node) + # otherwise, use commandline argument or default + if lang is None: + lang = self.cb_default_lang + # replace tabs if required + srccode = node.rawsource + if (self.cb_replace_tabs): + srccode = '\n'.join (adjust_indent_spaces (x, + new_width=self.cb_replace_tabs) for x in srccode.split ('\n')) + # hilight the code + hilite_code = highlight_code (srccode, lang) + self.out.append ('\n' + hilite_code + '\n') + raise nodes.SkipNode + + def depart_codeblock (self, node): + pass + + def visit_bullet_list (self, node): + # NOTE: required by the loss of 'topic_classes' in docutils 0.6 + # TODO: so what replaces it? + if (hasattr (self, 'topic_classes') and + ('contents' in self.topic_classes)): + if self.use_latex_toc: + raise nodes.SkipNode + self.out.append( '\\begin{list}{}{}\n' ) + else: + begin_str = '\\begin{itemize}' + if self.node_overlay_check(node): + begin_str += '[<+-| alert@+>]' + begin_str += '\n' + self.out.append (begin_str) + + + def node_overlay_check(self, node): + """Assuming that the bullet or enumerated list is the child of + a slide, check to see if the slide has either nooverlay or + overlay in its classes. If not, default to the commandline + specification for overlaybullets.""" + if 'nooverlay' in node.parent.attributes['classes']: + return False + elif 'overlay' in node.parent.attributes['classes']: + return True + else: + return self.overlay_bullets + + + def depart_bullet_list (self, node): + # NOTE: see `visit_bullet_list` + if (hasattr (self, 'topic_classes') and + ('contents' in self.topic_classes)): + self.out.append( '\\end{list}\n' ) + else: + self.out.append( '\\end{itemize}\n' ) + +## def latex_image_length(self, width_str): +## if ('\\textheight' in width_str) or ('\\textwidth' in width_str): +## return width_str +## else: +## return LaTeXTranslator.latex_image_length(self, width_str) + + def visit_enumerated_list (self, node): + #LaTeXTranslator has a very complicated + #visit_enumerated_list that throws out much of what latex + #does to handle them for us. I am going back to relying + #on latex. + if ('contents' in getattr (self, 'topic_classes', [])): + if self.use_latex_toc: + raise nodes.SkipNode + self.out.append( '\\begin{list}{}{}\n' ) + else: + begin_str = '\\begin{enumerate}' + if self.node_overlay_check(node): + begin_str += '[<+-| alert@+>]' + begin_str += '\n' + self.out.append(begin_str) + if node.has_key('start'): + self.out.append('\\addtocounter{enumi}{%d}\n' \ + % (node['start']-1)) + + + def depart_enumerated_list (self, node): + if ('contents' in getattr (self, 'topic_classes', [])): + self.out.append ('\\end{list}\n') + else: + self.out.append ('\\end{enumerate}\n' ) + + +## def astext (self): +## if self.pdfinfo is not None and self.pdfauthor: +## self.pdfinfo.append ('pdfauthor={%s}' % self.pdfauthor) +## if self.pdfinfo: +## pdfinfo = '\\hypersetup{\n' + ',\n'.join (self.pdfinfo) + '\n}\n' +## else: +## pdfinfo = '' +## head = '\\title{%s}\n' % self.title +## if self.auth_stack: +## auth_head = '\\author{%s}\n' % ' \\and\n'.join (\ +## ['~\\\\\n'.join (auth_lines) for auth_lines in self.auth_stack]) +## head += auth_head +## if self.date: +## date_head = '\\date{%s}\n' % self.date +## head += date_head +## return ''.join (self.head_prefix + [head] + self.head + [pdfinfo] +## + self.out_prefix + self.out + self.out_suffix) + + +## def visit_docinfo (self, node): +## """ +## Docinfo is ignored for Beamer documents. +## """ +## pass + +## def depart_docinfo (self, node): +## # see visit_docinfo +## pass + + def visit_columnset (self, node): + assert not self.in_columnset, \ + "already in column set, which cannot be nested" + self.in_columnset = True + self.out.append ('\\begin{columns}[T]\n') + + def depart_columnset (self, node): + assert self.in_columnset, "not in column set" + self.in_columnset = False + self.out.append ('\\end{columns}\n') + + def visit_column (self, node): + assert not self.in_column, "already in column, which cannot be nested" + self.in_column = True + self.out.append ('\\column{%.2f\\textwidth}\n' % node.width) + + def depart_column (self, node): + self.in_column = False + self.out.append ('\n') + + def visit_beamer_note (self, node): + assert not self.in_note, "already in note, which cannot be nested" + self.in_note = True + self.out.append ('\\note{\n') + + def depart_beamer_note (self, node): + self.in_note = False + self.out.append ('}\n') + + def visit_container (self, node): + """ + Handle containers with 'special' names, ignore the rest. + """ + # NOTE: theres something wierd here where ReST seems to translate + # underscores in container identifiers into hyphens. So for the + # moment we'll allow both. + if (node_has_class (node, 'r2b-simplecolumns')): + self.visit_columnset (node) + wrap_children_in_columns (node, node.children) + elif (node_has_class (node, 'r2b-note')): + self.visit_beamer_note (node) + else: + # currently the LaTeXTranslator does nothing, but just in case + LaTeXTranslator.visit_container (self, node) + + def depart_container (self, node): + if (node_has_class (node, 'r2b-simplecolumns')): + self.depart_columnset (node) + elif (node_has_class (node, 'r2b-note')): + self.depart_beamer_note (node) + else: + # currently the LaTeXTranslator does nothing, but just in case + LaTeXTranslator.depart_container (self, node) + + + # Convert strong from \textbf to \alert + def visit_strong(self, node): + self.out.append('\\alert{') + if node['classes']: + self.visit_inline(node) + + def depart_strong(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + +class BeamerWriter (Latex2eWriter): + """ + A docutils writer that produces Beamer-flavoured LaTeX. + """ + settings_spec = BEAMER_SPEC + settings_default_overrides = BEAMER_DEFAULT_OVERRIDES + def __init__(self): + self.settings_defaults.update(BEAMER_DEFAULTS) + Latex2eWriter.__init__(self) + self.translator_class = BeamerTranslator + + +### TEST & DEBUG ### +# TODO: should really move to a test file or dir + +def test_with_file (fpath, args=[]): + """ + Call rst2beamer on the given file with the given args. + + During development, it's handy to be able to easily call the writer from + within Python. This is a convenience function that wraps the docutils + functions to do so. + """ + return publish_cmdline (writer=BeamerWriter(), argv=args+[fpath]) + + +### MAIN ### + +def main (): + description = ( + "Generates Beamer-flavoured LaTeX for PDF-based presentations." + + default_description) + publish_cmdline (writer=BeamerWriter(), description=description) + + +if __name__ == '__main__': + main() + + +### END ### +