Files
rip-help-system/docs/razdeljane-na-sekcii.md
2026-09-14 11:47:29 +03:00

16 KiB
Raw Blame History

Разделяне на произволен текст на секции

Този документ описва реалното поведение на RIP Help System при разделяне на help-файл на секции. Източникът е help_processor.py след последните корекции (вкл. merge_short_sections / merge_preamble_sections / номерирани глави). Не е маркетингово резюме.

Генерирането на ключови думи е извън обхвата, освен един ред в края.

За да прегенерираш PDF-а:

python docs/md_to_pdf.py

1. Място в обработката

За всеки файл process_file() прави:

  1. Избира парсер по разширение (.html / .htm, .docx, .doc, .pdf, .txt).
  2. Парсерът връща списък от Section(title, text, level, images, html_text).
  3. Върху списъка се вика merge_short_sections(), после merge_preamble_sections().
  4. Празните блокове се филтрират при запис; заглавие без тяло се запазва (виж §8).

ZIP не е формат за разделяне: разопакова се и всеки вътрешен файл минава по същия път.

Нива: 1 = H1, 2 = H2, 3 = H3 (по-дълбоки HTML/Word заглавия се режат до 3), 0 = без заглавие.


2. Какво е граница на секция

В HTML и DOCX граница е едно от:

  1. H1 / Heading 1 / Заглавие 1 (класическа граница); или
  2. Номерирана top-level глава от вида 1. Заглавие / 2) Заглавие — дори ако е H2, bold или Normal — когато редът изглежда като заглавие на глава (виж §2.1).

Текстът на границата става title и не се копира в тялото на секцията.

В HTML/DOCX не отварят нова секция:

  • Word Title / Наименование / CSS Title / MsoTitle — корица; става заглавие на преамбюла (или ред в тялото, ако вече има съдържание);
  • „Съдържание“ / Contents / TOC и еквиваленти — остават в тялото и включват TOC фаза (виж §2.1);
  • H2–H6, Subtitle / Подзаглавие, изцяло bold параграфи — освен ако текстът е номерирана глава (N. Title);
  • редове от таблици (a | b | c), „Фигура N: …“, дълги изречения с номерация.

TXT и PDF запазват своите правила (номерирани редове / по-голям шрифт) — там ниво 2 е нормална граница.

Ако преди първата глава има корица + съдържание, те образуват преамбюл (title от корицата/първото H1).

Ако парсерът не открие нито една секция, файлът става една секция без заглавие, с целия извлечен текст.

2.1. Номерирани глави и TOC

Шаблон: ^\d{1,2}[\.\)]\s+\S , дължина < 120, ≤ 12 думи след номера, без |, без „Фигура N“.

След заглавие „Съдържание“/TOC парсерът влиза в TOC фаза:

  • първите срещания на 1. …, 2. … (Normal / списък) остават в преамбюла;
  • повторно срещане на същия текст (реалната глава) затваря TOC фазата и отваря нова секция;
  • ако TOC е в <ol>/<ul> или след TOC има не-номериран блок, TOC фазата приключва и следващата номерирана глава пак реже.

Без блок „Съдържание“ всяка номерирана глава реже директно.

Пример (atra-manual.docx / NESPERTCAM): единственото Heading 1 е заглавието на документа; главите 1–9 са Heading 2. Очакване: 10 секции — преамбюл (title + Съдържание + TOC) + по една за 1. … 9.. Не една мега-секция.


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 — граница (освен ако е само корица преди TOC; виж §2)
Heading 2 / 3 2 / 3 — в тялото, освен номерирана глава N. Title
Title / MsoTitle / Наименование корица — не граница
Subtitle / Подзаглавие 2 — в тялото
Заглавие / Заглавие 1 1 — граница
Заглавие 2 / 3 2 / 3 — в тялото (освен номерирана глава)
Überschrift / Überschrift 1 1 — граница
MsoHeading1 / 2 / 3 1 / 2 / 3

Число над 3 се ограничава до 3. Subtitle / Подзаглавие без цифра → ниво 2; останалите именувани heading-стилове без цифра → ниво 1. Title / Наименование не връщат ниво за сегментиране.

Ако стилът не е heading, има fallback: Word outlineLvl 0–2 и текст под 120 символа → ниво = outlineLvl + 1.

3.2. Bold / H2 като подзаглавие

Изцяло bold кратък параграф или H2 без N. Title остава в тялото.

Ако текстът е номерирана глава (1. Преглед…), реже секция дори при Heading 2 / bold / Normal (с TOC правилата от §2.1).

