add: переносы
This commit is contained in:
parent
24fd3fcf55
commit
0c83da52c6
@ -1,6 +1,6 @@
|
|||||||
| in progress // в процессе разработки |
|
| in progress // в процессе разработки |
|
||||||
|--------------------------------------|
|
|--------------------------------------|
|
||||||
| 0 |
|
| -0 |
|
||||||
|
|
||||||
# Типограф для Web
|
# Типограф для Web
|
||||||
|
|
||||||
|
14
etpgrf/__init__.py
Normal file
14
etpgrf/__init__.py
Normal 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
89
etpgrf/hyphenation.py
Executable 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
0
etpgrf/processor.py
Normal file
8
main.py
Normal file
8
main.py
Normal 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)
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user