Нарисуйте текст заглавными буквами TrueType, используя подушку - PullRequest
0 голосов
/ 12 февраля 2020

Я использую Подушку для рисования текста на изображении:

from PIL import Image, ImageDraw, ImageFont

im = Image.new("L", (400, 50), color="black")
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Roboto-Medium.ttf", 28)
draw.text((0, 0), "How To Get Small Caps?", font=font, fill="white")
im.save('small_caps.png')

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

1 Ответ

1 голос
/ 13 февраля 2020

В последней версии Roboto-Medium содержится , в которой фактически указаны малые прописные буквы, но они не сопоставлены с кодовой точкой Unicode. Это означает, что обычный способ указания символов в простой строке не будет работать.

Программное обеспечение, поддерживающее OpenType, может обращаться к глифам через функцию OpenType c2sc (которая заменяет заглавные буквы на маленькие заглавные буквы) ) и smcp (что делает то же самое, но только для строчных букв). Эти таблицы перевода находятся внутри шрифта в таблице TrueType GSUB, которая закодирована в особенно неявном формате. Вероятно, поэтому программисты PIL никогда не удосужились реализовать его (и другие потенциально полезные функции OpenType, такие как супер- и подписка, кернинг и локализованные буквенные формы для каждого языка).

Модуль Python freetype может получить доступ к необработанным глифам. Но, аналогично, разработчики FreeType кратко рассмотрели возможность добавления функции разбора OpenType в свою библиотеку, но решили отказаться от этого и сосредоточить внимание на FreeType только рисование . Тем не менее, вы все равно можете использовать freetype для извлечения и растеризации любого глифа во всех шрифтах. Единственное, вы собираетесь жестко закодировать индексы глифов.

«Индексы глифов» - это не что иное, как «какое значение кодирования соответствует какому изображению», начиная с 0 и продолжая до последнего символа , Возможные кодировки для шрифта находятся в другом месте в данных шрифта; он может содержать нешифрованные Windows кодировки (более древние «кодовые страницы», но больше), полные кодировки Unicode, MacRoman и некоторые другие. Вам нужно только выбрать кодировку и указать, какой код символа в этой кодировке вы хотите, freetype найдет правильный глиф для вас.

Но функции OpenType могут это переопределить. Это отдельный набор переводов, управляемый тем, какую функцию вы выбираете (и какой скрипт и даже для какого языка ). Полностью поддерживающий OpenType интерфейс увидит ваши глифы, поднимет ставки с необходимыми функциями и вернет необработанный индекс глифов .

, поскольку в одном файле шрифта может быть много необработанных глифов Не каждая кодировка будет указывать на каждый глиф в шрифте. Представьте себе бедного старого Макромана, например, только с 256 возможными символами. Никоим образом вы не можете обратиться ко всем 1294 символам Робото с этим. Даже Unicode, с тысячами и тысячами определенных записей, не может адресовать их всех, потому что возможно создать stylisti c альтернатив для некоторых символов (таких как swa sh прописные), которые, насколько это возможно что касается Unicode, они идентичны их оригиналу и комбинациям символов, которые используются в качестве лигатур , где полная последовательность, такая как Zapfino, нарисована как один сложный глиф (который находится в шрифте "Zapfino "; и это потрясающе).

Некоторые дизайнеры шрифтов разрешают доступ к специальным символам через« частные »кодовые точки Unicode (что означает« свободно определять каждый »), но я не думаю, что Roboto имеет это.

Можно запросить у FreeType полную таблицу GSUB, а затем проанализировать ее, чтобы найти правильные переводы для маленьких заглавных букв, и если вы это сделаете, вы будете рады обнаружить, что ваш код будет работать с любой шрифт с настоящими маленькими заглавными буквами (!), Но это колоссальная работа.

Так что я обманул. Некоторое программное обеспечение позволяет проверять шрифты по каждому глифу, и я использовал Adobe InDesign, чтобы найти индекс глифа для всего A..Z в маленьких заглавных буквах.

Однако у этого есть недостаток. Он будет только работать (надежно) с одним конкретным выпуском Roboto-Medium, потому что дизайнер может добавлять, удалять и вообще перемещать глифы по желанию для каждого нового выпуска. Помните, что это никак не влияет на то, как другое программное обеспечение обрабатывает шрифт! Таблицы кодирования и OpenType настроены так, чтобы соответствовать, поэтому программному обеспечению не нужно , чтобы знать что-либо об индексах глифа каждого шрифта.
Также не гарантируется , что эти же индексы будут работать с другими шрифтами в семействе Roboto. Дизайнер, возможно, использовал точно такой же макет, а может и нет. Практически, как указано выше, им даже не нужно заботиться об этом.

Я скачал самую последнюю версию 2.137; 2017; если у вас его нет, загрузите его первым!

Ниже приведен пример кода для распечатки значений градаций серого для всех глифов с маленькими заглавными буквами в порядке их горизонтального смещения от начальной точки глифа (смещение влево). ) и вертикальная высота в пикселях, измеренная от базовой линии. Настройте по желанию, чтобы соответствовать собственным процедурам рисования текста PIL; это может занять некоторое время, чтобы получить его Just Right ™.

(Извиняюсь за довольно простой c пример кода. Мне удалось испортить мою установку PIL для Python 2.7 и 3. (что-то) и поэтому я не могу показать что-то графически, к счастью, модуль freetype все еще работал.

import freetype
import os.path

smcapGlyphs = [563,562,561,560,552,486,485,484,483,482,481,480,479,
 478,477,476,475,474,473,472,471,470,469,468,467,466]

def dump_bytes(buf,wide,high):
    for y in range(high):
        for x in range(wide):
            print ('%02x ' % buf[y*wide+x], end='')
        print ()

face = freetype.Face(os.path.expanduser('~')+"/Library/Fonts/Roboto-Medium.ttf")
face.set_char_size( 48*64 )

for index,i in enumerate(smcapGlyphs):
    face.load_glyph(i)

    print (chr(65+index))
    bitmap = face.glyph.bitmap
    width  = face.glyph.bitmap.width
    rows   = face.glyph.bitmap.rows
    pitch = face.glyph.bitmap.pitch
    print (face.glyph.bitmap_left)
    print (face.glyph.bitmap_top)

    dump_bytes (bitmap.buffer, pitch,rows)
    print ()
...