mod: Новое описание для "висячей пунктуации"

This commit is contained in:
2026-03-04 15:18:03 +03:00
parent e8ef4c1bca
commit 4b62564c6b

103
README.md
View File

@@ -104,7 +104,7 @@ result = typo_mixed_mode.process(text="Этот текст будет обраб
двумя символами `\u228a\ufe00` и превратится в `⊊\ufe00`. Символ `\ufe00` — это невидимый символ, cелектор
варианта начертания (Variant Selector), который изменяет начертание предыдущего символа и для него нет
html-мнемоники. К счастью, в стандарте таких мнемоник (превращающихся в два символа) исчезающе мало и они крайне
редко применяются в тексте, поэтому это не должно вызывать проблем.
редко применляются в тексте, поэтому это не должно вызывать проблем.
### Переносы слов
@@ -294,9 +294,9 @@ result = typo.process("100 км/ч") # Останется без
### Висячая типографика
Висячая типографика — это приём из классической вёрстки, когда некоторые знаки препинания (кавычки, скобки, иногда
тире и маркеры списков) выносятся на левое (и иногда и по правому, при выравнивании текст по правому краю) поле текста.
Это создаёт идеально ровный край не по формальным границам знаков, а по оптическому краю — по первым буквам строк.
Текст выглядит гораздо аккуратнее и профессиональнее.
tире и маркеры списков) выносятся на левое (и иногда и по правому) поле текста. Это создаёт идеально ровный край не по
формальным границам знаков, а по оптическому краю — по первым буквам строк. Текст выглядит гораздо аккуратнее и
профессиональнее.
Интернет публикации (да и бумажные издания) практически игнорируют висячую типографику. Но иногда это отличный
инструмент для акцентной типографики: крупные заголовки, цитаты, выносы, подписи к иллюстрациям, оформленные с помощью
@@ -306,9 +306,10 @@ Safari), поэтому на него полагаться нельзя. Поэ
через оборачивание висячих символов в специальные HTML-теги с CSS-классами.
Оборачивая "висячий" символ или слово в `<span>` и применяя к нему, например, отрицательный `text-indent` или
`margin-left` (`<span style="margin-left:-0.44em;">&laquo;</span>`). Важный нюанс: степень "вывешивания" у символов
разная. И не только потому, что кавычка, скобка или точка имеют разную ширину, но еще и потому, что кавычка, например,
может "висеть" на 100% своей ширины, а точка или запятая — только на 50-70%, чтобы не отрываться от слова совсем.
`margin-left` (`<span style="margin-left:-0.44em">&laquo;</span>`), мы можем сместить сам символ, но нужно ещё и
сохранить расстояние до соседнего слова. Поэтому типограф оборачивает не только сам висячий символ, но и ближайшее слово
(до пробела или границы узла), а также, при необходимости, окружающий пробел. Сама визуальная компенсация оформляется через
отрицательные `margin`/`padding` в CSS-классах — никаких `position:absolute`, чтобы не нарушать поток текста.
По умолчанию эта функция висячей типографики **отключена**. Чтобы её включить, нужно задать параметр
`hanging_punctuation` при конфигурировании типографа (по умолчанию `hanging_punctuation=None`):
@@ -318,40 +319,74 @@ typo = etpgrf.Typographer(hanging_punctuation='left')
```
Параметр `hanging_punctuation` может принимать следующие значения:
* `None`или `False` — функция отключена (по умолчанию);
* `'left'` — включены только левые висячие символы (выравнивание по левому краю);
* `'right'` — включены только правые висячие символы (выравнивание по правому краю);
* `'both'` или `True` — включены и левые, и правые висячие символы.
* `None` или `False` — функция отключена (по умолчанию);
* `'left'` или `True` — включены только левые висячие символы (выравнивание по левому краю);
* `'right'` — включены только правые висячие символы (выравнивание по правому краю).
Так же через `hanging_punctuation` можно задать список тегов, внутри которых висячая типографика будет применяться
(всегда в режиме `'both'`). Это нерекомендованный способ (так как предполагает знание об исходном тексте, и не сработает
для сточных тегов, а поведение "блочных" и "строчных" может быть переназначено через CSS). Тем не менее управление
висячей пунктуацией на уровне тегов может быть полезно в некоторых, редких случаях:
```python
typo = etpgrf.Typographer(hanging_punctuation=['blockquote', 'h2', 'h3'])
Значения `'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-*`, чтобы не нарушить последовательность;
* если компенсирующий пробел является "непереносимым пробелом" (или любым другим: шпацией, em-пробелом и т.п.), то тогда, для правильного выравнивания, оборачивается он, например: `<span class="etp-sp-laquo">&nbsp;</span><span class="etp-laquo">«АукЫон»</span>`.
* Для `hanging_punctuation='right'`:
* слово с висячим символом оборачивается в соответствующий класс (`.etp-raquo`, `.etp-rpar` и т.д.);
* пробел сразу после символа получает класс `etp-sp-raquo`, `etp-sp-rpar` и т.д., чтобы сохранить переносную ширину и
аккуратно компенсировать смещение;
Пример вывода для `'left'`:
```html
Завтра концерт группы<span class="etp-sp-laquo"> </span><span class="etp-laquo">«АукЫон»</span>
```
Рекомендуемый CSS для этих классов выглядит так (возможно, вам придется подкорректировать значения `em` в зависимости
от используемого шрифта, его толщины и начертания):
### CSS для висячих символов
Предлагаемый CSS теперь работает только с `margin` и `padding`, без `position:absolute`. Пробелы получают собственные
классы, поэтому их компенсация контролируется отдельно, а не встроена в сам висячий символ. Убедитесь, что эти стили
подключены к странице и не конфликтуют с `text-justify`, который вытягивает пробелы по всей строке и разрушает аккуратное
выравнивание.
```css
/* --- ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по левому краю) --- */
.etp-laquo { margin-left: -0.44em; } /* « */
.etp-ldquo, .etp-bdquo { margin-left: -0.4em; } /* “ „ */
.etp-lsquo { margin-left: -0.22em; } /* */
.etp-lpar, .etp-lsqb, .etp-lcub { margin-left: -0.25em; } /* ( [ { */
/* --- ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ --- */
.etp-laquo { margin-left: -0.44em; }
.etp-ldquo, .etp-bdquo { margin-left: -0.4em; }
.etp-lsquo { margin-left: -0.22em; }
.etp-lpar, .etp-lsqb, .etp-lcub { margin-left: -0.25em; }
/* компенсирующие пробелы для левых висячих символов */
.etp-sp-laquo { padding-right: 0.44em; }
.etp-sp-ldquo, .etp-sp-bdquo { padding-right: 0.4em; }
.etp-sp-lsquo { padding-right: 0.22em; }
.etp-sp-lpar, .etp-sp-lsqb, .etp-sp-lcub { padding-right: 0.25em; }
/* --- ПРАВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по правому краю) --- */
/* Общая механика: "вырываем" символ из потока для идеального выравнивания текста */
[class^="etp-r"], [class*=" etp-r"] { position: absolute; }
/* Точечная настройка смещения для каждого символа */
.etp-raquo { right: -0.44em; } /* » */
.etp-rdquo { right: -0.4em; } /* ” */
.etp-rsquo { right: -0.22em; } /* */
.etp-rpar, .etp-rsqb, .etp-rcub { right: -0.25em; } /* ) ] } */
.etp-r-dot, .etp-r-comma, .etp-r-colon { right: -0.15em; } /* . , : */
/* --- ПРАВЫЕ ВИСЯЧИЕ СИМВОЛЫ --- */
.etp-raquo { padding-right: 0.44em; margin-left: -0.44em; }
.etp-rdquo { padding-right: 0.4em; margin-left: -0.4em; }
.etp-rsquo { padding-right: 0.22em; margin-left: -0.22em; }
.etp-rpar, .etp-rsqb, .etp-rcub { padding-right: 0.25em; margin-left: -0.25em; }
/* компенсирующие пробелы для правых висячих символов */
.etp-sp-raquo { margin-left: -0.44em; }
.etp-sp-rdquo { margin-left: -0.4em; }
.etp-sp-rsquo { margin-left: -0.22em; }
.etp-sp-rpar, .etp-sp-rsqb, .etp-sp-rcub { margin-left: -0.25em; }
```
*Комментарии:* Двухстороннее выравнивание текстового блока с помощью стиля `text-justify` в принципе плохо совместим концепцией типографики — он растягивает или сжимает пробелы по всей строке (а это пробелы между словами) и уже этими, переменными, пробелами, делает текст трудночитаемым. Если же вы используете `text-justify` для выравнивания текста по ширине, то, чтобы сохранить оптимальную читаемость текста, включать висячую типографику не рекомендуется.
## Известные особенности и ограничения
При обработке сложного HTML-кода типограф стремится сохранить структуру документа, но некоторые пограничные случаи могут обрабатываться не так, как ожидается. В частности:
@@ -371,4 +406,4 @@ typo = etpgrf.Typographer(hanging_punctuation=['blockquote', 'h2', 'h3'])
## Credits
**Разработка:**
Проект разработан Sergei Erjemin при активном участии Gemini Assistant (LLM) в роли pair-programmer.
Проект разработан Sergei Erjemin при активном участии различных LLM в роли pair-programmer.