Ответ 1
Сильвия прокомментирует OP...
Возможно, вы захотите рассмотреть параметр
encoding
, напримерImageFont.truetype(font_path,font_size,encoding="big5")
... получает вас на полпути, но похоже, что вы также должны вручную переводить символы Unicode, если вы не используете шрифт Unicode.
Для шрифтов, которые используют кодировку "big5hkscs", мне пришлось это сделать...
>>> u = u'\u6211' # Unicode for 我
>>> u.encode('big5hkscs')
'\xa7\xda'
... затем используйте u'\ua7da'
, чтобы получить правильный глиф, который немного странный, но он выглядит как единственный способ передать многобайтовый символ в PIL.
Следующий код работает для меня как на Python 2.7.4, так и на Python 3.3.1, с PIL 1.1.7...
from PIL import Image, ImageDraw, ImageFont
# Declare font files and encodings
FONT1 = ('Jin_Wen_Da_Zhuan_Ti.ttf', 'unicode')
FONT2 = ('Zhong_Guo_Long_Jin_Shi_Zhuan.ttf', 'big5hkscs')
FONT3 = ('Zhong_Yan_Yuan_Jin_Wen.ttf', 'big5hkscs')
# Declare a mapping from encodings used by str.encode() to encodings used by
# the FreeType library
ENCODING_MAP = {'unicode': 'unic',
'big5': 'big5',
'big5hkscs': 'big5',
'shift-jis': 'sjis'}
# The glyphs we want to draw
GLYPHS = ((FONT1, u'\u6211'),
(FONT2, u'\u6211'),
(FONT3, u'\u6211'),
(FONT3, u'\u66ce'),
(FONT2, u'\u4e36'))
# Returns PIL Image object
def draw_glyph(font_file, font_encoding, unicode_char, glyph_size=128):
# Translate unicode string if necessary
if font_encoding != 'unicode':
mb_string = unicode_char.encode(font_encoding)
try:
# Try using Python 2.x unichr
unicode_char = unichr(ord(mb_string[0]) << 8 | ord(mb_string[1]))
except NameError:
# Use Python 3.x-compatible code
unicode_char = chr(mb_string[0] << 8 | mb_string[1])
# Load font using mapped encoding
font = ImageFont.truetype(font_file, glyph_size, encoding=ENCODING_MAP[font_encoding])
# Now draw the glyph
img = Image.new('L', (glyph_size, glyph_size), 'white')
draw = ImageDraw.Draw(img)
draw.text((0, 0), text=unicode_char, font=font)
return img
# Save an image for each glyph we want to draw
for (font_file, font_encoding), unicode_char in GLYPHS:
img = draw_glyph(font_file, font_encoding, unicode_char)
filename = '%s-%s.png' % (font_file, hex(ord(unicode_char)))
img.save(filename)
Обратите внимание, что я переименовал файлы шрифтов в те же имена, что и файлы 7zip. Я стараюсь избегать использования символов, отличных от ASCII, в примерах кода, поскольку иногда они копируются/копируются.
Этот пример должен отлично работать для типов, объявленных в ENCODING_MAP
, которые могут быть расширены при необходимости (см. строки кодировки FreeType для действительных Коды FreeType), но вам нужно будет изменить часть кода в случаях, когда Python str.encode()
не создает многобайтную строку длины 2.
Обновление
Если проблема в файле ttf, как вы могли бы найти ответ в исходный код PIL и FreeType? Вы, кажется, говорите, что PIL виноват, но зачем нужно проходить unicode_char.encode(...). decode (...), когда вы просто хотите unicode_char?
Как я понимаю, формат TrueType был разработан до того, как Unicode стал широко распространенным, поэтому, если вы хотите создать китайский шрифт тогда вам пришлось бы использовать один из кодировок, который использовался в то время, и Китай в основном использовал Big5 с середины 1980-х годов.
Разумеется, тогда должен был быть способ получить глифы из TTF с кодировкой Big5 с использованием кодировок символов Big5.
Код C для визуализации строки с PIL начинается с функции font_render()
и в конечном итоге вызывает FT_Get_Char_Index()
, чтобы найти правильный глиф, учитывая код символа как unsigned long
.
Однако функция PIL font_getchar()
, которая дает, что unsigned long
принимает только типы Python string
и unicode
, и поскольку он, похоже, не делает никакого перевода кодировок символов сам по себе, казалось, что единственным способом получить правильное значение для набора символов Big5 было принуждение символа Python unicode
к правильному значению unsigned long
, используя факт, что u'\ua7da'
хранился внутри как целое число 0xa7da
, либо в 16 бит, либо в 32 бита, в зависимости от того, как вы скомпилировали Python.
TBH, было довольно много догадок, так как я не стал исследовать, что именно эффект параметра ImageFont.truetype()
encoding
есть, но по внешнему виду он не должен делать никакого перевода кодирования символов, а скорее для того, чтобы один TTF файл поддерживал несколько кодировок символов одних и тех же глифов, используя функцию FT_Select_Charmap()
для переключения между их.
Итак, как я понимаю, взаимодействие библиотеки FreeType с файлами TTF работает примерно так:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class TTF(object):
glyphs = {}
encoding_maps = {}
def __init__(self, encoding='unic'):
self.set_encoding(encoding)
def set_encoding(self, encoding):
self.current_encoding = encoding
def get_glyph(self, charcode):
try:
return self.glyphs[self.encoding_maps[self.current_encoding][charcode]]
except KeyError:
return ' '
class MyTTF(TTF):
glyphs = {1: '我',
2: '曎'}
encoding_maps = {'unic': {0x6211: 1, 0x66ce: 2},
'big5': {0xa7da: 1, 0x93be: 2}}
font = MyTTF()
print 'Get via Unicode map: %s' % font.get_glyph(0x6211)
font.set_encoding('big5')
print 'Get via Big5 map: %s' % font.get_glyph(0xa7da)
... но это зависит от каждого TTF, чтобы предоставить переменную encoding_maps
, и нет требования для TTF предоставить один для Unicode. Действительно, маловероятно, чтобы шрифт, созданный до принятия Unicode, имел бы.
Предполагая, что все правильно, то нет ничего плохого в TTF - проблема только с PIL, что делает его немного неудобным для доступа к глифам для шрифтов, которые не имеют Unicode-сопоставления и для которых требуется глиф unsigned long
символьный код больше 255.