5/06/2013

ЛаТеХ для продвинутых. Как подружить LaTeX и Inkscape.

(La)TeX является одной из наиболее продвинутых систем обработки текста. Тексты созданные в ЛаТеХе, обычно естественно-научной направленности, легко узнать не только по красивым формулам, но и по исключительно сбалансированному тексту.

В тоже время, графики и рисунки, обычно созданные в "посторонней" программе и импортированные в ЛаТеХ, зачастую изрядно портят внешний вид документа. Хотя их расположением занимается ЛаТеХ (см. пост о плавающих объектах), сам внешний вид рисунков полностью на совести пользователя.

Одним из наиболее бросающихся в глаза недостатков рисунков является несоотвествие шрифтов (размера и начертания) между иллюстрацией и основным текстом. В одном из предыдущих постов мы разобрали как с этим бороться в случае графиков функций, импортируемых из MATLAB. Одним из самых удобных средств для создания рисунков (поясняющих иллюстраций, диаграм, схем и т.п.) является Inkscape: свободный, кросплатформенный векторный графический редактор.

Цель этого поста: применение аналогичного трюка для экспорта иллюстраций, схем и рисунков из Inkscape таким образом, что текст на них будет соответствовать использованному в основном тексте.

Импорт рисунков Inkscape в LaTeX

Основная идея и простой пример

Inkscape создаёт рисунки в формате svg. Наша задача: "красиво" вставить их в ЛаТеХ-документ. Итак, по порядку:

  1. Рисуем рисунок :) Лучше всего задать размер страницы равным желаемому размеру иллюстрации в ЛаТеХе. Все надписи выполняем, как если бы мы это делали в ЛаТеХе. Т.е. пишем, например, $\vec{F}$, если хотим получить F с вектором-стрелочкой сверху. Размер и тип шрифта роли не играет: в результате получится шрифт как в документе (хотя можно указать размер посредством \large и т.п. команд). Цвет и "поворот" текста, напротив, будут приняты во внимание. Уделяем особое внимание горизонтальному выравниванию текста (влево, вправо, по центру), иначе он "переедет" в ЛаТеХе.
  2. Сохраняем в PDF формате: через пункт меню "Сохранить как..."/"Save as...". В появившемся окошке ставим галочку напротив "PDF+LaTeX: опустить текст в ПДФ и создать ЛаТеХ файл"/"PDF+LaTeX: Omit text in PDF, and create LaTeX file".
    Будут созданы два файла: "имя.pdf" с рисунком и "имя.pdf_tex" со всеми надписями (ниже полагаем имя=drawing).
  3. В ЛаТеХ-файл вставляем вот так:
    01:  % преамбула
    02:  \usepackage{graphicx,xcolor}
    03:  ...
    04:  % тело документа
    05:  \begin{figure}\centering
    06:    %\def\svgwidth{5cm} % если надо изменить размер
    07:    \input{drawing.pdf_tex}
    08:    \caption{...}\label{...}
    09:  \end{figure}
    
    Если необходимо изменить размер, то нужно переопределить параметр \svgwidth. Здесь также предполагается, что все файлы были сохранены в текущей директории.
  4. Компилируем: pdflatex имя-tex-файла. ЛаТеХ сначала вставит pdf-картинку из drawing.pdf, а затем наложит на неё текст из drawing.pdf_tex.
Результат:

Оптимизация процесса

У описанного выше процесса есть два больших недостатка. Во-первых, каждый раз, когда что-то изменено в Inkscape, надо заново сохранять файл как PDF+LaTeX, тыкая мышкой в разные пункты меню. Кроме того, включение файла срабатывает только, если файлы картинок находятся в текущей директории. От обоих недостатков можно легко избавиться, используя следующий код в ЛаТеХ-файле:

