django-headline или @font-face средствами Django

    На этой неделе в очередной раз столкнулся с проблемой «нестандартных» шрифтов, и если ранее можно было иногда обойтись самой обычной нарезкой в графическом редакторе или использовать какие-то клиентские приёмы подмены, то в этот раз задача стоит наиболее глобально. Динамических заголовков в надвигающемся проекте реально много.

    Своими мыслями, поиском и конечным решением я и хотел бы поделиться с вами.


    Преамбула


    Итак, в принципе, какие у нас есть варианты:
    1. Заменять на Flash: sIFR, swftype;
    2. рисовать на canvas JavaScript`ом: cufon, typeface;
    3. использовать для этих целей сервер.

    Первый вариант я уже использовал в паре проектов, правда не собственно sIFR, но не суть важно, результатом я остался не очень доволен. Сборки-пересборки Flash-файла со шрифтом, проверки на флеш, «рамочка» в IE7, не кошерно в конце концов. Второй вариант — всегда считал слишком тяжеловесным, как по трафику (файлы с отрендеренными шрифтами весят прилично, да ещё и не всегда корректно конвертируются), так и по нагрузке на процессор клиента. Так что в продакшен его ни разу не пустил.

    Так я пришёл к третьему варианту. Однажды я уже прорабатывал этот способ на одном из тогда ещё php-проектов, вычитав идею на A List Apart. Работает и работает вполне сносно, хотя в этом методе по-прежнему использовался JavaScript. Более того, теперь мы разрабатываем проекты только на Django, так что я пошёл на поиски и через некоторое время нашёл два похожих проекта: django-cairo-text и django-image-replacement. Первый обладает достаточно широкими возможностями, но не умеет рисовать шрифтом из файла, только одним из установленных в систему, что в моём случае абсолютно не подходит, второй же слишком прост для моих нужд и использует не совсем удобные для меня подходы.

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

    Итак django-headline


    Что мы умеем на сегодня


    • Рендеринг картинок из шаблона тремя путями: фильтром, тегом и тегом, меняющим контекст
    • Естественно, кеширование ранее отрендеренных изображений
    • Определение стандартных стилей шрифта через классы
    • Автоматическая замена html-entities на юникодные символы
    • Разбиение текстовой строки по br или по словам
    • Собственные настройки расположения шрифтов и папки для кеша
    • Опциональная оптимизация выходного png, через внешние утилиты


    Установка и настройка


    Для работы требуются установленные: PIL и Freetype2

    Разместите файл headline.py в папку templatetags вашего приложения.

    Для настройки доступны такие параметры:

    # Папка для кеша картинок (относительно вашего MEDIA_ROOT/MEDIA_URL)
    HEADLINE_CACHE_DIR = 'upload/textcache'
    
    # Папка для шрифтов (относительно вашего MEDIA_ROOT)
    HEADLINE_FONTS_DIR = 'fonts'
    
    # Если это требуется, путь к оптимизатору
    HEADLINE_PNG_OPTIMIZER = "optipng -o7 %(file)s"
    
    # Пресеты настроек отображения
    HEADLINE_CLASSES = {
       "<class_name>": {
           'font': <file>,
           'size': <size>,
           'color': <hex color>,
           'decoration': ['underline', 'strikeout'], # Optional
       },
       ...
    }


    Примеры использования


    {% load headline %}

    Как фильтр


       {{ foo|headline:"font.ttf,20,#000" }}
       {{ foo|headline:"font.ttf,20,#000,underline,all" }}
       {{ foo|headline:"base,br" }}
    


    Как тег


       {% headline "font.ttf,20,#000" %}Big {{ foo }}{% endheadline %}
       {% headline "font.ttf,20,#000,strikeout,none" %}Big {{ foo }}{% endheadline %}
       {% headline "base" %}Big {{ foo }}{% endheadline %}
    


    Как тег, модифицирующий контекст


       {% headlines foo_list bar_dict baz_var "And some text" as headers "font.ttf,20,#000" %}
       {% headlines foo_list bar_dict baz_var "And some text" as headers "font.ttf,20,#000,all" %}
       {% headlines foo_list bar_dict baz_var "And some text" as headers "base" %}
    


    В этом случае, в качестве параметров можно передавать как переменные и простые строки, заключенные в двойные кавычки, так и списки и словари. На выходе имеем список headers из объектов {file, text, width, height}:

       {% for head in headers %}
          <img src="{{ head.file }}" alt="{{ head.text }}" width="{{ head.width }}" height="{{ head.height }}" />
       {% endfor %}
    


    Вот, что, к примеру, может получиться в финале. Я не дизайнер и шрифты подбирал «на глазок», так что за них прошу ногами не пинать.

    Таким образом для клиента это выглядит как ручная нарезка, для сервера — нагрузка носит разовый характер, для разработчика всё просто и прозрачно. Профит!

    Ещё раз ссылки по теме:


    За сим спасибо, принимаю отзывы по теме, любой степени лестности.

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 52

      –8
      Осведомлю читателей о том, что на www.tinyurl.com/serverfonts давно предлагается ещё одно серверное решение, но на PHP сочинённое.
        +2
        Я специально не упоминал в обзоре других серверных решений, так как сложно сказать насколько читателям блога Django Framework будет интересно знать о подобном решении на PHP. К тому же приведённый вами проект, по прежнему требует использования Javascript для подмены на клиентской стороне, а это именно то, от чего хотелось уйти.
          0
          А как же еврейское расовое происхождение PHP?
          0
          Спасибо, вещь вполне полезная, на мой взгляд.
            0
            Это очень круто.
              0
              Причем не говорите, что это только для тех, у кого font-face не поддерживается. Картинка еще и весить меньше может.
                0
                Это скорее очень частный случай, если бы @font-face умели бы все браузеры, придумывать подобные решения не было бы надобности: средний вес шрифта, из тех что я использовал 60-90 Кб, вес всей графики на странице примера, без оптимизации 38 Кб. На полноценном продакшен-сервере заголовков будет намного больше. Плюс не будет никаких ограничений и однажды закешированный в браузере шрифт не будет грузиться заново при появлении новых заголовков.

                Так что это всё в любом случае от «бедности».
                  0
                  На крупных сайтах, на сайтах с огромным графическим оформлением это шрифт 90-350 кб графика 500-3000 кб
              –3
              Серваку с большой посещалкой ХАНА xD
                +1
                По какой причине, простите?
                  –3
                  Ну представьте себе сайт с огромной посещалкой, и то как будет нагружаться процессор генерируя постоянно картинки?!
                  Вы думаете это бред? тогда задайте себе вопрос почему подобное не используется на крупных сайтах…

                  ps/ А вообще помню давненько верстал 5socks.net там использовал JS не так надежно зато нагрузка нулевая.
                    +1
                    Если бы вы достаточно внимательно прочитали мою статью, вы бы могли уловить тот факт, что изображения генерируются единожды и впоследствии используются как статичные.

                    Да, думаю вы действительно правы, на крупных проектах подобные техники не используются, а amazon.com и nytimes.com из ссылки г-на Mithgol — это чистейшей воды случайность. =)
                      +2
                      Автор же написал про кеширование…
                        0
                        Я хоть в одном слове упомянул что «Как же так!, без кеширования?!» я знаю что кеширует, я говорю мальца о другом. Что вы придрались то к этому кешированию!? я умею читать не бойтесь.
                        0
                        Просто таким способом не стоит генерировать начертания для текстов, которые часто меняются или персональны для пользователей. Это сугубо для заголовков и подзаголовков страниц или разделов, названий кнопок и т. п. Для интернационализации, например, сгодится тоже. Но не для динамики.
                    • НЛО прилетело и опубликовало эту надпись здесь
                        –1
                        На счет кеширования, если надписей мало то сойдет если нет то представьте сколько могут весить PNG 32ух битные ну да пусть мы на память щедрые (как на свою так и клиентов)… DXI фильтр который использует PNG HACK для IE при большом количестве подобных надписей просто убьет клинта (Хотя тут спорный момент у некоторых и тысячи профильтрованых надписей держит)!
                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            32-битные png, учитывая их однотонность, весят не так уж и много, а после оптимизации ещё меньше. А DX-фильтр, требующийся только для IE6, уверен, требует не больше памяти, чем explorercanvas, нужный для всей линейки IE.
                              0
                              Вообще-то AlphaImageLoader действительно реально «убивает» IE6 при большом количестве вызовов.

                              У меня на одном проекте как-то так получилось, что на странице их число достигало около 40-50 (: страница в IE6 загружалась почти 2 минуты (в IE 7 около 30 сек.) причем как я понимаю кеш тут не поможет. Пришлось «пересматривать взгляды на жизнь» XD
                                0
                                А кто с этим спорит? Ясное дело 40-50 подмен на странице — это слишком много для старенького Иа-Иа. При ручном подходе, я всегда стараюсь для избранных подгонять gif-ы под фон. Но диалог то о png против svg. Есть подозрение, что 40-50 объектов типа explorercanvas в IE6 сложит его не менее быстро. Но ведь задача может стоять чуть иначе: на одной странице 3-5 заголовков, а страниц стопицотмилионов.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Парсера CSS у меня нет, просто в отдельном файле можно обозначить необходимые классы, подобно CSS-подходу.
                          • НЛО прилетело и опубликовало эту надпись здесь
                        • НЛО прилетело и опубликовало эту надпись здесь
                            +1
                            Обязательно, в ответ на парочку разумных примеров, где требуется нечто отличное от 16-ти цветной ASCII-консоли. =)
                            • НЛО прилетело и опубликовало эту надпись здесь
                              +2
                              Дизайнер бывает нарисует, а заказчик утвердит — и можно долго упиваца в письмах своей веб-стандартностью, евангелистскостью и т.п. — делать всё равно придётся.
                              • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              А система поддерживает отрисовку ClearType, а также ОТ-фичи такие как кернинг?
                                0
                                Естественно, я не писал собственного рендера шрифтов. Система поддерживает всё, что поддерживает библиотека Freetype2. ClearType не умеет, только обычный AA. По-идее, кернинг должна поддерживать. Более точно на ваш вопрос, увы, ответить не могу.
                                  0
                                    0
                                    Не претендую на звание самого лучшего решения, есть только пара возражений по отношению к приведённой ссылке: во-первых, .NET — это хорошо, но не так много ценителей запускать Django на IronPython, во-вторых, это скорее не решение, а техника. В остальном, чтож, я рад что есть решения лучше моего, значит мне есть куда расти и откуда заимствовать идеи. =)
                                      –2
                                      Ваше решение ужасно, мы уже говорили по этому поводу.
                                        –1
                                        Напишите лучше
                                          0
                                          SkAZi написал.
                                            0
                                            Право, господа, не стоит ссориться. Моя цель поделиться с теми, кому нужно, и получить фидбэк от того, кто заинтересован, а не помериться у кого длиннее и толще. Лучше ли хуже, решать не нам, а тем кто в конечном итоге будет использовать и помогать в развитии.

                                            По теме: особой нужды в ClearType я не ощущаю, а вот ручной кернинг и альтернативное начертание последней буквы отложил на дальнюю полку памяти.
                                  0
                                  Единственная мелочь  — лучше прикрутить сюда как-то технику CSS image-replacement, ставить фоновыми картинками, чтобы при отключенных картинках был обычный текст.
                                    0
                                    С помощью последнего тега, модифицирующего контент, можно выводить полученную графику любой интересующей вас техникой. Моё кажется не стоит привязывать библиотеку к каким-то конкретным клиентским техникам. Но, думаю, неплохой идеей было бы, в качестве опции, прикрутить возможность использовать шаблон для вывода.
                                      0
                                      Добавил возможность использовать собственный шаблон вывода для остальных тегов и фильтров.
                                      0
                                      Что-то по демо-ссылке не совсем понятно внутреннее строение. Как до уровня броузера опускаются темплейтовые теги?.. Или это у меня сафари что-то не то накачал:

                                      <span style='color:#696969; '>{% block body %}</span>
                                        0
                                        Хм, у меня 4-ка всё нормально показывает…
                                        Под ссылкой Page source скрывается кусок кода Django-template, который использован для генерации страницы. Стили и всё не относящееся к делу я убрал, оставив только блок вывода центральной части страницы.
                                        0
                                        И после этого кто-будет говорить, что дизайнер дожен знать вёрстку ибо шрифты и т.п. Надеюсь, красивых сайтов станет больше благодаря django-headline.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                            +2
                                            Да, ещё вы не сможете копировать выпадающие меню, флэш, или ролики с ётюба. Разумеется в реальной жизни мало кто будет заморачиваться с текстовой версией для тех, кому нужно скопировать, но всё же некоторые так делают. В борьбе красоты с копипастом, я на стороне браузеров, которые будут копировать alt в буфер обмена.
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                          0
                                          Имхо было бы интереснее тогда уже рендерить SVG/VML (посмотрев user-agent). А то на сегодняшний день заменять экзотические шрифты растровыми картинками кажется архаизмом, учитывая возможности браузеров.

                                          Для старых браузеров опять же можно откатываться к растровой картинке. Раз уж грузить сервер, так с максимальной пользой.
                                            0
                                            Если честно, я не вижу реальной выгоды в использовании svg, учитывая насколько для этого должна быть усложнена логика приложения: кеширование с учётом юзер-агента, разные шаблоны, разные технологии и подходы. А смысл то в чём? Просто использовать технологии поновее? Так все вменяемые уже на пороге нормально функционирующего @font-face, а для отстающих костыли и есть костыли, пытаться разогнать IE6 до возможностей Opera 10 я смысла не вижу. =)
                                              0
                                              0
                                              С одной стороны решение-как-решение, но вот разбивать фразу на слова а потом заменять каждое слово на картинкой, этож кроме веса еще и отдельный запрос на сервак вроде.

                                              PS: Когда начинаешь задумываться о таких вещах в голову в основном лезет: «я ненавижу дизайнеров» :))
                                                +1
                                                Разбивать фразу на слова нужно только в случае острой необходимости, при помощи специальнообученного параметра all. По-умолчанию строка разбивается только по
                                                , а в случае гарантированной однострочности можно вообще использовать параметр none и тогда любые разделители будут проигнорированы обработчиком.

                                                А по поводу PS, тоже немного поофтоплю: мне наоборот интересно искать элегантные выходы из сложных ситуаций, а шаблонный трёхколоночный-Arial-Georgia-дизайн-с-шапкой порядком набил оскомину. Конечно, стрёмно работать в вёрстке из десятков разных wrapper`ов, которые нужны для всех этих закруглённых уголочков и градиентиков, а сердце постанывает при виде возможностей CSS3 и «ух, как бы я сейчас тут с его помощью!», но другой раз находишь какое-то изящное решение для нестандартной задачи и радость с гордостью просто перехлёстывает через край. =)
                                                  0
                                                  * по <br />

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

                                              Самое читаемое