Skip to content

wirenboard/wiki-pdf-typst

Repository files navigation

wiki2pdf — Генератор PDF-руководств из вики Wiren Board

Конвертирует страницы wiki.wirenboard.com в PDF-руководства с помощью Typst.

Как это работает

  1. Редактор вики добавляет {{Wbincludes:pdf}} на страницу устройства
  2. GitHub Actions каждые 10 минут находит такие страницы и генерирует/загружает PDF
  3. На странице вики появляется ссылка для скачивания PDF

Использование

Генерация одной страницы (локально)

python3 wiki2pdf.py "https://wiki.wirenboard.com/wiki/ZMCT205D"
python3 wiki2pdf.py "https://wiki.wirenboard.com/wiki/PageName" -o custom_output.pdf
python3 wiki2pdf.py "https://wiki.wirenboard.com/wiki/PageName" --keep-typst

Публикация всех страниц на вики

export WIKI_BOT_USER="EvgenyBoger@PdfUploader"
export WIKI_BOT_PASS="..."
python3 wiki_publish.py              # найти страницы, сгенерировать устаревшие PDF, загрузить
python3 wiki_publish.py --dry-run    # только показать список страниц
python3 wiki_publish.py --force      # перегенерировать все, игнорируя актуальность
python3 wiki_publish.py --page NAME  # обработать одну страницу
python3 wiki_publish.py --no-upload  # сгенерировать локально без загрузки

Добавление шаблона на страницы вики

export WIKI_BOT_USER="EvgenyBoger@PdfDev"
export WIKI_BOT_PASS="..."
python3 wiki_add_template.py "Название страницы 1" "Название страницы 2" ...

Добавляет {{Wbincludes:pdf}} на каждую страницу и подтверждает ревизию (требуется право approverevisions).

GitHub Actions

Рабочий процесс (.github/workflows/update-pdfs.yml) работает в двух режимах:

По расписанию (каждые 10 минут)

  • Запрашивает у вики все страницы с {{Wbincludes:pdf}}
  • Для каждой страницы сравнивает текущую ревизию вики с ревизией, записанной в комментарии загруженного PDF
  • Пропускает страницы, которые уже актуальны
  • Генерирует и загружает только устаревшие PDF

При пуше в master

  • Срабатывает при изменении кода (конвертер, шаблон, шрифты и т.д.)
  • Запускается с --force для перегенерации ВСЕХ PDF, так как отрисовка могла измениться
  • Игнорирует пуши в файлы, не влияющие на генерацию (README, .gitignore, вспомогательные скрипты)

Необходимые секреты

Настраиваются в репозитории: Settings → Secrets → Actions:

  • WIKI_BOT_USER — имя бота MediaWiki (например, EvgenyBoger@PdfUploader)
  • WIKI_BOT_PASS — пароль бота MediaWiki

Бот-аккаунт должен иметь права upload, writeapi и состоять в группе wb_editors на вики.

Установка (локальная разработка)

Требуется Python 3.11+ с зависимостями:

pip install -r requirements.txt

Бинарник Typst должен находиться в bin/typst:

curl -sL https://github.com/typst/typst/releases/download/v0.13.1/typst-x86_64-unknown-linux-musl.tar.xz | tar xJ
mkdir -p bin
mv typst-x86_64-unknown-linux-musl/typst bin/typst

Пользовательские шрифты размещаются в fonts/ (PT Sans включён).

Структура проекта

wiki2pdf.py              Конвертация одной страницы (CLI)
wiki_publish.py          Пакетная обработка: поиск страниц, генерация PDF, загрузка на вики
wiki_add_template.py     Добавление {{Wbincludes:pdf}} на страницы и подтверждение
batch_generate.py        Локальная пакетная генерация (фиксированный список страниц)
audit_pdfs.py            Проверка сгенерированных PDF на ошибки рендеринга
lib/
  fetcher.py             Клиент API MediaWiki, загрузка изображений, встраивание разделов
  html_converter.py      Движок конвертации HTML → Typst
  typst_runner.py        Обёртка компиляции Typst (с автоисправлением colspan)
  wiki_api.py            Бот-клиент MediaWiki (авторизация, загрузка, запросы страниц)
templates/
  manual.typ             Шаблон Typst (макет, стили, обложка, оглавление)
fonts/                   Файлы шрифтов (PT Sans)
.github/workflows/
  update-pdfs.yml        Рабочий процесс GitHub Actions

Архитектурные решения

Источник данных: MediaWiki API action=parse (не сырой викитекст)

Вики использует множество шаблонов {{Wbincludes:...}} с параметрами (например, note=true, no_description=true). Разбор сырого викитекста потребовал бы реализации шаблонизатора MediaWiki. API action=parse возвращает полностью отрендеренный HTML со всеми раскрытыми шаблонами, вычисленными условиями и применённым class="hidden" к скрытым элементам.

Разбор HTML (не викитекста)

