Для чего мне понадобилась корректная подсветка исходного кода Tex / minted в vim, о способе достижения которой я рассказывал здесь? Ну, например, для того, чтобы, как на то намекает название статьи, с особым комфортом и шиком генерировать качественные технические презентации прямо из vim! Например, вот такую:
Исходный код этой презентации на tex:
\documentclass{beamer}
\usepackage{lmodern}
\usepackage{minted}
\usetheme{CambridgeUS}
\usecolortheme{seahorse}
\definecolor{scriptbg}{rgb}{0.95,0.95,0.95}
\logo{\includegraphics[height=0.5cm]{MyCompanyLogo.png}}
\title {My Cool Presentation}
\author{IT Team}
\date{December 12, 2012}
\begin{document}
\maketitle
\begin{frame}
\frametitle{Our network}
\framesubtitle{(autogenerated from a dia file)}
\begin{center}
\includegraphics[width=0.8\paperwidth]{my_dia.mps}
\end{center}
\end{frame}
\begin{frame}[fragile]
\frametitle{Code samples from different languages}
\framesubtitle{(highlighted by Python Pygments via minted)}
\textbf{C++}
\begin{minted}[fontsize=\tiny,bgcolor=scriptbg,gobble=2]{c++}
#include <iostream>
int main( void )
{
std::cout << "Hello world" << std::endl;
return 0;
}
\end{minted}
\vspace{0.5cm}
\textbf{Python}
\begin{minted}[fontsize=\tiny,bgcolor=scriptbg,gobble=2]{python}
#!/usr/bin/python
print "Hello, World!"
\end{minted}
\vspace{0.5cm}
\textbf{Bash}
\begin{minted}[fontsize=\tiny,bgcolor=scriptbg,gobble=2]{sh}
#!/usr/bin/bash
echo Hello, World!
\end{minted}
\end{frame}
\end{document}
Кроме текста для построения презентации были использованы изображение-логотип компании MyCompanyLogo.png (надпись My Company Logo c красным кругом в правом нижнем углу каждого слайда) и файл в формате dia, из которого было автоматически сгенерировано векторное изображение my_dia.mps на втором слайде.
Кто же в ответе за всю эту красоту? Конечно же пакет beamer из репозитория tex: именно он создает структуру презентации и раскрашивает слайды в соответствии с темами, заданными командами \usetheme и \usecolortheme. Кстати, стандартные темы можно просмотреть на сайте Beamer Theme Matrix (но будьте осторожны - грузится он долго).
Пакет beamer создает прекрасные качественные презентации в формате PDF с перекрестными ссылками и панелью управления, расположенной внизу каждого слайда. А что делать, если нам нужен формат презентаций ODP OpenOffice / LibreOffice? Для этого нам нужно найти какой-нибудь качественный конвертор из PDF в ODP. В качестве движка конвертора прекрасно подходит программа pdftocairo из пакета Poppler (в моей Fedora 17 она входит в пакет rpm poppler-utils). Программа конвертора должна качественно, быстро и прозрачно преобразовать исходную презентацию в формате PDF в отдельные файлы PNG, а затем скомпоновать их в презентацию ODP.
На роль подобного менеджера подходит скрипт pdf2odp из пакета latexslides, однако он использует в качестве движка не Poppler, а Ghostscript, поэтому делает это, на мой взгляд, медленно и некачественно, кроме того, в нем нельзя задать желаемое разрешение PNG, которое всегда равно 300. Поэтому я написал патч для pdf2odp относительно текущей версии в репозитории, в котором реализованы опции по выбору движка конвертора (Ghostscript или pdftocairo из Poppler) и выходного разрешения картинок PNG. Вот этот патч:
--- bin/pdf2odp 2012-12-13 00:25:08.072750679 +0400
+++ bin/pdf2odp.new 2012-12-13 00:22:48.384713551 +0400
@@ -1,15 +1,48 @@
-#!/usr/bin/env python
+#!/usr/bin/python
+
+import sys, subprocess, os, glob, getopt
+
+def usage():
+ usage = """
+ Usage: %s [-x|--engine=] [-s|--scale=] pdffile [outfile]
+ -h --help Prints help
+ -x --engine Converter engine (gs or pdftocairo), default gs
+ -s --scale Scale value, default 300
+ """
+ print usage %(os.path.basename(sys.argv[0]))
+
+# converter engine: gs or pdftocairo
+engine = 'gs'
+scale = 300
+pdffile = ''
+outfile = ''
+
+options, remainder = getopt.getopt(sys.argv[1:], 'hx:s:',
+ ['help','engine=', 'scale='])
+
+for opt, arg in options:
+ if opt in ('-x', '--engine'):
+ engine = arg
+ elif opt in ('-s', '--scale'):
+ scale = arg
+ elif opt in ('-h', '--help'):
+ usage()
+ sys.exit()
+
+if len(remainder) > 0:
+ pdffile = remainder[0]
+if len(remainder) > 1:
+ outfile = remainder[1]
-import sys, subprocess, os, glob
# Check for odfpy and file argument
try:
from odf.opendocument import OpenDocumentPresentation
- filename = sys.argv[1]
+ filename = pdffile
except ImportError:
print "You need odfpy, exiting."
sys.exit(1)
except IndexError:
- print "Usage: %s pdfile [outfile]" %sys.argv[0]
+ usage()
sys.exit(2)
from odf.style import Style, MasterPage, PageLayout, PageLayoutProperties, \
@@ -27,21 +60,32 @@
print "%s only accepts pdf files, exiting." %sys.argv[0]
sys.exit(4)
-# Check for gs
+# Check for converter engine
try:
- subprocess.call(['gs', '-v'], stdout=subprocess.PIPE)
+ if engine == 'pdftocairo':
+ subprocess.call(['pdftocairo', '-v'], stdout=subprocess.PIPE)
+ else:
+ subprocess.call(['gs', '-v'], stdout=subprocess.PIPE)
except OSError:
- print "You need Ghostscript, exiting."
+ if engine == 'pdftocairo':
+ print "You need Poppler utils, exiting."
+ else:
+ print "You need Ghostscript, exiting."
sys.exit(5)
-gs_args = ['gs', '-dNOPAUSE', '-dSAFER', '-dBATCH', '-sDEVICE=pngalpha',
- '-r300', '-sOutputFile=tmp_%s_%%03d.png' %(file), filename]
+if engine == 'pdftocairo':
+ engine_args = ['pdftocairo', '-png', '-scale-to', '%s' %(scale), filename,
+ 'tmp_%s_' %(file)]
+else:
+ engine_args = ['gs', '-dNOPAUSE', '-dSAFER', '-dBATCH',
+ '-sDEVICE=pngalpha', '-r%s' %(scale),
+ '-sOutputFile=tmp_%s_%%03d.png' %(file), filename]
-# Try to run gs
-print 'Converting %s to images using gs\n' %filename
-result = subprocess.Popen(gs_args)
+# Try to run converter engine
+print 'Converting %s to images using %s\n' %(filename, engine)
+result = subprocess.Popen(engine_args)
if result.wait():
- print '\nRunning gs failed with the error above, exiting.'
+ print '\nRunning %s failed with the error above, exiting.' %engine
sys.exit(6)
print "\nDone..."
@@ -90,7 +134,7 @@
imageframe.addElement(Image(href=href))
# Save file
-file = os.path.splitext(sys.argv[2])[0] if len(sys.argv) > 2 else file
+file = os.path.splitext(outfile)[0] if len(outfile) > 0 else file
doc.save(file, True)
print "Presentation saved as %s.odp" %file
Для преобразования картинок PNG в ODP pdf2odp использует пакет odfpy, так что его тоже необходимо установить.
Преобразовать презентацию из ODP в PPT нам поможет OpenOffice или LibreOffice. И у того и у другого есть пакетный режим конвертации, который, как это ни странно, не работает, если запущен хотя бы один графический инстанс офисного приложения (sic!), поэтому команда make ppt, о которой речь пойдет ниже, не сделает ничего и завершится при этом без ошибки, если у вас открыто какое-либо офисное приложение из указанных пакетов!
Итак, речь зашла о make. Извольте, это Makefile, который делает все:
# Produce main.pdf in output directory specified in latexmkrc
GREP = grep
SED = sed
DIA = dia
LATEXMK = latexmk
MPOST = mpost
PDF2ODP = pdf2odp
OFFICE = libreoffice
LATEXMKRC = ./latexmkrc
PDF_MODE_PTN = ^\s*$$pdf_mode\s*=\s*
OUT_EXT = $(shell case \
`$(GREP) '$(PDF_MODE_PTN)' $(LATEXMKRC) 2>/dev/null | \
$(SED) 's/$(PDF_MODE_PTN)\([0-3]\).*/\1/'` \
in ([1-3]) echo pdf ;; (*) echo dvi ;; esac)
OUT_DIR_PTN = ^\s*$$out_dir\s*=\s*
OUT_DIR = $(shell \
$(GREP) '$(OUT_DIR_PTN)' $(LATEXMKRC) 2>/dev/null | \
$(SED) 's/$(OUT_DIR_PTN)["\x27]\(.*\)["\x27].*/\1/')
ifeq ($(strip $(OUT_DIR)),)
OUT_DIR = .
endif
MAIN = main
TARGET = $(OUT_DIR)/$(MAIN).$(OUT_EXT)
ODP = $(OUT_DIR)/$(MAIN).odp
PPT = $(OUT_DIR)/$(MAIN).ppt
TEX_SOURCES = $(wildcard *.tex)
DIA_SOURCES = $(wildcard *.dia)
EPS_IMAGES = $(wildcard *.eps)
DIA_MP_SOURCES = $(DIA_SOURCES:.dia=.mp)
DIA_MPS_IMAGES = $(DIA_SOURCES:.dia=.mps)
DIA_MP_LOGS = $(DIA_SOURCES:.dia=.log)
DIA_MPX_FILES = $(DIA_SOURCES:.dia=.mpx)
DIA_MP_TRANS_FILES = $(DIA_MP_LOGS) $(DIA_MPX_FILES)
DIA_INTERMEDIATES = $(DIA_MP_SOURCES) $(DIA_MP_TRANS_FILES)
DIA_ALL_PRODUCTS = $(DIA_INTERMEDIATES) $(DIA_MPS_IMAGES)
MAIN_BBL = $(OUT_DIR)/$(MAIN).bbl
.PHONY: all clean clean-all odp ppt
.SECONDARY: $(DIA_MP_SOURCES)
all: $(TARGET)
odp: $(ODP)
ppt: $(PPT)
%.mp: %.dia
$(DIA) -e $@ $<
%.mps: %.mp
$(MPOST) -s 'outputtemplate="%j.mps"' $<
$(TARGET): $(EPS_IMAGES) $(DIA_MPS_IMAGES) $(TEX_SOURCES)
$(LATEXMK) $(MAIN)
$(ODP): $(MAIN).pdf
$(PDF2ODP) -x pdftocairo -s 1600 $(MAIN).pdf
$(PPT): $(ODP)
$(OFFICE) --headless --convert-to ppt --outdir $(OUT_DIR) $(ODP)
clean:
$(LATEXMK) -c
rm -f $(DIA_INTERMEDIATES)
clean-all:
$(LATEXMK) -C
rm -f *-eps-converted-to.pdf $(DIA_ALL_PRODUCTS) $(MAIN_BBL) $(ODP) $(PPT)
К нему прилагается файл latexmkrc (он должен находится в той же директории, где находится Makefile, т.е. в нашей рабочей директории), который необходим для правильной работы latexmk:
$pdf_mode = 1; # use pdflatex
$pdflatex = 'pdflatex --shell-escape %O %S' # needed by minted
Команда make без параметров строит презентацию в формате PDF, make odp - презентацию в формате ODP, а make ppt - презентацию в формате PPT.
В данном Makefile определены абстрактные правила преобразования форматов, поэтому его можно использовать в разных проектах, связанных с tex. Главная переменная, которую, как предполагается, должен определять пользователь, это MAIN - она определяет имена исходного файла tex и сгнерированных файлов презентаций. В нашем примере предполагается, что исходный файл tex называется main.tex и, соответственно, сгенерированные файлы презентаций будут иметь имена main.pdf, main.odp и main.ppt.