Я периодически вижу как на блогах люди переводят код в картинки дабы не бороться с глючным движком той или иной платформы. В большинстве случаев, авторы просто делают скриншот, но я пошел по более прямому пути – встроил возможность «растеризации» кода в свой собственный редактор. Этот пост – о том, как я это сделал. Пост также является иллюстрацией того, что он описывает, т.к. код тут действительно растеризован. Все исходники тут: http://butbucket.org/nesteruk/typografix.

Уже существуют решения для подсветки синтаксиса, который используются например в HabraEditor. Само по себе решение основано на вот этом проекте, который лично я взял и слегка переработал дабы иметь на том же Хабре подсветку таких языков как Boo. Но как растеризовать полученный HTML в картинку? Понятное дело что можно, например, использовать реальный браузер (тот же IE, например), выводить в нем HTML, делать скриншот, клиппинг, ну и готово. Но мне этот подход не понравился по двум причинам:
Давайте сначала расскажу про текстовый растеризатор, про то что это за зверь и зачем он понадобился.

Видите заголовки в этой статье? Они – это графика, JPG-файлы. Естественно что я не создавал их по одиночке в Photoshop’е чтобы потом вставить в пост – для этого у меня используется как раз растеризатор, то есть компонент, который на входе может взять некую разметку (а-ля HTML, но интересней), а на выходе дать графический файл.
Вот небольшой пример: если я в разметке напишу

Соответственно разметка, которую я использую, дает мне возможность растеризовать как привычные всем bold и italic, так и ОТ-фичи вроде капители, лигатур, и т.п.

Делается все это с помощью таких тэгов как

Весь текст (или код) который растеризуется является тем самым всего лишь последовательностью таких элементов. У нас есть класс

Всем этим пользуется рекурсивный парсер, который отделяет разметку от текста.

Корневым же элементом является следующий метод, который вызывает редактор (сам он написан на WPF).

Как видите, в метод передается «прототип» текстового форматирования, который будет применяться ко всем элементам у которых не переопределена гарнитура, размер шрифта, цвет и т.д.
Теперь про то, как это растеризуется. Тут несколько шагов. Во-первых, поскольку я использую DirectWrite (напоминалка: «драйвер» DirectWrite для .Net уже готов, но не работает в 64-битной среде), у меня идет море инфраструктуры, которая к тому же использует COM.

Все это «добро» так или иначе фигурирует в создании битмапов или фотматировании текста. Самое интересное – это
Дальше парсится прототип, и производится разбор разметки:

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

Содержимое созданного render target впоследствии копируется в байти того битмапа (имеется ввиду

Итак, имеем HTML и растеризатор, нужно получить правильную разметку. Делается это просто:

Тут небольшая проблемка – нам приходится менять


Вот так просто было подогнать существующую инфраструктуру под растеризатор. Результаты его работы вы, надеюсь, уже видите. Да, конечно, с такого текста нельзя делать cut & paste, но помимо этого все очень красиво (имхо), а главное — можно начать использовать возможность растеризации для всяких аннотаций – прямо в коде. Думаю стоит попробовать.

Уже существуют решения для подсветки синтаксиса, который используются например в HabraEditor. Само по себе решение основано на вот этом проекте, который лично я взял и слегка переработал дабы иметь на том же Хабре подсветку таких языков как Boo. Но как растеризовать полученный HTML в картинку? Понятное дело что можно, например, использовать реальный браузер (тот же IE, например), выводить в нем HTML, делать скриншот, клиппинг, ну и готово. Но мне этот подход не понравился по двум причинам:
- Во-первых такое решение слишком хрупкое – имея обширный опыт работы с автоматизацией браузеров, смею утверждать что подобные операции будут регулярно валиться вне зависимости от браузера (а использовать захочется все равно IE из-за лучшей отрисовки текста).
- Во-вторых у меня уже был к тому моменту свой «растеризатор», а терять вложенные усилия не хотелось.
Давайте сначала расскажу про текстовый растеризатор, про то что это за зверь и зачем он понадобился.

Видите заголовки в этой статье? Они – это графика, JPG-файлы. Естественно что я не создавал их по одиночке в Photoshop’е чтобы потом вставить в пост – для этого у меня используется как раз растеризатор, то есть компонент, который на входе может взять некую разметку (а-ля HTML, но интересней), а на выходе дать графический файл.
Вот небольшой пример: если я в разметке напишу
Hello, World
, то получу
Соответственно разметка, которую я использую, дает мне возможность растеризовать как привычные всем bold и italic, так и ОТ-фичи вроде капители, лигатур, и т.п.

Делается все это с помощью таких тэгов как
[b]
, [i]
, [sw]
и т.д. Для каждого индивидуального отрезка текста существует следующая структура:
Весь текст (или код) который растеризуется является тем самым всего лишь последовательностью таких элементов. У нас есть класс
MarkupParser
который разбирает mark-up с помощью обычного текстового сравнения, применяя разметку к тому или иному элементу.
Всем этим пользуется рекурсивный парсер, который отделяет разметку от текста.

Корневым же элементом является следующий метод, который вызывает редактор (сам он написан на WPF).

Как видите, в метод передается «прототип» текстового форматирования, который будет применяться ко всем элементам у которых не переопределена гарнитура, размер шрифта, цвет и т.д.
Теперь про то, как это растеризуется. Тут несколько шагов. Во-первых, поскольку я использую DirectWrite (напоминалка: «драйвер» DirectWrite для .Net уже готов, но не работает в 64-битной среде), у меня идет море инфраструктуры, которая к тому же использует COM.

Все это «добро» так или иначе фигурирует в создании битмапов или фотматировании текста. Самое интересное – это
IDWriteTypography
– именно тут определяется набор OT-фич для конкретного куска текста.Дальше парсится прототип, и производится разбор разметки:

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

Содержимое созданного render target впоследствии копируется в байти того битмапа (имеется ввиду
System.Drawing.Bitmap
), который мы «залочили» прежде чем прокидывать через P/Invoke.
Итак, имеем HTML и растеризатор, нужно получить правильную разметку. Делается это просто:

Тут небольшая проблемка – нам приходится менять
[
на \[
потому что квадратные скобки используются для разметки. Ничего страшного. Теперь последний шаг – готовим пустой Bitmap
и рисуем на нем с помощью нашего DirectWrite-растеризатора:

Вот так просто было подогнать существующую инфраструктуру под растеризатор. Результаты его работы вы, надеюсь, уже видите. Да, конечно, с такого текста нельзя делать cut & paste, но помимо этого все очень красиво (имхо), а главное — можно начать использовать возможность растеризации для всяких аннотаций – прямо в коде. Думаю стоит попробовать.