3.3. Таблици и картинки

Таблица: всеки ред се сплесква до клетка | клетка | клетка в plain text; в html_text — прост <table>.

Картинки в параграф: записват се като [IMG: img_NN] в plain text и в html_text (viewer ги заменя с <img>). Поддържат се r:embed (вградени) и r:link (външни file:/// / UNC), ако файлът е достъпен при сканиране.

Rich HTML (DOCX): за всеки параграф в тялото се строи html_text от Word runs — <b> / <i> / <u> (изричен run или наследен от paragraph/character style), <span style="color:#RRGGBB"> при изричен RGB цвят, спец. символи (•, →, …) чрез HTML escape. Runs вътре в w:hyperlink (TOC редове) също влизат в html_text; ако runs липсват, има fallback към escaped para.text. Plain text остава без markup (за split / Claude). Theme/auto цветове без RGB не се записват. Viewer показва html_text когато е наличен — затова TOC/bold/картинки трябва да са в него, не само в plain text.

Празен параграф без картинка се пропуска.

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. Заглавие

  1. Тагове: h1→ граница (ниво 1); h2–h6→ в тялото освен номерирана глава.
  2. CSS class с heading / заглавие / msoheading / überschrift (+ опционална цифра). Title / MsoTitle / наименование са корица, не граница. subtitle / подзаглавие → в тялото.
  3. p или div с текст < 120 символа, изцяло обвит в <b> / <strong> → в тялото, освен номерирана глава.
  4. Текст „Съдържание“ / Contents / TOC → в тялото + TOC фаза (§2.1).
  5. Номерирана глава N. Title на p / h2 / bold → граница според TOC правилата.

5.2. Съдържание

Списъци и таблици влизат в текущата секция. В plain text редовете им са с нов ред (не сплескани в един ред). Декоративни атрибути (class, id, on*, data-* …) се махат; от style се пазят color, font-weight (bold) и font-style (italic). Тагове <b>/<strong>/<i>/<em> и <font color> остават.

Картинки: локални и 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 / merge_preamble_sections)

Константа: MIN_SECTION_TOKENS = 60 (думи в тялото, str.split()).

merge_short_sections:

  • Секция с непразно заглавие не се слива, дори тялото да е късо или празно.
  • Секция без заглавие и с под 60 думи се залепва към предишната (текст, картинки, HTML).
  • Няма предишна секция → късият untitled блок остава сам.

merge_preamble_sections (след горното):

  • Ако първата секция е корица с късо/празно тяло, а втората е озаглавена „Съдържание“/TOC → сливат се в един преамбюл (title = корицата; TOC заглавието влиза в тялото).

Следствия:

  • Корица + съдържание + първа глава → две секции (не четири от Title / TOC / H2-подзаглавия).
  • Пълен документ с номерирани глави 1…N (дори като H2) → преамбюл + N секции.
  • Последователни H1 без тяло („Глава 1“ веднага следвано от друг H1) остават две секции.
  • H2 / bold подзаглавия без номерация („Какво е …?“) не режат секция.
  • Untitled текст в началото на файла остава отделна секция (докато не е под прага и няма към какво да се слее — първият елемент не се слива).

При запис в process_file():

  • Ако няма текст, картинки и HTML, но има заглавие → тялото става самото заглавие. Затова „голо“ заглавие оцелява като секция.
  • Ако няма нито текст, нито заглавие, нито картинки → секцията се пропуска.

clean_text() срива поредици от интервали/табове, но пази нови редове (списъци и абзаци). Маха опасни C0 контроли (NUL и др.); Unicode символи (•, →) остават; NBSP става обикновен интервал.


9. Текст без заглавие

Типични случаи:

  • Увод преди първата граница.
  • HTML/DOCX без нито едно разпознато заглавие/номерирана глава → целият файл е една untitled секция.
  • TXT без markdown/номерация.
  • Къс untitled остатък след заглавие се слива с предишната секция (§8).

Празното title по-късно се попълва от класификатора (извън този документ).


10. Таблици — обобщение

Формат Поведение
DOCX Редове a | b | c в текущата секция; не са граница
HTML Целият <table> е блок в текущата секция
TXT / PDF Няма отделен table parser; таблицата е просто текст/редове

11. Ключови думи (един ред)

След като секциите са вече разделени, всяка получава заглавие и ключови думи чрез Claude или fallback; ръчните думи от таб „Сканиране“ се записват първи във всяка секция.