From f48dd5bb53367944a6e53a8d809c851e71a20a91 Mon Sep 17 00:00:00 2001 From: erjemin Date: Tue, 13 May 2025 00:46:36 +0300 Subject: [PATCH] mod: minor . --- etpgrf/__init__.py | 3 ++- etpgrf/config.py | 5 ++++- etpgrf/hyphenation.py | 15 ++++++++++----- etpgrf/typograph.py | 41 ++++++++++++++++++++++------------------- main.py | 14 +++++++++----- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/etpgrf/__init__.py b/etpgrf/__init__.py index 895603e..4dcabfb 100644 --- a/etpgrf/__init__.py +++ b/etpgrf/__init__.py @@ -11,4 +11,5 @@ Typography - библиотека для экранной типографики __version__ = "0.1.0" from etpgrf.typograph import Typographer -from etpgrf.hyphenation import Hyphenator \ No newline at end of file +from etpgrf.hyphenation import Hyphenator +import etpgrf.config \ No newline at end of file diff --git a/etpgrf/config.py b/etpgrf/config.py index 310845e..1fedc97 100644 --- a/etpgrf/config.py +++ b/etpgrf/config.py @@ -14,7 +14,10 @@ LANG_EN = 'en' # Английский SUPPORTED_LANGS = frozenset([LANG_RU, LANG_EN]) # Язык(и) по умолчанию, если не указаны пользователем и не заданы через ETPGRF_DEFAULT_LANGS_MODULE DEFAULT_LANGS = LANG_RU -# + +# Значения по умолчанию для параметров Hyphenator +DEFAULT_HYP_MAX_LEN = 10 # Максимальная длина слова без переносов +DEFAULT_HYP_MIN_LEN = 3 # Минимальный "хвост" слова для переноса # ----------------- соответствия `unicode` и `mnemonic` для типографа diff --git a/etpgrf/hyphenation.py b/etpgrf/hyphenation.py index 9a617a3..83c07b9 100755 --- a/etpgrf/hyphenation.py +++ b/etpgrf/hyphenation.py @@ -1,5 +1,5 @@ import regex -from etpgrf.config import LANG_RU, LANG_EN, DEFAULT_MODE, DEFAULT_LANGS, SHY_ENTITIES, MODE_UNICODE +from etpgrf.config import LANG_RU, LANG_EN, SHY_ENTITIES, MODE_UNICODE, DEFAULT_HYP_MAX_LEN, DEFAULT_HYP_MIN_LEN from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs _RU_VOWELS_UPPER = frozenset(['А', 'О', 'И', 'Е', 'Ё', 'Э', 'Ы', 'У', 'Ю', 'Я']) @@ -17,8 +17,8 @@ class Hyphenator: def __init__(self, langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None, mode: str = None, # Режим обработки текста - max_unhyphenated_len: int = 14, # Максимальная длина непереносимой группы - min_chars_per_part: int = 3): # Минимальная длина после переноса (хвост, который разрешено переносить) + max_unhyphenated_len: int = DEFAULT_HYP_MAX_LEN, # Максимальная длина непереносимой группы + min_chars_per_part: int = DEFAULT_HYP_MIN_LEN): # Минимальная длина после переноса (хвост, который разрешено переносить) self.langs: frozenset[str] = parse_and_validate_langs(langs) self.mode: str = parse_and_validate_mode(mode) self.max_unhyphenated_len = max_unhyphenated_len @@ -35,6 +35,7 @@ class Hyphenator: self._load_language_resources_for_hyphenation() # Определяем символ переноса в зависимости от режима self._split_code: str = SHY_ENTITIES['SHY'][0] if self.mode == MODE_UNICODE else SHY_ENTITIES['SHY'][1] + print(f"========={self.max_unhyphenated_len}===========") def _load_language_resources_for_hyphenation(self): @@ -87,12 +88,13 @@ class Hyphenator: if len(word) <= self.max_unhyphenated_len or not any(self._is_vow(c) for c in word): # Если слово короткое или не содержит гласных, перенос не нужен return word - + print("слово:", word, " // mode:", self.mode, " // langs:", self.langs) # 2. ОБНАРУЖЕНИЕ ЯЗЫКА И ПОДКЛЮЧЕНИЕ ЯЗЫКОВОЙ ЛОГИКИ # Поиск вхождения букв строки (слова) через `frozenset` -- O(1). Это быстрее регулярного выражения -- O(n) # 2.1. Проверяем RU if LANG_RU in self.langs and frozenset(word.upper()) <= self._ru_alphabet_upper: # Пользователь подключил русскую логику, и слово содержит только русские буквы + print(f"#### Applying Russian rules to: {word}") # Поиск допустимой позиции для переноса около заданного индекса def find_hyphen_point_ru(word_segment: str, start_idx: int) -> int: vow_indices = [i for i, char_w in enumerate(word_segment) if self._is_vow(char_w)] @@ -181,8 +183,11 @@ class Hyphenator: hyphenated_word = self.hyp_in_word(word_to_process) # ============= Для отладки (слова в которых появились переносы) ================== + print(f"hyp_in_text: '{word_to_process}'", end="") if word_to_process != hyphenated_word: - print(f"hyp_in_text: '{word_to_process}' -> '{hyphenated_word}'") + print(f" -> '{hyphenated_word}'") + else: + print(" (no change)") return hyphenated_word diff --git a/etpgrf/typograph.py b/etpgrf/typograph.py index 850ad97..521df34 100644 --- a/etpgrf/typograph.py +++ b/etpgrf/typograph.py @@ -1,6 +1,5 @@ from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs from etpgrf.hyphenation import Hyphenator -import copy # --- Основной класс Typographer --- @@ -8,37 +7,41 @@ class Typographer: def __init__(self, langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None, mode: str | None = None, - hyphenation_rule: Hyphenator | None = None, # Перенос слов и параметры расстановки переносов + hyphenation: Hyphenator | bool | None = True, # Перенос слов и параметры расстановки переносов # glue_prepositions_rule: GluePrepositionsRule | None = None, # Для других правил # ... другие модули правил ... ): - # --- Обработка и валидация параметра langs --- + # A. --- Обработка и валидация параметра langs --- self.langs: frozenset[str] = parse_and_validate_langs(langs) - - # --- Обработка и валидация параметра mode --- + # B. --- Обработка и валидация параметра mode --- self.mode: str = parse_and_validate_mode(mode) - - # Сохраняем переданные модули правил - if hyphenation_rule is not None: - # 1. Создаем поверхностную копию объекта hyphenation_rule. - self.hyphenation_rule = copy.copy(hyphenation_rule) - # 2. Наследуем режим типографа, если он не задан в hyphenation_rule. - if self.hyphenation_rule.mode is None: - self.hyphenation_rule.mode = self.mode - # 2. Наследуем языки от типографа, если они не заданы в hyphenation_rule. - if self.hyphenation_rule.langs is None: - self.hyphenation_rule.langs = self.langs + print("Typographer: langs:", self.langs, "// mode:", self.mode) # Для отладки + # C. --- Инициализация правила переноса --- + # Предпосылка: если вызвали типограф, значит, мы хотим обрабатывать текст и переносы тоже нужно расставлять. + # А для специальных случаев, когда переносы не нужны, пусть не ленятся и делают `hyphenation=False`. + self.hyphenation: Hyphenator | None = None + if hyphenation is True or hyphenation is None: + # 1. Создаем новый объект Hyphenator с заданными языками и режимом, а все остальное по умолчанию + self.hyphenation = Hyphenator(langs=self.langs, mode=self.mode) + elif isinstance(hyphenation, Hyphenator): + # 2. Если hyphenation - это объект Hyphenator, то просто сохраняем его (и используем его langs и mode) + self.hyphenation = hyphenation + elif hyphenation is False: + # 3. Если hyphenation - False, то правило переноса выключено. + self.hyphenation = None else: - self.hyphenation_rule = hyphenation_rule + # 4. Если hyphenation что-то неведомое, то игнорируем его и правило переноса выключено + self.hyphenation = None + # D. --- Конфигурация других правил--- # Конвейер для обработки текста def process(self, text: str) -> str: processed_text = text - if self.hyphenation_rule: + if self.hyphenation is not None: # Обработчик переносов (Hyphenator) активен. Обрабатываем текст... - processed_text = self.hyphenation_rule.hyp_in_text(processed_text) + processed_text = self.hyphenation.hyp_in_text(processed_text) # if self.glue_prepositions_rule: # processed_text = self.glue_prepositions_rule.hyp_in_text(processed_text, non_breaking_space_char=self._get_nbsp()) diff --git a/main.py b/main.py index 219be73..98168a4 100644 --- a/main.py +++ b/main.py @@ -4,6 +4,9 @@ import etpgrf if __name__ == '__main__': # --- Пример использования --- print("\n--- Пример использования класса---\n") + + etpgrf.config.DEFAULT_HYP_MAX_LEN = 6 + # Определяем пользовательские правила переносов hyphen_settings = etpgrf.Hyphenator(langs='ru', max_unhyphenated_len=8) # Определяем пользовательские правила типографа @@ -14,13 +17,14 @@ if __name__ == '__main__': print(result, "\n\n") hyphen_settings2 = etpgrf.Hyphenator(langs='en', max_unhyphenated_len=8) - result = hyphen_settings2.hyp_in_text("frozenseter") + result = hyphen_settings2.hyp_in_text("floccinaucinihilipilification") print(result, "\n\n") - typo = etpgrf.Typographer(langs='ru', mode='mnemonic', hyphenation_rule=hyphen_settings) - result = typo.process(text="Какой-то длинный текст для проверки переносов. Перпердикюляция!") + typo_ru = etpgrf.Typographer(langs='ru', mode='mixed', hyphenation=hyphen_settings) + result = typo_ru.process(text="Какой-то длинный текст для проверки переносов. Перпердикюляция!") print(result, "\n\n") - result = typo.process(text="Привет, frozenseter! Это тестовый текст для проверки расстановки переносов" + typo_ru_en = etpgrf.Typographer(langs='ru-en', mode='mixed', hyphenation=True) + result = typo_ru_en.process(text="Расприветище, floccinaucinihilipilification. Это тестовый текст для проверки расстановки переносов" " в словах. Миллион 100-метровошеих жирножирафов.") print(result, "\n\n") @@ -29,5 +33,5 @@ if __name__ == '__main__': " чувствовать себя собой. Мы не шьём одина­ковые пальто. Мы шьём ваше. Ниже —" " как устроен процесс заказа.

") - result = typo.process(text=txt) + result = typo_ru.process(text=txt) print(result, "\n\n")