add: переносы

This commit is contained in:
Sergei Erjemin 2025-05-09 21:57:03 +03:00
parent 24fd3fcf55
commit 0c83da52c6
5 changed files with 112 additions and 1 deletions

View File

@ -1,6 +1,6 @@
| in progress // в процессе разработки | | in progress // в процессе разработки |
|--------------------------------------| |--------------------------------------|
| 0 | | -0 |
# Типограф для Web # Типограф для Web

14
etpgrf/__init__.py Normal file
View File

@ -0,0 +1,14 @@
"""
Typography - библиотека для экранной типографики текста с поддержкой HTML.
Основные возможности:
- Автоматическая расстановка переносов
- Неразрывные пробелы для союзов и предлогов
- Корректные кавычки в зависимости от языка
- Висячая пунктуация
- Очистка и обработка HTML
"""
__version__ = "0.1.0"
import regex
from etpgrf import processor, hyphenation

89
etpgrf/hyphenation.py Executable file
View File

@ -0,0 +1,89 @@
import regex
def hyphenation_in_word(s: str, max_chunk: int = 14, sep: str = "-") -> str:
""" Расстановка переносов в русском слове с учетом максимальной длины непереносимой группы.
Переносы ставятся половинным делением слова, рекурсивно.
:param s: Слово, в котором надо расставить переносы
:param max_chunk: Максимальная длина непереносимой группы символов (по умолчанию 14)
:param sep: Символ переноса (по умолчанию "-")
:return: Слово с расставленными переносами
"""
# Проверка гласных букв
def is_vow(let: str) -> bool:
return let.upper() in ['А', 'О', 'И', 'Е', 'Ё', 'Э', 'Ы', 'У', 'Ю', 'Я']
# Проверка согласных букв
def is_cons(let: str) -> bool:
return let.upper() in ['Б', 'В', 'Г', 'Д', 'Ж', 'З', 'К', 'Л', 'М', 'Н', 'П', 'Р', 'С', 'Т', 'Ф', 'Х', 'Ц',
'Ч', 'Ш', 'Щ']
# Поиск допустимой позиции для переноса около заданного индекса
def find_hyphen_point(word: str, start_idx: int) -> int:
vow_indices = [i for i in range(len(word)) if is_vow(word[i])]
if not vow_indices:
# Если в слове нет гласных, то перенос невозможен
return -1
# Ищем ближайшую гласную до или после start_idx
for i in vow_indices:
if i >= start_idx - 2 and i + 2 < len(word): # Проверяем, что после гласной есть минимум 2 символа
ind = i + 1
if (is_cons(word[ind]) or word[ind] in 'йЙ') and not is_vow(word[ind + 1]):
# Й -- полугласная. Перенос после неё только в случае, если дальше идет согласная
# (например, "бой-кий"), но запретить, если идет гласная (например, "ма-йка" не пройдет).
ind += 1
if ind <= 3 or ind >= len(word) - 3:
# Не отделяем 3 символ с начала или конца (это некрасиво)
continue
if word[ind] in 'ьЬЪъ' or word[-1] in 'ьЬЪъ':
# Пропускаем мягкий/твердый знак. Согласно правилам русской типографики (например, ГОСТ 7.62-2008
# или рекомендации по набору текста), перенос не должен разрывать слово так, чтобы мягкий или
# твердый знак оказывался в начале или конце строки
continue
return ind
return -1 # Не нашли подходящую позицию
# Рекурсивное деление слова
def split_word(word: str) -> str:
if len(word) <= max_chunk: # Если длина укладывается в лимит, перенос не нужен
return word
mid = len(word) // 2 # Середина слова
hyphen_idx = find_hyphen_point(word, mid) # Ищем точку переноса около середины
if hyphen_idx == -1: # Если не нашли точку переноса
return word
left_part = word[:hyphen_idx]
right_part = word[hyphen_idx:]
# Рекурсивно делим левую и правую части
return split_word(left_part) + sep + split_word(right_part)
# Основная логика
if len(s) <= max_chunk or not any(is_vow(c) for c in s):
# Короткое слово или без гласных
return s
return split_word(s)
def hyphenation_in_text(text: str, min_len_word_hyphenation: int = 14, sep: str = "") -> str:
""" Расстановка переносов в тексте
:param text: Строка, которую надо обработать (главный аргумент).
:param min_len_word_hyphenation: Минимальная длина слова для расстановки переносов.
:param sep: Символ переноса.
:return: str:
"""
rus_worlds = regex.findall(r'\b[а-яА-Я]+\b', text) # ищем все русскоязычные слова в тексте
rus_worlds = list(set(rus_worlds)) # убираем повторяющиеся слова
for word in rus_worlds:
if len(word) > min_len_word_hyphenation:
hyphenated_word = hyphenation_in_word(word, max_chunk=6, sep=sep)
print(f'{word} -> {hyphenated_word}')
text = text.replace(word, hyphenated_word)
return text

0
etpgrf/processor.py Normal file
View File

8
main.py Normal file
View File

@ -0,0 +1,8 @@
import etpgrf
if __name__ == '__main__':
text_in = 'Привет, World! Это <i>тестовый текст для проверки расстановки</i> переносов в словах. Миллион 1000000'
result = etpgrf.hyphenation.hyphenation_in_text(text_in, min_len_word_hyphenation=8, sep='-')
print(result)