v0.5.2
Python application / build (push) Waiting to run

Что то сделал
This commit is contained in:
Igor20264
2026-09-08 19:37:54 +03:00
parent 1a5b35eb54
commit 510f7e7adf
90 changed files with 11720 additions and 5547 deletions
+170
View File
@@ -0,0 +1,170 @@
"""Second pass: replace «Рисунок 1.1» / «табл. 2» with @Тип:id."""
from __future__ import annotations
import re
from .blocks import (
Block,
CaptionBlock,
EquationBlock,
ImageBlock,
InlineSpan,
ListBlock,
ListingBlock,
ParagraphBlock,
TableBlock,
)
from .captions import caption_unique_id
# Number like 1 / 1.1 / 2.3.4 — do NOT use [\d.]+ (eats trailing sentence period).
_NUM = r"(?P<num>\d+(?:\.\d+)*)"
_REF_PATTERNS = [
(
re.compile(
rf"(?P<full>(?:Рисунк(?:е|а|у|ом)?|Рисунок|Рис\.?)\s+{_NUM})",
re.IGNORECASE,
),
"figure",
),
(
re.compile(
rf"(?P<full>(?:Таблиц[аыеу]|табл\.?)\s+{_NUM})",
re.IGNORECASE,
),
"table",
),
(
re.compile(
rf"(?P<full>(?:Листинг(?:а|у|е|ом)?|лист\.?)\s+{_NUM})",
re.IGNORECASE,
),
"listing",
),
(
re.compile(
rf"(?P<full>(?:Формул[аыеу]|форм\.?)\s+{_NUM})",
re.IGNORECASE,
),
"equation",
),
]
_TYPE_LABEL = {
"figure": "Рисунок",
"table": "Таблица",
"listing": "Листинг",
"equation": "Формула",
}
def collect_number_maps(blocks: list[Block]) -> dict[str, dict[str, str]]:
"""kind → {number → unique_id}."""
maps: dict[str, dict[str, str]] = {
"figure": {},
"table": {},
"listing": {},
"equation": {},
}
for b in blocks:
if isinstance(b, CaptionBlock) and not b.is_continuation:
maps[b.kind][b.number] = b.unique_id or caption_unique_id(b.kind, b.number)
elif isinstance(b, ImageBlock) and b.caption_id:
num = _id_to_number(b.caption_id, "fig")
if num:
maps["figure"][num] = b.caption_id
elif isinstance(b, TableBlock) and b.caption_id:
num = _id_to_number(b.caption_id, "tbl")
if num:
maps["table"][num] = b.caption_id
elif isinstance(b, ListingBlock) and b.caption_id:
num = _id_to_number(b.caption_id, "lst")
if num:
maps["listing"][num] = b.caption_id
elif isinstance(b, EquationBlock) and b.number and b.unique_id:
maps["equation"][b.number] = b.unique_id
return maps
def _id_to_number(uid: str, prefix: str) -> str | None:
if not uid.startswith(prefix):
return None
rest = uid[len(prefix):]
if not rest:
return None
return rest.replace("_", ".")
def rewrite_text(text: str, maps: dict[str, dict[str, str]]) -> str:
"""Replace object references; leave bibliography [1] alone."""
if not text:
return text
def repl_factory(kind: str):
label = _TYPE_LABEL[kind]
num_map = maps.get(kind) or {}
def repl(m: re.Match) -> str:
num = m.group("num")
uid = num_map.get(num)
if not uid:
return m.group(0)
return f"@{label}:{uid}"
return repl
out = text
for pattern, kind in _REF_PATTERNS:
out = pattern.sub(repl_factory(kind), out)
return out
def _rewrite_paragraph_spans(para: ParagraphBlock, maps: dict[str, dict[str, str]]) -> None:
"""Rewrite refs even when Word split «Таблице 5.1» across runs."""
spans = para.spans
if not spans:
return
new_spans: list[InlineSpan] = []
i = 0
while i < len(spans):
s = spans[i]
if s.math or s.href:
new_spans.append(s)
i += 1
continue
j = i
chunk: list[InlineSpan] = []
while j < len(spans) and not spans[j].math and not spans[j].href:
chunk.append(spans[j])
j += 1
joined = "".join(p.text or "" for p in chunk)
rewritten = rewrite_text(joined, maps)
if rewritten == joined:
new_spans.extend(chunk)
else:
first = chunk[0]
same_fmt = all(
p.bold == first.bold and p.italic == first.italic and p.strike == first.strike
for p in chunk
)
new_spans.append(
InlineSpan(
text=rewritten,
bold=first.bold if same_fmt else False,
italic=first.italic if same_fmt else False,
strike=first.strike if same_fmt else False,
)
)
i = j
para.spans = new_spans
def apply_reference_pass(blocks: list[Block]) -> list[Block]:
maps = collect_number_maps(blocks)
for b in blocks:
if isinstance(b, ParagraphBlock):
_rewrite_paragraph_spans(b, maps)
elif isinstance(b, ListBlock):
b.items = [rewrite_text(item, maps) for item in b.items]
return blocks