Вы, наверное удивитесь, но чтобы написать учебник, надо знать системы сборки из софтверного БигТеха и, как ни странно, старый добрый сишный препроцессор (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

+

Система контроля версий для ваших текстов учебника

9

tree

+

Обозреватель содержимого папки

10

m4

+

Универсальный препроцессор. Целый скриптовый язык.

11

grep

+

Поиск по ключевому слову на жестком диске

Большинство этих утилит берутся из CygWin или MinGW.

Каков план?

Я предлагаю построить и пустить вот такой конвейер метаморфоза файликов.

конвейер метаморфоза файликов
конвейер метаморфоза файликов

Зелёным цветом покрашены те файлы, которые являются для нас исходниками. Это то самое, что надо подвергать версионному контролю в git репозитории. GNU Make тут выступает дирижером оркестра и механической коробкой передач одновременно.

На каждую главу книги создается *.texi файл, *.mk файл и папка с картинками. На вход препроцессора cpp подаются файлы *.texi. На утилиту pdflatex мы подаем выход самого обыкновенного сишного препроцессора (консольная утилита cpp) - то есть файл *.tex.

Если проводить аналогию, то LaTex - это как компоновщик LD из разработки ПО, только не для бинарного кода, а для обыкновенного человекочитаемого текста. Вот такие вот пирожки с капустой.

Допустим, вы пишете курсовой проект, магистерский диплом, datasheet на навороченный ASIC с 270-ю SPI-регистрами или учебник по программированию. Что надо уметь делать на Latex? На самом деле достаточно много всего разного. Однако вот минимальный джентльменский набор:

  1. Вставка изображения

  2. Вставка цитат

  3. Вставка кусков кода (так называемые листинги)

  4. Вставка гиперссылок

  5. Вставка уравнений

  6. Вставка оглавления

  7. Вставка списка литературы

    А теперь обо всем по порядку.

Вставка изображения

Этот кусок 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=Битовое поле для регистра]