BeautifulSoup с html.parser используется для обхода DOM. Это надёжнее разбора викитекста:

  • Шаблоны уже вычислены с их параметрами
  • Структура HTML предсказуема (<table class="wikitable">, <div class="thumb">, <span class="note"> и т.д.)
  • CSS-классы hidden, noprint, mw-editsection удаляются за один проход

Встраивание разделов

Многие страницы вики содержат разделы, которые являются просто ссылкой на подстраницу (например, «Ревизии устройства» ссылается на отдельную страницу). Фетчер обнаруживает такие разделы с единственной ссылкой и встраивает содержимое связанной страницы:

  • Заголовки подконтента понижаются относительно уровня родительского заголовка
  • Обратные ссылки («Перейти на страницу устройства») удаляются
  • Ссылки на встроенные страницы переписываются как локальные якоря (#fragment)
  • Пространства имён MediaWiki (File:, Special:, Template: и т.д.) исключаются из встраивания

Проверка актуальности

При публикации скрипт сравнивает текущую ревизию страницы вики с ревизией, сохранённой в комментарии загруженного PDF ("Auto-generated from revision NNNNN"). Страницы пропускаются, если ревизии совпадают. Проверки ревизий выполняются пакетно (до 50 страниц за один API-запрос).

Typst (не LaTeX, не Pandoc)

Typst выбран за:

  • Нативный вывод в PDF с хорошей типографикой
  • Простой язык разметки, хорошо отображающийся из HTML
  • Встроенная поддержка таблиц с colspan/rowspan, нумерации рисунков, переворота страниц
  • Функция layout() для измерения и ограничения размеров изображений
  • #page(flipped: true) для альбомных страниц

Обработка изображений

  • Изображения загружаются параллельно (ThreadPoolExecutor, 8 потоков)
  • Предпочитаются полноразмерные изображения вместо миниатюр (URL миниатюр конвертируются удалением сегмента /thumb/)
  • Портретные изображения (высота > 1.2× ширины) определяются по заголовкам PNG/JPEG и автоматически группируются в 2-колоночные сетки при последовательном расположении
  • Функция Typst constrained-image использует layout() + measure() для ограничения высоты изображений до 50% высоты страницы с сохранением пропорций
  • Изображения галерей используют более строгое ограничение высоты (40%), чтобы два помещались на странице
  • Первое изображение извлекается для обложки

Анимированные GIF

Из анимированных GIF извлекается до 8 визуально различных кадров методом жадного выбора наиболее удалённых точек. Короткие анимации (< 4 кадров) показывают 2 полных цикла. Каждый кадр подписан временной меткой. Кадры отображаются в сетке из 4 колонок внутри рисунка или инлайн в ячейках таблиц с размером, совпадающим со статичными изображениями.

Таблицы

Количество колонок определяется по строке заголовка (или моде всех строк), чтобы избежать завышения из-за строк легенды с увеличенными colspan. Режим отображения зависит от ширины содержимого:

  • Обычный: таблицы с < 5 колонками
  • Компактный: 5–7 колонок, уменьшенный шрифт (8.5pt)
  • Альбомный: 8+ колонок с широким содержимым (максимальный текст строки > 80 символов) или 12+ колонок; #page(flipped: true) с шрифтом 7pt

Когда заголовок непосредственно предшествует альбомной таблице, он перемещается внутрь перевёрнутой страницы.

Таблицы с <caption> оборачиваются в #figure(kind: table) для автонумерованных подписей. Цвета фона ячеек разрешаются из инлайн-стилей и CSS-классов (cell-green, cell-red, cell-yellow).

Компилятор Typst автоматически исправляет ошибки переполнения colspan, уменьшая значение вдвое и повторяя попытку (до 20 раз).

Галереи

Элементы <ul class="gallery"> MediaWiki конвертируются в отдельные рисунки с подписью галереи (например, «Обновление прошивки. Выбор файла»). Изображения галерей ограничены по высоте (40% страницы) для компактной компоновки.

Блоки примечаний и предупреждений

Два паттерна обнаружения:

  • <span class="note note-note"> / <span class="note note-warning"> — семантические классы
  • <div style="border:...;background:..."> — эвристика CSS для блоков шаблонов

Оба отображаются как стилизованные блоки с цветной левой границей.

Цвета текста и ячеек

Цвета разрешаются из нескольких источников:

  • CSS-классы: text-green, text-red, text-orange для текста; cell-green, cell-red, cell-yellow для фона ячеек
  • Инлайн-стили: style="color: #xxx" и style="background-color: #xxx"

Скрытый контент

Элементы с class="hidden" или class="noprint" удаляются. Это учитывает параметры шаблонов MediaWiki (например, no_description=true) и сам блок загрузки {{Wbincludes:pdf}}.

Обложка и метаданные

  • Первое изображение извлекается и размещается на обложке
  • URL страницы вики отображается и кликабелен
  • ID ревизии и временная метка из API вики
  • Заголовок на каждой странице ссылается на статью вики

Шрифт

PT Sans (Paratype) — свободный шрифт без засечек с полной поддержкой кириллицы. Пользовательские шрифты можно добавить в fonts/ и указать в templates/manual.typ.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors