Вёрстка Учебника (LaTeX + CPP + GNU Make = Учебник)

Вы наверное удивитесь, но чтобы написать ученик надо знать системы сборки из софтверного БигТеха и, как не странно, старый-добрый си-шный препроцессор (cpp.exe). Да, господа… Именно так… Сейчас объясню почему…
Есть такой язык программирования LaTeХ. Многие про него слышали и некоторые его используют. Это язык создан для вёрстки прекрасно скомпонованных документов. Например все datasheet (ы) на культовые мировые микроконтроллеры по 5000+ страниц как раз собраны именно через утилиту LaTeX. Обычно на LaTeX профессионально программируют так называемые технические писатели.
При оформлении книги в latex автор не должен думать об оформлении. Автор думает о содержании. За оформление автор ответственности не несёт. Вся ответственность за оформление страниц перекладывается на компилятор LaTeX. В этом основная концепция.
Ещё достоинство LaTeX в том, что для того чтобы создать *.pdf документ с нужными отступами, нумерацией, картинками, 7 ми этажными формулами, уравнениями и прочей прекрасной полиграфией вам абсолютно не нужна компьютерная мышка. Да… Вот так…
В этой заметке я покажу несколько трюков по работе с LaTeX.
Итак, танцуем от печки…
Что надо из софтвера (ПО)?
№ | Название утилиты | Пояснение |
1 | cpp | Препроцессор языка Си |
2 | pdf viewer | Обозреватель pdf файлов |
3 | pandoc | утилита для преобразования latex в docx |
4 | cmd | Интерпретатор языка Batch |
5 | make | Интерпретатор языка make |
6 | pdflatex | Интерпретатор языка LaTeX |
7 | Eclipse IDE | Текстовый редактор Eclipse |
8 | git | Система контроля версиями для ваших текстов учебника |
9 | tree | Обозреватель содержимого папки |
Каков план?
Я предлагаю построить и пустить вот такой конвейер метаморфоза файликов.

