11 KiB
Разделяне на произволен текст на секции
Този документ описва реалното поведение на RIP Help System при разделяне на help-файл на секции. Източникът е help_processor.py след последните корекции (вкл. merge_short_sections). Не е маркетингово резюме.
Генерирането на ключови думи е извън обхвата, освен един ред в края.
За да прегенерираш PDF-а:
python docs/md_to_pdf.py
1. Място в обработката
За всеки файл process_file() прави:
- Избира парсер по разширение (
.html/.htm,.docx,.doc,.pdf,.txt). - Парсерът връща списък от
Section(title, text, level, images, html_text). - Върху списъка се вика
merge_short_sections(). - Празните блокове се филтрират при запис; заглавие без тяло се запазва (виж §8).
ZIP не е формат за разделяне: разопакова се и всеки вътрешен файл минава по същия път.
Нива: 1 = H1, 2 = H2, 3 = H3 (по-дълбоки HTML/Word заглавия се режат до 3), 0 = без заглавие.
2. Какво е граница на секция
Граница = открито заглавие. Текущата секция се затваря (flush) и започва нова. Текстът на заглавието става title и не се копира в тялото на секцията.
Всичко, което не е заглавие — параграфи, списъци, таблици, картинки — се добавя към текущата секция. Таблица никога не отваря нова секция.
Ако преди първото заглавие има съдържание, то образува секция с празно title.
Ако парсерът не открие нито една секция, файлът става една секция без заглавие, с целия извлечен текст.
3. Word (.docx)
Обхождат се децата на w:body в document-order: параграфи и таблици (_iter_docx_blocks). Стандартният Document.paragraphs пропуска таблиците, затова не се ползва за сегментиране.
3.1. Стилово заглавие
_docx_heading_level() гледа style_id и style.name, после base_style нагоре по веригата. Токенът се нормализира (без интервали, _, -, без значение на регистъра).
Разпознават се поне:
| Токен (примери) | Ниво |
|---|---|
| Heading 1 / heading1 | 1 |
| Heading 2 / 3 | 2 / 3 |
| Title | 1 |
| Subtitle | 2 |
| Заглавие / Заглавие 1 | 1 |
| Заглавие 2 / 3 | 2 / 3 |
| Подзаглавие | 2 |
| Наименование | 1 |
| Überschrift / Überschrift 1 | 1 |
| MsoHeading1 / 2 / 3 | 1 / 2 / 3 |
Число над 3 се ограничава до 3. Subtitle / Подзаглавие без цифра → ниво 2; останалите именувани стилове без цифра → ниво 1.
Ако стилът не е heading, има fallback: Word outlineLvl 0–2 и текст под 120 символа → ниво = outlineLvl + 1.
3.2. Bold като заглавие
Ако няма стилово ниво, параграфът се приема за заглавие ниво 2, когато:
- видимият текст е непразен и ≤ 120 символа;
- всички run-ове с текст са bold;
- стилът не започва с
list; - в параграфа няма картинки.
3.3. Таблици и картинки
Таблица: всеки ред се сплесква до клетка | клетка | клетка и редовете се добавят в тялото на текущата секция.
Картинки в параграф: записват се като [IMG: img_NN] в същото тяло.
Празен параграф без картинка се пропуска.
3.4. Fallback
Ако след обхождането няма секции: една секция от всички непразни параграфи; ако и те са празни — от таблиците.
4. Стар .doc
Конвертира се към .docx с LibreOffice (soffice) или, на Windows, MS Word през COM. После се вика същият parse_docx. Ако и двете конверсии пропаднат, файлът се чете като обикновен текст (parse_txt).
5. HTML / HTM (вкл. Word „Запиши като уеб“)
Премахват се script, style, nav, footer, header, noscript.
Събират се top-level блокове: h1–h6, p, ul, ol, table, dl, pre, blockquote, figure, hr, самостоятелни img, и div само ако изглежда като заглавие. Вложен блок не се обработва втори път.
5.1. Заглавие
- Тагове:
h1→1,h2→2,h3–h6→3. - CSS class съдържа
heading,заглавие,title,subtitle,msoheadingилиüberschrift, с опционална цифра. ТакаMsoHeading1/MsoNormalне се бъркат: само heading-класовете режат секция. Пример: Word HTML<p class="MsoHeading1">. pилиdivс текст < 120 символа, изцяло обвит в<b>/<strong>→ ниво 2.
5.2. Съдържание
Списъци и таблици влизат в текущата секция. В plain text редовете им са с нов ред (не сплескани в един ред). Декоративни атрибути (class, style, on*, data-* …) се махат от запазения HTML.
Картинки: локални и data: URI; HTTP(S) се пропускат. Дребни иконки под 50×50 px се изхвърлят.
Ако няма нито една секция: цялото body като една секция без заглавие.
6. Обикновен текст (.txt)
Кодирането се детектира с chardet.
Ред е граница, ако след strip():
- съвпада с Markdown заглавие
#{1,3} текст— нивото е броят#, заглавието е текстът след тях; или - съвпада с номерирано заглавие:
1. …,2) …,I. …,А. …(1–2 цифри / римски / една главна буква, после.или)), дължина на реда < 120.
Всички останали редове отиват в тялото (десният край се реже, водещите интервали се пазят).
Ако няма такива редове: една секция с целия файл.
7. PDF
Няма стилове. Текстът се събира като визуални редове по Y (не по PDF stream), буквите от drop-cap се слепват, сплескани списъци от вида 1 Foo 2 Bar се разбиват на редове.
Граница: група редове с почти еднакъв размер на шрифта, ако размерът е по-голям от предишния с повече от 1 pt и текстът е под 150 символа → нова секция, ниво 2.
Това е по-ненадеждно от Word/HTML. Картинките се кропват от страницата и се редят в текущата секция по вертикална позиция.
8. Сливане на кратки секции (merge_short_sections)
Константа: MIN_SECTION_TOKENS = 60 (думи в тялото, str.split()).
Правило след последните корекции:
- Секция с непразно заглавие не се слива, дори тялото да е късо или празно.
- Секция без заглавие и с под 60 думи се залепва към предишната (текст, картинки, HTML).
- Няма предишна секция → късият untitled блок остава сам.
Следствия:
- Последователни заглавия без тяло („Глава 1“ веднага следвано от „1.1 Увод“) остават две секции.
- Къс абзац без заглавие след секция се присъединява към нея, вместо да стане отделен къс запис.
- Untitled текст в началото на файла остава отделна секция (докато не е под прага и няма към какво да се слее — първият елемент не се слива).
При запис в process_file():
- Ако няма текст, картинки и HTML, но има заглавие → тялото става самото заглавие. Затова „голо“ заглавие оцелява като секция.
- Ако няма нито текст, нито заглавие, нито картинки → секцията се пропуска.
clean_text() срива поредици от интервали/табове, но пази нови редове (списъци и абзаци).
9. Текст без заглавие
Типични случаи:
- Увод преди първия Heading 1.
- HTML/DOCX без нито едно разпознато заглавие → целият файл е една untitled секция.
- TXT без markdown/номерация.
- Къс untitled остатък след заглавие се слива с предишната секция (§8).
Празното title по-късно се попълва от класификатора (извън този документ).
10. Таблици — обобщение
| Формат | Поведение |
|---|---|
| DOCX | Редове a | b | c в текущата секция; не са граница |
| HTML | Целият <table> е блок в текущата секция |
| TXT / PDF | Няма отделен table parser; таблицата е просто текст/редове |
11. Ключови думи (един ред)
След като секциите са вече разделени, всяка получава заглавие и ключови думи чрез Claude или fallback; ръчните думи от таб „Сканиране“ се записват първи във всяка секция.