Вы, наверное удивитесь, но чтобы написать учебник, надо знать системы сборки из софтверного БигТеха и, как ни странно, старый добрый сишный препроцессор (cpp). Да, господа... Именно так... Сейчас объясню, почему...
В front-end разработке существует язык разметки LaTeХ. Многие про него слышали и некоторые его используют. Это язык создан для вёрстки прекрасно скомпонованных документов. Например все datasheet(ы) на культовые мировые микроконтроллеры по 5000+ страниц как раз собраны именно через утилиту LaTeX. Обычно на LaTeX профессионально работают так называемые технические писатели. В западной академической среде тоже активно применяют LaTeX для вёрстки своих публикаций на IEEE Explore и для подготовки слайдов на всяческие околонаучные конференции.
При оформлении книги в latex автор не должен думать об оформлении. Автор думает о содержании. За оформление автор ответственности не несёт. Вся ответственность за оформление страниц перекладывается на компилятор LaTeX. В этом основная концепция.
Ещё достоинство LaTeX в том, что для того, чтобы создать *.pdf документ с нужными отступами, нумерацией, картинками, 7-ми этажными формулами, уравнениями и прочей прекрасной полиграфией вам абсолютно не нужна компьютерная мышка. Да... Вот так...
В этой заметке я покажу несколько трюков по работе с LaTeX.
Итак, танцуем от печки...
Что надо из софтвера (ПО)?
№ | Название утилиты | CygWin | Пояснение |
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 | + | Система контроля версий для ваших текстов учебника |
12 | realpath | + | Вычислить абсолютный путь к папке |
13 | sort | + | Утилита для сортировки строк. Например для списка акронимов. |
9 | tree | + | Обозреватель содержимого папки |
10 | m4 | + | Универсальный препроцессор. Целый скриптовый язык. |
11 | grep | + | Поиск по ключевому слову на жестком диске |
Большинство этих утилит преспокойно берутся из CygWin или MinGW.
Каков план?
Я предлагаю построить и пустить вот такой конвейер метаморфоза файликов.
Зелёным цветом покрашены те файлы, которые являются для нас исходниками. Это то самое, что надо подвергать версионному контролю в git репозитории. GNU Make тут выступает дирижером оркестра и механической коробкой передач одновременно.
На каждую главу книги создается *.texi файл, *.mk файл и папка с картинками. Вот пример *.texi файла для главы:
sinclude(`latex_m4_preproc_utils.texi')
\graphicspath{ {PATH_CHAPTER_X_DIR/pix} }
\chapter{Название Главы}
\section{Пролог}
ххххххххххххх
\section{Параграф\_1}
хххххххххх
\section{Итог}
хххххххххххх
\section{Гиперссылки}
\begin{enumerate}
\item \href{wwwwwwwwwwwwwwwww}{zzzzzzzzzzzz}
\end{enumerate}
На вход препроцессора cpp(или m4) подаются файлы *.texi. На утилиту pdflatex мы подаем выход самого обыкновенного сишного препроцессора (консольная утилита cpp) - то есть файл *.tex.
Если проводить аналогию, то LaTex - это как компоновщик LD из разработки ПО, только не для бинарного кода, а для обыкновенного человекочитаемого текста. Вот такие вот пирожки с капустой.
Допустим, вы пишете курсовой проект, магистерский диплом, 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 ). LaTeX сам найдет подходящий по контексту файл изображения в проиндексированных путях.
#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=Правильный if]
int ret = NVRAM_Get(ID_IPv4_ROLE, tmp, 1, &tmp_len);
if (MM_RET_CODE_OK != ret) {
return ERROR_CODE_HARDWARE_FAULT;
}
\end{lstlisting}
Если вы пишете учебник по Си и вставляете листинги с Си-кодом в LaTex код, то препроцессор будет пытаться вставить include-ы, которых на самом деле нет, и выдаст ошибку. Всё заклинит. Поэтому придется убирать символ # из листингов с кодом и писать справку (*), что надо подразумевать первым символом в строке символ #.
Как оформить таблицу?
Таблицы постоянно встречаются в текстах. Вот так они пишутся на LaTeX:
\begin{tabular} {ll}
Акроним & Расшифровка \\
\hline
TUI & Text-based user interface \\
CLI & command-line interface \\
UART & universal asynchronous receiver / transmitter \\
JTAG & Joint Test Action Group \\
LED & light-emitting diode \\
CIC & Cascaded integrator–comb \\
\end{tabular}
Как оформить перечисление "чиво-либо"?
В любых текстах то там то здесь постоянно появляются списки всяческих пунктиков. В LaTex это оформляется так:
\begin{enumerate}
\item One
\item Two
\item Three
\end{enumerate}
Как оформить гиперссылки?
В гиперссылки можно заточить список литераторы:
\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?
Как многим известно, Си-препроцессор можно преспокойно использовать для любого другого языка программирования (не только Си) или любого другого текста в общем. Разработчики препроцессора даже добавили специальные ключи для этого. Вот такой пучок опций надо подать на утилиту 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 | Do 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 можно не только собирать Си-код, но также и верстать 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}
преамбула preamble.texi у меня вот такая
\usepackage[T2A]{fontenc}
\usepackage{cmap} % для копипасты из PDF
\usepackage{uarial}
\renewcommand{\familydefault}{\sfdefault}
\usepackage{hyperref}
\usepackage{graphicx}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{listings}
\usepackage{listingsutf8}
\usepackage[margin=0.6in]{geometry}
\usepackage{indentfirst} % для русских красных строк
\geometry{top=20mm}
\setlength{\parindent}{1.25cm}
\sffamily
\usepackage[fontsize=14.0pt]{fontsize}
\renewcommand{\sfdefault}{cmss}
Специально вынес преамбулу в отдельный *.texi файл, чтобы без причины не мозолить глаза.
Отдельный же *.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
Теперь только остается дзыкнуть по *.bat-скрипту, который выполнит в консоли make -i all и у вас в этой папке появится *.docx и готовый для печати *.pdf файл. Easy!
Демо-версию получившегося учебника можно посмотреть тут
Важные моменты
1--Все latex исходники должны быть в формате кодирования UTF-8. Иначе утилита pandoc вам сгенерирует *.docs с кракозябрами.
Сборка учебника на CI сервере Jenkins
А теперь приятный бонус. Этот make скрипт сборки учебника, который мы написали скармливаем серверу сборки Jenkins и сервер сам нам теперь будет собирать *.pdf(ку) после каждого коммита в репозиторий. Автоматически. Здорово! Вы уже любите скрипты cборки?
Плюс в том, что сервер сборки даст вам гарантию, что вы ничего не забыли загрузить в git репозиторий.
Достоинства тандема LaTex + препроцессора
++ Вы получаете полностью конфигурируемый процесс сборки своей брошюры. Благодаря системе сборки Вы можете автоматически синтезировать множество вариаций одного и того же учебника, меняя набор глав и содержание, просто манипулируя переменными окружения в скриптах сборки make. Вам уже нравится сиcтема сборки GNU Make?...
++ Вы получаете полностью бесплатный инструментарий для вёрстки своего дока. Все консольные утилиты свободно скачиваются.
++ Благодаря скриптам Вы можете верстать учебник автоматически на серверах сборки рядом с прошивками. Всё, что от вас требуется это сделать комит в git и, вуаля, у вас новое издание книги.
Недостатки CPP
-- Если вы пишете учебник по Си и вставляете листинги с Си-кодом в LaTex код, то препроцессор будет пытаться вставить include-ы, которых на самом деле нет, и выдаст ошибку. Всё заклинит. Поэтому придется убирать символ # из листингов с кодом и писать справку, что надо подразумевать тут символ #.
Однако это легко решается. Существует ещё более универсальный препроцессор c лаконичным названием: m4. Надо просто реинкарнировать препроцессор m4 из 197x. Тут он даже более применим нежели cpp.
Как мигрировать с препроцессора CPP на препроцессор M4?
Так происходит условная вставка содержимого заголовочного файла:
-----------------------------
CPP
#define HAS_FILE1
#ifdef HAS_FILE1
#include "file1.txt"
#endif
----------------------------
M4
define(HAS_FILE2)
ifdef(`HAS_FILE1', `include(`file1.txt')', )
-------------------------------
Так происходит макро-подстановка с заменой:
---------------------------------------------
CPP
#define INSERT_PIX(FILE_NAME,HINT, TOKEN ) \
\begin{figure}[h] \
\centering \
\includegraphics[width=0.99\textwidth]{FILE_NAME} \
\caption{HINT } \
\label{fig:TOKEN} \
\end{figure}
INSERT_PIX(IL_62.jpg, IL-62 cocpit, IL_62)
INSERT_PIX(cat.jpg, Изображение кота, cat)
------------------------------------
M4
define(INSERT_PIX, format(
\begin{figure}[h]
\centering
\includegraphics[width=0.99\textwidth]{%s}
\caption{%s }
\label{fig:%s}
\end{figure}
, $1, $2 , $3
)
)
INSERT_PIX(IL_62.jpg, IL-62 cocpit, IL_62)
INSERT_PIX(cat.jpg, Изображение кота, cat)
Условная вставка кода:
----------------------------------------------------------
CPP
#ifdef HAS_FOREIGN_AUTHORS
\item Цифровая обработка сигналов, Стивен Смит,2018
\item Extreme C, Kamran Amini, 2019
\item Язык С в XXI веке, Бен Клеменс , 2015
\item Алгоритмические трюки для программистов, Генри Уоррен-мл., 2014
\item Архитектура встраиваемых систем, Даниэле Лакамера, 2023
#endif
-------------------------------------------------------------------
M4
ifdef(`HAS_FOREIGN_AUTHORS',
`
\item Цифровая обработка сигналов, Стивен Смит,2018
\item Extreme C, Kamran Amini, 2019
\item Язык С в XXI веке, Бен Клеменс , 2015
\item Алгоритмические трюки для программистов, Генри Уоррен-мл., 2014
\item Архитектура встраиваемых систем, Даниэле Лакамера, 2023
'
)
-------------------------------------------------------------------------------------------------------------------
Как сами видите, препроцессор M4 может всё тоже, что и препроцессор CPP, притом даже больше! M4 - это полноценный интерпретируемый язык программирования типа awk или python. Препроцессор M4 можно использовать даже как калькулятор формул.
Итоги
Удалось научиться верстать высокодобротную документацию кодом на 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 |
Ссылки
# | Название источника |
1 | Using the GNU Compiler Collection, Richard M. Stallman |
4 | |
6 | |
5 | |
2 | |
3 | |
7 | |
8 |
Вопросы:
Есть ли способ пометить участок кода так, чтобы Си препроцессор его не менял? Чтобы как встретилась строка #include "file.h" так и осталась в первозданном виде. (Спойлер: Никак, надо переходить на препроцессор M4)
Как сишным препроцессрором cpp вставить кусок текста макро функцией, сохранив при этом переносы строк? (Спойлер: Никак, надо переходить на препроцессор M4)
Существует ли аналог консольной утилиты clang-format только для LaTeX кода?
Как сделать так, чтобы LaTeX проверял русскую грамотность?