Рецепты
Свой набор правил
RuleSet rules = RuleSet.Default
.Without(RuleId.Common.Space.DelBeforePercent) // ГОСТ 9.6 требует там пробел
.With(RuleId.Common.Number.DigitGrouping); // 1 000 000 вместо 1000000
var typographer = new HtmlTypographer(new HtmlOptions { Rules = rules });
Правило можно найти и по имени, как в JS-typograf:
if (RuleId.TryParse("ru/nbsp/abbr", out RuleId rule))
{
rules = rules.Without(rule);
}
Контейнер
dotnet add package Typographer.DependencyInjection
services.AddTypographer(); // настройки по умолчанию
services.AddTypographer(new HtmlOptions { Entities = EntityMode.Named });
Регистрируются одиночками HtmlTypographer и TextTypographer. Своя регистрация, сделанная
раньше, побеждает: внутри TryAddSingleton.
ASP.NET Core
dotnet add package Typographer.AspNetCore
@addTagHelper *, Typographer.AspNetCore
<typographer><p>Он - человек и "цитата"</p></typographer>
Тег-хелпер типографирует содержимое и исчезает сам. Требует services.AddTypographer().
Там, где удобнее вызов, а не элемент:
@inject HtmlTypographer Typographer
@Typographer.ToHtmlContent(Model.Text)
Markdown
dotnet add package Typographer.Markdig
MarkdownPipeline pipeline = new MarkdownPipelineBuilder().UseTypographer().Build();
string html = Markdown.ToHtml(source, pipeline);
Правки вносятся в дерево документа после разбора, а не в готовый HTML: рендерер кодирует
прямую кавычку в ", и в отрендеренном HTML ёлочки уже не появились бы. Поэтому код,
адреса ссылок и встроенный HTML остаются нетронутыми, а кавычки вокруг разметки —
"**слово**" — смотрят в разные стороны.
Защищено и содержимое HTML-элементов code, pre, kbd, samp, script, style и
textarea, записанных прямо в Markdown: для Markdig <code>a - b</code> — это три узла,
средний из которых обычный текст, и без учёта таких зон типограф правил бы код и клавиши.
Markdown-код в обратных кавычках (`a - b`) защищён и без этого — он отдельный тип узла.
Командная строка
dotnet tool install --global dotnet-typographer
echo 'Он - человек' | dotnet-typographer
dotnet-typographer --entities named --in-place статья.html
dotnet-typographer --rules gost --text заметка.txt
dotnet-typographer --list-rules
Без аллокаций
var writer = new ArrayBufferWriter<char>();
typographer.Process(source.AsSpan(), writer);
Путь Process(ReadOnlySpan<char>, IBufferWriter<char>) после прогрева пула не выделяет в
куче ничего. Путь Process(string) выделяет ровно одну строку результата — и ту не
выделяет, если правок не было.
Защита от разрастания вывода
var typographer = new HtmlTypographer(new HtmlOptions { MaxOutputLength = 1_000_000 });
Превышение предела даёт OutputTooLargeException — единственное исключение, которое
типограф имеет право выбросить. Приёмник при этом не получает половину результата:
предел проверяется до записи.
Не трогать кусок текста
Содержимое code, pre, script, style, textarea, kbd, samp и комментариев
защищено само. Если нужно защитить что-то ещё, оберните это в один из этих элементов или
выключите правило точечно через Without.