Search
Write a publication
Pull to refresh

Про растеризацию исходного кода

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



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

  • Во-первых такое решение слишком хрупкое – имея обширный опыт работы с автоматизацией браузеров, смею утверждать что подобные операции будут регулярно валиться вне зависимости от браузера (а использовать захочется все равно IE из-за лучшей отрисовки текста).
  • Во-вторых у меня уже был к тому моменту свой «растеризатор», а терять вложенные усилия не хотелось.

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

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

Вот небольшой пример: если я в разметке напишу Hello, World, то получу

Hello,World


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

Ordinary,BoldandItalic


Делается все это с помощью таких тэгов как [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, но помимо этого все очень красиво (имхо), а главное — можно начать использовать возможность растеризации для всяких аннотаций – прямо в коде. Думаю стоит попробовать.
Tags:
Hubs:
Total votes 34: ↑18 and ↓16+2
Comments30

Articles