Извлечение (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
Причиной мусорных заголовков у Брикмана оказался не порог кегля, а повёрнутый
на 90° текст: боковые врезки и подписи к таблицам распознаются в кашу
(«aoHegoduenueArdng») и попадают в h1, потому что кегль у них крупный. Замер:
105 строк из 17 300, все до одной брак. Блоки с неgоризонтальным направлением
строки выбрасываются целиком (is_rotated).
Остаток — подписи внутри иллюстраций — понижается до <p>, а не удаляется:
looks_garbled() в bookhtml.py, шесть признаков структуры, любых двух хватает.
Проверка повторяется после склейки соседних заголовков: по отдельности «LF»,
«FF» и «TIT» проходят как аббревиатуры, склеенные — обрывок таблицы.
Вместе: 99 h1 у Брикмана против 48.
djvu2html.py — третий вход конвейера. Родной текстовый слой DjVu чище нашего
OCR (у Праты 10,9 против 2,8 слов со смесью алфавитов на 10 000), а через PDF
он не проходит: ddjvu -format=pdf кладёт страницы картинками.
Самопроверки: djvu2html.py --selftest, pdf2html.py --selftest --selftest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCiPWRi3Jqy4saD2LwU1Dp
Дамп, собранный конвертером из PDF, теряет всю семантику: ни заголовков, ни
листингов, только абзацы с обфусцированными классами. Такие книги уезжали в
перевод одним куском, а код — вместе с текстом.
Теперь при нулевом числе заголовков или листингов разметка восстанавливается из
таблицы стилей книги: три самых крупных кегля становятся уровнями заголовков,
моноширинная гарнитура — листингом, соседние строки листинга склеиваются в один
блок. Книга со своей разметкой в этот путь не попадает.
Проверено на двух настоящих книгах: дамп APoSD — 1833 плоских абзаца стали 29
главами и 58 листингами; Страуструп с собственной разметкой не изменился.
- epub2html.py: EPUB -> тот же XHTML, что pdf2html.py, только стандартная
библиотека. Уровень глав определяется по книге, а не берётся из <h1>:
в EPUB там обычно название и части.
- pdf2html.py: листинг узнаётся по флагу моноширинного шрифта PyMuPDF,
переносы и отступы внутри <pre> сохраняются, де-дефисация к коду не
применяется.
- translate.py: <pre> исключён из проверок языка (английский код утягивал
долю кириллицы ниже порога приёмки), рабочий каталог привязан к книге,
устаревшие главы прошлого прогона чистятся, инлайновый <code> переживает
переводчика.
- bookhtml.py: общий каркас документа и CSS для обоих входов.
- Тесты: test_epub2html.py на собранном в памяти EPUB, четыре новых случая
в test_translate.py.