# etpgrf/conf.py # Настройки по умолчанию и "источник правды" для типографа etpgrf from html import entities # === КОНФИГУРАЦИИ === # Режимы "отдачи" результатов обработки MODE_UNICODE = "unicode" MODE_MNEMONIC = "mnemonic" MODE_MIXED = "mixed" # Языки, поддерживаемые библиотекой LANG_RU = 'ru' # Русский LANG_RU_OLD = 'ruold' # Русская дореволюционная орфография LANG_EN = 'en' # Английский SUPPORTED_LANGS = frozenset([LANG_RU, LANG_RU_OLD, LANG_EN]) DEFAULT_LANGS = (LANG_RU, LANG_EN) # Языки по умолчанию # === ИСТОЧНИК ПРАВДЫ === # --- Базовые алфавиты: Эти константы используются как для правил переноса, так и для правил кодирования --- # Русский алфавит RU_VOWELS_UPPER = frozenset(['А', 'О', 'И', 'Е', 'Ё', 'Э', 'Ы', 'У', 'Ю', 'Я']) RU_CONSONANTS_UPPER = frozenset(['Б', 'В', 'Г', 'Д', 'Ж', 'З', 'К', 'Л', 'М', 'Н', 'П', 'Р', 'С', 'Т', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ']) RU_J_SOUND_UPPER = frozenset(['Й']) RU_SIGNS_UPPER = frozenset(['Ь', 'Ъ']) RU_ALPHABET_UPPER = RU_VOWELS_UPPER | RU_CONSONANTS_UPPER | RU_J_SOUND_UPPER | RU_SIGNS_UPPER RU_ALPHABET_LOWER = frozenset([char.lower() for char in RU_ALPHABET_UPPER]) RU_ALPHABET_FULL = RU_ALPHABET_UPPER | RU_ALPHABET_LOWER # Английский алфавит EN_VOWELS_UPPER = frozenset(['A', 'E', 'I', 'O', 'U', 'Æ', 'Œ']) EN_CONSONANTS_UPPER = frozenset(['B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z']) EN_ALPHABET_UPPER = EN_VOWELS_UPPER | EN_CONSONANTS_UPPER EN_ALPHABET_LOWER = frozenset([char.lower() for char in EN_ALPHABET_UPPER]) EN_ALPHABET_FULL = EN_ALPHABET_UPPER | EN_ALPHABET_LOWER # --- Специальные символы --- CHAR_NBSP = '\u00a0' # Неразрывный пробел ( ) CHAR_SHY = '\u00ad' # Мягкий перенос (­) CHAR_THIN_SP = '\u2009' # Тонкий пробел (шпация,  ) CHAR_NDASH = '\u2013' # Cреднее тире (– / –) CHAR_MDASH = '\u2014' # Длинное тире (— / —) CHAR_HELLIP = '\u2026' # Многоточие (… / …) CHAR_RU_QUOT1_OPEN = '«' # Русские кавычки открывающие (« / «) CHAR_RU_QUOT1_CLOSE = '»' CHAR_RU_QUOT2_OPEN = '„' CHAR_RU_QUOT2_CLOSE = '“' CHAR_EN_QUOT1_OPEN = '“' CHAR_EN_QUOT1_CLOSE = '”' CHAR_EN_QUOT2_OPEN = '‘' CHAR_EN_QUOT2_CLOSE = '’' CHAR_COPY = '\u00a9' # Символ авторского права / © / © CHAR_REG = '\u00ae' # Зарегистрированная торговая марка / ® / ® CHAR_COPYP = '\u2117' # Знак звуковой записи / ℗ / ©p; CHAR_TRADE = '\u2122' # Знак торговой марки / ™ / ™ CHAR_ARROW_LR_DOUBLE = '\u21d4' # Двойная двунаправленная стрелка / ⇔ / ⇔ CHAR_ARROW_L_DOUBLE = '\u21d0' # Двойная стрелка влево / ⇐ / ⇐ CHAR_ARROW_R_DOUBLE = '\u21d2' # Двойная стрелка вправо / ⇒ / ⇒ CHAR_AP = '\u2248' # Приблизительно равно / ≈ / ≈ CHAR_ARROW_L = '\u27f5' # Стрелка влево / ← / ← CHAR_ARROW_R = '\u27f6' # Стрелка вправо / → / → CHAR_ARROW_LR = '\u27f7' # Длинная двунаправленная стрелка ↔ / ↔ CHAR_ARROW_L_LONG_DOUBLE = '\u27f8' # Длинная двойная стрелка влево CHAR_ARROW_R_LONG_DOUBLE = '\u27f9' # Длинная двойная стрелка вправо CHAR_ARROW_LR_LONG_DOUBLE = '\u27fa' # Длинная двойная двунаправленная стрелка CHAR_MIDDOT = '\u00b7' # Средняя точка (· иногда используется как знак умножения) / · CHAR_UNIT_SEPARATOR = '\u25F0' # Символ временный разделитель для составных единиц (◰), чтобы не уходить # в "мертвый" цикл при замене на тонкий пробел. Можно взять любой редкий символом. # === КОНСТАНТЫ ПСЕВДОГРАФИКИ === # Для простых замен "строка -> символ" используем список кортежей. # Порядок важен: более длинные последовательности должны идти раньше более коротких, которые # могут быть их частью (например, '<---' до '---', а та, в свою очередь, до '--'). STR_TO_SYMBOL_REPLACEMENTS = [ # 5-символьные последовательности ('<===>', CHAR_ARROW_LR_LONG_DOUBLE), # Длинная двойная двунаправленная стрелка # 4-символьные последовательности ('<===', CHAR_ARROW_L_LONG_DOUBLE), # Длинная двойная стрелка влево ('===>', CHAR_ARROW_R_LONG_DOUBLE), # Длинная двойная стрелка вправо ('<==>', CHAR_ARROW_LR_DOUBLE), # Двойная двунаправленная стрелка ('(tm)', CHAR_TRADE), ('(TM)', CHAR_TRADE), # Знак торговой марки (нижний и верхний регистр) ('<-->', CHAR_ARROW_LR), # Длинная двунаправленная стрелка # 3-символьные последовательности ('<--', CHAR_ARROW_L), # Стрелка влево ('-->', CHAR_ARROW_R), # Стрелка вправо ('==>', CHAR_ARROW_R_DOUBLE), # Двойная стрелка вправо ('<==', CHAR_ARROW_L_DOUBLE), # Двойная стрелка влево ('---', CHAR_MDASH), # Длинное тире ('...', CHAR_HELLIP), # Многоточие ('(c)', CHAR_COPY), ('(C)', CHAR_COPY), # Знак авторского права (нижний и верхний регистр) ('(r)', CHAR_REG), ('(R)', CHAR_REG), # Знак зарегистрированной торговой марки (нижний и верхний регистр) ('(p)', CHAR_COPYP), ('(P)', CHAR_COPYP), # Знак права на звукозапись (нижний и верхний регистр) # 2-символьные последовательности ('--', CHAR_NDASH), # Среднее тире (дефисные соединения и диапазоны) ('~=', CHAR_AP), # Приблизительно равно (≈) ] # === КОНСТАНТЫ ДЛЯ КОДИРОВАНИЯ HTML-МНЕМНОИКОВ === # --- ЧЕРНЫЙ СПИСОК: Символы, которые НИКОГДА не нужно кодировать в мнемоники --- NEVER_ENCODE_CHARS = (frozenset(['!', '#', '%', '(', ')', '*', ',', '.', '/', ':', ';', '=', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', '\n', '\t', '\r']) | RU_ALPHABET_FULL | EN_ALPHABET_FULL) # 2. БЕЛЫЙ СПИСОК (ДЛЯ БЕЗОПАСНОСТИ): # Символы, которые ВСЕГДА должны превращаться в мнемоники в "безопасных" режимах вывода. Сюда добавлены символы, # которые не видны, на глаз и не отличимы друг от друга в обычном тексте, или очень специфичные SAFE_MODE_CHARS_TO_MNEMONIC = frozenset([ '<', '>', '&', '"', '\'', CHAR_SHY, # Мягкий перенос (Soft Hyphen) -- ­ CHAR_NBSP, # Неразрывный пробел (Non-Breaking Space) --   '\u2002', # Полужирный пробел (En Space) --   '\u2003', # Широкий пробел (Em Space) --   '\u2007', # Цифровой пробел --   '\u2008', # Пунктуационный пробел --   CHAR_THIN_SP, # Межсимвольный пробел, тонкий пробел, шпация --  ' '\u200A', # Толщина волоса (Hair Space) --   '\u200B', # Негативный пробел (Negative Space) -- ​ '\u200C', # Нулевая ширина (без объединения) (Zero Width Non-Joiner) -- ‍ '\u200D', # Нулевая ширина (с объединением) (Zero Width Joiner) -- ‌ '\u200E', # Изменить направление текста на слева-направо (Left-to-Right Mark /LRE) -- ‎ '\u200F', # Изменить направление текста направо-налево (Right-to-Left Mark /RLM) -- ‏ '\u2010', # Дефис (Hyphen) -- ‐ '\u205F', # Средний пробел (Medium Mathematical Space) --   '\u2060', # ⁠ '\u2062', # ⁢ -- для семантической разметки математических выражений '\u2063', # ⁣ -- для семантической разметки математических выражений ]) # 3. СПИСОК ДЛЯ ЧИСЛОВОГО КОДИРОВАНИЯ: Символы без стандартного имени. ALWAYS_ENCODE_TO_NUMERIC_CHARS = frozenset([ '\u058F', # Знак армянского драма (֏) '\u20BD', # Знак русского рубля (₽) '\u20B4', # Знак украинской гривны (₴) '\u20B8', # Знак казахстанского тенге (₸) '\u20B9', # Знак индийской рупии (₹) '\u20BC', # Знак азербайджанского маната '\u20BE', # Знак грузинский лари (₾) ]) # 4. СЛОВАРЬ ПРИОРИТЕТОВ: Кастомные и/или предпочитаемые мнемоники. # Некоторые utf-символы имеют несколько мнемоник, а значит для таких символов преобразование # в из utf во html-мнемоники может иметь несколько вариантов. Словарь приоритетов задает предпочтительное # преобразование. Эти правила применяются в последнюю очередь и имеют наивысший приоритет, # гарантируя предсказуемый результат для символов с несколькими именами. # # Также можно использовать для создания исключений из "черного списка" NEVER_ENCODE_CHARS. CUSTOM_ENCODE_MAP = { # '\u2010': '‐', # Для \u2010 всегда предпочитаем ‐, а не ‐ # # Исключения для букв, которые есть в алфавитах, но должны кодироваться (для обеспечения консистентности): # # 'Æ': 'Æ', # # 'Œ': 'Œ', # # 'æ': 'æ', # # 'œ': 'œ', # '\u002a': '*', # * / * / * # '\u005b': '[', # [ / [ / [ # '\u005d': ']', # ] / ] / ] # '\u005f': '_', # _ / _ / _ # '\u007b': '{', # { / { / { # '\u007d': '}', # } / } / } # '\u007c': '|', # | / | / | / | CHAR_NBSP: ' ', # /   /   CHAR_REG: '®', # ® / ® / ® / ® CHAR_COPY: '©', # © / © / © '\u0022': '"', # " / " / " '\u0026': '&', # & / & / & '\u003e': '>', # > / > / > '\u003c': '<', # < / < / < CHAR_MIDDOT: '·', # · / · / · / · '\u0060': '`', # ` / ` / ` '\u00a8': '¨', # ¨ / ¨ / ¨ / ¨ / ¨ '\u00b1': '±', # ± / ± / ± '\u00bd': '½', # ½ / ½ / ½ '\u00af': '¯', # ¯ / ¯ / ¯ '\u201a': '‚', # ‚ / ‚ / ‚ '\u223e': '∾', # ∾ / ∾ / ∾ '\u2207': '∇', # ∇ / ∇ / ∇ '\u2061': '⁡', # / ⁡ / ⁡ '\u2221': '∡', # ∡ / ∡ / ∡ CHAR_AP: '≈', # ≈ / ≈ / ≈ / ≈ / ≈ / ≈ '\u224a': '≊', # ≊ / ≊ / ≊ '\u2254': '≔', # ≔ / ≔ / ≔ / ≔ '\u224d': '≍', # ≍ / ≍ / ≍ '\u2233': '∳', # ∳ / ∳ / ∳ '\u224c': '≌', # ≌ / ≌ / ≌ '\u03f6': '϶', # ϶ / ϶ / ϶ '\u2035': '‵', # ‵ / ‵ / ‵ '\u223d': '∽', # ∽ / ∽ / ∽ '\u22cd': '⋍', # ⋍ / ⋍ / ⋍ '\u2216': '∖', # ∖ / ∖ / ∖ / ∖ / ∖ / ∖ '\u2306': '⌆', # ⌆ / ⌆ / ⌆ '\u2305': '⌅', # ⌅ / ⌅ / ⌅ '\u23b5': '⎵', # ⎵ / ⎵ / ⎵ '\u2235': '∵', # ∵ / ∵ / ∵ / ∵ '\u212c': 'ℬ', # ℬ / ℬ / ℬ / ℬ '\u2264': '≤', # ≤ / ≤ / ≤ '\u226c': '≬', # ≬ / ≬ / ≬ '\u22c2': '⋂', # ⋂ / ⋂ / ⋂ / ⋂ '\u25ef': '◯', # ◯ / ◯ / ◯ '\u22c3': '⋃', # ⋃ / ⋃ / ⋃ / ⋃ '\u2a00': '⨀', # ⨀ / ⨀ / ⨀ '\u2a01': '⨁', # ⨁ / ⨁ / ⨁ '\u2a02': '⨂', # ⨂ / ⨂ / ⨂ '\u2a06': '⨆', # ⨆ / ⨆ / ⨆ '\u2605': '★', # ★ / ★ / ★ '\u25bd': '▽', # ▽ / ▽ / ▽ '\u25b3': '△', # △ / △ / △ '\u2a04': '⨄', # ⨄ / ⨄ / ⨄ '\u22c1': '⋁', # ⋁ / ⋁ / ⋁ / ⋁ '\u22c0': '⋀', # ⋀ / ⋀ / ⋀ / $bigwedge; '\u2227': '∧', # ∧ / ∧ / ∧ '\u290d': '⤍', # ⤍ / ⤍ / ⤍ '\u29eb': '⧫', # ⧫ / ⧫ / ⧫ '\u25ca': '◊', # ◊ / ◊ / &lozenge '\u25aa': '▪', # ▪ / ▪ / ▪ / ▪ / ▪ '\u25b4': '▴', # ▴ / ▴ / ▴ '\u25be': '▾', # ▾ / ▾ / ▾ '\u25c2': '◂', # ◂ / ◂ / ◂ '\u25b8': '▸', # ▸ / ▸ / ▸ '\u22a5': '⊥', # ⊥ / ⊥ / ⊥ / ⊥ / ⊥ '\u2500': '─', # ─ / ─ / ─ '\u229f': '⊟', # ⊟ / ⊟ / ⊟ '\u229e': '⊞', # ⊞ / ⊞ / ⊞ '\u22a0': '⊠', # ⊠ / ⊠ / ⊠ '\u02d8': '˘', # ˘ / ˘ / ˘ '\u224e': '≎', # ≎ / ≎ / ≎ / ≎ '\u224f': '≏', # ≏ / ≏ / ≏ / ≏ '\u2145': 'ⅅ', # ⅅ / ⅅ / ⅅ '\u02c7': 'ˇ', # ˇ / ˇ / ˇ '\u212d': 'ℭ', # ℭ / ℭ / ℭ '\u2713': '✓', # ✓ / ✓ / ✓ '\u2257': '≗', # ≗ / ≗ / ≗ '\u21ba': '↺', # ↺ / ↺ / ↺ '\u21bb': '↻', # ↻ / ↻ / ↻ '\u229b': '⊛', # ⊛ / ⊛ / ⊛ '\u229a': '⊚', # ⊚ / ⊚ / ⊚ '\u229d': '⊝', # ⊝ / ⊝ / ⊝ '\u2299': '⊙', # ⊙ / ⊙ / ⊙ '\u2200': '∀', # ∀ / ∀ / ∀ '\u24c8': 'Ⓢ', # Ⓢ / Ⓢ / Ⓢ '\u2296': '⊖', # ⊖ / ⊖ / ⊖ '\u2232': '∲', # ∲ / ∲ / ∲ '\u201d': '”', # ” / ” / ” / ” '\u2019': '’', # ’ / ’ / ’ / ’ '\u2237': '∷', # ∷ / ∷ / ∷ '\u2201': '∁', # ∁ / ∁ / ∁ '\u2218': '∘', # ∘ / ∘ / ∘ '\u2102': 'ℂ', # ℂ / ℂ / ℂ '\u222f': '∯', # ∯ / ∯ / ∯ '\u222e': '∮', # ∮ / ∮ / ∮ / ∮ '\u2210': '∐', # ∐ / ∐ / ∐ '\u22de': '⋞', # ⋞ / ⋞ / ⋞ '\u22df': '⋟', # ⋟ / ⋟ / ⋟ '\u21b6': '↶', # ↶ / ↶ / ↶ '\u21b7': '↷', # ↷ / ↷ / ↷ '\u22ce': '⋎', # ⋎ / ⋎ / ⋎ '\u22cf': '⋏', # ⋏ / ⋏ / ⋏ '\u2010': '‐', # ‐ / ‐ / ‐ '\u2ae4': '⫤', # ⫤ / ⫤ / ⫤ '\u22a3': '⊣', # ⊣ / ⊣ / ⊣ '\u290f': '⤏', # ⤏ / ⤏ / ⤏ '\u02dd': '˝', # ˝ / ˝ / ˝ '\u2146': 'ⅆ', # ⅆ / ⅆ / ⅆ '\u21ca': '⇊', # ⇊ / ⇊ / ⇊ '\u2a77': '⩷', # ⩷ / ⩷ / ⩷ '\u21c3': '⇃', # ⇃ / ⇃ / ⇃ / ⇃ '\u21c2': '⇂', # ⇂ / ⇂ / ⇂ / ⇂ '\u02d9': '˙', # ˙ / ˙ / ˙ '\u222b': '∫', # ∫ / ∫ / ∫ '\u22c4': '⋄', # ⋄ / ⋄ / ⋄ / ⋄ '\u03b5': 'ε', # ε / ε / ε '\u03dd': 'ϝ', # ϝ / ϝ / ϝ '\u22c7': '⋇', # ⋇ / ⋇ / ⋇ '\u231e': '⌞', # ⌞ / ⌞ / ⌞ '\u2250': '≐', # ≐ / ≐ / ≐ / ≐ '\u2251': '≑', # ≑ / ≑ / ≑ '\u2238': '∸', # ∸ / ∸ / ∸ '\u2214': '∔', # ∔ / ∔ / ∔ '\u22a1': '⊡', # ⊡ / ⊡ / ⊡ '\u21d3': '⇓', # ⇓ / ⇓ / ⇓ / ⇓ CHAR_ARROW_R_DOUBLE: '⇒', # ⇒ / ⇒ / ⇒ / ⇒ / ⇒ CHAR_ARROW_L_DOUBLE: '⇐', # ⇐ / ⇐ / ⇐ / ⇐ CHAR_ARROW_LR_DOUBLE: '⇔', # ⇔ / ⇔ / ⇔ / ⇔ / ⇔ CHAR_ARROW_L_LONG_DOUBLE: '⟸', # ⟸ / ⟸ / ⟸ / ⟸ CHAR_ARROW_R_LONG_DOUBLE: '⟹', # ⟹ / ⟹ / ⟹ / ⟹ CHAR_ARROW_LR_LONG_DOUBLE: '⟺', # ⟺ / ⟺ / ⟺ / ⟺ '\u22a8': '⊨', # ⊨ / ⊨ / ⊨ '\u21d1': '⇑', # ⇑ / ⇑ / ⇑ / ⇑ '\u2202': '∂', # ∂ / ∂ / ∂ '\u21d5': '⇕', # ⇕ / ⇕ / ⇕ / ⇕ '\u2225': '∥', # ∥ / ∥ / ∥ / ∥ / ∥ / ∥ '\u2193': '↓', # ↓ / ↓ / ↓ / ↓ / ↓ '\u21f5': '⇵', # ⇵ / ⇵ / ⇵ '\u21bd': '↽', # ↽ / ↽ /↽ / ↽ '\u21c1': '⇁', # ⇁ / ⇁ / ⇁ / ⇁ '\u22a4': '⊤', # ⊤ / ⊤ / ⊤ '\u21a7': '↧', # ↧ / ↧ / ↧ '\u2910': '⤐', # ⤐ / ⤐ / ⤐ '\u231f': '⌟', # ⌟ / ⌟ / ⌟ '\u25bf': '▿', # ▿ / ▿ / ▿ '\u296f': '⥯', # ⥯ / ⥯ / ⥯ '\u2256': '≖', # ≖ / ≖ / ≖ '\u2255': '≕', # ≕ / ≕ / ≕ '\u2147': 'ⅇ', # ⅇ / ⅇ / ⅇ / ⅇ '\u2252': '≒', # ≒ / ≒ / ≒ '\u2a96': '⪖', # ⪖ / ⪖ / ⪖ '\u2208': '∈', # ∈ / ∈ / ∈ / ∈ / ∈ '\u2a95': '⪕', # ⪕ / ⪕ / ⪕ '\u2205': '∅', # ∅ / ∅ / ∅ / ∅ / ∅ '\u03f5': 'ϵ', # ϵ / ϵ / ϵ / ϵ '\u2242': '≂', # ≂ / ≂ / ≂ / ≂ '\u225f': '≟', # ≟ / ≟ / ≟ '\u21cc': '⇌', # ⇌ / ⇌ / ⇌ / ⇌ '\u2253': '≓', # ≓ / ≓ / ≓ '\u2130': 'ℰ', # ℰ / ℰ / ℰ '\u22d4': '⋔', # ⋔ / ⋔ / ⋔ '\u2131': 'ℱ', # ℱ / ℱ / ℱ '\u2322': '⌢', # ⌢ / ⌢ / ⌢ '\u2a86': '⪆', # ⪆ / ⪆ / ⪆ '\u2267': '≧', # ≧ / ≧ / ≧ / ≧ '\u2a8c': '⪌', # ⪌ / ⪌ / ⪌ '\u22db': '⋛', # ⋛ / ⋛ / ⋛ / ⋛ '\u2265': '≥', # ≥ / ≥ / ≥ / ≥ '\u2a7e': '⩾', # ⩾ / ⩾ / ⩾ / ⩾ '\u22d9': '⋙', # ⋙ / ⋙ / ⋙ '\u226b': '≫', # ≫ / &gg ;/ ≫ / ≫ '\u2277': '≷', # ≷ / ≷ / ≷ / ≷ '\u2a8a': '⪊', # ⪊ / ⪊ / ⪊ '\u2269': '≩', # ≩ / ≩ / ≩ '\u2260': '≠', # ≠ / ≠ / ≠ '\u2a88': '⪈', # ⪈ / ⪈ / ⪈ '\u2273': '≳', # ≳ / ≳ / ≳ / ≳ '\u22d7': '⋗', # ⋗ / ⋗ / ⋗ '\u200a': ' ', # /   /   '\u210b': 'ℋ', # ℋ / ℋ / ℋ / ℋ '\u21ad': '↭', # ↭ / ↭ / ↭ '\u210f': 'ℏ', # ℏ / ℏ / ℏ / ℏ / ℏ '\u210c': 'ℌ', # ℌ / ℌ / ℌ '\u2925': '⤥', # ⤥ / ⤥ / ⤥ '\u2926': '⤦', # ⤦ / ⤦ / ⤦ '\u21a9': '↩', # ↩ / ↩ / ↩ '\u21aa': '↪', # ↪ / ↪ / ↪ '\u210d': 'ℍ', # ℍ / ℍ / ℍ '\u2063': '⁣', # ⁣ / ⁣ / ⁣ '\u2111': 'ℑ', # ℑ / ℑ / ℑ / ℑ / ℑ '\u2148': 'ⅈ', # ⅈ / ⅈ / ⅈ '\u2a0c': '⨌', # ⨌ / ⨌ / ⨌ '\u222d': '∭', # ∭ / ∭ / ∭ '\u2110': 'ℐ', # ℐ / ℐ / ℐ '\u0131': 'ı', # ı / ı / ı '\u22ba': '⊺', # ⊺ / ⊺ / ⊺ '\u2124': 'ℤ', # ℤ / ℤ / ℤ '\u2a3c': '⨼', # ⨼ / ⨼ / ⨼ '\u2062': '⁢', # ⁢ / ⁢ / ⁢ '\u03f0': 'ϰ', # ϰ / ϰ / ϰ '\u21da': '⇚', # ⇚ / ⇚ / ⇚ '\u2112': 'ℒ', # ℒ / ℒ / ℒ / ℒ '\u27e8': '⟨', # ⟨ / ⟨ / ⟨ / ⟨ '\u2a85': '⪅', # ⪅ / ⪅ / ⪅ '\u219e': '↞', # ↞ / ↞ / ↞ '\u21e4': '⇤', # ⇤ / ⇤ / ⇤ '\u21ab': '↫', # ↫ / ↫ / ↫ '\u21a2': '↢', # ↢ / ↢ / ↢ '\u2266': '≦', # ≦ / ≦ / ≦ / ≦ '\u2190': '←', # ← / ← / ← / ← / ← / ← '\u21c6': '⇆', # ⇆ / ⇆ / ⇆ / ⇆ '\u27e6': '⟦', # ⟦ / ⟦ / ⟦ '\u21bc': '↼', # ↼ / ↼ / ↼ / ↼ '\u21c7': '⇇', # ⇇ / ⇇ / ⇇ '\u2194': '↔', # ↔ / ↔ / ↔ / ↔ '\u21cb': '⇋', # ⇋ / ⇋ / ⇋ / ⇋ '\u21a4': '↤', # ↤ / ↤ / ↤ '\u22cb': '⋋', # ⋋ / ⋋ / ⋋ '\u22b2': '⊲', # ⊲ / ⊲ / ⊲ / ⊲ '\u22b4': '⊴', # ⊴ / ⊴ / ⊴ / ⊴ '\u21bf': '↿', # ↿ / ↿ / ↿ / ↿ '\u2308': '⌈', # ⌈ / ⌈ / ⌈ '\u230a': '⌊', # ⌊ / ⌊ / ⌊ '\u2a8b': '⪋', # ⪋ / ⪋ / ⪋ '\u22da': '⋚', # ⋚ / ⋚ / ⋚ / ⋚ '\u2a7d': '⩽', # ⩽ / ⩽ / ⩽ / ⩽ '\u22d6': '⋖', # ⋖ / ⋖ / ⋖ '\u2276': '≶', # ≶ / ≶ / ≶ / ≶ '\u2272': '≲', # ≲ / ≲ / ≲ / ≲ '\u226a': '≪', # ≪ / ≪ / ≪ / ≪ '\u23b0': '⎰', # ⎰ / ⎰ / ⎰ '\u2a89': '⪉', # ⪉ / ⪉ / ⪉ '\u2268': '≨', # ≨ / ≨ / ≨ '\u2a87': '⪇', # ⪇ / ⪇ / ⪇ CHAR_ARROW_L: '⟵', # ⟵ / ⟵ / ⟵ / ⟵ CHAR_ARROW_R: '⟶', # ⟶ / ⟶ / ⟶ / ⟶ CHAR_ARROW_LR: '⟷', # ⟷ / ⟷ / ⟷ / ⟷ '\u27fc': '⟼', # ⟼ / ⟼ / ⟼ '\u21ac': '↬', # ↬ / ↬ / ↬ '\u201e': '„', # „ / „ / „ '\u2199': '↙', # ↙ / ↙ / ↙ / ↙ '\u2198': '↘', # ↘ / ↘ / ↘ / ↘ '\u21b0': '↰', # ↰ / ↰ / ↰ '\u25c3': '◃', # ◃ / ◃ / ◃ '\u2720': '✠', # ✠ / ✠ / ✠ '\u21a6': '↦', # ↦ / ↦ / ↦ / ↦ '\u21a5': '↥', # ↥ / ↥ / ↥ '\u2133': 'ℳ', # ℳ / ℳ / ℳ / ℳ '\u2223': '∣', # ∣ / ∣ / ∣ / ∣ / ∣ '\u2213': '∓', # ∓ / ∓ / ∓ / ∓ CHAR_HELLIP: '…', # … / … / … '\u22b8': '⊸', # ⊸ / ⊸ / ⊸ '\u2249': '≉', # ≉ / ≉ / ≉ / ≉ '\u266e': '♮', # ♮ / ♮ / ♮ '\u2115': 'ℕ', # ℕ / ℕ / ℕ '\u2247': '≇', # ≇ / ≇ / ≇ '\u2197': '↗', # ↗ / ↗ / ↗ / ↗ '\u200b': '​', # / ​ / ​ / ​ # ​ / ​ '\u2262': '≢', # ≢ / ≢ / ≢ '\u2928': '⤨', # ⤨ / ⤨ / ⤨ '\u2203': '∃', # ∃ / ∃ / ∃ '\u2204': '∄', # ∄ / ∄ / ∄ / ∄ '\u2271': '≱', # ≱ / ≱ / ≱ / ≱ '\u2275': '≵', # ≵ / ≵ / ≵ '\u226f': '≯', # ≯ / ≯ / ≯ / ≯ '\u21ce': '⇎', # ⇎ / ⇎ / ⇎ '\u21ae': '↮', # ↮ / ↮ / ↮ '\u220b': '∋', # ∋ / ∋ / ∋ / ∋ / ∋ '\u21cd': '⇍', # ⇍ / ⇍ / ⇍ '\u219a': '↚', # ↚ / ↚ / ↚ '\u2270': '≰', # ≰ / ≰ / ≰ / ≰ '\u226e': '≮', # ≮ / ≮ / ≮ / ≮ '\u2274': '≴', # ≴ / ≴ / ≴ '\u22ea': '⋪', # ⋪ / ⋪ / ⋪ / ⋪ '\u22ec': '⋬', # ⋬ / ⋬ / ⋬ / ⋬ '\u2224': '∤', # ∤ / ∤ / ∤ / ∤ / ∤ '\u2226': '∦', # ∦ / ∦ / ∦ / ∦ / ∦ / ∦ '\u2209': '∉', # ∉ / ∉ / ∉ / ∉ '\u2279': '≹', # ≹ / ≹ / ≹ '\u2278': '≸', # ≸ / ≸ / ≸ '\u220c': '∌', # ∌ / ∌ / ∌ / ∌ '\u2280': '⊀', # ⊀ / ⊀ / ⊀ / ⊀ '\u22e0': '⋠', # ⋠ / ⋠ / ⋠ '\u22eb': '⋫', # ⋫ / ⋫ / ⋫ / ⋫ '\u22ed': '⋭', # ⋭ / ⋭ / ⋭ / ⋭ '\u22e2': '⋢', # ⋢ / ⋢ / ⋢ '\u22e3': '⋣', # ⋣ / ⋣ / ⋣ '\u2288': '⊈', # ⊈ / ⊈ / ⊈ / ⊈ '\u2281': '⊁', # ⊁ / ⊁ / ⊁ / ⊁ '\u22e1': '⋡', # ⋡ / ⋡ / ⋡ '\u2289': '⊉', # ⊉ / ⊉ / ⊉ / ⊉ '\u2241': '≁', # ≁ / ≁ / ≁ '\u2244': '≄', # ≄ / ≄ / ≄ / ≄ '\u21cf': '⇏', # ⇏ / ⇏ / ⇏ '\u219b': '↛', # ↛ / ↛ / ↛ '\u2196': '↖', # ↖ / ↖ / ↖ / ↖ '\u2134': 'ℴ', # ℴ / ℴ / ℴ / ℴ '\u203e': '‾', # ̄ / ‾ / ‾ '\u23b4': '⎴', # ⎴ / ⎴ / ⎴ '\u03d6': 'ϖ', # ϖ / ϖ / ϖ '\u03d5': 'ϕ', # ϕ / ϕ / ϕ / ϕ '\u2665': '♥', # ♥ / ♥ / ♥ / '\u2119': 'ℙ', # ℙ / ℙ / ℙ '\u227a': '≺', # ≺ / ≺ / ≺ / ≺ '\u2ab7': '⪷', # ⪷ / ⪷ / ⪷ '\u227c': '≼', # ≼ / ≼ / ≼ / ≼ '\u2aaf': '⪯', # ⪯ / ⪯ / ⪯ / ⪯ '\u227e': '≾', # ≾ / ≾ / ≾ / ≾ '\u2ab9': '⪹', # ⪹ / ⪹ / ⪹ '\u2ab5': '⪵', # ⪵ / ⪵ / ⪵ '\u22e8': '⋨', # ⋨ / ⋨ / ⋨ '\u220f': '∏', # ∏ / ∏ / ∏ '\u221d': '∝', # ∝ / ∝ / ∝ / ∝ / ∝ / ∝ '\u211a': 'ℚ', # ℚ / ℚ / ℚ '\u21db': '⇛', # ⇛ / ⇛ / ⇛ '\u27e9': '⟩', # ⟩ / ⟩ / ⟩ / ⟩ '\u21a0': '↠', # ↠ / ↠ / ↠ '\u21e5': '⇥', # ⇥ / ⇥ / ⇥ '\u21a3': '↣', # ↣ / ↣ / ↣ '\u2309': '⌉', # ⌉ / ⌉ / ⌉ '\u219d': '↝', # ↝ / ↝ / ↝ '\u03a9': 'Ω', # Ω / Ω / Ω '\u211c': 'ℜ', # ℜ / ℜ / ℜ / ℜ / ℜ '\u211b': 'ℛ', # ℛ / ℛ / ℛ '\u211d': 'ℝ', # ℝ / ℝ / ℝ '\u21c0': '⇀', # ⇀ / ⇀ / ⇀ / ⇀ '\u03f1': 'ϱ', # ϱ / ϱ / ϱ '\u2192': '→', # → / → / → / → / → / → '\u21c4': '⇄', # ⇄ / ⇄ / ⇄ / ⇄ '\u27e7': '⟧', # ⟧ / ⟧ / ⟧ '\u230b': '⌋', # ⌋ / ⌋ / ⌋ '\u21c9': '⇉', # ⇉ / ⇉ / ⇉ '\u22a2': '⊢', # ⊢ / ⊢ / ⊢ '\u22cc': '⋌', # ⋌ / ⋌ / ⋌ '\u22b3': '⊳', # ⊳ / ⊳ / ⊳ / ⊳ '\u22b5': '⊵', # ⊵ / ⊵ / ⊵ / ⊵ '\u21be': '↾', # ↾ / ↾ / ↾ / ↾ '\u23b1': '⎱', # ⎱ / ⎱ / ⎱ '\u201c': '“', # “ / “ / “ '\u2018': '‘', # ‘ / ‘ / ‘ '\u21b1': '↱', # ↱ / ↱ / ↱ '\u25b9': '▹', # ▹ / ▹ / ▹ '\u227b': '≻', # ≻ / ≻ / ≻ / ≻ '\u2ab8': '⪸', # ⪸ / ⪸ / ⪸ '\u227d': '≽', # ≽ / ≽ / ≽ / ≽ '\u2ab0': '⪰', # ⪰ / ⪰ / ⪰ / ⪰ '\u2aba': '⪺', # ⪺ / ⪺ / ⪺ '\u2ab6': '⪶', # ⪶ / ⪶ / ⪶ '\u22e9': '⋩', # ⋩ / ⋩ / ⋩ '\u227f': '≿', # ≿ / ≿ / ≿ / ≿ '\u2929': '⤩', # ⤩ / ⤩ / ⤩ '\u03c2': 'ς', # ς / ς / ς / ς '\u2243': '≃', # ≃ / ≃ / ≃ / ≃ '\u2323': '⌣', # ⌣ / ⌣ / ⌣ '\u2660': '♠', # ♠ / ♠ / ♠ / '\u2293': '⊓', # ⊓ / ⊓ / ⊓ '\u2294': '⊔', # ⊔ / ⊔ / ⊔ '\u221a': '√', # √ / √ / √ '\u228f': '⊏', # ⊏ / ⊏ / ⊏ / ⊏ '\u2291': '⊑', # ⊑ / ⊑ / ⊑ / ⊑ '\u2290': '⊐', # ⊐ / ⊐ / ⊐ / ⊐ '\u2292': '⊒', # ⊒ / ⊒ / ⊒ / ⊒ '\u25a1': '□', # □ / □ / □ / □ '\u22c6': '⋆', # ⋆ / ⋆ / ⋆ '\u22d0': '⋐', # ⋐ / ⋐ / ⋐ '\u2282': '⊂', # ⊂ / ⊂ / ⊂ '\u2ac5': '⫅', # ⫅ / ⫅ / ⫅ '\u2acb': '⫋', # ⫋ / ⫋ / ⫋ '\u228a': '⊊', # ⊊ / ⊊ / ⊊ '\u2286': '⊆', # ⊆ / ⊆ / ⊆ / ⊆ '\u2211': '∑', # ∑ / ∑ / ∑ '\u22d1': '⋑', # ⋑ / ⋑ / ⋑ '\u2ac6': '⫆', # ⫆ / ⫆ / ⫆ '\u2283': '⊃', # ⊃ / ⊃ / ⊃ / ⊃ '\u2287': '⊇', # ⊇ / ⊇ / ⊇ / ⊇ '\u2acc': '⫌', # ⫌ / ⫌ / ⫌ '\u228b': '⊋', # ⊋ / ⊋ / ⊋ '\u223c': '∼', # ∼ / ∼ / ∼ / ∼ / ∼ '\u2245': '≅', # ≅ / ≅ / ≅ '\u20db': '⃛', # ⃛ / ⃛ / ⃛ '\u2234': '∴', # ∴ / ∴ / ∴ / ∴ '\u03d1': 'ϑ', # ϑ / ϑ / ϑ / ϑ CHAR_TRADE: '™', # ™ / ™ / ™ '\u25b5': '▵', # ▵ / ▵ / ▵ '\u225c': '≜', # ≜ / ≜ / ≜ '\u21c5': '⇅', # ⇅ / ⇅ / ⇅ '\u296e': '⥮', # ⥮ / ⥮ / ⥮ '\u231c': '⌜', # ⌜ / ⌜ / ⌜ '\u03d2': 'ϒ', # ϒ / ϒ / ϒ '\u03c5': 'υ', # υ / υ / υ '\u228e': '⊎', # ⊎ / ⊎ / ⊎ '\u2195': '↕', # ↕ / ↕ / ↕ / ↕ '\u2191': '↑', # ↑ / ↑ / ↑ / ↑ / ↑ '\u21c8': '⇈', # ⇈ / ⇈ / ⇈ '\u231d': '⌝', # ⌝ / ⌝ / ⌝ '\u2016': '‖', # ‖ / ‖ / ‖ '\u2228': '∨', # ∨ / ∨ / ∨ CHAR_THIN_SP: ' ', # /   /   '\u2240': '≀', # ≀ / ≀ / ≀ / ≀ '\u2128': 'ℨ', # ℨ / ℨ / ℨ '\u2118': '℘', # ℘ / ℘ / ℘ } # === Динамическая генерация карт преобразования === def _build_translation_maps() -> dict[str, str]: """ Создает карту для кодирования на лету, используя все доступные источники из html.entities и строгий порядок приоритетов для обеспечения предсказуемого и детерминированного результата. """ # ШАГ 1: Создаем ЕДИНУЮ и ПОЛНУЮ карту {каноническое_имя: числовой_код}. # Это решает проблему разных форматов и дубликатов с точкой с запятой. unified_name2codepoint = {} # Сначала обрабатываем большой исторический словарь. for name, codepoint in entities.name2codepoint.items(): # Нормализуем имя СРАЗУ, убирая опциональную точку с запятой (в html.entities предусмотрено, что иногда # символ `;` не ставится всякими неаккуратными верстальщиками и парсерами). canonical_name = name.rstrip(';') unified_name2codepoint[canonical_name] = codepoint # Затем обновляем его современным стандартом html5. # Это гарантирует, что если мнемоника есть в обоих, будет использована версия из html5. for name, char in entities.html5.items(): # НОВОЕ: Проверяем, что значение является ОДИНОЧНЫМ символом. # Наш кодек, основанный на str.translate, не может обрабатывать # мнемоники, которые соответствуют строкам из нескольких символов # (например, символ + вариативный селектор). Мы их игнорируем. if len(char) != 1: continue # Нормализуем имя СРАЗУ. canonical_name = name.rstrip(';') unified_name2codepoint[canonical_name] = ord(char) # Теперь у нас есть полный и консистентный словарь unified_name2codepoint. # На его основе строим нашу карту для кодирования. encode_map = {} # ШАГ 2: Высший приоритет. Загружаем наши кастомные правила. encode_map.update(CUSTOM_ENCODE_MAP) # ШАГ 3: Следующий приоритет. Добавляем числовое кодирование. for char in ALWAYS_ENCODE_TO_NUMERIC_CHARS: if char not in encode_map: encode_map[char] = f'&#{ord(char)};' # ШАГ 4: Низший приоритет. Заполняем все остальное из нашей # объединенной и нормализованной карты unified_name2codepoint. for name, codepoint in unified_name2codepoint.items(): char = chr(codepoint) if char not in encode_map and char not in NEVER_ENCODE_CHARS: # Теперь 'name' - это уже каноническое имя без ';', # поэтому дополнительная нормализация не нужна. Код стал проще! encode_map[char] = f'&{name};' return encode_map # Создаем карту один раз при импорте модуля. ENCODE_MAP = _build_translation_maps() # --- Публичный API модуля --- def get_encode_map(): """Возвращает готовую карту для кодирования.""" return ENCODE_MAP # === КОНСТАНТЫ ДЛЯ ЕДИНИЦ ИЗМЕРЕНИЯ === # ТОЛЬКО АТОМАРНЫЕ единицы измерения: 'г', 'м', 'с', 'км', 'кв', 'куб', 'ч' и так далее. # Никаких сложных и составных, типа: 'кв.м.', 'км/ч' или "до н.э." ... # Пост-позиционные (10 км). DEFAULT_POST_UNITS = [ # Русские 'гг', 'г.', 'в.', 'вв', 'н', 'э', 'кг', 'мг', 'ц', 'т', 'кв', 'куб', 'мм', 'см', 'м', 'км', 'л', 'мл', 'сот', 'га', 'сек', 'с.', 'мин', 'ч', 'руб', 'коп', 'тыс', 'млн', 'млрд', 'трлн', 'трлрд', 'пп', 'стр', 'рис', 'табл', 'гл', 'п', 'шт', 'об' # Английские 'pp', 'p', 'para', 'sect', 'fig', 'vol', 'ed', ] # Пред-позиционные (№ 5, $ 10) DEFAULT_PRE_UNITS = ['№', '$', '€', '£', '₽', '#'] # Операторы, которые могут стоять между единицами измерения (км/ч) # Сложение и вычитание здесь намеренно отсутствуют. UNIT_MATH_OPERATORS = ['/', '*', '×', CHAR_MIDDOT, '÷']