etpgrf

0

Описание

etpgrf — effortless typography for web / единая типографика для веба

Языки

  • Python100%
README.md

etpgrf — единый типограф для веба / effortless typography for web

PyPI version Python Version License

Типограф для веба

Экранная типографика для веба — способствует повышению читабельности текста в интернете, приближая его к печатной типографике.

Репозитории / Repositories

Исходный код доступен на нескольких площадках:

  • Gitea (Основной self-hosted)
  • GitHub (Главное зеркало)
  • GitVerse (Зеркало на GitVerse)

Демонстрация / Demo

Работа etpgrf-типографа представлена по адресу: typograph.cube2.ru. Подготовьте верстку вашего текста, сайтов, статей и постов к публикации в интернете за один клик.

Установка

Быстрый старт

Кодировки и html-мнемоники

Внутри типографа используется кодировка UTF-8. Но при использовании может быть другие кодировки (например, для русскоязычных текстов все ещё могут использовать Windows-1251). При таких кодировках, для отображения в браузерах некоторых специфических символов (например, кавычек, тире, стрелочек, математических символов) используют html-мнемоники (например,

—
для длинного тире,
«
для открывающей кавычки-ёлочки и т.д.).

Библиотека etpgrf имеет три режима работы с кодировками:

  • Режим
    unicode
    — весь вывод осуществляется в кодировке UTF-8. ВЕСЬ! Включая невидимые символы, типа неразрывных и нулевых пробелов, мягких переносов и т.д. Это не всегда удобно зато типографированый текст (строки) будет максимально компактен и занимать меньше места в памяти. В этом режиме в html-мнемоники преобразуются только опасные символы:
    • <
      — знак меньше
      <
      ;
    • &gt;
      — знак больше
      >
      ;
    • &amp;
      — амперсанд
      &
      ;
    • &quot;
      — двойные кавычки
      "
      ;
    • &apos;
      — одинарные кавычки (апостроф)
      '
      .
  • Режим
    mixed
    — вывод осуществляется в кодировке UTF-8, но наиболее критичные символы заменяются на html-мнемоники. Они невидимы или неотличимы друг от друга на экране:
    • &shy;
      — мягкий перенос (Soft Hyphen);
    • &nbsp;
      — неразрывный пробел (Non-Breaking Space);
    • &ensp;
      — полужирный пробел (En Space)
    • &emsp;
      — широкий пробел (Em Space)
    • &numsp;
      — цифровой пробел;
    • &puncsp;
      — пунктуационный пробел;
    • &thinsp;
      — межсимвольный пробел;
    • &hairsp;
      — пробел "толщина волоса" (Hair Space);
    • &NegativeThinSpace;
      — негативный пробел (Negative Space);
    • &zwj;
      — пробел нулевой ширины (без объединения) (Zero Width Non-Joiner);
    • &zwnj;
      — нулевая ширина (с объединением) (Zero Width Joiner);
    • &lrm;
      — изменение направления текста на слева-направо (Left-to-Right Mark);
    • &rlm;
      — изменение направления текста на направо-налево (Right-to-Left Mark);
    • &dash;
      — дефис (Hyphen);
    • &MediumSpace;
      — средний пробел (Medium Mathematical Space);
    • &NoBreak;
      — неразрывный пробел (No-Break Space);
    • &InvisibleTimes;
      — невидимый знак умножения (Invisible Times) для семантической разметки математических выражений;
    • &InvisibleComma;
      — невидимая запятая (Invisible Comma) для семантической разметки математических выражений.
  • Режим
    mnemonic
    — применяются все возможные html-мнемоники (кроме русских букв) и символов первой половины ASCII (плюс, минус, знак равенства, знаки препинания и т.д.).

Переключение режимов осуществляется с помощью параметра

mode
при конфигурировании типографа:

