mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Start work on subsetting CFF tables
This commit is contained in:
parent
03ac5dc280
commit
88a0bb48e7
153
src/calibre/utils/fonts/sfnt/cff.py
Normal file
153
src/calibre/utils/fonts/sfnt/cff.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from struct import unpack_from, unpack
|
||||||
|
|
||||||
|
from calibre.utils.fonts.sfnt import UnknownTable
|
||||||
|
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
|
||||||
|
|
||||||
|
# Useful links
|
||||||
|
# http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
|
||||||
|
# http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
|
||||||
|
|
||||||
|
class CFF(object):
|
||||||
|
|
||||||
|
def __init__(self, raw):
|
||||||
|
(self.major_version, self.minor_version, self.header_size,
|
||||||
|
self.offset_size) = unpack_from(b'>4B', raw)
|
||||||
|
if (self.major_version, self.minor_version) != (1, 0):
|
||||||
|
raise UnsupportedFont('The CFF table has unknown version: '
|
||||||
|
'(%d, %d)'%(self.major_version, self.minor_version))
|
||||||
|
offset = self.header_size
|
||||||
|
|
||||||
|
# Read Names Index
|
||||||
|
self.font_names = Index(raw, offset)
|
||||||
|
offset = self.font_names.pos
|
||||||
|
if len(self.font_names) > 1:
|
||||||
|
raise UnsupportedFont('CFF table has more than one font.')
|
||||||
|
# Read Top Dict
|
||||||
|
self.top_index = Index(raw, offset)
|
||||||
|
offset = self.top_index.pos
|
||||||
|
|
||||||
|
# Read strings
|
||||||
|
self.strings = Strings(raw, offset)
|
||||||
|
offset = self.strings.pos
|
||||||
|
print (self.strings[len(cff_standard_strings):])
|
||||||
|
|
||||||
|
class Index(list):
|
||||||
|
|
||||||
|
def __init__(self, raw, offset):
|
||||||
|
list.__init__(self)
|
||||||
|
|
||||||
|
count = unpack_from(b'>H', raw, offset)[0]
|
||||||
|
offset += 2
|
||||||
|
self.pos = offset
|
||||||
|
|
||||||
|
if count > 0:
|
||||||
|
self.offset_size = unpack_from(b'>B', raw, offset)[0]
|
||||||
|
offset += 1
|
||||||
|
if self.offset_size == 3:
|
||||||
|
offsets = [unpack(b'>L', b'\0' + raw[i:i+3])[0]
|
||||||
|
for i in xrange(offset, 3*(count+2), 3)]
|
||||||
|
else:
|
||||||
|
fmt = {1:'B', 2:'H', 4:'L'}.get(self.offset_size)
|
||||||
|
fmt = ('>%d%s'%(count+1, fmt)).encode('ascii')
|
||||||
|
offsets = unpack_from(fmt, raw, offset)
|
||||||
|
offset += self.offset_size * (count+1) - 1
|
||||||
|
|
||||||
|
for i in xrange(len(offsets)-1):
|
||||||
|
off, noff = offsets[i:i+2]
|
||||||
|
obj = raw[offset+i:offset+noff]
|
||||||
|
self.append(obj)
|
||||||
|
|
||||||
|
self.pos = offset + offsets[-1]
|
||||||
|
|
||||||
|
class Strings(Index):
|
||||||
|
|
||||||
|
def __init__(self, raw, offset):
|
||||||
|
super(Strings, self).__init__(raw, offset)
|
||||||
|
for x in reversed(cff_standard_strings):
|
||||||
|
self.insert(0, x)
|
||||||
|
|
||||||
|
class CFFTable(UnknownTable):
|
||||||
|
|
||||||
|
def decompile(self):
|
||||||
|
self.cff = CFF(self.raw)
|
||||||
|
|
||||||
|
# cff_standard_strings {{{
|
||||||
|
# The 391 Standard Strings as used in the CFF format.
|
||||||
|
# from Adobe Technical None #5176, version 1.0, 18 March 1998
|
||||||
|
|
||||||
|
cff_standard_strings = [
|
||||||
|
'.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
|
||||||
|
'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus',
|
||||||
|
'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four',
|
||||||
|
'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal',
|
||||||
|
'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||||
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
|
||||||
|
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft',
|
||||||
|
'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling',
|
||||||
|
'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
|
||||||
|
'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl',
|
||||||
|
'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet',
|
||||||
|
'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright',
|
||||||
|
'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex',
|
||||||
|
'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla',
|
||||||
|
'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash',
|
||||||
|
'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe',
|
||||||
|
'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf',
|
||||||
|
'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn',
|
||||||
|
'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply',
|
||||||
|
'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave',
|
||||||
|
'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
|
||||||
|
'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
|
||||||
|
'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
|
||||||
|
'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
|
||||||
|
'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
|
||||||
|
'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
|
||||||
|
'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
|
||||||
|
'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
|
||||||
|
'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
|
||||||
|
'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
|
||||||
|
'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
|
||||||
|
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
|
||||||
|
'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
|
||||||
|
'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
|
||||||
|
'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
|
||||||
|
'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
|
||||||
|
'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
|
||||||
|
'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
|
||||||
|
'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
|
||||||
|
'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
|
||||||
|
'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
|
||||||
|
'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
|
||||||
|
'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
|
||||||
|
'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
|
||||||
|
'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
|
||||||
|
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
|
||||||
|
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'foursuperior',
|
||||||
|
'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior',
|
||||||
|
'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
|
||||||
|
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
|
||||||
|
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
|
||||||
|
'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall',
|
||||||
|
'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall',
|
||||||
|
'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
|
||||||
|
'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
|
||||||
|
'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
|
||||||
|
'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
|
||||||
|
'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
|
||||||
|
'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
|
||||||
|
'001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
|
||||||
|
'Semibold'
|
||||||
|
]
|
||||||
|
# }}}
|
||||||
|
|
11
src/calibre/utils/fonts/sfnt/cff/__init__.py
Normal file
11
src/calibre/utils/fonts/sfnt/cff/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
|
200
src/calibre/utils/fonts/sfnt/cff/dict_data.py
Normal file
200
src/calibre/utils/fonts/sfnt/cff/dict_data.py
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from struct import unpack
|
||||||
|
|
||||||
|
t1_operand_encoding = [None] * 256
|
||||||
|
t1_operand_encoding[0:32] = (32) * ["do_operator"]
|
||||||
|
t1_operand_encoding[32:247] = (247 - 32) * ["read_byte"]
|
||||||
|
t1_operand_encoding[247:251] = (251 - 247) * ["read_small_int1"]
|
||||||
|
t1_operand_encoding[251:255] = (255 - 251) * ["read_small_int2"]
|
||||||
|
t1_operand_encoding[255] = "read_long_int"
|
||||||
|
|
||||||
|
t2_operand_encoding = t1_operand_encoding[:]
|
||||||
|
t2_operand_encoding[28] = "read_short_int"
|
||||||
|
t2_operand_encoding[255] = "read_fixed_1616"
|
||||||
|
|
||||||
|
cff_dict_operand_encoding = t2_operand_encoding[:]
|
||||||
|
cff_dict_operand_encoding[29] = "read_long_int"
|
||||||
|
cff_dict_operand_encoding[30] = "read_real_number"
|
||||||
|
cff_dict_operand_encoding[255] = "reserved"
|
||||||
|
|
||||||
|
real_nibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'.', 'E', 'E-', None, '-']
|
||||||
|
|
||||||
|
class SimpleConverter(object):
|
||||||
|
|
||||||
|
def read(self, parent, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
def write(self, parent, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
class TODO(SimpleConverter):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Reader(dict):
|
||||||
|
|
||||||
|
def read_byte(self, b0, data, index):
|
||||||
|
return b0 - 139, index
|
||||||
|
|
||||||
|
def read_small_int1(self, b0, data, index):
|
||||||
|
b1 = ord(data[index])
|
||||||
|
return (b0-247)*256 + b1 + 108, index+1
|
||||||
|
|
||||||
|
def read_small_int2(self, b0, data, index):
|
||||||
|
b1 = ord(data[index])
|
||||||
|
return -(b0-251)*256 - b1 - 108, index+1
|
||||||
|
|
||||||
|
def read_short_int(self, b0, data, index):
|
||||||
|
bin = data[index] + data[index+1]
|
||||||
|
value, = unpack(b">h", bin)
|
||||||
|
return value, index+2
|
||||||
|
|
||||||
|
def read_long_int(self, b0, data, index):
|
||||||
|
bin = data[index] + data[index+1] + data[index+2] + data[index+3]
|
||||||
|
value, = unpack(b">l", bin)
|
||||||
|
return value, index+4
|
||||||
|
|
||||||
|
def read_fixed_1616(self, b0, data, index):
|
||||||
|
bin = data[index] + data[index+1] + data[index+2] + data[index+3]
|
||||||
|
value, = unpack(b">l", bin)
|
||||||
|
return value / 65536.0, index+4
|
||||||
|
|
||||||
|
def read_real_number(self, b0, data, index):
|
||||||
|
number = ''
|
||||||
|
while True:
|
||||||
|
b = ord(data[index])
|
||||||
|
index = index + 1
|
||||||
|
nibble0 = (b & 0xf0) >> 4
|
||||||
|
nibble1 = b & 0x0f
|
||||||
|
if nibble0 == 0xf:
|
||||||
|
break
|
||||||
|
number = number + real_nibbles[nibble0]
|
||||||
|
if nibble1 == 0xf:
|
||||||
|
break
|
||||||
|
number = number + real_nibbles[nibble1]
|
||||||
|
return float(number), index
|
||||||
|
|
||||||
|
class Dict(Reader):
|
||||||
|
|
||||||
|
operand_encoding = cff_dict_operand_encoding
|
||||||
|
TABLE = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Reader.__init__(self)
|
||||||
|
table = self.TABLE[:]
|
||||||
|
for i in xrange(len(table)):
|
||||||
|
op, name, arg, default, conv = table[i]
|
||||||
|
if conv is not None:
|
||||||
|
continue
|
||||||
|
if arg in ("delta", "array", 'number', 'SID'):
|
||||||
|
conv = SimpleConverter()
|
||||||
|
else:
|
||||||
|
raise Exception('Should not happen')
|
||||||
|
table[i] = op, name, arg, default, conv
|
||||||
|
|
||||||
|
|
||||||
|
self.operators = {op:(name, arg) for op, name, arg, default, conv in
|
||||||
|
table}
|
||||||
|
|
||||||
|
def decompile(self, strings, data):
|
||||||
|
self.strings = strings
|
||||||
|
self.stack = []
|
||||||
|
index = 0
|
||||||
|
while index < len(data):
|
||||||
|
b0 = ord(data[index])
|
||||||
|
index += 1
|
||||||
|
handler = getattr(self, self.operand_encoding[b0])
|
||||||
|
value, index = handler(b0, data, index)
|
||||||
|
if value is not None:
|
||||||
|
self.stack.append(value)
|
||||||
|
|
||||||
|
def do_operator(self, b0, data, index):
|
||||||
|
if b0 == 12:
|
||||||
|
op = (b0, ord(data[index]))
|
||||||
|
index += 1
|
||||||
|
else:
|
||||||
|
op = b0
|
||||||
|
operator, arg_type = self.operators[op]
|
||||||
|
self.handle_operator(operator, arg_type)
|
||||||
|
return None, index
|
||||||
|
|
||||||
|
def handle_operator(self, operator, arg_type):
|
||||||
|
if isinstance(arg_type, tuple):
|
||||||
|
value = ()
|
||||||
|
for i in xrange(len(arg_type)-1, -1, -1):
|
||||||
|
arg = arg_type[i]
|
||||||
|
arghandler = getattr(self, 'arg_' + arg)
|
||||||
|
value = (arghandler(operator),) + value
|
||||||
|
else:
|
||||||
|
arghandler = getattr(self, 'arg_' + arg_type)
|
||||||
|
value = arghandler(operator)
|
||||||
|
self[operator] = value
|
||||||
|
|
||||||
|
def arg_number(self, name):
|
||||||
|
return self.stack.pop()
|
||||||
|
|
||||||
|
def arg_SID(self, name):
|
||||||
|
return self.strings[self.stack.pop()]
|
||||||
|
|
||||||
|
def arg_array(self, name):
|
||||||
|
ans = self.stack[:]
|
||||||
|
del self.stack[:]
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def arg_delta(self, name):
|
||||||
|
out = []
|
||||||
|
current = 0
|
||||||
|
for v in self.stack:
|
||||||
|
current = current + v
|
||||||
|
out.append(current)
|
||||||
|
del self.stack[:]
|
||||||
|
return out
|
||||||
|
|
||||||
|
class TopDict(Dict):
|
||||||
|
|
||||||
|
TABLE = [
|
||||||
|
#opcode name argument type default converter
|
||||||
|
((12, 30), 'ROS', ('SID','SID','number'), None, SimpleConverter()),
|
||||||
|
((12, 20), 'SyntheticBase', 'number', None, None),
|
||||||
|
(0, 'version', 'SID', None, None),
|
||||||
|
(1, 'Notice', 'SID', None, None),
|
||||||
|
((12, 0), 'Copyright', 'SID', None, None),
|
||||||
|
(2, 'FullName', 'SID', None, None),
|
||||||
|
((12, 38), 'FontName', 'SID', None, None),
|
||||||
|
(3, 'FamilyName', 'SID', None, None),
|
||||||
|
(4, 'Weight', 'SID', None, None),
|
||||||
|
((12, 1), 'isFixedPitch', 'number', 0, None),
|
||||||
|
((12, 2), 'ItalicAngle', 'number', 0, None),
|
||||||
|
((12, 3), 'UnderlinePosition', 'number', None, None),
|
||||||
|
((12, 4), 'UnderlineThickness', 'number', 50, None),
|
||||||
|
((12, 5), 'PaintType', 'number', 0, None),
|
||||||
|
((12, 6), 'CharstringType', 'number', 2, None),
|
||||||
|
((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
|
||||||
|
(13, 'UniqueID', 'number', None, None),
|
||||||
|
(5, 'FontBBox', 'array', [0,0,0,0], None),
|
||||||
|
((12, 8), 'StrokeWidth', 'number', 0, None),
|
||||||
|
(14, 'XUID', 'array', None, None),
|
||||||
|
((12, 21), 'PostScript', 'SID', None, None),
|
||||||
|
((12, 22), 'BaseFontName', 'SID', None, None),
|
||||||
|
((12, 23), 'BaseFontBlend', 'delta', None, None),
|
||||||
|
((12, 31), 'CIDFontVersion', 'number', 0, None),
|
||||||
|
((12, 32), 'CIDFontRevision', 'number', 0, None),
|
||||||
|
((12, 33), 'CIDFontType', 'number', 0, None),
|
||||||
|
((12, 34), 'CIDCount', 'number', 8720, None),
|
||||||
|
(15, 'charset', 'number', 0, TODO()),
|
||||||
|
((12, 35), 'UIDBase', 'number', None, None),
|
||||||
|
(16, 'Encoding', 'number', 0, TODO()),
|
||||||
|
(18, 'Private', ('number','number'), None, TODO()),
|
||||||
|
((12, 37), 'FDSelect', 'number', None, TODO()),
|
||||||
|
((12, 36), 'FDArray', 'number', None, TODO()),
|
||||||
|
(17, 'CharStrings', 'number', None, TODO()),
|
||||||
|
]
|
||||||
|
|
159
src/calibre/utils/fonts/sfnt/cff/table.py
Normal file
159
src/calibre/utils/fonts/sfnt/cff/table.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from struct import unpack_from, unpack
|
||||||
|
|
||||||
|
from calibre.utils.fonts.sfnt import UnknownTable
|
||||||
|
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
|
||||||
|
from calibre.utils.fonts.sfnt.cff.dict_data import TopDict
|
||||||
|
|
||||||
|
# Useful links
|
||||||
|
# http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
|
||||||
|
# http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
|
||||||
|
|
||||||
|
class CFF(object):
|
||||||
|
|
||||||
|
def __init__(self, raw):
|
||||||
|
(self.major_version, self.minor_version, self.header_size,
|
||||||
|
self.offset_size) = unpack_from(b'>4B', raw)
|
||||||
|
if (self.major_version, self.minor_version) != (1, 0):
|
||||||
|
raise UnsupportedFont('The CFF table has unknown version: '
|
||||||
|
'(%d, %d)'%(self.major_version, self.minor_version))
|
||||||
|
offset = self.header_size
|
||||||
|
|
||||||
|
# Read Names Index
|
||||||
|
self.font_names = Index(raw, offset)
|
||||||
|
offset = self.font_names.pos
|
||||||
|
if len(self.font_names) > 1:
|
||||||
|
raise UnsupportedFont('CFF table has more than one font.')
|
||||||
|
|
||||||
|
# Read Top Dict
|
||||||
|
self.top_index = Index(raw, offset)
|
||||||
|
self.top_dict = TopDict()
|
||||||
|
offset = self.top_index.pos
|
||||||
|
|
||||||
|
# Read strings
|
||||||
|
self.strings = Strings(raw, offset)
|
||||||
|
offset = self.strings.pos
|
||||||
|
|
||||||
|
# Decompile Top Dict
|
||||||
|
self.top_dict.decompile(self.strings, self.top_index[0])
|
||||||
|
import pprint
|
||||||
|
pprint.pprint(self.top_dict)
|
||||||
|
|
||||||
|
class Index(list):
|
||||||
|
|
||||||
|
def __init__(self, raw, offset, prepend=()):
|
||||||
|
list.__init__(self)
|
||||||
|
self.extend(prepend)
|
||||||
|
|
||||||
|
count = unpack_from(b'>H', raw, offset)[0]
|
||||||
|
offset += 2
|
||||||
|
self.pos = offset
|
||||||
|
|
||||||
|
if count > 0:
|
||||||
|
self.offset_size = unpack_from(b'>B', raw, offset)[0]
|
||||||
|
offset += 1
|
||||||
|
if self.offset_size == 3:
|
||||||
|
offsets = [unpack(b'>L', b'\0' + raw[i:i+3])[0]
|
||||||
|
for i in xrange(offset, 3*(count+2), 3)]
|
||||||
|
else:
|
||||||
|
fmt = {1:'B', 2:'H', 4:'L'}.get(self.offset_size)
|
||||||
|
fmt = ('>%d%s'%(count+1, fmt)).encode('ascii')
|
||||||
|
offsets = unpack_from(fmt, raw, offset)
|
||||||
|
offset += self.offset_size * (count+1) - 1
|
||||||
|
|
||||||
|
for i in xrange(len(offsets)-1):
|
||||||
|
off, noff = offsets[i:i+2]
|
||||||
|
obj = raw[offset+off:offset+noff]
|
||||||
|
self.append(obj)
|
||||||
|
|
||||||
|
self.pos = offset + offsets[-1]
|
||||||
|
|
||||||
|
class Strings(Index):
|
||||||
|
|
||||||
|
def __init__(self, raw, offset):
|
||||||
|
super(Strings, self).__init__(raw, offset, prepend=cff_standard_strings)
|
||||||
|
|
||||||
|
class CFFTable(UnknownTable):
|
||||||
|
|
||||||
|
def decompile(self):
|
||||||
|
self.cff = CFF(self.raw)
|
||||||
|
|
||||||
|
# cff_standard_strings {{{
|
||||||
|
# The 391 Standard Strings as used in the CFF format.
|
||||||
|
# from Adobe Technical None #5176, version 1.0, 18 March 1998
|
||||||
|
|
||||||
|
cff_standard_strings = [
|
||||||
|
'.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
|
||||||
|
'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus',
|
||||||
|
'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four',
|
||||||
|
'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal',
|
||||||
|
'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||||
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
|
||||||
|
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft',
|
||||||
|
'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling',
|
||||||
|
'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
|
||||||
|
'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl',
|
||||||
|
'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet',
|
||||||
|
'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright',
|
||||||
|
'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex',
|
||||||
|
'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla',
|
||||||
|
'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash',
|
||||||
|
'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe',
|
||||||
|
'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf',
|
||||||
|
'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn',
|
||||||
|
'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply',
|
||||||
|
'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave',
|
||||||
|
'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
|
||||||
|
'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
|
||||||
|
'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
|
||||||
|
'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
|
||||||
|
'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
|
||||||
|
'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
|
||||||
|
'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
|
||||||
|
'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
|
||||||
|
'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
|
||||||
|
'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
|
||||||
|
'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
|
||||||
|
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
|
||||||
|
'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
|
||||||
|
'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
|
||||||
|
'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
|
||||||
|
'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
|
||||||
|
'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
|
||||||
|
'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
|
||||||
|
'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
|
||||||
|
'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
|
||||||
|
'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
|
||||||
|
'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
|
||||||
|
'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
|
||||||
|
'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
|
||||||
|
'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
|
||||||
|
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
|
||||||
|
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'foursuperior',
|
||||||
|
'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior',
|
||||||
|
'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
|
||||||
|
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
|
||||||
|
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
|
||||||
|
'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall',
|
||||||
|
'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall',
|
||||||
|
'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
|
||||||
|
'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
|
||||||
|
'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
|
||||||
|
'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
|
||||||
|
'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
|
||||||
|
'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
|
||||||
|
'001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
|
||||||
|
'Semibold'
|
||||||
|
]
|
||||||
|
# }}}
|
||||||
|
|
@ -21,6 +21,7 @@ from calibre.utils.fonts.sfnt.maxp import MaxpTable
|
|||||||
from calibre.utils.fonts.sfnt.loca import LocaTable
|
from calibre.utils.fonts.sfnt.loca import LocaTable
|
||||||
from calibre.utils.fonts.sfnt.glyf import GlyfTable
|
from calibre.utils.fonts.sfnt.glyf import GlyfTable
|
||||||
from calibre.utils.fonts.sfnt.cmap import CmapTable
|
from calibre.utils.fonts.sfnt.cmap import CmapTable
|
||||||
|
from calibre.utils.fonts.sfnt.cff.table import CFFTable
|
||||||
|
|
||||||
# OpenType spec: http://www.microsoft.com/typography/otspec/otff.htm
|
# OpenType spec: http://www.microsoft.com/typography/otspec/otff.htm
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ class Sfnt(object):
|
|||||||
b'loca' : LocaTable,
|
b'loca' : LocaTable,
|
||||||
b'glyf' : GlyfTable,
|
b'glyf' : GlyfTable,
|
||||||
b'cmap' : CmapTable,
|
b'cmap' : CmapTable,
|
||||||
|
b'CFF ' : CFFTable,
|
||||||
}.get(table_tag, UnknownTable)(table)
|
}.get(table_tag, UnknownTable)(table)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
|
@ -66,6 +66,11 @@ def subset_truetype(sfnt, character_map):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
def subset_postscript(sfnt, character_map):
|
||||||
|
cff = sfnt[b'CFF ']
|
||||||
|
cff.decompile()
|
||||||
|
raise Exception('TODO: Implement CFF subsetting')
|
||||||
|
|
||||||
def subset(raw, individual_chars, ranges=()):
|
def subset(raw, individual_chars, ranges=()):
|
||||||
chars = list(map(ord, individual_chars))
|
chars = list(map(ord, individual_chars))
|
||||||
for r in ranges:
|
for r in ranges:
|
||||||
@ -91,7 +96,11 @@ def subset(raw, individual_chars, ranges=()):
|
|||||||
subset_truetype(sfnt, character_map)
|
subset_truetype(sfnt, character_map)
|
||||||
elif b'CFF ' in sfnt:
|
elif b'CFF ' in sfnt:
|
||||||
# PostScript Outlines
|
# PostScript Outlines
|
||||||
raise UnsupportedFont('This font contains PostScript outlines, '
|
from calibre.utils.config_base import tweaks
|
||||||
|
if tweaks['subset_cff_table']:
|
||||||
|
subset_postscript(sfnt, character_map)
|
||||||
|
else:
|
||||||
|
raise UnsupportedFont('This font contains PostScript outlines, '
|
||||||
'subsetting not supported')
|
'subsetting not supported')
|
||||||
else:
|
else:
|
||||||
raise UnsupportedFont('This font does not contain TrueType '
|
raise UnsupportedFont('This font does not contain TrueType '
|
||||||
|
Loading…
x
Reference in New Issue
Block a user