Мягкий перенос U+00AD: русские PDF рвали слова пополам

Питер и ДМК переносят строку мягким дефисом, а не обычным. Ветка склейки
проверяла только "-", поэтому строки соединялись через пробел: «введен ные»,
«Практиче ски». Теперь U+00AD снимается при склейке, остатки внутри строки
удаляются, а в ошибочно опознанных листингах перенос со строкой схлопывается.

Измерено на Шоттсе (Питер, 2020, 544 стр.): было 8 мягких переносов на
проверенной выборке, стало ноль.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2PNJCFFXvizoNjTFuTaqi
This commit is contained in:
Илья Поляков
2026-08-24 11:50:26 +03:00
parent f19410d9ce
commit 1a2c03f999
+13 -2
View File
@@ -39,6 +39,7 @@ MAX_IMG_SHARE = 0.6 # доля площади страницы: больше
# Запасное опознание листинга, когда издатель вычистил и имена шрифтов, и флаги:
# кегль мельче основного текста плюс пунктуация, которой в прозе не бывает.
CODEY = re.compile(r"[{}();\[\]<>=*/#]|::|->")
SOFT_HYPHEN = "\u00ad"
def style(font, flags=0):
@@ -137,7 +138,10 @@ def block_text(b, pre=False):
# слова. Ни склейки строк, ни де-дефисации здесь быть не должно.
rows = ["".join(span_html(s, plain=True) for s in l["spans"])
for l in b["lines"]]
return "\n".join(rows).rstrip()
# В настоящем коде мягкого переноса не бывает: если он тут есть, блок
# опознан как листинг ошибочно (обычно это таблица опций), и слово надо
# склеить, а не оставить разорванным переводом строки.
return "\n".join(rows).replace(SOFT_HYPHEN + "\n", "").rstrip()
out = []
for i, l in enumerate(b["lines"]):
line = "".join(span_html(s) for s in l["spans"])
@@ -145,7 +149,12 @@ def block_text(b, pre=False):
continue
if out:
prev = out[-1]
if prev.endswith("-") and not prev.endswith("--"):
# Русские издания переносят мягким дефисом U+00AD, а не обычным:
# без этой ветки строки склеиваются через пробел и слово рвётся
# пополам («введен ные»). Измерено на Шоттсе (Питер, 2020).
if prev.endswith(SOFT_HYPHEN):
out[-1] = prev[:-1]
elif prev.endswith("-") and not prev.endswith("--"):
out[-1] = prev[:-1] # de-hyphenate
else:
out.append(" ")
@@ -154,6 +163,8 @@ def block_text(b, pre=False):
txt = re.sub(r"</i>(\s*)<i>", r"\1", txt)
txt = re.sub(r"</b>(\s*)<b>", r"\1", txt)
txt = re.sub(r"(?<=\S) {2,}(?=\S)", " ", txt)
# Мягкие переносы внутри строки читалке не нужны и ломают поиск по тексту.
txt = txt.replace(SOFT_HYPHEN, "")
return txt.strip()