add: codec (внутренний utf-8 и мнемокод для in/out
This commit is contained in:
54
etpgrf/codec.py
Normal file
54
etpgrf/codec.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# etpgrf/codec.py
|
||||||
|
# Модуль для преобразования текста между Unicode и HTML-мнемониками.
|
||||||
|
|
||||||
|
import regex
|
||||||
|
import html
|
||||||
|
from etpgrf.config import (ALL_ENTITIES, ALWAYS_MNEMONIC_IN_SAFE_MODE, MODE_MNEMONIC, MODE_MIXED)
|
||||||
|
|
||||||
|
# --- Создаем словарь для кодирования Unicode -> Mnemonic ---
|
||||||
|
# {'\u00A0': ' ', '\u2014': '—', ...}
|
||||||
|
_ENCODE_MAP = {}
|
||||||
|
|
||||||
|
|
||||||
|
for name, (uni_char, mnemonic) in ALL_ENTITIES.items():
|
||||||
|
_ENCODE_MAP[uni_char] = mnemonic
|
||||||
|
|
||||||
|
# --- Основные функции кодека ---
|
||||||
|
|
||||||
|
def decode_to_unicode(text: str) -> str:
|
||||||
|
"""
|
||||||
|
Преобразует все известные HTML-мнемоники в их Unicode-эквиваленты,
|
||||||
|
используя стандартную библиотеку html.
|
||||||
|
"""
|
||||||
|
if not text or '&' not in text:
|
||||||
|
return text
|
||||||
|
return html.unescape(text)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_from_unicode(text: str, mode: str) -> str:
|
||||||
|
"""
|
||||||
|
Преобразует Unicode-символы в HTML-мнемоники в соответствии с режимом.
|
||||||
|
"""
|
||||||
|
if not text or mode not in [MODE_MNEMONIC, MODE_MIXED]:
|
||||||
|
# В режиме 'unicode' или неизвестном режиме ничего не делаем
|
||||||
|
return text
|
||||||
|
|
||||||
|
# 1. Определяем, какие символы нужно заменить
|
||||||
|
if mode == MODE_MNEMONIC:
|
||||||
|
# В режиме 'mnemonic' заменяем все известные нам символы
|
||||||
|
chars_to_replace = set(_ENCODE_MAP.keys())
|
||||||
|
else: # mode == MODE_MIXED
|
||||||
|
# В смешанном режиме заменяем только "безопасные" символы
|
||||||
|
# (те, что могут вызывать проблемы с отображением или переносами)
|
||||||
|
safe_chars = {ALL_ENTITIES[name][0] for name in ALWAYS_MNEMONIC_IN_SAFE_MODE}
|
||||||
|
chars_to_replace = set(_ENCODE_MAP.keys()) & safe_chars
|
||||||
|
|
||||||
|
if not chars_to_replace:
|
||||||
|
return text
|
||||||
|
|
||||||
|
# 2. Создаем паттерн для поиска только нужных символов
|
||||||
|
# regex.escape важен, если в наборе будут спецсимволы, например, '-'
|
||||||
|
pattern = regex.compile(f"[{regex.escape(''.join(chars_to_replace))}]")
|
||||||
|
|
||||||
|
# 3. Заменяем найденные символы, используя нашу карту
|
||||||
|
return pattern.sub(lambda m: _ENCODE_MAP[m.group(0)], text)
|
@@ -19,21 +19,28 @@ SUPPORTED_LANGS = frozenset([LANG_RU, LANG_RU_OLD, LANG_EN])
|
|||||||
# DEFAULT_HYP_MAX_LEN = 10 # Максимальная длина слова без переносов
|
# DEFAULT_HYP_MAX_LEN = 10 # Максимальная длина слова без переносов
|
||||||
# DEFAULT_HYP_MIN_LEN = 3 # Минимальный "хвост" слова для переноса
|
# DEFAULT_HYP_MIN_LEN = 3 # Минимальный "хвост" слова для переноса
|
||||||
|
|
||||||
# ----------------- соответствия `unicode` и `mnemonic` для типографа
|
# === Соответствия `unicode` и `mnemonic` для типографа
|
||||||
|
|
||||||
# Переносы
|
# Переносы
|
||||||
|
KEY_SHY = 'SHY'
|
||||||
SHY_ENTITIES = {
|
SHY_ENTITIES = {
|
||||||
'SHY': ('\u00AD', '­'), # Мягкий перенос
|
KEY_SHY: ('\u00AD', '­'), # Мягкий перенос
|
||||||
}
|
}
|
||||||
|
|
||||||
# Пробелы и неразрывные пробелы
|
# Пробелы и неразрывные пробелы
|
||||||
|
KEY_NBSP = 'NBSP'
|
||||||
|
KEY_THINSP = 'THINSP'
|
||||||
|
KEY_ENSP = 'ENSP'
|
||||||
|
KEY_EMSP = 'EMSP'
|
||||||
|
KEY_ZWNJ = 'ZWNJ'
|
||||||
|
KEY_ZWJ = 'ZWJ'
|
||||||
SPACE_ENTITIES = {
|
SPACE_ENTITIES = {
|
||||||
'NBSP': ('\u00A0', ' '), # Неразрывный пробел
|
KEY_NBSP: ('\u00A0', ' '), # Неразрывный пробел
|
||||||
'THINSP': ('\u2009', ' '), # Тонкий пробел
|
KEY_THINSP: ('\u2009', ' '), # Тонкий пробел
|
||||||
'ENSP': ('\u2002', ' '), # Полу-широкий пробел
|
KEY_ENSP: ('\u2002', ' '), # Полу-широкий пробел
|
||||||
'EMSP': ('\u2003', ' '), # Широкий пробел
|
KEY_EMSP: ('\u2003', ' '), # Широкий пробел
|
||||||
'ZWNJ': ('\u200C', '‌'), # Разрывный пробел нулевой ширины (без пробела)
|
KEY_ZWNJ: ('\u200C', '‌'), # Разрывный пробел нулевой ширины (без пробела)
|
||||||
'ZWJ': ('\u200D', '‍'), # Неразрывный пробел нулевой ширины
|
KEY_ZWJ: ('\u200D', '‍'), # Неразрывный пробел нулевой ширины
|
||||||
}
|
}
|
||||||
|
|
||||||
# Тире и дефисы
|
# Тире и дефисы
|
||||||
@@ -60,6 +67,7 @@ QUOTE_ENTITIES = {
|
|||||||
'RSAQUO': ('\u203A', '›'), # Закрывающая французская угловая кавычка -- ‹
|
'RSAQUO': ('\u203A', '›'), # Закрывающая французская угловая кавычка -- ‹
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Символы валют
|
||||||
CURRENCY_ENTITIES = {
|
CURRENCY_ENTITIES = {
|
||||||
'DOLLAR': ('\u0024', '$'), # Доллар
|
'DOLLAR': ('\u0024', '$'), # Доллар
|
||||||
'CENT': ('\u00A2', '¢'), # Цент
|
'CENT': ('\u00A2', '¢'), # Цент
|
||||||
@@ -70,14 +78,46 @@ CURRENCY_ENTITIES = {
|
|||||||
'RUBLE': ('\u20BD', '₽'), # Российский рубль (₽)
|
'RUBLE': ('\u20BD', '₽'), # Российский рубль (₽)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Математические символы
|
||||||
|
KEY_LT = 'LT'
|
||||||
|
KEY_GT = 'GT'
|
||||||
|
MATH_ENTITIES = {
|
||||||
|
KEY_LT: ('\u00B7', '<'), # Меньше (<)
|
||||||
|
KEY_GT: ('\u00B7', '>'), # Больше (>)
|
||||||
|
'PLUS': ('\u002B', '+'), # Плюс (+)
|
||||||
|
'MINUS': ('\u2212', '−'), # Минус (−)
|
||||||
|
'MULTIPLY': ('\u00D7', '×'), # Умножение (×)
|
||||||
|
'DIVIDE': ('\u00F7', '÷'), # Деление (÷)
|
||||||
|
'EQUALS': ('\u003D', '='), # Равно (=)
|
||||||
|
'NOT_EQUAL': ('\u2260', '≠'), # Не равно (≠)
|
||||||
|
'PLUSMN': ('\u00B1', '±'), # Плюс-минус (±)
|
||||||
|
'LESS_EQUAL': ('\u2264', '≤'), # Меньше или равно (≤)
|
||||||
|
'GREATER_EQUAL': ('\u2265', '≥'), # Больше или равно (≥)
|
||||||
|
'APPROX_EQUAL': ('\u2245', '≅'), # Приблизительно равно (≅)
|
||||||
|
'APPROX_EQ': ('\u2245', '≊'), # Приблизительно равно (≅)
|
||||||
|
'APPROX': ('\u2248', '≈'), # Приблизительно равно (≈)
|
||||||
|
}
|
||||||
|
|
||||||
# Другие символы (пример для расширения)
|
# Другие символы (пример для расширения)
|
||||||
|
KEY_AMP = 'AMP'
|
||||||
SYMBOL_ENTITIES = {
|
SYMBOL_ENTITIES = {
|
||||||
|
KEY_AMP: ('\u0026', '&smp;'), #Амперсанд (&)
|
||||||
'HELLIP': ('\u2026', '…'), # Многоточие
|
'HELLIP': ('\u2026', '…'), # Многоточие
|
||||||
'COPY': ('\u00A9', '©'), # Копирайт
|
'COPY': ('\u00A9', '©'), # Копирайт
|
||||||
# ... стрелочки, математические символы и т.д. по мере необходимости
|
# ... стрелочки, математические символы и т.д. по мере необходимости
|
||||||
}
|
}
|
||||||
|
|
||||||
# Сущности, которые ВСЕГДА должны выводиться как мнемоники в режиме MODE_MIXED
|
# --- Сборка и валидация ---
|
||||||
# Указываются их ИМЕНА (ключи из словарей выше)
|
|
||||||
ALWAYS_MNEMONIC_IN_SAFE_MODE = frozenset(['SHY', 'NBSP', 'ZWSP'])
|
# 1. Создаем единый словарь всех сущностей для удобного доступа
|
||||||
|
ALL_ENTITIES = {
|
||||||
|
**SHY_ENTITIES, **SPACE_ENTITIES, **DASH_ENTITIES, **MATH_ENTITIES,
|
||||||
|
**QUOTE_ENTITIES, **CURRENCY_ENTITIES, **SYMBOL_ENTITIES
|
||||||
|
}
|
||||||
|
|
||||||
|
# Сущности, которые ВСЕГДА должны выводиться как мнемоники в режиме MODE_MIXED
|
||||||
|
# Указываются их ИМЕНА (ключи из словарей выше).
|
||||||
|
# NOTE: Повторное использование магических строк 'SHY', 'NBSP' и т.д. не создает новый объект в памяти. Умный Python
|
||||||
|
# когда видит одинаковую строку в коде применяет интернирование строк (string interning).
|
||||||
|
ALWAYS_MNEMONIC_IN_SAFE_MODE = frozenset([KEY_AMP, KEY_LT, KEY_GT, KEY_SHY, KEY_NBSP, KEY_ZWNJ, KEY_ZWJ])
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ class EtpgrfDefaultSettings:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.LANGS: list[str] | str = LANG_RU
|
self.LANGS: list[str] | str = LANG_RU
|
||||||
self.MODE: str = MODE_MIXED
|
self.MODE: str = MODE_MIXED
|
||||||
|
# self.PROCESS_HTML: bool = False # Флаг обработки HTML-тегов
|
||||||
self.logging_settings = LoggingDefaults()
|
self.logging_settings = LoggingDefaults()
|
||||||
self.hyphenation = HyphenationDefaults()
|
self.hyphenation = HyphenationDefaults()
|
||||||
# self.quotes = EtpgrfQuoteDefaults()
|
# self.quotes = EtpgrfQuoteDefaults()
|
||||||
|
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
import regex
|
import regex
|
||||||
import logging
|
import logging
|
||||||
from etpgrf.config import LANG_RU, LANG_RU_OLD, LANG_EN, SHY_ENTITIES, MODE_UNICODE
|
from etpgrf.config import LANG_RU, LANG_RU_OLD, LANG_EN, KEY_SHY, ALL_ENTITIES
|
||||||
from etpgrf.defaults import etpgrf_settings
|
from etpgrf.defaults import etpgrf_settings
|
||||||
from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs, is_inside_unbreakable_segment
|
from etpgrf.comutil import parse_and_validate_langs, is_inside_unbreakable_segment
|
||||||
|
|
||||||
_RU_VOWELS_UPPER = frozenset(['А', 'О', 'И', 'Е', 'Ё', 'Э', 'Ы', 'У', 'Ю', 'Я'])
|
_RU_VOWELS_UPPER = frozenset(['А', 'О', 'И', 'Е', 'Ё', 'Э', 'Ы', 'У', 'Ю', 'Я'])
|
||||||
_RU_CONSONANTS_UPPER = frozenset(['Б', 'В', 'Г', 'Д', 'Ж', 'З', 'К', 'Л', 'М', 'Н', 'П', 'Р', 'С', 'Т', 'Ф', 'Х',
|
_RU_CONSONANTS_UPPER = frozenset(['Б', 'В', 'Г', 'Д', 'Ж', 'З', 'К', 'Л', 'М', 'Н', 'П', 'Р', 'С', 'Т', 'Ф', 'Х',
|
||||||
@@ -46,11 +46,9 @@ class Hyphenator:
|
|||||||
"""
|
"""
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None,
|
langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None,
|
||||||
mode: str = None, # Режим обработки текста
|
|
||||||
max_unhyphenated_len: int | None = None, # Максимальная длина непереносимой группы
|
max_unhyphenated_len: int | None = None, # Максимальная длина непереносимой группы
|
||||||
min_tail_len: int | None = None): # Минимальная длина после переноса (хвост, который разрешено переносить)
|
min_tail_len: int | None = None): # Минимальная длина после переноса (хвост, который разрешено переносить)
|
||||||
self.langs: frozenset[str] = parse_and_validate_langs(langs)
|
self.langs: frozenset[str] = parse_and_validate_langs(langs)
|
||||||
self.mode: str = parse_and_validate_mode(mode)
|
|
||||||
self.max_unhyphenated_len = etpgrf_settings.hyphenation.MAX_UNHYPHENATED_LEN if max_unhyphenated_len is None else max_unhyphenated_len
|
self.max_unhyphenated_len = etpgrf_settings.hyphenation.MAX_UNHYPHENATED_LEN if max_unhyphenated_len is None else max_unhyphenated_len
|
||||||
self.min_chars_per_part = etpgrf_settings.hyphenation.MIN_TAIL_LEN if min_tail_len is None else min_tail_len
|
self.min_chars_per_part = etpgrf_settings.hyphenation.MIN_TAIL_LEN if min_tail_len is None else min_tail_len
|
||||||
if self.min_chars_per_part < 2:
|
if self.min_chars_per_part < 2:
|
||||||
@@ -72,10 +70,10 @@ class Hyphenator:
|
|||||||
self._en_alphabet_upper: frozenset = frozenset()
|
self._en_alphabet_upper: frozenset = frozenset()
|
||||||
# Загружает наборы символов на основе self.langs
|
# Загружает наборы символов на основе self.langs
|
||||||
self._load_language_resources_for_hyphenation()
|
self._load_language_resources_for_hyphenation()
|
||||||
# Определяем символ переноса в зависимости от режима
|
# Так как внутри типографа кодировка html, то символ переноса независим от режима
|
||||||
self._split_code: str = SHY_ENTITIES['SHY'][0] if self.mode == MODE_UNICODE else SHY_ENTITIES['SHY'][1]
|
self._split_code: str = ALL_ENTITIES[KEY_SHY][0]
|
||||||
# ...
|
# ...
|
||||||
logger.debug(f"Hyphenator `__init__`. Langs: {self.langs}, Mode: {self.mode},"
|
logger.debug(f"Hyphenator `__init__`. Langs: {self.langs},"
|
||||||
f" Max unhyphenated_len: {self.max_unhyphenated_len},"
|
f" Max unhyphenated_len: {self.max_unhyphenated_len},"
|
||||||
f" Min chars_per_part: {self.min_chars_per_part}")
|
f" Min chars_per_part: {self.min_chars_per_part}")
|
||||||
|
|
||||||
@@ -135,7 +133,7 @@ class Hyphenator:
|
|||||||
if len(word) <= self.max_unhyphenated_len or not any(self._is_vow(c) for c in word):
|
if len(word) <= self.max_unhyphenated_len or not any(self._is_vow(c) for c in word):
|
||||||
# Если слово короткое или не содержит гласных, перенос не нужен
|
# Если слово короткое или не содержит гласных, перенос не нужен
|
||||||
return word
|
return word
|
||||||
logger.debug(f"Hyphenator: word: `{word}` // langs: {self.langs} // mode: {self.mode} // max_unhyphenated_len: {self.max_unhyphenated_len} // min_tail_len: {self.min_chars_per_part}")
|
logger.debug(f"Hyphenator: word: `{word}` // langs: {self.langs} // max_unhyphenated_len: {self.max_unhyphenated_len} // min_tail_len: {self.min_chars_per_part}")
|
||||||
# 2. ОБНАРУЖЕНИЕ ЯЗЫКА И ПОДКЛЮЧЕНИЕ ЯЗЫКОВОЙ ЛОГИКИ
|
# 2. ОБНАРУЖЕНИЕ ЯЗЫКА И ПОДКЛЮЧЕНИЕ ЯЗЫКОВОЙ ЛОГИКИ
|
||||||
# Поиск вхождения букв строки (слова) через `frozenset` -- O(1). Это быстрее регулярного выражения -- O(n)
|
# Поиск вхождения букв строки (слова) через `frozenset` -- O(1). Это быстрее регулярного выражения -- O(n)
|
||||||
# 2.1. Проверяем RU и RU_OLD (правила одинаковые, но разные наборы букв)
|
# 2.1. Проверяем RU и RU_OLD (правила одинаковые, но разные наборы букв)
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import html
|
||||||
try:
|
try:
|
||||||
from bs4 import BeautifulSoup, NavigableString
|
from bs4 import BeautifulSoup, NavigableString
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -6,6 +7,7 @@ except ImportError:
|
|||||||
from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs
|
from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs
|
||||||
from etpgrf.hyphenation import Hyphenator
|
from etpgrf.hyphenation import Hyphenator
|
||||||
from etpgrf.unbreakables import Unbreakables
|
from etpgrf.unbreakables import Unbreakables
|
||||||
|
from etpgrf.codec import decode_to_unicode, encode_from_unicode
|
||||||
|
|
||||||
|
|
||||||
# --- Настройки логирования ---
|
# --- Настройки логирования ---
|
||||||
@@ -40,7 +42,7 @@ class Typographer:
|
|||||||
self.hyphenation: Hyphenator | None = None
|
self.hyphenation: Hyphenator | None = None
|
||||||
if hyphenation is True or hyphenation is None:
|
if hyphenation is True or hyphenation is None:
|
||||||
# C1. Создаем новый объект Hyphenator с заданными языками и режимом, а все остальное по умолчанию
|
# C1. Создаем новый объект Hyphenator с заданными языками и режимом, а все остальное по умолчанию
|
||||||
self.hyphenation = Hyphenator(langs=self.langs, mode=self.mode)
|
self.hyphenation = Hyphenator(langs=self.langs)
|
||||||
elif isinstance(hyphenation, Hyphenator):
|
elif isinstance(hyphenation, Hyphenator):
|
||||||
# C2. Если hyphenation - это объект Hyphenator, то просто сохраняем его (и используем его langs и mode)
|
# C2. Если hyphenation - это объект Hyphenator, то просто сохраняем его (и используем его langs и mode)
|
||||||
self.hyphenation = hyphenation
|
self.hyphenation = hyphenation
|
||||||
@@ -49,7 +51,7 @@ class Typographer:
|
|||||||
self.unbreakables: Unbreakables | None = None
|
self.unbreakables: Unbreakables | None = None
|
||||||
if unbreakables is True or unbreakables is None:
|
if unbreakables is True or unbreakables is None:
|
||||||
# D1. Создаем новый объект Unbreakables с заданными языками и режимом, а все остальное по умолчанию
|
# D1. Создаем новый объект Unbreakables с заданными языками и режимом, а все остальное по умолчанию
|
||||||
self.unbreakables = Unbreakables(langs=self.langs, mode=self.mode)
|
self.unbreakables = Unbreakables(langs=self.langs)
|
||||||
elif isinstance(unbreakables, Unbreakables):
|
elif isinstance(unbreakables, Unbreakables):
|
||||||
# D2. Если unbreakables - это объект Unbreakables, то просто сохраняем его (и используем его langs и mode)
|
# D2. Если unbreakables - это объект Unbreakables, то просто сохраняем его (и используем его langs и mode)
|
||||||
self.unbreakables = unbreakables
|
self.unbreakables = unbreakables
|
||||||
@@ -69,8 +71,8 @@ class Typographer:
|
|||||||
"""
|
"""
|
||||||
# Шаг 1: Декодируем весь входящий текст в канонический Unicode
|
# Шаг 1: Декодируем весь входящий текст в канонический Unicode
|
||||||
# (здесь можно использовать html.unescape, но наш кодек тоже подойдет)
|
# (здесь можно использовать html.unescape, но наш кодек тоже подойдет)
|
||||||
# processed_text = decode_to_unicode(text)
|
processed_text = decode_to_unicode(text)
|
||||||
processed_text = text # ВРЕМЕННО: используем текст как есть
|
# processed_text = text # ВРЕМЕННО: используем текст как есть
|
||||||
|
|
||||||
# Шаг 2: Применяем правила к чистому Unicode-тексту
|
# Шаг 2: Применяем правила к чистому Unicode-тексту
|
||||||
if self.unbreakables is not None:
|
if self.unbreakables is not None:
|
||||||
@@ -79,10 +81,7 @@ class Typographer:
|
|||||||
processed_text = self.hyphenation.hyp_in_text(processed_text)
|
processed_text = self.hyphenation.hyp_in_text(processed_text)
|
||||||
# ... вызовы других активных модулей правил ...
|
# ... вызовы других активных модулей правил ...
|
||||||
|
|
||||||
# Шаг 3: Кодируем результат в запрошенный формат (mnemonic или mixed)
|
return processed_text
|
||||||
# final_text = encode_from_unicode(processed_text, self.mode)
|
|
||||||
final_text = processed_text # ВРЕМЕННО: используем текст как есть
|
|
||||||
return final_text
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ class Typographer:
|
|||||||
if not node.string.strip() or node.parent.name in ['style', 'script', 'pre', 'code']:
|
if not node.string.strip() or node.parent.name in ['style', 'script', 'pre', 'code']:
|
||||||
continue
|
continue
|
||||||
# К каждому текстовому узлу применяем "внутренний" процессор
|
# К каждому текстовому узлу применяем "внутренний" процессор
|
||||||
processed_node_text = self._process_text_node(node.string)
|
processed_node_text: str = self._process_text_node(node.string)
|
||||||
# Отладочная печать, чтобы видеть, что происходит
|
# Отладочная печать, чтобы видеть, что происходит
|
||||||
if node.string != processed_node_text:
|
if node.string != processed_node_text:
|
||||||
logger.info(f"Processing node: '{node.string}' -> '{processed_node_text}'")
|
logger.info(f"Processing node: '{node.string}' -> '{processed_node_text}'")
|
||||||
@@ -116,13 +115,12 @@ class Typographer:
|
|||||||
# Однако, replace_with достаточно умен, чтобы справиться с этим.
|
# Однако, replace_with достаточно умен, чтобы справиться с этим.
|
||||||
node.replace_with(processed_node_text)
|
node.replace_with(processed_node_text)
|
||||||
|
|
||||||
# Возвращаем измененный HTML. BeautifulSoup по умолчанию выводит без тегов <html><body>
|
# Получаем измененный HTML. BeautifulSoup по умолчанию выводит без тегов <html><body>
|
||||||
# если их не было в исходной строке.
|
# если их не было в исходной строке.
|
||||||
return str(soup)
|
processed = str(soup)
|
||||||
else:
|
else:
|
||||||
# Если HTML-режим выключен, работаем как раньше
|
# Если HTML-режим выключен
|
||||||
return self._process_text_node(text)
|
processed = self._process_text_node(text)
|
||||||
|
# Возвращаем
|
||||||
# def _get_nbsp(self): # Пример получения неразрывного пробела
|
return encode_from_unicode(processed, self.mode)
|
||||||
# return "\u00A0" if self.mode in UTF else " "
|
|
||||||
|
|
||||||
|
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
import regex
|
import regex
|
||||||
import logging
|
import logging
|
||||||
from etpgrf.config import LANG_RU, LANG_RU_OLD, LANG_EN, SPACE_ENTITIES, MODE_UNICODE
|
from etpgrf.config import LANG_RU, LANG_RU_OLD, LANG_EN, KEY_NBSP, ALL_ENTITIES
|
||||||
|
from etpgrf.comutil import parse_and_validate_langs
|
||||||
from etpgrf.defaults import etpgrf_settings
|
from etpgrf.defaults import etpgrf_settings
|
||||||
|
|
||||||
# --- Наборы коротких слов для разных языков ---
|
# --- Наборы коротких слов для разных языков ---
|
||||||
@@ -17,7 +18,7 @@ _RU_UNBREAKABLE_WORDS = frozenset([
|
|||||||
# Предлоги (только короткие... длинные, типа `ввиду`, `ввиду` и т.п., могут быть "висячими")
|
# Предлоги (только короткие... длинные, типа `ввиду`, `ввиду` и т.п., могут быть "висячими")
|
||||||
'в', 'без', 'до', 'из', 'к', 'на', 'по', 'о', 'от', 'перед', 'при', 'через', 'с', 'у', 'за', 'над',
|
'в', 'без', 'до', 'из', 'к', 'на', 'по', 'о', 'от', 'перед', 'при', 'через', 'с', 'у', 'за', 'над',
|
||||||
'об', 'под', 'про', 'для', 'ко', 'со', 'без', 'то', 'во', 'из-за', 'из-под', 'как'
|
'об', 'под', 'про', 'для', 'ко', 'со', 'без', 'то', 'во', 'из-за', 'из-под', 'как'
|
||||||
# Союзы (без сложных, тип 'как будто', 'как если бы', `за то` и т.п.)
|
# Союзы (без сложных, тип `как будто`, `как если бы`, `за то` и т.п.)
|
||||||
'и', 'а', 'но', 'да', 'как',
|
'и', 'а', 'но', 'да', 'как',
|
||||||
# Частицы
|
# Частицы
|
||||||
'не', 'ни',
|
'не', 'ни',
|
||||||
@@ -62,15 +63,11 @@ class Unbreakables:
|
|||||||
от последующих слов.
|
от последующих слов.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None):
|
||||||
langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None,
|
|
||||||
mode: str = None):
|
|
||||||
from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs
|
|
||||||
self.langs = parse_and_validate_langs(langs)
|
self.langs = parse_and_validate_langs(langs)
|
||||||
self.mode = parse_and_validate_mode(mode)
|
|
||||||
|
|
||||||
# Определяем символ неразрывного пробела в зависимости от режима
|
# Так как внутри типографа кодировка html, то символ неразрывного пробела независим от режима
|
||||||
self._nbsp_char = SPACE_ENTITIES['NBSP'][0] if self.mode == MODE_UNICODE else SPACE_ENTITIES['NBSP'][1]
|
self._nbsp_char = ALL_ENTITIES[KEY_NBSP][0]
|
||||||
|
|
||||||
# --- 1. Собираем наборы слов для обработки ---
|
# --- 1. Собираем наборы слов для обработки ---
|
||||||
pre_words = set()
|
pre_words = set()
|
||||||
@@ -104,7 +101,7 @@ class Unbreakables:
|
|||||||
# Паттерн для слов, ПЕРЕД которыми нужен nbsp.
|
# Паттерн для слов, ПЕРЕД которыми нужен nbsp.
|
||||||
self._post_pattern = regex.compile(r"(?i)(\s)\b(" + "|".join(map(regex.escape, sorted_particles)) + r")\b")
|
self._post_pattern = regex.compile(r"(?i)(\s)\b(" + "|".join(map(regex.escape, sorted_particles)) + r")\b")
|
||||||
|
|
||||||
logger.debug(f"Unbreakables `__init__`. Langs: {self.langs}, Mode: {self.mode}, "
|
logger.debug(f"Unbreakables `__init__`. Langs: {self.langs}, "
|
||||||
f"Pre-words: {len(pre_words)}, Post-words: {len(post_words)}")
|
f"Pre-words: {len(pre_words)}, Post-words: {len(post_words)}")
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user