ВАЖНО:

  1. Если в тексте уже есть html-мнемоники, они будут преобразованы в unicode, и после обработки типографом будут заменены на html-мнемоники, соответствующие текущему режиму работы типографа.
  2. Некоторым символам соответствуют несколько html-мнемоник. Например,
    (стрелочка влево) может кодироваться как
    &rarr;
    ,
    &srarr;
    ,
    &rightarrow
    ,
    &RightArrow;
    и
    &ShortRightArrow;
    . Типограф будет использовать самое короткое из них (для компактности), а значит:
    • если в исходном тексте были html-мнемоники, то они будут заменены на более короткие;
    • если html-мнемоники использовались как элементы семантической разметки (например, для математических выражений), то после замены на более короткие html-мнемоники, текст может потерять такую семантику. Например
      F = A &Intersection; B
      (F = A ⋂ B) будет преобразовано в
      F = A &xcap; B
      ;
  3. Мнемоники для русских букв не используются в типографе. Все мнемоники русских букв будут преобразованы в русские буквы и останутся в тексте в виде русских букв.
  4. Все исходные html-мнемоники, которые превращаются в два unicode-символа будут превращены обратно в мнемоники каждый как отдельный символ. Например, множество собственное другого подмножества
    &varsubsetneq;
    в unicode отображается двумя символами
    \u228a\ufe00
    и превратится в
    &subne;\ufe00
    . Символ
    \ufe00
    — это невидимый символ, селектор варианта начертания (Variant Selector), который изменяет начертание предыдущего символа и для него нет html-мнемоники. К счастью, в стандарте таких мнемоник (превращающихся в два символа) исчезающе мало и они крайне редко применяются в тексте, поэтому это не должно вызывать проблем.

Переносы слов

Обычно в основе переносов слов лежит фонетический принцип — деление по слогам и морфемный принцип — деление по морфемам (приставки, корни, суффиксы, окончания). В типографе etpgrf реализован эвристический подход к переносу слов, основанный на фонетических правилах. Он не является строгим и не учитывает все нюансы языка, но обеспечивает вполне приемлемое качество для большинства случаев. Особенно если "неразрывные" блоки задать достаточно длинными (и именно это и требуется от хорошего типографа, ведь перенос трех-четырех букв слова на новую строку почти не улучшит читабельность и внешний вид текста).

Настройки по умолчанию для переноса слов (в

etpgrf.defaults
):

  • Длина слова которое не подлежит переносам (
    MAX_UNHYPHENATED_LEN
    ) — 12 символов.
  • Длина части слова, которое недопустимо переносить или оставлять на строке ("хвост", "сироты") (
    MIN_TAIL_LEN
    ) — 5 символов

Управление этими параметрами осуществляется через переопределение. Например:

Или через параметры конфигурации переносов типографа:

Результат обработки текста с переносами будет выглядеть так:

Предлоги, союзы и частицы

Правилом хорошего тона в любой типографике считается, когда короткие слова, такие как предлоги, союзы и частицы, не остаются в конце строки в одиночестве («висеть»). Это ухудшает читаемость.

Типограф

etpgrf
автоматически решает эту проблему, «приклеивая» такие слова к последующему слову с помощью неразрывного пробела (
&nbsp;
).

  • в доме
    в&nbsp;доме
  • и сказал
    и&nbsp;сказал

Это правило работает для коротких слов в русском, старорусском и английском языках.

Кроме того, обрабатываются и постпозитивные частицы (например,

ли
,
же
,
бы
), которые, наоборот, для улучшения читабельности, «приклеиваются» к предыдущему слову:

  • сказал бы
    сказал&nbsp;бы

Кавычки

В текстах кавычки бывают двух видов: «ёлочки» (для русского языка) и “лапки” (для английского языка). В типографе реализована автоматическая замена кавычек на соответствующие типографские символы в зависимости от языка текста.

Большинство типографов при обработке кавычек находят парные (и определяют вложенность). В etpgrf же реализован другой подход. Он ищет и обрабатывает кавычки, которые находятся рядом со словами. То есть какие-то буквы следуют слева или справа от кавычки.

Преобразование рядом с цифрами (например, когда обозначаются дюймы (

17"
) или секунды (
3' 25"
)) не производится. Также не обрабатываются кавычки, окруженные пробелами. Все кавычки, которые в исходном тексте уже были оформлены в виде «ёлочек» или “лапок” — тоже не обрабатываются.

ВАЖНО1: По правилам орфографии перед закрывающей кавычкой разрешены только определенные знаки препинания: вопросительный (?), восклицательный (!) знаки и многоточие (…). Такие конструкции используются для цитат. Это учтено в etpgrf, и кавычки будут обработаны:

Она воскликнула: "Какая красота!"
будет преобразовано в
Она воскликнула: «Какая красота!»
. По правилам пунктуации, точка
.
перед закрывающей кавычкой не допускается, но существуют исключения, когда перед кавычкой стоит сокращение (например,
т. д.
,
и т. п.
). В таких случаях точка сохраняется:
Он сказал: "Это важно, и т. д."
Он сказал: «Это важно, и т. д.»
. Типограф допускает точку перед закрывающей кавычкой.

ВАЖНО2: Если в настройке типографа указано несколько языков (

langs='ru+en'
), то кавычки будут преобразованы по правилам для языка, который идет первым в списке. Например, для
langs='ru+en'
кавычки будут преобразованы в «ёлочки».

