diff --git a/src/calibre/utils/fonts/sfnt/cff.py b/src/calibre/utils/fonts/sfnt/cff.py new file mode 100644 index 0000000000..641fa17d97 --- /dev/null +++ b/src/calibre/utils/fonts/sfnt/cff.py @@ -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 ' +__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' +] +# }}} + diff --git a/src/calibre/utils/fonts/sfnt/cff/__init__.py b/src/calibre/utils/fonts/sfnt/cff/__init__.py new file mode 100644 index 0000000000..743a9d0561 --- /dev/null +++ b/src/calibre/utils/fonts/sfnt/cff/__init__.py @@ -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 ' +__docformat__ = 'restructuredtext en' + + + diff --git a/src/calibre/utils/fonts/sfnt/cff/dict_data.py b/src/calibre/utils/fonts/sfnt/cff/dict_data.py new file mode 100644 index 0000000000..df573bb26c --- /dev/null +++ b/src/calibre/utils/fonts/sfnt/cff/dict_data.py @@ -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 ' +__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()), + ] + diff --git a/src/calibre/utils/fonts/sfnt/cff/table.py b/src/calibre/utils/fonts/sfnt/cff/table.py new file mode 100644 index 0000000000..af76d1f254 --- /dev/null +++ b/src/calibre/utils/fonts/sfnt/cff/table.py @@ -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 ' +__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' +] +# }}} + diff --git a/src/calibre/utils/fonts/sfnt/container.py b/src/calibre/utils/fonts/sfnt/container.py index 2e08ef3cee..a6c0c5c81d 100644 --- a/src/calibre/utils/fonts/sfnt/container.py +++ b/src/calibre/utils/fonts/sfnt/container.py @@ -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.glyf import GlyfTable 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 @@ -42,6 +43,7 @@ class Sfnt(object): b'loca' : LocaTable, b'glyf' : GlyfTable, b'cmap' : CmapTable, + b'CFF ' : CFFTable, }.get(table_tag, UnknownTable)(table) def __getitem__(self, key): diff --git a/src/calibre/utils/fonts/sfnt/subset.py b/src/calibre/utils/fonts/sfnt/subset.py index e3883c180d..c7e3ec8c34 100644 --- a/src/calibre/utils/fonts/sfnt/subset.py +++ b/src/calibre/utils/fonts/sfnt/subset.py @@ -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=()): chars = list(map(ord, individual_chars)) for r in ranges: @@ -91,7 +96,11 @@ def subset(raw, individual_chars, ranges=()): subset_truetype(sfnt, character_map) elif b'CFF ' in sfnt: # 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') else: raise UnsupportedFont('This font does not contain TrueType '