# 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]) # === ИСТОЧНИК ПРАВДЫ === # --- Базовые алфавиты: Эти константы используются как для правил переноса, так и для правил кодирования --- # Русский алфавит 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 # --- Специальные символы --- NBSP_CHAR = '\u00A0' # Неразрывный пробел ( ) SHY_CHAR = '\u00AD' # Мягкий перенос (­) # === КОНСТАНТЫ ДЛЯ КОДИРОВАНИЯ HTML-МНЕМНОИКОВ === # --- ЧЕРНЫЙ СПИСОК: Символы, которые НИКОГДА не нужно кодировать в мнемоники --- NEVER_ENCODE_CHARS = (frozenset(['!', '#', '%', '(', ')', '*', ',', '.', '/', ':', ';', '=', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', '\n', '\t', '\r']) | RU_ALPHABET_FULL | EN_ALPHABET_FULL) # 2. БЕЛЫЙ СПИСОК (ДЛЯ БЕЗОПАСНОСТИ): # Символы, которые ВСЕГДА должны превращаться в мнемоники в "безопасных" режимах вывода. Сюда добавлены символы, # которые не видны, на глаз и не отличимы друг от друга в обычном тексте, или очень специфичные SAFE_MODE_CHARS_TO_MNEMONIC = frozenset([ '<', '>', '&', '"', '\'', '\u00AD', # Мягкий перенос (Soft Hyphen) -- ­ '\u00A0', # Неразрывный пробел (Non-Breaking Space) --   '\u2002', # Полужирный пробел (En Space) --   '\u2003', # Широкий пробел (Em Space) --   '\u2007', # Цифровой пробел --  ) '\u2008', # Пунктуационный пробел --   '\u2009', # Междусимвольный пробел --  ' '\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': '|', # | / | / | / | # '\u0026': '&', # & / & / & # '\u00A0': ' ', # /   /   '\u0022': '"', # " / " / " '\u0026': '&', # & / & / & '\u003e': '>', # > / > / > '\u003c': '<', # < / < / < '\u00ae': '®', # ® / ® / ® / ® '\u00b7': '·', # · / · / · / · '\u0060': '`', # ` / grave / DiacriticalGrave '\u00a8': '¨', # ¨ / ¨ / ¨ / ¨ / ¨ '\u00b1': '±', # ± / ± / ± '\u00bd': '½', # ½ / ½ / ½ '\u00af': '¯', # ¯ / ¯ / ¯ '\u201a': '‚', # ‚ / ‚ / ‚ '\u223e': '∾', # ∾ / ∾ / ∾ '\u2207': '∇', # ∇ / ∇ / ∇ '\u2061': '⁡', # / ⁡ / ⁡ '\u2221': '∡', # ∡ / ∡ / ∡ '\u2248': '≈', # ≈ / ≈ / ≈ / ≈ / ≈ / ≈ '\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': '⇓', # ⇓ / ⇓ / ⇓ / ⇓ '\u21d0': '⇐', # ⇐ / ⇐ / ⇐ / ⇐ '\u21d4': '⇔', # ⇔ / ⇔ / ⇔ / ⇔ / ⇔ '\u27f8': '⟸', # ⟸ / ⟸ / ⟸ / ⟸ '\u27fa': '⟺', # ⟺ / ⟺ / ⟺ / ⟺ '\u27f9': '⟹', # ⟹ / ⟹ / ⟹ / ⟹ '\u21d2': '⇒', # ⇒ / ⇒ / ⇒ / ⇒ / ⇒ '\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': '⪇', # ⪇ / ⪇ / ⪇ '\u27f5': '⟵', # ⟵ / ⟵ / ⟵ / ⟵ '\u27f7': '⟷', # ⟷ / ⟷ / ⟷ / ⟷ '\u27fc': '⟼', # ⟼ / ⟼ / ⟼ '\u27f6': '⟶', # ⟶ / ⟶ / ⟶ / ⟶ '\u21ac': '↬', # ↬ / ↬ / ↬ '\u201e': '„', # „ / „ / „ '\u2199': '↙', # ↙ / ↙ / ↙ / ↙ '\u2198': '↘', # ↘ / ↘ / ↘ / ↘ '\u21b0': '↰', # ↰ / ↰ / ↰ '\u25c3': '◃', # ◃ / ◃ / ◃ '\u2720': '✠', # ✠ / ✠ / ✠ '\u21a6': '↦', # ↦ / ↦ / ↦ / ↦ '\u21a5': '↥', # ↥ / ↥ / ↥ '\u2133': 'ℳ', # ℳ / ℳ / ℳ / ℳ '\u2223': '∣', # ∣ / ∣ / ∣ / ∣ / ∣ '\u2213': '∓', # ∓ / ∓ / ∓ / ∓ '\u2026': '…', # … / … / … '\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': 'ϑ', # ϑ / ϑ / ϑ / ϑ '\u2122': '™', # ™ / ™ / ™ '\u25b5': '▵', # ▵ / ▵ / ▵ '\u225c': '≜', # ≜ / ≜ / ≜ '\u21c5': '⇅', # ⇅ / ⇅ / ⇅ '\u296e': '⥮', # ⥮ / ⥮ / ⥮ '\u231c': '⌜', # ⌜ / ⌜ / ⌜ '\u03d2': 'ϒ', # ϒ / ϒ / ϒ '\u03c5': 'υ', # υ / υ / υ '\u228e': '⊎', # ⊎ / ⊎ / ⊎ '\u2195': '↕', # ↕ / ↕ / ↕ / ↕ '\u2191': '↑', # ↑ / ↑ / ↑ / ↑ / ↑ '\u21c8': '⇈', # ⇈ / ⇈ / ⇈ '\u231d': '⌝', # ⌝ / ⌝ / ⌝ '\u2016': '‖', # ‖ / ‖ / ‖ '\u2228': '∨', # ∨ / ∨ / ∨ '\u2009': ' ', # /   /   '\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