Если при типорафировании преобразование не требуется, то можно обработку кавычек можно отключить с помощью параметра

quotes=False
:

Компоновка (тире, диапазоны, инициалы, единицы измерения, сокращения и т.п.)

После того как псевдографика заменена на правильные символы, в дело вступает модуль компоновки (layout), который отвечает за расстановку неразрывных и тонких пробелов. Он применяет несколько важных правил для улучшения читаемости.

Тире

По правилам русской типографики, длинное тире (

) должно отбиваться пробелами от соседних слов. Чтобы тире не "повисло" в начале строки и визуально не смешивалось с диалогами, etpgrf заменяет пробел перед тире на неразрывный (
&nbsp;
).

  • слово — слово
    слово&nbsp;— слово

В английской типографике, наоборот, тире пишется слитно. Типограф учитывает это при указании языка

langs='en'
.

  • word — word
    word—word

Если минус или диапазон стоят между числами (арабскими или римскими), то это считается обозначением числового диапазона (или отрицательным числом, или математическим выражением), и никаких изменений не производится. Неважно есть пробелы вокруг тире/минуса или нет. Если между цифрами тире, то это тоже считается диапазоном и неразрывные пробелы не ставятся:

1941 — 1945
1941 — 1945
,
-10 — -5
-10 — -5
,

Если минус стоит перед числом (например,

-5
), то это считается отрицательным числом, и перед ним ставится неразрывный пробел:
от -5 до +5
от&nbsp;-5 до +5
.

Инициалы и акронимы

Чтобы инициалы не отрывались друг от друга и от фамилии при переносе строки, типограф расставляет между ними специальные пробелы:

  • Неразрывный пробел (
    &nbsp;
    ) ставится между фамилией и инициалом/инициалами (
    А. Пушкин
    А.&nbsp;Пушкин
    ). Неважно стоят ли инициалы перед фамилией или после неё. Важно наличие точки и буквы (инициала), написанного с заглавной буквы.
  • Тонкая шпация (
    &thinsp;
    ) ставится между самими инициалами, если они написаны слитно, для улучшения внешнего вида (
    Пушкин А. С.
    Пушкин&nbsp;А.&thinsp;С.
    ). Число инициалов не ограничено (
    J.R.R. Tolkien
    J.&thinsp;R.&thinsp;R.&nbsp;Tolkien
    ), наличие или отсутствие пробелов между инициалами в исходном тексте неважно.
  • Акронимы, написанные через точку (не слитно, например, Н.Л.О.), разделяются так же, как инициалы, через тонкую шпацию (
    Н.Л.О.
    Н.&thinsp;Л.&thinsp;О.
    ). Наличие или отсутствие пробелов между буквами в исходном тексте неважно.

Это правило может давать побочные эффекты (в частности, тонкая шпация не является неразрывным пробелом, и в длинных акронимах может привести к разрыву строки). Поэтому его обработку можно отключить с помощью параметра

process_initials_and_acronyms
:

Единицы измерения

Типограф предотвращает отрыв единиц измерения от чисел, ставя между ним и предшествующей цифрой неразрывный пробел. Это работает для:

  • Простых единиц:
    100 км.
    100&nbsp;км.
    ,
    -5 °C
    -5&nbsp;°C
    '
  • Составных единиц:
    120 кв.м.
    120&nbsp;кв.&thinsp;м.
    ,
    50 тыс. руб.
    50&nbsp;тыс.&thinsp;руб.
    Пробелы (есть они или нет) между составными частями единицы изменения не важны. Между частями составной единицы измерения ставится тонкая шпация (
    &thinsp;
    ).
  • Единиц с предлогом:
    № 5
    №&nbsp;5
    ,
    § 7
    §&nbsp;7
    ,
    $ 100
    $&nbsp;100
  • Чисел, записанных и арабскими, и римскими цифрами:
    V в.н.э.
    V&nbsp;в.&thinsp;н.&thinsp;э.
  • Если между единицами изменений есть математические символы (например, умножение или деление):
    10 км / ч
    10&nbsp;км/ч
    (неважно есть пробелы вокруг
    /
    или нет). Распознаются и другие символы:
    ·
    ,
    *
    ,
    ×
    ,
    ÷
    .
  • Символы
    x
    ,
    X
    ,
    х
    ,
    Х
    , стоящие между двумя числами, заменяются на знак умножения
    ×
    , чтобы выражения вида
    100x100
    или
    100 х 100
    корректно обрабатывались и выглядели типографски правильными (
    100×100
    или
    100&nbsp;×&nbsp;100
    ).