конвейер метаморфоза файликов
Зелёным цветом покрашены те файлы, которые являются для нас исходниками. Это то самое, что надо подвергать версионному контролю в git репозитории. GNU Make тут выступает дирижером оркестра и механической коробкой передач одновременно.
На каждую главу создается *.texi файл, *.mk файл и папку с картинками. На вход препроцессора cpp подаются файлы *.texi. На утилиту PdfLaTeX мы подаем выход самого обыкновенного Cи-шного препроцессора (консольная утилита cpp.exe). То есть файл *.tex.
Если проводить аналогию, то LaTex — это как компоновщик LD.exe из разработки ПО только не для бинарного кода, а для обыкновенного человеко-читаемого текста. Вот такие вот пирожки с капустой…
Допустим вы пишите курсовой проект, магистерский диплом, datasheet на навороченный ASIC с 270ю SPI регистрами или учебник по программированию. Что надо уметь делать на Latex? На самом деле достаточно много всего разного. Однако вот минимальный джентельменский набор.
Вставка цитат
Вставка изображения
Вставка кусков кода (так называемые листинги)
Вставка гиперссылок
Вставка уравнений
Вставка оглавления
Вставка списка литературы
А теперь обо всем по порядку…
Вставка изображения
Этот кусок LaTeX кода производит вставку изображения с именем arch.png
\begin{figure}[h]
\centering
\includegraphics[width=0.99\textwidth]{arch}
\caption{Harvard architecture }
\label{fig:mesh1}
\end{figure}
Можно даже создать макрофункцию для вставки картинки одной строчкой. Вот так INSERT_PIX (arch, Harvard architecture).
#define INSERT_PIX(FILE_NAME,HINT ) \
\begin{figure}[h] \
\centering \
\includegraphics[width=0.99\textwidth]{FILE_NAME} \
\caption{HINT } \
\label{fig:FILE_NAME} \
\end{figure}
Как на Latex оформить цитату?
В любом тексте от случая к случаю приходится вставлять цитаты. На LaTeX это можно сделать так.
\begin{quote}
В любом деле важно определить приоритеты.
Иначе второстепенное, хотя и нужное, отнимет все силы
и не даст дойти до главного.
\end{quote}
Как оформить вставку куска с кодом?
Вставка листинга с исходным кодом.
\begin{lstlisting}[label=some-code,caption=Битовое поле для регистра]
/* page 105 7.2.27 Register file: 0x19 – DW1000 State Information*/
typedef union {
uint8_t byte[4];
uint32_t dword;
struct {
uint32_t tx_state : 4; /*bit 0-3: TX_STATE*/
uint32_t res1 : 4; /*bit 4-7: */
uint32_t rx_state: 5; /*bit 8-12: RX_STATE*/
uint32_t res2 : 3; /*bit 13-15:*/
uint32_t pmsc_state : 4; /*bit 16-19: PMSC_STATE*/
uint32_t res3 : 12; /*bit 20-31:*/
};
} Dwm1000SysState_t;
\end{lstlisting}
Если вы пишите учебник по Си и вставляете листинги с Си-кодом в LaTex код, то препроцессор будет пытаться вставить include (ы), которых на самом деле нет и выдаст ошибку. Всё заклинит. Поэтому придется убирать символ # из листингов с кодом и писать справку (*), что надо подразумевать первым символом в строке символ #.
Как оформить гиперссылки?
\section{Гиперссылки}
\begin{enumerate}
\item \href{https://habr.com/ru/post/673522/}{Настройка ToolChain(а) для Win10+GCC+С+Makefile+ARM Cortex-Mx+GDB}
\item \href{https://habr.com/ru/post/111691/}{Пример Makefile}
\item \href{https://www.opennet.ru/docs/RUS/gnumake/}{Эффективное использование GNU Make}
\item \href{https://habr.com/ru/articles/857416}{Обновление Прошивки из Make Скрипта}
\item \href{https://www.youtube.com/watch?v=HEEVxZ4rBCo}{CI/CD прошивок для микроконтроллеров в Wiren Board ( начало на 25:20)}
\item \href{https://www.youtube.com/watch?v=vmuO4bHjTSo&t=7s}{Конвеерум 30: Эволюция рабочего окружения для embedded разработки}
\item \href{https://habr.com/ru/post/47513/}{GNU Make может больше чем ты думаешь}
\end{enumerate}
Как настроить препроцессор CPP?
Как многим известно, Си-препроцессор можно преспокойно использовать для любого другого языка программирования (не только Си) или любого другого текста в общем. Разработчики препроцессора даже добавили специальные ключи для этого. Вот они перед вами…
Опция ключ CLI | Пояснение | Пояснение |
-E | textual output from the preprocessor will be in UTF-8. | Заставить препроцессор сохранять в кодировке UTF-8 |
-P | Inhibit generation of linemarkers | Убрать лишние комментарии на выходе препроцессора. Эта опция специально для не Си кода |
-C | Do not discard comments | Не отбрасывать комментарии |
-traditional-cpp | Traditional Mode | Не удалять на выходе последовательно идущие пробелы |
-nostdinc | o not search the standard system directories for header files | Не искать стандартные заголовочные файлы |
-fexec-charset=UTF-8 | Set the execution character set | Задать кодирование символов в кодировке UTF-8 |
-DHAS_XXX | Передать макрос HAS_XXX через командную строку | |
-undef | Do not predefine any system-specific or GCC-specific macros. The standard | Не использовать макросы от компилятора Си кода. Они тут не нужны так как нет самого исполняемого кода. |
Препроцессором cpp.exe можно не только собирать Си-код, но также и верстать LaTeX, компоновать GraphViz, перекраивать Assembler код, перетасовывать LD скрипты компоновщика и всё, на что только вам фантазии хватит. Си (шный) препроцессор это дубовая вещь.
Структура репозитория
Вот так может выглядеть папка с исходниками учебника. На каждую главу по отдельной папочке.
> C:\cygwin64\bin\tree.exe
.
├── about_author
│ ├── about_author.mk
│ ├── about_author.texi
│ └── pix
├── attributes_of_good_firmware
│ ├── attributes_of_good_firmware.mk
│ ├── attributes_of_good_firmware.texi
│ └── pix
│ ├── free.png
│ ├── nano.png
│ └── ubolx_od.png
├── bibliography
│ ├── bibliography.mk
│ ├── bibliography.texi
│ └── pix
├── good_swc
│ ├── good_swc.mk
│ ├── good_swc.texi
│ └── pix
│ ├── arch.png
│ ├── folder.png
│ ├── list.png
│ ├── reg.png
│ └── scan.png
├── latex_misc.texi
├── library.mk
├── preamble.texi
└── why_make
├── pix
│ ├── IAR.png
│ ├── diff.png
│ ├── pc.png
│ ├── perf.png
│ ├── reactor.png
│ └── spher.png
├── why_make.mk
└── why_make.texi
12 directories, 33 files
>
Корневой Latex файл
А это корневой LaTeX файл в который в зависимости от конфига препроцессор и вставит все кусочки глав.
\documentclass[a4paper, 16pt]{book}
#include "preamble.texi"
\begin{document}
\title{ \textbf{ Название брошюры }}
\author{aabzel}
\date{\today}
\maketitle
\tableofcontents
#ifdef HAS_ABOUT_AUTHOR
#include "about_author.texi"
#endif
#ifdef HAS_ATTRIBUTES_OF_GOOD_FIRMWARE
#include "attributes_of_good_firmware.texi"
#endif
#ifdef HAS_GOOD_SWC
#include "good_swc.texi"
#endif
#ifdef HAS_WHY_MAKE
#include "why_make.texi"
#endif
#ifdef HAS_BIBLIOGRAPHY
#include "bibliography.texi"
#endif
\end{document}
отдельный же *.texi файл выглядит вот так.
\graphicspath{ {PATH_CHAPTER_X_DIR/pix} }
\chapter{Название главы}
Текст главы
Тут можно заметить, что препроцессор вставит переменную PATH_CHAPTER_X_DIR которая укажет абсолютный путь к этой папке. Сам путь расcчитает Make скрипт. Поэтому куда бы вы не клонировали из GIT исходники LaTeX путь к картинкам встанет в нужное значение автоматически. Вы уже любите препроцессор?
Скрипты MAKE файлов
Это корневой файл
ARTIFACT_NAME=main_generated
FINAL_LATEX_FILE =$(ARTIFACT_NAME).tex
ARTIFACT_DOCS=$(ARTIFACT_NAME).docx
ARTIFACT_PDF=$(ARTIFACT_NAME).pdf
BUILD_DIR=artifacts
CPP_OPT += -undef
CPP_OPT += -E
CPP_OPT += -P
CPP_OPT += -C
CPP_OPT += -fexec-charset=UTF-8
CPP_OPT += -traditional-cpp
CPP_OPT += -nostdinc
CPP_OPT += $(OPT)
PANDOC_OPT += -f latex
PANDOC_OPT += -t docx
$(ARTIFACT_DOCS) : $(FINAL_LATEX_FILE)
pandoc -s $^ $(PANDOC_OPT) -o $@
$(FINAL_LATEX_FILE):$(SOURCES_DOT) $(BUILD_DIR)
$(info Preproc...)
cpp main.texi $(CPP_OPT) $(INCDIR) -E -o $@
$(ARTIFACT_PDF): $(FINAL_LATEX_FILE) $(BUILD_DIR)
$(info generate_pdf...)
./latex.bat $(FINAL_LATEX_FILE)
move_artifacts: $(BUILD_DIR)
mv $(ARTIFACT_DOCS) $(BUILD_DIR)/$(ARTIFACT_DOCS)
mv $(ARTIFACT_PDF) $(BUILD_DIR)/$(ARTIFACT_PDF)
all: $(ARTIFACT_PDF) $(ARTIFACT_DOCS) move_artifacts
$(info All)
$(BUILD_DIR):
mkdir -p $@
clean:
$(info clean)
rm $(ART_PDV)
rm $(FINAL_LATEX_FILE)
-rm -fR $(BUILD_DIR)
include $(DOCUMENTATION_DIR)/library/library.mk
Это файл library.mk
$(info LIBRARY_MK_INC=$(LIBRARY_MK_INC) )
ifneq ($(LIBRARY_MK_INC),Y)
LIBRARY_MK_INC=Y
LIBRARY_DIR=$(DOCUMENTATION_DIR)/library
INCDIR += -I$(LIBRARY_DIR)
OPT += -DHAS_LIBRARY
ifeq ($(ABOUT_AUTHOR),Y)
include $(LIBRARY_DIR)/about_author/about_author.mk
endif
ifeq ($(CHAPTER_1),Y)
include $(LIBRARY_DIR)/chapter_1/chapter_1.mk
endif
ifeq ($(CHAPTER_2),Y)
include $(LIBRARY_DIR)/chapter_2/chapter_2.mk
endif
ifeq ($(CHAPTER_3),Y)
include $(LIBRARY_DIR)/chapter_3/chapter_3.mk
endif
ifeq ($(BIBLIOGRAPHY),Y)
include $(LIBRARY_DIR)/bibliography/bibliography.mk
endif
endif
Ну и make файл для главы. Тут вместо CHAPTER_X и chapter_x вы просто подставите название своей главы.
$(info CHAPTER_X_MK_INC=$(CHAPTER_X_MK_INC) )
ifneq ($(CHAPTER_X_MK_INC),Y)
CHAPTER_X_MK_INC=Y
CHAPTER_X_DIR=$(LIBRARY_DIR)/chapter_x
#@echo $(error CHAPTER_X_DIR=$(CHAPTER_X_DIR))
INCDIR += -I$(CHAPTER_X_DIR)
OPT += -DHAS_CHAPTER_X_DIR
OPT += -DHAS_FOREIGN_AUTHORS
OPT += -DPATH_CHAPTER_X_DIR=$(CHAPTER_X_DIR)
endif
В файле config.mk вы просто декларативно выбираете какие главы вставлять в вашу книгу, а какие выпиливать
ABOUT_AUTHOR=Y
CHAPTER_1=N
CHAPTER_2=Y
CHAPTER_3=N
BIBLIOGRAPHY=Y
И, наконец, сам Makefile. Как можно заметить в языке make тоже есть свой препроцессор — команда include
PROJECT_PATH:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
DOCUMENTATION_DIR:=$(PROJECT_PATH)../../../docs
PROJECT_PATH:=$(subst /cygdrive/c/,C:/,$(PROJECT_PATH))
DOCUMENTATION_DIR:=$(subst /cygdrive/c/,C:/,$(DOCUMENTATION_DIR))
INCDIR += -I$(PROJECT_PATH)
INCDIR += -I$(WORKSPACE_LOC)
include $(PROJECT_PATH)config.mk
include $(DOCUMENTATION_DIR)/docs.mk
include $(DOCUMENTATION_DIR)/make_scripts/typeset_book.mk
Теперь только остается набрать в консоли make -i all и у вас в этой папке появится *.docx и готовый для печати *.pdf файл. Easy!
Демо-версию получившегося учебника можно посмотреть тут
Важные моменты
1--Все latex исходники должны быть в формате кодирования UTF-8. Иначе утилита pandoc вам сгенерирует *.docs с кракозябрами.
Достоинства тандема LaTex + препроцессора
++Вы получаете полностью конфигурируемый процесс сборки своей брошюры. Благодаря системе сборки Вы можете автоматически синтезировать множество вариаций одного и того же учебника меняя набор глав и содержание, просто манипулируя переменными окружения в скриптах сборки GNU Make. Вам уже нравится сиcтема сборки GNU Make?…
++Вы получаете полностью бесплатный инструментарий для вёрстки своего дока. Все консольные утилиты свободно скачиваются.
Недостатки
--Если вы пишите учебник по Си и вставляете листинги с Си-кодом в LaTex код, то препроцессор будет пытаться вставить include (ы), которых на самом деле нет и выдаст ошибку. Всё заклинит. Поэтому придется убирать символ # из листингов с кодом и писать справку, что надо подразумевать тут символ #.
Итоги
Удалось научиться верстать высокодобротную документацию кодом на LaTeX. Написание учебника ничем не отличается от сборки компьютерной программы.
Этот текст поможет ВУЗ (овцам) верстать свои курсовые, дипломные работы и тезисы. Этот текст поможет техническим писателям, программистам оформлять приятный doc food.
Теперь и Вы умеете писать учебники и можете учить этому других.
Словарь
Акроним | Расшифровка |
CPP | C PreProcessor |
Portable Document Format | |
UTF | Unicode Transformation Format |
CLI | Command-line interface |
GNU | GNU«s Not UNIX |
UNIX | Uniplexed Information and Computing System |
Ссылки
Вопросы:
Есть ли способ пометить участок кода так, чтобы Си препроцессор его не менял? Чтобы как встретилась строка #include «file.h» так и осталась в первозданном виде.
Как Си пре-процессрором cpp.exe вставить кусок текста макро функцией, сохранив при этом переносы строк?
Существует ли Clang Format для LaTeX кода?
Как сделать так чтобы LaTeX проверял русскую грамотность?
