tmp: код etpgrf перенесен внутрь проекта...

This commit is contained in:
2026-01-05 22:43:57 +03:00
parent 0a4fcb44be
commit 9740887359
14 changed files with 2416 additions and 0 deletions

View File

@@ -0,0 +1,722 @@
# 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) # Языки по умолчанию
# Виды санитизации (очистки) входного текста
SANITIZE_ALL_HTML = "html" # Полная очистка от HTML-тегов
SANITIZE_ETPGRF = "etp" # Очистка от "span-оберток" символов висячей пунктуации (если она была расставлена
# при предыдущих проходах типографа)
SANITIZE_NONE = None # Без очистки (режим по умолчанию). False тоже можно использовать.
# === ИСТОЧНИК ПРАВДЫ ===
# --- Базовые алфавиты: Эти константы используются как для правил переноса, так и для правил кодирования ---
# Русский алфавит
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' # Знак звуковой записи / ℗ / &copyp;
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) -- &shy;
CHAR_NBSP, # Неразрывный пробел (Non-Breaking Space) -- &nbsp;
'\u2002', # Полужирный пробел (En Space) -- &ensp;
'\u2003', # Широкий пробел (Em Space) -- &emsp;
'\u2007', # Цифровой пробел -- &numsp;
'\u2008', # Пунктуационный пробел -- &puncsp;
CHAR_THIN_SP, # Межсимвольный пробел, тонкий пробел, шпация -- &thinsp;'
'\u200A', # Толщина волоса (Hair Space) -- &hairsp;
'\u200B', # Негативный пробел (Negative Space) -- &NegativeThinSpace;
'\u200C', # Нулевая ширина (без объединения) (Zero Width Non-Joiner) -- &zwj;
'\u200D', # Нулевая ширина (с объединением) (Zero Width Joiner) -- &zwnj;
'\u200E', # Изменить направление текста на слева-направо (Left-to-Right Mark /LRE) -- &lrm;
'\u200F', # Изменить направление текста направо-налево (Right-to-Left Mark /RLM) -- &rlm;
'\u2010', # Дефис (Hyphen) -- &dash;
'\u205F', # Средний пробел (Medium Mathematical Space) -- &MediumSpace;
'\u2060', # &NoBreak;
'\u2062', # &InvisibleTimes; -- для семантической разметки математических выражений
'\u2063', # &InvisibleComma; -- для семантической разметки математических выражений
])
# 3. СПИСОК ДЛЯ ЧИСЛОВОГО КОДИРОВАНИЯ: Символы без стандартного имени.
ALWAYS_ENCODE_TO_NUMERIC_CHARS = frozenset([
'\u058F', # Знак армянского драма (֏)
'\u20B4', # Знак украинской гривны (₴)
'\u20B8', # Знак казахстанского тенге (₸)
'\u20B9', # Знак индийской рупии (₹)
'\u20BA', # Знак турецкой лиры (₺)
'\u20BB', # Знак итальянской лиры (₻)
'\u20BC', # Знак азербайджанского маната
'\u20BD', # Знак русского рубля (₽)
'\u20BE', # Знак грузинский лари (₾)
'\u20BF', # Знак биткоина (₿)
])
# 4. СЛОВАРЬ ПРИОРИТЕТОВ: Кастомные и/или предпочитаемые мнемоники.
# Некоторые utf-символы имеют несколько мнемоник, а значит для таких символов преобразование
# в из utf во html-мнемоники может иметь несколько вариантов. Словарь приоритетов задает предпочтительное
# преобразование. Эти правила применяются в последнюю очередь и имеют наивысший приоритет,
# гарантируя предсказуемый результат для символов с несколькими именами.
#
# Также можно использовать для создания исключений из "черного списка" NEVER_ENCODE_CHARS.
CUSTOM_ENCODE_MAP = {
# '\u2010': '&hyphen;', # Для \u2010 всегда предпочитаем &hyphen;, а не &dash;
# # Исключения для букв, которые есть в алфавитах, но должны кодироваться (для обеспечения консистентности):
# # 'Æ': '&AElig;',
# # 'Œ': '&OElig;',
# # 'æ': '&aelig;',
# # 'œ': '&oelig;',
# '\u002a': '&ast;', # * / &ast; / &midast;
# '\u005b': '&lsqb;', # [ / &lsqb; / &lbrack;
# '\u005d': '&rsqb;', # ] / &rsqb; / &rbrack;
# '\u005f': '&lowbar;', # _ / &lowbar; / &UnderBar;
# '\u007b': '&lcub;', # { / &lcub; / &lbrace;
# '\u007d': '&rcub;', # } / &rcub; / &rbrace;
# '\u007c': '&vert;', # | / &vert; / &verbar; / &VerticalLine;
CHAR_NBSP: '&nbsp;', # / &nbsp; / &NonBreakingSpace;
CHAR_REG: '&reg;', # ® / &reg; / &REG; / &circledR;
CHAR_COPY: '&copy;', # © / &copy; / &COPY;
'\u0022': '&quot;', # " / &quot; / &QUOT;
'\u0026': '&amp;', # & / &amp; / &AMP;
'\u003e': '&gt;', # > / &gt; / &GT;
'\u003c': '&lt;', # < / &lt; / &LT;
CHAR_MIDDOT: '&middot;', # · / &middot; / &centerdot; / &CenterDot;
'\u0060': '&grave;', # ` / &grave; / &DiacriticalGrave;
'\u00a8': '&die;', # ¨ / &die; / &Dot; / &uml; / &DoubleDot;
'\u00b1': '&pm;', # ± / &pm; / &PlusMinus;
'\u00bd': '&half;', # ½ / &frac12; / &half;
'\u00af': '&macr;', # ¯ / &macr; / &strns;
'\u201a': '&sbquo;', # / &sbquo; / &lsquor;
'\u223e': '&ac;', # ∾ / &ac; / &mstpos;
'\u2207': '&Del;', # ∇ / &Del; / &nabla;
'\u2061': '&af;', # / &af; / &ApplyFunction;
'\u2221': '&angmsd;', # ∡ / &angmsd; / &measuredangle;
CHAR_AP: '&ap;', # ≈ / &ap; / &thkap; / &approx; / &TildeTilde; / &thickapprox;
'\u224a': '&ape;', # ≊ / &ape; / &approxeq;
'\u2254': '&Assign;', # ≔ / &Assign; / &colone; / &coloneq;
'\u224d': '&CupCap;', # ≍ / &CupCap; / &asympeq;
'\u2233': '&awconint;', # ∳ / &awconint; / &CounterClockwiseContourIntegral;
'\u224c': '&bcong;', # ≌ / &bcong; / &backcong;
'\u03f6': '&bepsi;', # ϶ / &bepsi; / &backepsilon;
'\u2035': '&bprime;', # / &bprime; / &backprime;
'\u223d': '&bsim;', # ∽ / &bsim; / &backsim;
'\u22cd': '&bsime;', # ⋍ / &bsime; / &backsimeq;
'\u2216': '&setmn;', # / &setmn; / &ssetmn; / &setminus; / &Backslash; / &smallsetminus;
'\u2306': '&Barwed;', # ⌆ / &Barwed; / &doublebarwedge;
'\u2305': '&barwed;', # ⌅ / &barwed; / &barwedge;
'\u23b5': '&bbrk;', # ⎵ / &bbrk; / &UnderBracket;
'\u2235': '&becaus;', # ∵ / &becaus; / &because; / &Because;
'\u212c': '&Bscr;', # / &Bscr; / &bernou; / &Bernoullis;
'\u2264': '&le;', # ≤ / &le; / &leq;
'\u226c': '&twixt;', # ≬ / &twixt; / &between;
'\u22c2': '&xcap;', # ⋂ / &xcap; / &bigcap; / &Intersection;
'\u25ef': '&xcirc;', # ◯ / &xcirc; / &bigcirc;
'\u22c3': '&xcup;', # / &xcup; / &Union; / &bigcup;
'\u2a00': '&xodot;', # ⨀ / &xodot; / &bigodot;
'\u2a01': '&xoplus;', # ⨁ / &xoplus; / &bigoplus;
'\u2a02': '&xotime;', # ⨂ / &xotime; / &bigotimes;
'\u2a06': '&xsqcup;', # ⨆ / &xsqcup; / &bigsqcup;
'\u2605': '&starf;', # ★ / &starf; / &bigstar;
'\u25bd': '&xdtri;', # ▽ / &xdtri; / &bigtriangledown;
'\u25b3': '&xutri;', # △ / &xutri; / &bigtriangleup;
'\u2a04': '&xuplus;', # ⨄ / &xuplus; / &biguplus;
'\u22c1': '&Vee;', # / &Vee; / &xvee; / &bigvee;
'\u22c0': '&Wedge;', # ⋀ / &Wedge; / &xwedge; / $bigwedge;
'\u2227': '&and;', # ∧ / &and; / &wedge;
'\u290d': '&rbarr;', # ⤍ / &rbarr; / &bkarow;
'\u29eb': '&lozf;', # ⧫ / &lozf; / &blacklozenge;
'\u25ca': '&loz;', # ◊ / &loz; / &lozenge
'\u25aa': '&squf;', # ▪ / &squf; / &squarf; / &blacksquare; / &FilledVerySmallSquare;
'\u25b4': '&utrif;', # ▴ / &utrif; / &blacktriangle;
'\u25be': '&dtrif;', # ▾ / &dtrif; / &blacktriangledown;
'\u25c2': '&ltrif;', # ◂ / &ltrif; / &blacktriangleleft;
'\u25b8': '&rtrif;', # ▸ / &rtrif; / &blacktriangleright;
'\u22a5': '&bot;', # ⊥ / &bot; / &UpTee; / &bottom; / &perp;
'\u2500': '&boxh;', # ─ / &boxh; / &HorizontalLine;
'\u229f': '&minusb;', # ⊟ / &minusb; / &boxminus;
'\u229e': '&plusb;', # ⊞ / &plusb; / &boxplus;
'\u22a0': '&timesb;', # ⊠ / &timesb; / &boxtimes;
'\u02d8': '&breve;', # ˘ / &breve; / &Breve;
'\u224e': '&bump;', # ≎ / &bump; / &Bumpeq; / &HumpDownHump;
'\u224f': '&bumpe;', # ≏ / &bumpe; / &bumpeq; / &HumpEqual;
'\u2145': '&DD;', # / &DD; / &CapitalDifferentialD;
'\u02c7': '&caron;', # ˇ / &Hacek; / &caron;
'\u212d': '&Cfr;', # / &Cfr; / &Cayleys;
'\u2713': '&check;', # ✓ / &check; / &checkmark;
'\u2257': '&cire;', # ≗ / &cire; / &circeq;
'\u21ba': '&olarr;', # ↺ / &olarr; / &circlearrowleft;
'\u21bb': '&orarr;', # ↻ / &orarr; / &circlearrowright;
'\u229b': '&oast;', # ⊛ / &oast; / &circledast;
'\u229a': '&ocir;', # ⊚ / &ocir; / &circledcirc;
'\u229d': '&odash;', # ⊝ / &odash; / &circleddash;
'\u2299': '&odot;', # ⊙ / &odot; / &CircleDot;
'\u2200': '&forall;', # ∀ / &forall; / &ForAll;
'\u24c8': '&oS;', # Ⓢ / &oS; / &circledS;
'\u2296': '&ominus;', # ⊖ / &ominus; / &CircleMinus;
'\u2232': '&cwconint;', # ∲ / &cwconint; / &ClockwiseContourIntegral;
'\u201d': '&rdquo;', # ” / &rdquo; / &rdquor; / &CloseCurlyDoubleQuote;
'\u2019': '&rsquo;', # / &rsquo; / &rsquor; / &CloseCurlyQuote;
'\u2237': '&Colon;', # ∷ / &Colon; / &Proportion;
'\u2201': '&comp;', # ∁ / &comp; / &complement;
'\u2218': '&compfn;', # ∘ / &compfn; / &SmallCircle;
'\u2102': '&Copf;', # / &Copf; / &complexes;
'\u222f': '&Conint;', # ∯ / &Conint; / &DoubleContourIntegral;
'\u222e': '&oint;', # ∮ / &oint; / &conint; / &ContourIntegral;
'\u2210': '&coprod;', # ∐ / &coprod; / &Coproduct;
'\u22de': '&cuepr;', # ⋞ / &cuepr; / &curlyeqprec;
'\u22df': '&cuesc;', # ⋟ / &cuesc; / &curlyeqsucc;
'\u21b6': '&cularr;', # ↶ / &cularr; / &curvearrowleft;
'\u21b7': '&curarr;', # ↷ / &curarr; / &curvearrowright;
'\u22ce': '&cuvee;', # ⋎ / &cuvee; / &curlyvee;
'\u22cf': '&cuwed;', # ⋏ / &cuwed; / &curlywedge;
'\u2010': '&dash;', # / &dash; / &hyphen;
'\u2ae4': '&Dashv;', # ⫤ / &Dashv; / &DoubleLeftTee;
'\u22a3': '&dashv;', # ⊣ / &dashv; / &LeftTee;
'\u290f': '&rBarr;', # ⤏ / &rBarr; / &dbkarow;
'\u02dd': '&dblac;', # ˝ / &dblac; / &DiacriticalDoubleAcute;
'\u2146': '&dd;', # / &dd; / &DifferentialD;
'\u21ca': '&ddarr;', # ⇊ / &ddarr; / &downdownarrows;
'\u2a77': '&eDDot;', # ⩷ / &eDDot; / &ddotseq;
'\u21c3': '&dharl;', # ⇃ / &dharl; / &LeftDownVector; / &downharpoonleft;
'\u21c2': '&dharr;', # ⇂ / &dharr; / &RightDownVector; / &downharpoonright;
'\u02d9': '&dot;', # ˙ / &dot; / &DiacriticalDot;
'\u222b': '&int;', # ∫ / &int; / &Integral;
'\u22c4': '&diam;', # ⋄ / &diam; / &diamond; / &Diamond;
'\u03b5': '&epsi;', # ε / &epsi; / &epsilon;
'\u03dd': '&gammad;', # ϝ / &gammad; / &digamma;
'\u22c7': '&divonx;', # ⋇ / &divonx; / &divideontimes;
'\u231e': '&dlcorn;', # ⌞ / &dlcorn; / &llcorner;
'\u2250': '&esdot;', # ≐ / &esdot; / &doteq; / &DotEqual;
'\u2251': '&eDot;', # ≑ / &eDot; / &doteqdot;
'\u2238': '&minusd;', # ∸ / &minusd; / &dotminus;
'\u2214': '&plusdo;', # ∔ / &plusdo; / &dotplus;
'\u22a1': '&sdotb;', # ⊡ / &sdotb; / &dotsquare;
'\u21d3': '&dArr;', # ⇓ / &dArr; / &Downarrow; / &DoubleDownArrow;
CHAR_ARROW_R_DOUBLE: '&rArr;', # ⇒ / &rArr; / &Implies; / &Rightarrow; / &DoubleRightArrow;
CHAR_ARROW_L_DOUBLE: '&lArr;', # ⇐ / &lArr; / &Leftarrow; / &DoubleLeftArrow;
CHAR_ARROW_LR_DOUBLE: '&iff;', # ⇔ / &iff; / &hArr; / &Leftrightarrow; / &DoubleLeftRightArrow;
CHAR_ARROW_L_LONG_DOUBLE: '&xlArr;', # ⟸ / &xlArr; / &Longleftarrow; / &DoubleLongLeftArrow;
CHAR_ARROW_R_LONG_DOUBLE: '&xrArr;', # ⟹ / &xrArr; / &Longrightarrow; / &DoubleLongRightArrow;
CHAR_ARROW_LR_LONG_DOUBLE: '&xhArr;', # ⟺ / &xhArr; / &Longleftrightarrow; / &DoubleLongLeftRightArrow;
'\u22a8': '&vDash;', # ⊨ / &vDash; / &DoubleRightTee;
'\u21d1': '&uArr;', # ⇑ / &uArr; / &Uparrow; / &DoubleUpArrow;
'\u2202': '&part;', # ∂ / &part; / &PartialD;
'\u21d5': '&vArr;', # ⇕ / &vArr; / &Updownarrow; / &DoubleUpDownArrow;
'\u2225': '&par;', # ∥ / &par; / &spar; / &parallel; / &shortparallel; / &DoubleVerticalBar;
'\u2193': '&darr;', # ↓ / &darr; / &downarrow; / &DownArrow; / &ShortDownArrow;
'\u21f5': '&duarr;', # ⇵ / &duarr; / &DownArrowUpArrow;
'\u21bd': '&lhard;', # ↽ / &lhard; /&DownLeftVector; / &leftharpoondown;
'\u21c1': '&rhard;', # ⇁ / &rhard; / &DownRightVector; / &rightharpoondown;
'\u22a4': '&top;', # / &top; / &DownTee;
'\u21a7': '&mapstodown;', # ↧ / &mapstodown; / &DownTeeArrow;
'\u2910': '&RBarr;', # ⤐ / &RBarr; / &drbkarow;
'\u231f': '&drcorn;', # ⌟ / &drcorn; / &lrcorner;
'\u25bf': '&dtri;', # ▿ / &dtri; / &triangledown;
'\u296f': '&duhar;', # ⥯ / &duhar; / &ReverseUpEquilibrium;
'\u2256': '&ecir;', # ≖ / &ecir; / &eqcirc;
'\u2255': '&ecolon;', # ≕ / &ecolon; / &eqcolon;
'\u2147': '&ee;', # / &ee; / &exponentiale; / &ExponentialE;
'\u2252': '&efDot;', # ≒ / &efDot; / &fallingdotseq;
'\u2a96': '&egs;', # ⪖ / &egs; / &eqslantgtr;
'\u2208': '&in;', # ∈ / &in; / &isin; / &isinv; / &Element;
'\u2a95': '&els;', # ⪕ / &els; / &eqslantless;
'\u2205': '&empty;', # ∅ / &empty; / &emptyv; / &emptyset; / &varnothing;
'\u03f5': '&epsiv;', # ϵ / &epsiv; / &varepsilon; / &straightepsilon;
'\u2242': '&esim;', # ≂ / &esim; / &eqsim; / &EqualTilde;
'\u225f': '&equest;', # ≟ / &equest; / &questeq;
'\u21cc': '&rlhar;', # ⇌ / &rlhar; / &Equilibrium; / &rightleftharpoons;
'\u2253': '&erDot;', # ≓ / &erDot; / &risingdotseq;
'\u2130': '&Escr;', # / &Escr; / &expectation;
'\u22d4': '&fork;', # ⋔ / &fork; / &pitchfork;
'\u2131': '&Fscr;', # / &Fscr; / &Fouriertrf;
'\u2322': '&frown;', # ⌢ / &frown; / &sfrown;
'\u2a86': '&gap;', # ⪆ / &gap; / &gtrapprox;
'\u2267': '&gE;', # ≧ / &gE; / &geqq; / &GreaterFullEqual;
'\u2a8c': '&gEl;', # ⪌ / &gEl; / &gtreqqless;
'\u22db': '&gel;', # ⋛ / &gel; / &gtreqless; / &GreaterEqualLess;
'\u2265': '&ge;', # ≥ / &ge; / &geq; / &GreaterEqual;
'\u2a7e': '&ges;', # ⩾ / &ges; / &geqslant; / &GreaterSlantEqual;
'\u22d9': '&Gg;', # ⋙ / &Gg; / &ggg;
'\u226b': '&gg;', # ≫ / &gg ;/ &Gt; / &NestedGreaterGreater;
'\u2277': '&gl;', # ≷ / &gl; / &gtrless; / &GreaterLess;
'\u2a8a': '&gnap;', # ⪊ / &gnap; / &gnapprox;
'\u2269': '&gnE;', # ≩ / &gnE; / &gneqq;
'\u2260': '&ne;', # ≠ / &ne; / &NotEqual;
'\u2a88': '&gne;', # ⪈ / &gne; / &gneq;
'\u2273': '&gsim;', # ≳ / &gsim; / &gtrsim; / &GreaterTilde;
'\u22d7': '&gtdot;', # ⋗ / &gtdot; / &gtrdot;
'\u200a': '&hairsp;', # / &hairsp; / &VeryThinSpace;
'\u210b': '&Hscr;', # / &Hscr; / &hamilt; / &HilbertSpace;
'\u21ad': '&harrw;', # ↭ / &harrw; / &leftrightsquigarrow;
'\u210f': '&hbar;', # ℏ / &hbar; / &planck; / &hslash; / &plankv;
'\u210c': '&Hfr;', # / &Hfr; / &Poincareplane;
'\u2925': '&searhk;', # ⤥ / &searhk; / &hksearow;
'\u2926': '&swarhk;', # ⤦ / &swarhk; / &hkswarow;
'\u21a9': '&larrhk;', # ↩ / &larrhk; / &hookleftarrow;
'\u21aa': '&rarrhk;', # ↪ / &rarrhk; / &hookrightarrow;
'\u210d': '&Hopf;', # / &Hopf; / &quaternions;
'\u2063': '&ic;', # / &ic; / &InvisibleComma;
'\u2111': '&Im;', # / &Im; / &Ifr; / &image; / &imagpart;
'\u2148': '&ii;', # / &ii; / &ImaginaryI;
'\u2a0c': '&qint;', # ⨌ / &qint; / &iiiint;
'\u222d': '&tint;', # ∭ / &tint; / &iiint;
'\u2110': '&Iscr;', # / &Iscr; / &imagline;
'\u0131': '&imath;', # ı / &imath; / &inodot;
'\u22ba': '&intcal;', # ⊺ / &intcal; / &intercal;
'\u2124': '&Zopf;', # / &Zopf; / &integers;
'\u2a3c': '&iprod;', # ⨼ / &iprod; / &intprod;
'\u2062': '&it;', # / &it; / &InvisibleTimes;
'\u03f0': '&kappav;', # ϰ / &kappav; / &varkappa;
'\u21da': '&lAarr;', # ⇚ / &lAarr; / &Lleftarrow;
'\u2112': '&Lscr;', # / &Lscr; / &lagran; / &Laplacetrf;
'\u27e8': '&lang;', # ⟨ / &lang; / &langle; / &LeftAngleBracket;
'\u2a85': '&lap;', # ⪅ / &lap; / &lessapprox;
'\u219e': '&Larr;', # ↞ / &Larr; / &twoheadleftarrow;
'\u21e4': '&larrb;', # ⇤ / &larrb; / &LeftArrowBar;
'\u21ab': '&larrlp;', # ↫ / &larrlp; / &looparrowleft;
'\u21a2': '&larrtl;', # ↢ / &larrtl; / &leftarrowtail;
'\u2266': '&lE;', # ≦ / &lE; / &leqq; / &LessFullEqual;
'\u2190': '&larr;', # ← / &larr; / &slarr; / &LeftArrow; / &leftarrow; / &ShortLeftArrow;
'\u21c6': '&lrarr;', # ⇆ / &lrarr; / &leftrightarrows; / &LeftArrowRightArrow;
'\u27e6': '&lobrk;', # ⟦ / &lobrk; / &LeftDoubleBracket;
'\u21bc': '&lharu;', # ↼ / &lharu; / &LeftVector; / &leftharpoonup;
'\u21c7': '&llarr;', # ⇇ / &llarr; / &leftleftarrows;
'\u2194': '&harr;', # ↔ / &harr; / &leftrightarrow; / &LeftRightArrow;
'\u21cb': '&lrhar;', # ⇋ / &lrhar; / &leftrightharpoons; / &ReverseEquilibrium;
'\u21a4': '&mapstoleft;', # ↤ / &mapstoleft; / &LeftTeeArrow;
'\u22cb': '&lthree;', # ⋋ / &lthree; / &leftthreetimes;
'\u22b2': '&vltri;', # ⊲ / &vltri; / &LeftTriangle; / &vartriangleleft;
'\u22b4': '&ltrie;', # ⊴ / &ltrie; / &trianglelefteq; / &LeftTriangleEqual;
'\u21bf': '&uharl;', # ↿ / &uharl; / &LeftUpVector; / &upharpoonleft;
'\u2308': '&lceil;', # ⌈ / &lceil; / &LeftCeiling;
'\u230a': '&lfloor;', # ⌊ / &lfloor; / &LeftFloor;
'\u2a8b': '&lEg;', # ⪋ / &lEg; / &lesseqqgtr;
'\u22da': '&leg;', # ⋚ / &leg; / &lesseqgtr; / &LessEqualGreater;
'\u2a7d': '&les;', # ⩽ / &les; / &leqslant; / &LessSlantEqual;
'\u22d6': '&ltdot;', # ⋖ / &ltdot; / &lessdot;
'\u2276': '&lg;', # ≶ / &lg; / &lessgtr; / &LessGreater;
'\u2272': '&lsim;', # ≲ / &lsim; / &lesssim; / &LessTilde;
'\u226a': '&ll;', # ≪ / &ll; / &Lt; / &NestedLessLess;
'\u23b0': '&lmoust;', # ⎰ / &lmoust; / &lmoustache;
'\u2a89': '&lnap;', # ⪉ / &lnap; / &lnapprox;
'\u2268': '&lnE;', # ≨ / &lnE; / &lneqq;
'\u2a87': '&lne;', # ⪇ / &lne; / &lneq;
CHAR_ARROW_L: '&xlarr;', # ⟵ / &xlarr; / &longleftarrow; / &LongLeftArrow;
CHAR_ARROW_R: '&xrarr;', # ⟶ / &xrarr; / &LongRightArrow; / &longrightarrow;
CHAR_ARROW_LR: '&xharr;', # ⟷ / &xharr; / &longleftrightarrow; / &LongLeftRightArrow;
'\u27fc': '&xmap;', # ⟼ / &xmap; / &longmapsto;
'\u21ac': '&rarrlp;', # ↬ / &rarrlp; / &looparrowright;
'\u201e': '&bdquo;', # „ / &bdquo; / &ldquor;
'\u2199': '&swarr;', # ↙ / &swarr; / &swarrow; / &LowerLeftArrow;
'\u2198': '&searr;', # ↘ / &searr; / &searrow; / &LowerRightArrow;
'\u21b0': '&lsh;', # ↰ / &Lsh; / &lsh;
'\u25c3': '&ltri;', # ◃ / &ltri; / &triangleleft;
'\u2720': '&malt;', # ✠ / &malt; / &maltese;
'\u21a6': '&map;', # ↦ / &map; / &mapsto; / &RightTeeArrow;
'\u21a5': '&mapstoup;', # ↥ / &mapstoup; / &UpTeeArrow;
'\u2133': '&Mscr;', # / &Mscr; / &phmmat; / &Mellintrf;
'\u2223': '&mid;', # / &mid; / &smid; / &shortmid; / &VerticalBar;
'\u2213': '&mp;', # ∓ / &mp; / &mnplus; / &MinusPlus;
CHAR_HELLIP: '&mldr;', # … / &mldr; / &hellip;
'\u22b8': '&mumap;', # ⊸ / &mumap; / &multimap;
'\u2249': '&nap;', # ≉ / &nap; / &napprox; / &NotTildeTilde;
'\u266e': '&natur;', # ♮ / &natur; / &natural;
'\u2115': '&Nopf;', # / &Nopf; / &naturals;
'\u2247': '&ncong;', # ≇ / &ncong; / &NotTildeFullEqual;
'\u2197': '&nearr;', # ↗ / &nearr; / &nearrow; / &UpperRightArrow;
'\u200b': '&ZeroWidthSpace;', # / &ZeroWidthSpace; / &NegativeThinSpace; / &NegativeMediumSpace;
# &NegativeThickSpace; / &NegativeVeryThinSpace;
'\u2262': '&nequiv;', # ≢ / &nequiv; / &NotCongruent;
'\u2928': '&toea;', # ⤨ / &toea; / &nesear;
'\u2203': '&exist;', # ∃ / &exist; / &Exists;
'\u2204': '&nexist;', # ∄ / &nexist; / &nexists; / &NotExists;
'\u2271': '&nge;', # ≱ / &nge; / &ngeq; / &NotGreaterEqual;
'\u2275': '&ngsim;', # ≵ / &ngsim; / &NotGreaterTilde;
'\u226f': '&ngt;', # ≯ / &ngt; / &ngtr; / &NotGreater;
'\u21ce': '&nhArr;', # ⇎ / &nhArr; / &nLeftrightarrow;
'\u21ae': '&nharr;', # ↮ / &nharr; / &nleftrightarrow;
'\u220b': '&ni;', # ∋ / &ni; / &niv; / &SuchThat; / &ReverseElement;
'\u21cd': '&nlArr;', # ⇍ / &nlArr; / &nLeftarrow;
'\u219a': '&nlarr;', # ↚ / &nlarr; / &nleftarrow;
'\u2270': '&nle;', # ≰ / &nle; / &nleq; / &NotLessEqual;
'\u226e': '&nlt;', # ≮ / &nlt; / &nless; / &NotLess;
'\u2274': '&nlsim;', # ≴ / &nlsim; / &NotLessTilde;
'\u22ea': '&nltri;', # ⋪ / &nltri; / &ntriangleleft; / &NotLeftTriangle;
'\u22ec': '&nltrie;', # ⋬ / &nltrie; / &ntrianglelefteq; / &NotLeftTriangleEqual;
'\u2224': '&nmid;', # ∤ / &nmid; / &nsmid; / &nshortmid; / &NotVerticalBar;
'\u2226': '&npar;', # ∦ / &npar; / &nspar; / &nparallel; / &nshortparallel; / &NotDoubleVerticalBar;
'\u2209': '&notin;', # ∉ / &notin; / &notinva; / &NotElement;
'\u2279': '&ntgl;', # ≹ / &ntgl; / &NotGreaterLess;
'\u2278': '&ntlg;', # ≸ / &ntlg; / &NotLessGreater;
'\u220c': '&notni;', # ∌ / &notni; / &notniva; / &NotReverseElement;
'\u2280': '&npr;', # ⊀ / &npr; / &nprec; / &NotPrecedes;
'\u22e0': '&nprcue;', # ⋠ / &nprcue; / &NotPrecedesSlantEqual;
'\u22eb': '&nrtri;', # ⋫ / &nrtri; / &ntriangleright; / &NotRightTriangle;
'\u22ed': '&nrtrie;', # ⋭ / &nrtrie; / &ntrianglerighteq; / &NotRightTriangleEqual;
'\u22e2': '&nsqsube;', # ⋢ / &nsqsube; / &NotSquareSubsetEqual;
'\u22e3': '&nsqsupe;', # ⋣ / &nsqsupe; / &NotSquareSupersetEqual;
'\u2288': '&nsube;', # ⊈ / &nsube; / &nsubseteq; / &NotSubsetEqual;
'\u2281': '&nsc;', # ⊁ / &nsc; / &nsucc; / &NotSucceeds;
'\u22e1': '&nsccue;', # ⋡ / &nsccue; / &NotSucceedsSlantEqual;
'\u2289': '&nsupe;', # ⊉ / &nsupe; / &nsupseteq; / &NotSupersetEqual;
'\u2241': '&nsim;', # ≁ / &nsim; / &NotTilde;
'\u2244': '&nsime;', # ≄ / &nsime; / &nsimeq; / &NotTildeEqual;
'\u21cf': '&nrArr;', # ⇏ / &nrArr; / &nRightarrow;
'\u219b': '&nrarr;', # ↛ / &nrarr; / &nrightarrow;
'\u2196': '&nwarr;', # ↖ / &nwarr; / &nwarrow; / &UpperLeftArrow;
'\u2134': '&oscr;', # / &oscr; / &order; / &orderof;
'\u203e': '&oline;', # ̄ / &oline; / &OverBar;
'\u23b4': '&tbrk;', # ⎴ / &tbrk; / &OverBracket;
'\u03d6': '&piv;', # ϖ / &piv; / &varpi;
'\u03d5': '&phiv;', # ϕ / &phiv; / &varphi; / &straightphi;
'\u2665': '&hearts;', # ♥ / &hearts; / &heartsuit; /
'\u2119': '&Popf;', # / &Popf; / &primes;
'\u227a': '&pr;', # ≺ / &pr; / &prec; / &Precedes;
'\u2ab7': '&prap;', # ⪷ / &prap; / &precapprox;
'\u227c': '&prcue;', # ≼ / &prcue; / &preccurlyeq; / &PrecedesSlantEqual;
'\u2aaf': '&pre;', # ⪯ / &pre; / &preceq; / &PrecedesEqual;
'\u227e': '&prsim;', # ≾ / &prsim; / &precsim; / &PrecedesTilde;
'\u2ab9': '&prnap;', # ⪹ / &prnap; / &precnapprox;
'\u2ab5': '&prnE;', # ⪵ / &prnE; / &precneqq;
'\u22e8': '&prnsim;', # ⋨ / &prnsim; / &precnsim;
'\u220f': '&prod;', # ∏ / &prod; / &Product;
'\u221d': '&prop;', # ∝ / &prop; / &vprop; / &propto; / &varpropto; / &Proportional;
'\u211a': '&Qopf;', # / &Qopf; / &rationals;
'\u21db': '&rAarr;', # ⇛ / &rAarr; / &Rrightarrow;
'\u27e9': '&rang;', # ⟩ / &rang; / &rangle; / &RightAngleBracket;
'\u21a0': '&Rarr;', # ↠ / &Rarr; / &twoheadrightarrow;
'\u21e5': '&rarrb;', # ⇥ / &rarrb; / &RightArrowBar;
'\u21a3': '&rarrtl;', # ↣ / &rarrtl; / &rightarrowtail;
'\u2309': '&rceil;', # ⌉ / &rceil; / &RightCeiling;
'\u219d': '&rarrw;', # ↝ / &rarrw; / &rightsquigarrow;
'\u03a9': '&ohm;', # Ω / &ohm; / &Omega;
'\u211c': '&Re;', # / &real; / &Re; / &Rfr; / &realpart;
'\u211b': '&Rscr;', # / &Rscr; / &realine;
'\u211d': '&Ropf;', # / &Ropf; / &reals;
'\u21c0': '&rharu;', # ⇀ / &rharu; / &RightVector; / &rightharpoonup;
'\u03f1': '&rhov;', # ϱ / &rhov; / &varrho;
'\u2192': '&rarr;', # → / &rarr; / &srarr; / &rightarrow; / &RightArrow; / &ShortRightArrow;
'\u21c4': '&rlarr;', # ⇄ / &rlarr; / &rightleftarrows; / &RightArrowLeftArrow;
'\u27e7': '&robrk;', # ⟧ / &robrk; / &RightDoubleBracket;
'\u230b': '&rfloor;', # ⌋ / &rfloor; / &RightFloor;
'\u21c9': '&rrarr;', # ⇉ / &rrarr; / &rightrightarrows;
'\u22a2': '&vdash;', # ⊢ / &vdash; / &RightTee;
'\u22cc': '&rthree;', # ⋌ / &rthree; / &rightthreetimes;
'\u22b3': '&vrtri;', # ⊳ / &vrtri; / &RightTriangle; / &vartriangleright;
'\u22b5': '&rtrie;', # ⊵ / &rtrie; / &trianglerighteq; / &RightTriangleEqual;
'\u21be': '&uharr;', # ↾ / &uharr; / &RightUpVector; / &upharpoonright;
'\u23b1': '&rmoust;', # ⎱ / &rmoust; / &rmoustache;
'\u201c': '&ldquo;', # “ / &ldquo; / &OpenCurlyDoubleQuote;
'\u2018': '&lsquo;', # / &lsquo; / &OpenCurlyQuote;
'\u21b1': '&rsh;', # ↱ / &rsh; / &Rsh;
'\u25b9': '&rtri;', # ▹ / &rtri; / &triangleright;
'\u227b': '&sc;', # ≻ / &sc; / &succ; / &Succeeds;
'\u2ab8': '&scap;', # ⪸ / &scap; / &succapprox;
'\u227d': '&sccue;', # ≽ / &sccue; / &succcurlyeq; / &SucceedsSlantEqual;
'\u2ab0': '&sce;', # ⪰ / &sce; / &succeq; / &SucceedsEqual;
'\u2aba': '&scnap;', # ⪺ / &scnap; / &succnapprox;
'\u2ab6': '&scnE;', # ⪶ / &scnE; / &succneqq;
'\u22e9': '&scnsim;', # ⋩ / &scnsim; / &succnsim;
'\u227f': '&scsim;', # ≿ / &scsim; / &succsim; / &SucceedsTilde;
'\u2929': '&tosa;', # ⤩ / &tosa; / &seswar;
'\u03c2': '&sigmaf;', # ς / &sigmaf; / &sigmav; / &varsigma;
'\u2243': '&sime;', # ≃ / &sime; / &simeq; / &TildeEqual;
'\u2323': '&smile;', # ⌣ / &smile; / &ssmile;
'\u2660': '&spades;', # ♠ / &spades; / &spadesuit; /
'\u2293': '&sqcap;', # ⊓ / &sqcap; / &SquareIntersection;
'\u2294': '&sqcup;', # ⊔ / &sqcup; / &SquareUnion;
'\u221a': '&Sqrt;', # √ / &Sqrt; / &radic;
'\u228f': '&sqsub;', # ⊏ / &sqsub; / &sqsubset; / &SquareSubset;
'\u2291': '&sqsube;', # ⊑ / &sqsube; / &sqsubseteq; / &SquareSubsetEqual;
'\u2290': '&sqsup;', # ⊐ / &sqsup; / &sqsupset; / &SquareSuperset;
'\u2292': '&sqsupe;', # ⊒ / &sqsupe; / &sqsupseteq; / &SquareSupersetEqual;
'\u25a1': '&squ;', # □ / &squ; / &Square; / &square;
'\u22c6': '&Star;', # ⋆ / &Star; / &sstarf;
'\u22d0': '&Sub;', # ⋐ / &Sub; / &Subset;
'\u2282': '&sub;', # ⊂ / &sub; / &subset;
'\u2ac5': '&subE;', # ⫅ / &subE; / &subseteqq;
'\u2acb': '&subnE;', # ⫋ / &subnE; / &subsetneqq;
'\u228a': '&subne;', # ⊊ / &subne; / &subsetneq;
'\u2286': '&sube;', # ⊆ / &sube; / &subseteq; / &SubsetEqual;
'\u2211': '&sum;', # ∑ / &sum; / &Sum;
'\u22d1': '&Sup;', # ⋑ / &Sup; / &Supset;
'\u2ac6': '&supE;', # ⫆ / &supE; / &supseteqq;
'\u2283': '&sup;', # ⊃ / &sup; / &supset; / &Superset;
'\u2287': '&supe;', # ⊇ / &supe; / &supseteq; / &SupersetEqual;
'\u2acc': '&supnE;', # ⫌ / &supnE; / &supsetneqq;
'\u228b': '&supne;', # ⊋ / &supne; / &supsetneq;
'\u223c': '&sim;', # / &sim; / &Tilde; / &thksim; / &thicksim;
'\u2245': '&cong;', # ≅ / &cong; / &TildeFullEqual;
'\u20db': '&tdot;', # ⃛ / &tdot; / &TripleDot;
'\u2234': '&there4;', # ∴ / &there4; / &Therefore; / &therefore;
'\u03d1': '&thetav;', # ϑ / &thetav; / &vartheta; / &thetasym;
CHAR_TRADE: '&trade;', # ™ / &trade; / &TRADE;
'\u25b5': '&utri;', # ▵ / &utri; / &triangle;
'\u225c': '&trie;', # ≜ / &trie; / &triangleq;
'\u21c5': '&udarr;', # ⇅ / &udarr; / &UpArrowDownArrow;
'\u296e': '&udhar;', # ⥮ / &udhar; / &UpEquilibrium;
'\u231c': '&ulcorn;', # ⌜ / &ulcorn; / &ulcorner;
'\u03d2': '&Upsi;', # ϒ / &Upsi; / &upsih;
'\u03c5': '&upsi;', # υ / &upsi; / &upsilon;
'\u228e': '&uplus;', # ⊎ / &uplus; / &UnionPlus;
'\u2195': '&varr;', # ↕ / &varr; / &updownarrow; / &UpDownArrow;
'\u2191': '&uarr;', # ↑ / &uarr; / &uparrow; / &UpArrow; / &ShortUpArrow;
'\u21c8': '&uuarr;', # ⇈ / &uuarr; / &upuparrows;
'\u231d': '&urcorn;', # ⌝ / &urcorn; / &urcorner;
'\u2016': '&Vert;', # ‖ / &Vert; / &Verbar;
'\u2228': '&or;', # / &or; / &vee;
CHAR_THIN_SP: '&thinsp;', # / &thinsp; / &ThinSpace;
'\u2240': '&wr;', # ≀ / &wr; / &wreath; / &VerticalTilde;
'\u2128': '&Zfr;', # / &Zfr; / &zeetrf;
'\u2118': '&wp;', # ℘ / &wp; / &weierp;
}
# === Динамическая генерация карт преобразования ===
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
# === КОНСТАНТЫ ДЛЯ ЕДИНИЦ ИЗМЕРЕНИЯ ===
# ТОЛЬКО АТОМАРНЫЕ единицы измерения: 'г', 'м', 'с', 'км', 'кв', 'куб', 'ч' и так далее.
# Никаких сложных и составных, типа: 'кв.м.', 'км/ч' или "до н.э." ...
# Пост-позиционные (можно ставить точку после, но не обязательно) (км, г., с.)
DEFAULT_POST_UNITS = [
# Русские
# --- Время и эпохи ---
'гг', 'г.', 'в.', 'вв', 'н', 'э', 'сек', 'с.', 'мин', 'ч',
# --- Масса и объём ---
'кг', 'мг', 'ц', 'т', 'л', 'мл',
# --- Размеры ---
'кв', 'куб', 'мм', 'см', 'м', 'км', 'сот', 'га', 'м²', 'м³',
# --- Финансы и количество ---
'руб', 'коп', 'тыс', 'млн', 'млрд', 'трлн', 'трлрд', 'шт', 'об', 'ящ', 'уп', 'кор', 'пар', 'комп',
# --- Издательское дело ---
'пп', 'стр', 'рис', 'гр', 'табл', 'гл', 'п', 'пт', 'гл', 'том', 'т.', 'кн', 'илл', 'ред', 'изд', 'пер',
# --- Физические и технические ---
'дБ', 'Вт', 'кВт', 'МВт', 'ГВт', 'А', 'В', 'Ом', 'Па', 'кПа', 'МПа', 'Бар', 'кБар', 'Гц', 'кГц', 'МГц', 'ГГц',
'рад', 'К', '°C', '°F', '%', 'мкм', 'нм', 'А°', 'эВ', 'Дж', 'кДж', 'МДж', 'пкФ', 'нФ', 'мкФ', 'мФ', 'Ф',
'Гн', 'мГн', 'мкГн', 'Тл', 'Гс', 'эрг', 'бод', 'бит', 'байт', 'Кб', 'Мб', 'Гб', 'Тб', 'Пб', 'Эб', 'кал', 'ккал',
# Английские
# --- Издательское дело ---
'pp', 'p', 'para', 'sect', 'fig', 'vol', 'ed', 'rev', 'dpi',
# --- Имперские и американские единицы ---
'in', 'ft', 'yd', 'mi', 'oz', 'lb', 'st', 'pt', 'qt', 'gal', 'mph', 'rpm', 'hp', 'psi', 'cal',
]
# Пред-позиционные (№ 5, $ 10)
DEFAULT_PRE_UNITS = ['', '$', '', '£', '', '#', '§', '¤', '', '', '', '', '', '', '', '', '', '',
'ГОСТ', 'ТУ', 'ИСО', 'DIN', 'ASTM', 'EN', 'IEC', 'IEEE'] # технические стандарты перед числом работают как единицы измерения
# Операторы, которые могут стоять между единицами измерения (км/ч)
# Сложение и вычитание здесь намеренно отсутствуют.
UNIT_MATH_OPERATORS = ['/', '*', '×', CHAR_MIDDOT, '÷']
# === КОНСТАНТЫ ДЛЯ ФИНАЛЬНЫХ СОКРАЩЕНИЙ ===
# Эти сокращения (обычно в конце фразы) будут "склеены" тонкой шпацией, а перед ними будет поставлен неразрывный пробел.
# Важно, чтобы многосложные сокращения (типа "и т. д.") были в списке с разделителем пробелом (иначе мы не сможем их найти).
ABBR_COMMON_FINAL = [
# 'т. д.', 'т. п.', 'др.', 'пр.',
# УБРАНЫ из-за неоднозначности: др. -- "другой", "доктор", "драм" / пр. -- "прочие", "профессор", "проект", "проезд" ...
'т. д.', 'т. п.',
]
ABBR_COMMON_PREPOSITION = [
'т. е.', 'т. к.', 'т. о.', 'т. ч.',
'и. о.', 'ио', 'вр. и. о.', 'врио',
'тов.', 'г-н.', 'г-жа.', 'им.',
'д. о. с.', 'д. о. н.', 'д. м. н.', 'к. т. д.', 'к. т. п.',
'АО', 'ООО', 'ЗАО', 'ПАО', 'НКО', 'ОАО', 'ФГУП', 'НИИ', 'ПБОЮЛ', 'ИП',
]
# === КОНСТАНТЫ ДЛЯ HTML-ТЕГОВ, ВНУТРИ КОТОРЫХ НЕ НАДО ТИПОГРАФИРОВАТЬ ===
PROTECTED_HTML_TAGS = ['style', 'script', 'pre', 'code', 'kbd', 'samp', 'math']
# === КОНСТАНТЫ ДЛЯ ВИСЯЧЕЙ ТИПОГРАФИКИ ===
# 1. Набор символов, которые могут "висеть" слева
HANGING_PUNCTUATION_LEFT_CHARS = frozenset([
CHAR_RU_QUOT1_OPEN, # «
CHAR_EN_QUOT1_OPEN, # “
'(', '[', '{',
])
# 2. Набор символов, которые могут "висеть" справа
HANGING_PUNCTUATION_RIGHT_CHARS = frozenset([
CHAR_RU_QUOT1_CLOSE, # »
CHAR_EN_QUOT1_CLOSE, # ”
')', ']', '}',
'.', ',', ':',
])
# 3. Словарь, сопоставляющий символ с его CSS-классом
HANGING_PUNCTUATION_CLASSES = {
# Левая пунктуация: все классы начинаются с 'etp-l'
CHAR_RU_QUOT1_OPEN: 'etp-laquo',
CHAR_EN_QUOT1_OPEN: 'etp-ldquo',
'(': 'etp-lpar',
'[': 'etp-lsqb',
'{': 'etp-lcub',
# Правая пунктуация: все классы начинаются с 'etp-r'
CHAR_RU_QUOT1_CLOSE: 'etp-raquo',
CHAR_EN_QUOT1_CLOSE: 'etp-rdquo',
')': 'etp-rpar',
']': 'etp-rsqb',
'}': 'etp-rcub',
'.': 'etp-r-dot',
',': 'etp-r-comma',
':': 'etp-r-colon',
}