Библиотека "знает" множество стандартных единиц для русского и английского языков. Но не все. Вы можете расширить этот список, передав свои кастомные единицы через параметр

process_units
:

Если нужно отключить распознавание и обработку единиц измерения:

Сокращения

Типограф также обрабатывает распространённые русскоязычные сокращения, чтобы они корректно отображались и не разрывались при переносе строк. Правила делятся на два типа:

  • Финальные сокращения. Сокращения, которые обычно стоят в конце фразы (например, и т. д., и т. п.), обрабатываются особым образом: их части «склеиваются» тонкой шпацией, а перед всей конструкцией ставится неразрывный пробел, чтобы она не «повисла» на новой строке.
    ...и так далее, и т. д.
    ...и так далее, и&nbsp;т.&thinsp;д.
    Это правило работает независимо от того, как сокращение было написано в исходном тексте (т.д. или т. д.).
  • Препозиционные сокращения. Сокращения, которые стоят перед другим словом (например, и. о. директора, т. е. сказать), также «склеиваются» внутри, но неразрывный пробел ставится после них, чтобы привязать их к последующему слову.
    Назначить и. о. директора
    Назначить и.&thinsp;о.&nbsp;директора

Библиотека знает небольшой набор самых распространённых сокращений. Но не все, а некоторые принципиально невозможны к обработке. Например, сокращение

пр.
может оказаться как финальным (в значении «и так далее»), так и препозиционным (в значении «профессор» или «проспект»). Так же типограф не обрабатывает сокращения, связанные с адресами (ул., д., кв., пл., наб. ...) так как они могут быть как финальными, так и препозиционными.

Висячая типографика

Висячая типографика — это приём из классической вёрстки, когда некоторые знаки препинания (кавычки, скобки, иногда тире и маркеры списков) выносятся на левое (и иногда и по правому) поле текста. Это создаёт идеально ровный край не по формальным границам знаков, а по оптическому краю — по первым буквам строк. Текст выглядит гораздо аккуратнее и профессиональнее.

Интернет публикации (да и бумажные издания) практически игнорируют висячую типографику. Но иногда это отличный инструмент для акцентной типографики: крупные заголовки, цитаты, выносы, подписи к иллюстрациям, оформленные с помощью висячей типографики, выглядят гораздо эффектнее. В современном CSS есть свойство

hanging-punctuation
, которое должно делать всё это автоматически. Но на сегодняшний день (конец 2025) его поддержка браузерами практически нулевая (кроме Safari), поэтому на него полагаться нельзя. Поэтому в типографе etpgrf реализация висячей типографики осуществляется через оборачивание висячих символов в специальные HTML-теги с CSS-классами.

Оборачивая "висячий" символ или слово в

<span>
и применяя к нему, например, отрицательный
text-indent
или
margin-left
(
<span style="margin-left:-0.44em">&laquo;</span>
), мы можем сместить сам символ, но нужно ещё и сохранить расстояние до соседнего слова. Поэтому типограф оборачивает не только сам висячий символ, но и ближайшее слово (до пробела или границы узла), а также при необходимости, окружающий пробел. Сама визуальная компенсация оформляется через отрицательные
margin
/
padding
в CSS-классах — никаких
position:absolute
, чтобы не нарушать поток текста. Учтите, что набор символов, попадающих в
HANGING_PUNCTUATION_SPACE_CHARS
, помимо обычного пробела включает табуляции, переводы строки и множество тонких/математических пробелов. Именно поэтому компенсирующие обёртки иногда захватывают символы на границе узлов или переносов и сохраняют корректный визуальный зазор.

По умолчанию эта функция висячей типографики отключена. Чтобы её включить, нужно задать параметр

hanging_punctuation
при конфигурировании типографа (по умолчанию
hanging_punctuation=None
):

Параметр

hanging_punctuation
может принимать следующие значения:

  • None
    или
    False
    — функция отключена (по умолчанию);
  • 'left'
    или
    True
    — включены только левые висячие символы (выравнивание по левому краю);
  • 'right'
    — включены только правые висячие символы (выравнивание по правому краю).

Значения

'both'
недоступно, потому что совмещение левой и правой выверки одновременно приводит к конфликтам с размещением пробелов и делает невозможным контролировать визуальное выравнивание (см. блок про
text-justify
).

Также через

hanging_punctuation
можно задать список тегов, внутри которых висячая типографика будет применяться (всегда в режиме
'both'
). Это не рекомендованный способ, потому что он предполагает знание структуры HTML и неизбежно выпадает из общей логики вложенности и пробельных узлов.

Как работает оборачивание

