Извлечение (pdf2html.py), каждая правка вызвана конкретной книгой:
* колонтитул опознаётся по повтору текста в верхнем поле на многих полосах, а
не по одной позиции. Позиционный признак рубит и настоящие заголовки: у
Вернона («DDD Distilled») глава начинается ровно с верха полосы и короче 80
знаков, так пропали 14 заголовков из 15. Это ровно тот второй проход, о
котором говорил ponytail-комментарий на прежнем фильтре;
* блок режется, если шапка набрана крупнее следующего за ней текста. У Вернона
85 подзаголовков лежали в одном блоке с первым абзацем раздела: оглавление
теряло второй уровень, а абзац начинался с заголовка без точки;
* порог раздела снижен с 1.15 до 1.10 кегля текста. У Вернона подзаголовок
17.2 при тексте 15.0 — ровно на пять сотых ниже прежнего порога, и все 85
разделов уезжали в прозу. Тот же множитель, что у разреза блока, иначе
разрез и классификация расходятся;
* кандидатом в главы не может быть кегль, у которого в блоках нет букв. У
Нейгарда («Release it!») номер главы набран кеглем 100 отдельным блоком
(«1», «2», …), и главой объявлялся он, а настоящие заголовки в 30 пунктов
уезжали в разделы — оглавление выходило пустым. Отбор по наличию букв, а не
по длине: «Preface» — семь знаков, порог по длине отсекал бы и его.
Упаковка (pack_epub.py): XML 1.0 запрещает управляющие символы, а из PDF они
приезжают регулярно. У Бейера («Site Reliability Engineering») 33 главы из 45
не разбирались как XML из-за одного такого знака в начале абзаца, и читалка
спотыкается на таком файле молча. Чистим на упаковке, а не надеемся на
извлечение. Тест на это добавлен и проверен: с обезвреженной правкой он падает.
SKILL.md приведён в соответствие с репозиторием. Шаг упаковки с 25.08 делает
pack_epub.py, а текст всё ещё требовал calibre на PATH — из-за этого проверка
инструментов давала ложный отказ. Замерено 05.09.2026: ebook-convert
отсутствует на всех трёх здешних машинах, и все четыре книги собрались без
него. За calibre остался только .azw3 для Kindle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCiPWRi3Jqy4saD2LwU1Dp
Ручная настройка block_kind() под каждую книгу оказалась неприменима: у части
изданий имена шрифтов вычищены до «Fd820825», а флаги стилей у всех спанов
равны 4, то есть ни курсива, ни моноширинности по ним не видно. Пороги теперь
считаются из самого файла:
- кегль основного текста — по гистограмме символов;
- кегль глав — по частоте блочных кеглей, а не множителем от текста: иначе
подзаголовки разделов попадают в главы и оглавление пухнет до трёхсот пунктов;
- глава отличается от раздела положением в верхней трети полосы, и правило
применяется, только если глав так набирается хотя бы пять;
- красная строка — по распределению x0.
Листинг опознаётся флагом моноширинности, а при его отсутствии — кеглем мельче
основного текста плюс плотностью кодовой пунктуации.
Отсев картинок: мельче 24 pt (линейки и буллеты) и крупнее 60% площади полосы
(подложка отсканированной страницы). На «Программисте-прагматике» второе
правило убрало 370 сканов полос и уменьшило EPUB со 124 МБ до 17 МБ.
pack_epub.py собирает EPUB на стандартной библиотеке: calibre для упаковки не
нужен, а его установка требует root. Книга режется по h1, оглавление NCX
строится само. Тест test_pack_epub.py проверяет несжатый mimetype первым файлом,
экранирование разметки в метаданных, сохранение переносов в листинге и то, что
преамбула до первой главы не теряется.