01:  ...
02:  %----------------------------------
03:  % Вставляем это в преамбулу документа
04:  \graphicspath{{figs/}} % путь, где искать картинки
05:  % следующий код взят из 
06:  % http://mirrors.ctan.org/info/svg-inkscape/InkscapePDFLaTeX.pdf
07:  \newcommand{\executeiffilenewer}[3]{%
08:    \ifnum\pdfstrcmp{\pdffilemoddate{#1}}%
09:    {\pdffilemoddate{#2}} > 0 {\immediate\write18{#3}}\fi}
10:  \newcommand{\includesvg}[1]{%
11:    \executeiffilenewer{#1.svg}{#1.pdf}%
12:    {inkscape -z -D --file=#1.svg %
13:     --export-pdf=#1.pdf --export-latex}%
14:    \input{#1.pdf_tex}%
15:  }
16:  %-----------------------------------
17:  ...
18:  \begin{document}
19:  ...
20:  % Inkscape figure
21:  \begin{figure}\centering
22:    %\def\svgwidth{5cm} % используем для изменения размера, если надо
23:    \includesvg{figs/drawing}
24:    \caption{Example Inkscape: include svg.}
25:  \end{figure}
26:  ...
27:  \end{document}
Здесь для начала указано, что \includegraphics должен искать картинки в поддиректории figs. Затем определены две новые команды.

Первая \executeiffilenewer{файл1}{фаил2}{команда1} выполняет команду команда1, если файл1 новее, чем файл2.

Вторая \includesvg{} сначала, если нужно, конвертирует svg файл в пару PDF+LaTeX, а затем вставляет получившуюся картинку. Конвертирование происходит посредством вызова Inkscape "из командной строки". Параметр -z означает "without-gui", а -D говорит Инкскейпу, что нужно экспортировать всю картинку. Предполагается, что команда inkscape находится в "путях к исполняемым файлам" (переменная $PATH в Юниксах или %path% в нетрадиционных операционных системах). Проверить так ли это можно открыв терминал ("коммандную строку"), набрав там inkscape и нажав ENTER. Если ничего не запустилось, значит надо в 12-й строке прописать полный путь к исполняемому файлу, что-то вроде "c:/Programms/Inkscape/inskcape" или "/usr/bin/inkscape", в зависимости от операционной системы.

Для компиляции используем pdflatex -shell-escape имя-tex-файла. Ключик -shell-escape разрешает ЛаТеХу запускать сторонние программы: в нашем случае, Инкскейп для конвертации svg в pdf+tex.

Возможности и ограничения

После вставки в ЛаТеХ, не должно быть никаких проблем с цветами, градиентными заливками и т.п. вещами: они сохранены в pdf-файле. С текстовыми же эффектами могут быть проблемы. Поддерживается только цвет и поворот текста. Такие навороты, как "текст вдоль линии" или "отображение" работать не будут, хотя в большинстве случаев они и не нужны. Единственным действительно неприятным недостатком является то, что не поддерживается текст в несколько строк.

Связка MATLAB - Inkscape - LaTeX

Иногда приходится "усовершенствовать" график, например, указав стрелками на какие-то его особые точки или разместив дополнительный рисунок поверх графика. Инкскейп идеально для этого подходит: он может "читать" файлы в формате eps и pdf.

Однако, если график построен в MATLAB, то лучше его сохранить напрямую в svg-формате, вместо промежуточного (для наших целей) eps. Сделать это очень просто с помощью функции plot2svg, которую можно скачать с MATLAB Central. На всякий случай, файл plot2svg.m включён также в архив с примерами из этого поста.

Строим график, а затем сохраняем его как, например,
plot2svg('figs/example_matlab.svg')
После этого "график" можно открыть в Инкскейп и внести изменения.

Экспорт/импорт в ЛаТеХ осуществляется в точности как уже было описано выше. Результат:

ЛаТеХ в Инкскейпе

Итак, прямая задача — экспорт рисунков из Инкскейпа в ЛаТеХ — решена. Но иногда приходится решать и обратную задачу: вставлять формулы из ЛаТеХа в Инкскейп. Сделать это можно двумя способами.

Способ первый: Render LaTeX Formula

Стандартное расширение Инкскейпа (из базовой поставки) позволяет легко и просто вставить формулу. Идём в пункт меню Extensions, выбираем Render, а потом LaTeX Formula.

Если нужно, можно также подгрузить дополнительные пакеты, как, например, kpfonts в примере выше. В результате получаем суперкрасивую... нарисованную формулу. Да-да, не "написанную буквами", а "нарисованную линиями".

Если в вашем Инкскейпе отсутствует пункт меню "LaTeX Formula", то попытайтесь найти файл extension-errors.log и посмотреть, что именно произошло. Например, в Windows велики шансы, что Инкскейп не смог найти програмку pstoedit, необходимую для функционирования "LaTeX Formula": если дело действительно в этом, то установите pstoedit и добавьте его в путь %path%.

Способ второй: Tex Text

Это расширение не входит в базовую поставку Инкскейпа, но его можно установить самостоятельно, скачав отсюда. Пожалуй, наиболее очевидным преимуществом Tex Text перед описанным выше способом является возможность подключать свой стилевой файл, через который можно подгрузить другие пакеты или определить свой команды

Заключение и выводы

Итак, экспортировать рисунки из Инкскейпа совсем несложно. Процесс легко автоматизировать с помощью простого ЛаТеХ-скрипта, автоматически конвертирующего svg-рисунок в связку PDF (картинка) и ТеХ (текст), которые вставляются в ЛаТеХ-документ один поверх другого. Достоинством этого метода является весьма неплохо выглядящий результат (рисунок с ТеХ шрифтом, формулами, ссылками) при минимальных затратах времени.

Хотя мы рассмотрели данный рецепт на примере Инкскейпа, аналогичные методы могут быть использованы и с другими программами. Из графических редакторов, ЛаТеХ неплохо поддерживается Xfig: об этом написано, например, здесь.

Справедливости ради, стоит отметить, что весьма неплохих результатов можно также добиться с помощью inkscape2tikz. Но это уже совсем другая история...

Все примеры из данного поста доступны для скачивания по адресу tinyurl.com/amorua-inkscape.

Зеркала на случай сбоя: зеркало1 зеркало2

23 комментария:

  1. Иногда при создании eps+latex надписи "едут" в случайном направлении, это лечится созданием невидимого прямоугольника, включающего весь рисунок.

    ОтветитьУдалить
  2. Так как я в очередной раз не автор этого поста, а всего лишь читатель, добавлю свои два цента.

    При создании графиков в MATLAB и сохранении их в EPS вылезает неприятный баг: матлаб добавляет белый фон, который увеличивает размер рисунка. Я лечу это открытием EPS-файла в Inkscape и разгруппированием всех элементов - после этого белые поля легко удалить.

    Возможность отрендерить формулу латех в inkscape - тоже очень полезная возможность, особенно в плане того, что она вставится не как шрифт, а как кривая.


    @Анонимный комментирует...
    Иногда при создании eps+latex надписи "едут" в случайном направлении
    Странно, но у меня они не едут. У меня они иногда обрезаются внизу, но здесь (и может быть Анонимусу тоже) помогает задание полей:

    gnuplot> set bmargin 4

    In recent versions of gnuplot, we can set bmargin (bottom margin), tmargin, lmargin, and rmargin in plot and multiplot.[отсюда]

    ОтветитьУдалить
  3. Пост прямо вовремя. Однако у меня ещё одна задача. Как подоткнуть в текст LaTeX (формат А4) рисунки формата А2? :)

    ОтветитьУдалить
  4. Комментарий не по теме, но относительно содержания. Стрелки и подчеркивания в снимке рисунка, сделанные, скорее всего, мышкой в Gimp'е, как-то немного портят общее впечатление. Позволю себе порекомендовать shutter — снятие снимков экрана и базовый набор для аннотации в одном флаконе. Ну или что-то в этом роде.

    ОтветитьУдалить
  5. А в какой версии inkscape есть такая вот галочка? (Это я про PDF+Latex при сохранении).
    У меня сейчас в дебиане стоит 0.47 - а в ней при сохранении в PDF возникает окно с всяко-рахными галками, но на latex и намека нет. Есть возможность сохранить в связке latex-pstricks, но не уверен, что оно потянется pdflatex'ом. Пока что компилируется inkscape 0.48 стабильная с оффсайта. Нестабильную пока не брал.

    ОтветитьУдалить
  6. Спасибо за интересную статью, Михаил! Небольшим ограничением является требование многих журналов использовать один .eps-файл для каждой иллюстрации. XFig вместе с небольшим скриптом fig2eps отлично справляется с этим (см. www.math-biophys.info/SciToolkit.php#xfig). Игорь

    ОтветитьУдалить
  7. @Basil Orlov комментирует...
    Пост прямо вовремя.

    Так скубенты принялись строчить курсачи и дипломы - мои старые посты про оформление диплома и рисунков в латехе ожидаемо поползли вверх.

    Однако у меня ещё одна задача. Как подоткнуть в текст LaTeX (формат А4) рисунки формата А2? :)

    А в чём проблема?

    \includegraphics[width=0.9\linewidth]{porn.eps}

    Оно?

    @verdakafo комментирует...
    Стрелки и подчеркивания в снимке рисунка, сделанные, скорее всего, мышкой в Gimp'е, как-то немного портят общее впечатление.

    Так ведь это ж олдскул, вердакафо! Я тоже так делаю. Ручная работа, ну типа как яйца Фаберже :-)

    Позволю себе порекомендовать shutter

    # apt-get install shutter

    Reading package lists... Done
    Building dependency tree
    Reading state information... Done

    The following extra packages will be installed:

    libfile-basedir-perl libfile-copy-recursive-perl libfile-desktopentry-perl libfile-homedir-perl libfile-mimeinfo-perl libfile-which-perl libgnome2-gconf-perl
    libgnome2-wnck-perl libgtk2-imageview-perl libhttp-server-simple-perl libnet-dbus-perl libproc-simple-perl libsort-naturally-perl libwww-mechanize-perl libx11-protocol-perl
    libxml-twig-perl libxml-xpathengine-perl

    Suggested packages:

    xml-twig-tools gnome-web-photo libgoo-canvas-perl nautilus-sendto

    The following NEW packages will be installed:

    libfile-basedir-perl libfile-copy-recursive-perl libfile-desktopentry-perl libfile-homedir-perl libfile-mimeinfo-perl libfile-which-perl libgnome2-gconf-perl
    libgnome2-wnck-perl libgtk2-imageview-perl libhttp-server-simple-perl libnet-dbus-perl libproc-simple-perl libsort-naturally-perl libwww-mechanize-perl libx11-protocol-perl
    libxml-twig-perl libxml-xpathengine-perl shutter
    0 upgraded, 18 newly installed, 0 to remove and 210 not upgraded.

    Need to get 3,284 kB of archives.

    After this operation, 15.9 MB of additional disk space will be used.
    Do you want to continue [Y/n]?

    Кхм... как-то оно многовато для скриншотов на мой вкус.

    ОтветитьУдалить
  8. @Sergy Kanaev комментирует...

    Пока что компилируется inkscape 0.48 стабильная с оффсайта.
    А вон там wheezy уже зарелизело, можно оттуда тягать.

    @Анонимный комментирует...
    Спасибо за интересную статью, Михаил!
    А мопед^W пост-то не мой, а Amorua. Так что в данном случае спасибо ему, а не мне.

    Небольшим ограничением является требование многих журналов использовать один .eps-файл для каждой иллюстрации.
    В смысле один рисунок на страницу? А viewbox в includegraphics не решает проблему?

    XFig вместе с небольшим скриптом fig2eps отлично справляется с этим
    О, занятно. Спасибо за ссылку!

    ОтветитьУдалить
  9. @virens
    >> А вон там wheezy уже зарелизело, можно оттуда тягать.

    Да, слышал уже про релиз. Может обновлюсь...
    Но у меня 0.48 нормально с исходников встала, как по маслу прет.

    Статья отличная.

    ОтветитьУдалить
  10. Ещё из вкусного: существует добавление для Inkscape, позволяющее выгружать изображение в tikz-код.

    Приходится чуть дошаманивать, если имеется текст, но в целом — достойно делает.

    ОтветитьУдалить
  11. @virens комментирует...

    А в чём проблема?

    \includegraphics[width=0.9\linewidth]{porn.eps}

    Оно?
    Не оно :) В некоторых бумажных книгах есть (были?) специальные вложения другого формата. Раскрываешь, а там А1, А2, А3 и т.п. А предложенный вариант просто уменьшит изображение до формата книги :) Вот как такое сделать в электронном виде? :)

    ОтветитьУдалить
  12. @Basil Orlov комментирует...
    Раскрываешь, а там А1, А2, А3
    В pdflatex / xelatex / lualatex можно сделать так:

    \documentclass{article}
    \begin{document}
    % PAGE 1
    Normal page

    \newlength{\classpageheight}
    \setlength{\classpageheight}{\pdfpageheight}
    \newlength{\classpagewidth}
    \setlength{\classpagewidth}{\pdfpagewidth}

    % PAGE 2 -- a big one
    \eject \pdfpagewidth=30cm \pdfpageheight=50cm
    This page is 30cm wide and 50cm tall

    % PAGE 3 -- normal again
    \eject \pdfpagewidth=\classpagewidth \pdfpageheight=\classpageheight
    Normal page again

    \end{document}

    ОтветитьУдалить
  13. @const
    Ещё из вкусного: существует добавление для Inkscape, позволяющее выгружать изображение в tikz-код.
    Я его пробовал в 2010-м году. Для простых картинок годится. Посложнее: приходилось столько доделывать, что проще было с нуля писать в tikz. С тех наблюдались улучшения?

    ОтветитьУдалить
  14. @amorua комментирует...
    В pdflatex / xelatex / lualatex можно сделать так:

    Спасибо, попробую :)

    ОтветитьУдалить
  15. Назрел вопрос к товарищу amorua: вот создаём мы рисунок с надписью в TeX-нотации. Она же всегда больше, чем окончательная надпись после конвертации TeX-ом. Это должно выливаться в неправильный Bounding Box при вставке в LaTeX, завышенный в сравнении с реальными размерами. Так ли это? И насколько это критично/заметно?

    ОтветитьУдалить
  16. @Basil Orlov
    Не скажу, потому что недавно нашёл этот инструмент, когда потребовалось делать рисунок с заметным количеством кривых Безье. Но вот с этими самыми кривыми помощь была неоспоримая для меня.

    Ну а tikz помог обойтись только tex-файлом, что было необходимо строго соблюсти.

    ОтветитьУдалить
  17. @iv_vl комментирует...
    Назрел вопрос к товарищу amorua: вот создаём мы рисунок с надписью в TeX-нотации. Она же всегда больше, чем окончательная надпись после конвертации TeX-ом. Это должно выливаться в неправильный Bounding Box при вставке в LaTeX, завышенный в сравнении с реальными размерами. Так ли это? И насколько это критично/заметно?

    При сохранении "как ПДФ" inkscape создаёт Bounding Box по размеру страницы, т.е. не обращая внимания на выступающие за её пределы элементы. Т.е. ответ на ваш вопрос: абсолютно некритично и абсолютно незаметно :)

    ОтветитьУдалить
  18. Спасибо за статью.

    >> Единственным действительно неприятным недостатком является то, что не поддерживается текст в несколько строк.

    Это да, печально. Я нашёл как минимум 2 хака (примеры для 2 строк, центрированных по горизонтали):

    1. Использовать shortstack:
    \shortstack[c]{First line \\ Second line}
    Проблема в том, что shortstack старается минимизировать вертикальные расстояния, так что будет 1. слишком тесно по вертикали 2. разное расстояние при разных строках (к примеру, "I" по высоте != "o").

    2. Использовать tabular:
    \begin{tabular}{c}
    First line \\ Second line
    \end{tabular}
    Здесь, наоборот, слишком большие расстояния по вертикали (зато, в отличие от первого варианта, стабильно). Решается так (спасибо stackexchange), если я не ошибаюсь, то высота строк таблицы меняется только в этом окружении (рисунка), так что всё должно быть ОК:
    \renewcommand{\arraystretch}{0.5}
    И без того громоздкое решение стало ещё больше. Но слава Кнуту, можно сократить и сделать ещё грязнее:
    \newcommand{\mlnb}{\renewcommand{\arraystretch}{0.5} \begin{tabular}{c}}
    \newcommand{\mlne}{\end{tabular}}
    Юзать так:
    \mlnb
    First line \\ Second line
    \mlne

    Все хаки в этом комменте являются общественным достоянием :D. Предложения по улучшению (в частности, по уменьшению дополнительных букв, кроме переименования команд, разумеется) приветствуются.

    ОтветитьУдалить
  19. @infroma комментирует...
    Предложения по улучшению (в частности, по уменьшению дополнительных букв, кроме переименования команд, разумеется) приветствуются.
    Общепризнанные пути в таких случаях, это окружение minipage и parbox. Неужели не проверяли?

    ОтветитьУдалить
  20. У меня вопрос: а как использовать русские подписи на картинках?
    Что при простой вставке букв, что при использовании $\text{}$ ЛаТеХ ругается так:
    Missing $ inserted. ...x(0,0)[lb]{\smash{привет}}}

    Если важно, то я работаю в cp1251.

    P.S.: на Win7, не смотря на наличие inkscape в Path, все равно пришлось вручную прописывать путь к exe-шнику.

    ОтветитьУдалить
  21. Доброго времени суток!
    Подскажите, пожалуйста с чем может быть связано. Latex при компиляции пишет, что не находит файл drawing.pdf, хотя оба файла drawing.pdf drawing.pdf_tex находятся в подпапке /a.

    \begin{figure}\centering
    \input{a/drawing.pdf_tex}
    \caption{Example Inkscape}
    \end{figure}

    ОтветитьУдалить
  22. При сохранении рисунка, сделанного в inkscape, в pdf, один рисунок сохраняется в несколько страниц в pdf документе (окружность на одной странице, стрелки на другой, к примеру). В итоге, latex не может вставить рисунок в документ. Подскажите, что делать?

    ОтветитьУдалить
  23. Доброго времени суток!
    Подскажите, пожалуйста с чем может быть связано. Latex при компиляции пишет, что не находит файл drawing.pdf, хотя оба файла drawing.pdf drawing.pdf_tex находятся в подпапке /a.

    \begin{figure}\centering
    \input{a/drawing.pdf_tex}
    \caption{Example Inkscape}
    \end{figure}
    В шапке задайте
    \graphicspath{{fig/}}% здесь лежат рисунки
    возможно еще понадобится:
    \usepackage[pdftex]{graphicx} %графика в пдф

    ОтветитьУдалить