Процессор висячей типографики запускается после всех текстовых преобразований и работает с деревом BeautifulSoup. Он ищет последовательности «пробел + висячий символ» для левого выравнивания и «висячий символ + пробел» для правого, чтобы обернуть нужные фрагменты в пары

<span>
и не допустить «сиротства» символов. Порядок действий можно описать так:

  • Для
    hanging_punctuation='left'
    :
    • если символ стоит в начале текстового узла (без пробелов слева), оборачивается только сам символ и следующее слово (
      <span class="etp-laquo">«АукЫон»</span>
      );
    • если перед "висячим" символом внутри узла есть пробел, то пробел и слово слева от него оборачивается в
      <span class="etp-sp-laquo">слово </span>
      (компенсирующий пробел), а сам "висячий" символ вместе со словом справа — в
      <span class="etp-laquo">...</span>
      ;
    • если компенсирующий пробел оказался в соседнем узле (слева), то он тоже оборачивается в
      etp-sp-*
      , чтобы не нарушить последовательность;
    • если слева от "висячего" символа пробел является "неразрывным пробелом" (
      &nbsp;
      , нулевой неразрывный пробел, узкий неразрывный пробел или любой "не пробельный" символ) — это означает, что "висячий" символ не может "вывешиваться" в начале строки и оборачивания в
      <span>
      не проиходит.
  • Для
    hanging_punctuation='right'
    :
    • слово с "висячим символом" и слово слева оборачивается в соответствующий класс (
      .etp-raquo
      ,
      .etp-rpar
      и т.д.);
    • пробел сразу после символа (справа) получает класс
      etp-sp-raquo
      ,
      etp-sp-rpar
      и т.д., чтобы сохранить переносную ширину и аккуратно компенсировать смещение;
    • если компенсирующий пробел оказался в соседнем узле (справа), то он тоже оборачивается в
      etp-sp-*
      , чтобы не нарушить последовательность;
    • если справа от "висячего" символа пробел является "неразрывным пробелом" (
      &nbsp;
      , нулевой неразрывный пробел, узкий неразрывный пробел или любой "не пробельный" символ) — это означает, что "висячий" символ не может "вывешиваться" в конце строки и оборачивания в
      <span>
      не проиходит.

Пример вывода для

'left'
:

Пример вывода для

'right'
:

CSS для висячих символов

Предлагаемый, начиная с etpgrf v0.1.6, CSS теперь работает только с

margin
и
padding
, без
position:absolute
. Компенсирующие пробелы получают собственные классы, поэтому их компенсация контролируется отдельно, а не встроена в сам висячий символ. Убедитесь, что эти стили подключены к странице и не конфликтуют с
text-justify
, который увеличивает пробелы между словами по всей строке, делают текст менее удобным для чтения и не пригодны для выравнивания.

Комментарии: Двухстороннее выравнивание текстового блока с помощью стиля

text-justify
в принципе плохо совместим концепцией типографики — он растягивает или сжимает пробелы по всей строке (а это пробелы между словами) и уже этими, переменными, пробелами, делает текст трудночитаемым. Если же вы используете
text-justify
для выравнивания текста по ширине, то, чтобы сохранить оптимальную читаемость текста, включать висячую типографику не рекомендуется.

Известные особенности и ограничения

При обработке сложного HTML-кода типограф стремится сохранить структуру документа, но некоторые пограничные случаи могут обрабатываться не так, как ожидается. В частности:

  • Обработка на стыке тегов: Правила, требующие анализа контекста (например, расстановка неразрывных пробелов у тире или единиц измерения), могут работать некорректно, если анализируемые части текста разделены тегами. Например, конструкция
    $<b>100</b>
    не будет обработана (между $ и 100 не будет вставлен неразрывный пробел), так как типограф не видит их как соседние элементы.
  • "Ремонт" HTML: Библиотека использует
    BeautifulSoup
    для парсинга, который может "чинить" невалидный HTML (например, закрывать незакрытые теги). Это может привести к неожиданным изменениям в структуре, если исходный код был некорректен. Так же может меняться порядок атрибутов тега.

Мы знаем об этих особенностях и работаем над улучшением алгоритмов для более точной обработки сложных случаев.

P.S.

Если вам нравится этот проект, можете поддержать отправив любую сумму на мой Т-банк по ссылке или, для приверженцев децентрализованного будущего, через Toncoin (TON) (адрес кошелька

UQApEkzNMYOg5qesWwlyfGFf4ayFyki5Mrpcd2yadgS2_1cx
)

Credits

Разработка: Проект разработан Sergei Erjemin при активном участии различных LLM в роли pair-programmer.