/* 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?

Как многим известно, Си-препроцессор можно преспокойно использовать для любого другого языка программирования (не только Си) или любого другого текста в общем. Разработчики препроцессора даже добавили специальные ключи для этого. Вот такой пучок опций надо подать на утилиту 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
predefined macros remain defined.

Не использовать макросы от компилятора Си кода. Они тут не нужны, так как нет самого исполняемого кода.

Препроцессором 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
 

Теперь только остается набрать в консоли make all и у вас в этой папке появится *.docx и готовый для печати *.pdf файл. Easy!

Демо-версию получившегося учебника можно посмотреть тут

Важные моменты

1--Все latex исходники должны быть в формате кодирования UTF-8. Иначе утилита pandoc вам сгенерирует *.docs с кракозябрами.

Сборка учебника на CI сервере Jenkins

А теперь приятный бонус. Этот make скрипт сборки учебника, который мы написали скармливаем серверу сборки Jenkins и сервер сам нам теперь будет собирать *.pdf(ку) после каждого коммита в репозиторий. Автоматически. Здорово! Вы уже любите скрипты cборки?

Плюс в том, что сервер сборки даст вам гарантию, что вы ничего не забыли загрузить в git репозиторий.

Достоинства тандема LaTex + препроцессора

++ Вы получаете полностью конфигурируемый процесс сборки своей брошюры. Благодаря системе сборки Вы можете автоматически синтезировать множество вариаций одного и того же учебника, меняя набор глав и содержание, просто манипулируя переменными окружения в скриптах сборки make. Вам уже нравится сиcтема сборки GNU Make?...

++ Вы получаете полностью бесплатный инструментарий для вёрстки своего дока. Все консольные утилиты свободно скачиваются.

++ Благодаря скриптам Вы можете верстать учебник автоматически на серверах сборки рядом с прошивками. Всё, что от вас требуется это сделать комит в git и, вуаля, у вас новое издание книги.

Недостатки

-- Если вы пишете учебник по Си и вставляете листинги с Си-кодом в LaTex код, то препроцессор будет пытаться вставить include-ы, которых на самом деле нет, и выдаст ошибку. Всё заклинит. Поэтому придется убирать символ # из листингов с кодом и писать справку, что надо подразумевать тут символ #.  

Однако это легко решается. Есть ещё более универсальный препроцессор c лаконичным названием: 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)

и получится многострочная макро-подстановка с заменой. Надо реинкарнировать препроцессор m4 из 197x. Тут он даже более применим нежели cpp.

Итоги

Удалось научиться верстать высокодобротную документацию кодом на LaTeX. Оказывается написание учебника, в общем-то, ничем не отличается от классической сборки компьютерных программ.

Вероятно такой тандемный способ верстки окажется по нутру как раз тем, кто пришел к разработке документации из какого-то программирования. Со своими погремушками.

Этот текст поможет вузовцам варить свои курсовые, дипломные работы и всяческие тезисы. Также этот текст поможет техническим писателям, программистам оформлять приятный doc food.

Теперь и Вы умеете верстать учебники и можете учить этому других.

Словарь

Акроним

Расшифровка

CPP

C PreProcessor

pdf

Portable Document Format

UTF

Unicode Transformation Format

CLI

Command-line interface

GNU

GNU’s Not UNIX

UNIX

Uniplexed Information and Computing System

Ссылки

Вопросы:

  1. Есть ли способ пометить участок кода так, чтобы Си препроцессор его не менял? Чтобы как встретилась строка #include "file.h" так и осталась в первозданном виде.

  2. Как сишным препроцессрором cpp вставить кусок текста макро функцией, сохранив при этом переносы строк?

  3. Существует ли аналог консольной утилиты clang-format только для LaTeX кода?

  4. Как сделать так, чтобы LaTeX проверял русскую грамотность?

Комментарии (11)


  1. Qwertovsky
    04.12.2024 11:32

    Вы пробовали Typst? Говорят, он удобнее.


    1. aabzel Автор
      04.12.2024 11:32

      С тем же успехом я могу спросить почему не Sphinx?
      https://www.sphinx-doc.org/en/master/index.html


  1. maquefel
    04.12.2024 11:32

    Это прям совсем хорошо !

    LaTex хорош со всех сторон! Единственное, я не считаю, что его просто читать в исходном виде и для простых текстов предпочитаю Markdown.

    --Если вы пишите учебник по Си и вставляете листинги с Си-кодом в LaTex код, то препроцессор будет пытаться вставить include(ы), которых на самом деле нет и выдаст ошибку. Всё заклинит. Поэтому придется убирать символ # из листингов с кодом и писать справку, что надо подразумевать тут символ #.

    А почему не использовать lstlisting для кода и тогда не будет этого минуса ?


    1. artptr86
      04.12.2024 11:32

      Потому что препроцессор сработает раньше


      1. maquefel
        04.12.2024 11:32

        Да - не подумал над этим.


  1. artptr86
    04.12.2024 11:32

    Почему бы не использовать условные операторы и инклюды из самого латеха, не прибегая к сторонним препроцессорам?


  1. nerudo
    04.12.2024 11:32

    Я всё слегка подзабыл, но ТеХ позволяет создавать свои команды (функции, если хотите), которые могут быть полностью эквивалентны тем макросам, которые вы скармливаете препроцессору.


  1. Sadok
    04.12.2024 11:32

    на LaTeX профессионально программируют так называемые технические писатели

    ага. а верстальщики на html программируют. автор не знает самой сути того, о чем пишет.


  1. berez
    04.12.2024 11:32

    Есть ли способ пометить участок кода так, чтобы Си препроцессор его не менял? Чтобы как встретилась строка #include "file.h" так и осталась в первозданном виде.

    Нет.

    Вам от препроцессора по сути нужно только переменные в тексте менять на актуальные пути. С этим справится более подходящий инструмент (например, sed или awk). Инклюды латекс умеет и сам.

    Как сделать так, чтобы LaTeX проверял русскую грамотность?

    В википедии пишут вот что:

    \usepackage[russian]{babel} % Пакет поддержки русского языка

    Включает ли этот "пакет" проверку орфографии - затрудняюсь сказать. Могу предположить, что да (на это намекает слово babel).


    1. Zenitchik
      04.12.2024 11:32

      ЛатЕХ.

      Потому что

      LaT\varepsilon\chi