psCharStrings.py revision e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9
1a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard"""psCharStrings.py -- module implementing various kinds of CharStrings: 2a245d15da5d295af21ead9a01583c64796a31ad7Jean ChalardCFF dictionary data and Type1/Type2 CharStrings. 3a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard""" 4a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 5a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardimport types 6a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardimport struct 7a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardimport string 8a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 9a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 10a245d15da5d295af21ead9a01583c64796a31ad7Jean ChalardDEBUG = 0 11a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 12a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 13a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt1OperandEncoding = [None] * 256 14a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt1OperandEncoding[0:32] = (32) * ["do_operator"] 15a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt1OperandEncoding[32:247] = (247 - 32) * ["read_byte"] 16a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt1OperandEncoding[247:251] = (251 - 247) * ["read_smallInt1"] 17a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt1OperandEncoding[251:255] = (255 - 251) * ["read_smallInt2"] 189e51c6c0c62b78eaf14f92890981f9d35702b2c9Keisuke Kuroyanagit1OperandEncoding[255] = "read_longInt" 19a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardassert len(t1OperandEncoding) == 256 20a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 21a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt2OperandEncoding = t1OperandEncoding[:] 22a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt2OperandEncoding[28] = "read_shortInt" 2388bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagit2OperandEncoding[255] = "read_fixed1616" 2488bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi 2588bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke KuroyanagicffDictOperandEncoding = t2OperandEncoding[:] 2688bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke KuroyanagicffDictOperandEncoding[29] = "read_longInt" 2788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke KuroyanagicffDictOperandEncoding[30] = "read_realNumber" 2888bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke KuroyanagicffDictOperandEncoding[255] = "reserved" 296e4b674f83e0c287e00bfe6546db2a1f93daf5f0Keisuke Kuroyanagi 30a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 31a245d15da5d295af21ead9a01583c64796a31ad7Jean ChalardrealNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 32a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard '.', 'E', 'E-', None, '-'] 33a245d15da5d295af21ead9a01583c64796a31ad7Jean ChalardrealNibblesDict = {} 348dac7ce2e2b56c77e289507625b7695449b2e41aKeisuke Kuroyanagifor _i in range(len(realNibbles)): 356e4b674f83e0c287e00bfe6546db2a1f93daf5f0Keisuke Kuroyanagi realNibblesDict[realNibbles[_i]] = _i 36f87bb77a9183d126847d5925c2b03bec45fabd6dKeisuke Kuroyanagi 3788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi 38a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardclass ByteCodeBase: 39be6117058840492c2862f8ae9f7dc95c29f3a8f3Keisuke Kuroyanagi 40be6117058840492c2862f8ae9f7dc95c29f3a8f3Keisuke Kuroyanagi def read_byte(self, b0, data, index): 4188bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi return b0 - 139, index 42a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 43a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard def read_smallInt1(self, b0, data, index): 44a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard b1 = ord(data[index]) 45a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard return (b0-247)*256 + b1 + 108, index+1 46a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 47a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard def read_smallInt2(self, b0, data, index): 481d6afa179cd31010efe28f1c3e17698d6be79cabKeisuke Kuroyanagi b1 = ord(data[index]) 491d6afa179cd31010efe28f1c3e17698d6be79cabKeisuke Kuroyanagi return -(b0-251)*256 - b1 - 108, index+1 5088bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi 51660b00477c980d74be48529b9de70d9725ffc72bKeisuke Kuroyanagi def read_shortInt(self, b0, data, index): 52c4696b2eb6b25eea4d5c869683104ab99aec0421Keisuke Kuroyanagi bin = data[index] + data[index+1] 536b0561f9d26215209e8e8895f5c35982af5158f0Keisuke Kuroyanagi value, = struct.unpack(">h", bin) 54a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard return value, index+2 55a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 56a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard def read_longInt(self, b0, data, index): 5788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi bin = data[index] + data[index+1] + data[index+2] + data[index+3] 58a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard value, = struct.unpack(">l", bin) 59a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard return value, index+4 60a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 61a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard def read_fixed1616(self, b0, data, index): 62a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard bin = data[index] + data[index+1] + data[index+2] + data[index+3] 63a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard value, = struct.unpack(">l", bin) 64a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard return value / 65536.0, index+4 6588bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi 660a9c3f30b61ca50bb09c5cfc9ec3196c16efc656Keisuke Kuroyanagi def read_realNumber(self, b0, data, index): 67de3121dead395d32760379c03938faef6eac2f98Keisuke Kuroyanagi number = '' 68de3121dead395d32760379c03938faef6eac2f98Keisuke Kuroyanagi while 1: 6988bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi b = ord(data[index]) 7088bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi index = index + 1 7188bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi nibble0 = (b & 0xf0) >> 4 7288bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi nibble1 = b & 0x0f 7388bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi if nibble0 == 0xf: 7488bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi break 7588bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi number = number + realNibbles[nibble0] 7688bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi if nibble1 == 0xf: 7788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi break 7888bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi number = number + realNibbles[nibble1] 7988bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi return float(number), index 8088bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi 8188bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi 8288bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagidef buildOperatorDict(operatorList): 8388bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi oper = {} 8488bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi opc = {} 8588bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi for item in operatorList: 8688bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi if len(item) == 2: 8788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi oper[item[0]] = item[1] 8888bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi else: 8988bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi oper[item[0]] = item[1:] 90a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard if type(item[0]) == types.TupleType: 91a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard opc[item[1]] = item[0] 92a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard else: 93a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard opc[item[1]] = (item[0],) 94a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard return oper, opc 95a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 96a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard 97a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalardt2Operators = [ 98a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard# opcode name 99a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard (1, 'hstem'), 100e9121a68a67b8723477668130a16d4c72d98f6feKeisuke Kuroyanagi (3, 'vstem'), 101a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard (4, 'vmoveto'), 102a245d15da5d295af21ead9a01583c64796a31ad7Jean Chalard (5, 'rlineto'), 103c72652cb00eb0a02c46bfcd95202deec068ba5e0Yohei Yukawa (6, 'hlineto'), 10407e14126318f7661f76fdce421d723d64e7ea8deKeisuke Kuroyanagi (7, 'vlineto'), 10588bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi (8, 'rrcurveto'), 1066bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (10, 'callsubr'), 1076bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (11, 'return'), 1086bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (14, 'endchar'), 1096bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (16, 'blend'), 1106bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (18, 'hstemhm'), 1116bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (19, 'hintmask'), 1126bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (20, 'cntrmask'), 1136bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (21, 'rmoveto'), 11488bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi (22, 'hmoveto'), 1156bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (23, 'vstemhm'), 1166bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (24, 'rcurveline'), 1176bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (25, 'rlinecurve'), 1186bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (26, 'vvcurveto'), 1196bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (27, 'hhcurveto'), 12088bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi# (28, 'shortint'), # not really an operator 1216bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (29, 'callgsubr'), 1226bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi (30, 'vhcurveto'), 12307e14126318f7661f76fdce421d723d64e7ea8deKeisuke Kuroyanagi (31, 'hvcurveto'), 1246bf268132d60061fd26bd8cba63a12b56b22056eKeisuke Kuroyanagi ((12, 0), 'ignore'), # dotsection. Yes, there a few very early OTF/CFF 125c72652cb00eb0a02c46bfcd95202deec068ba5e0Yohei Yukawa # fonts with this deprecated operator. Just ignore it. 126b417d7d69f72f3f8224887f63b6d569dc1b19b02Yohei Yukawa ((12, 3), 'and'), 12788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 4), 'or'), 12888bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 5), 'not'), 12988bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 8), 'store'), 13088bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 9), 'abs'), 13188bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 10), 'add'), 13288bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 11), 'sub'), 13388bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 12), 'div'), 13488bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 13), 'load'), 13588bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 14), 'neg'), 13688bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 15), 'eq'), 13788bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 18), 'drop'), 13888bc312ad34321fb3e81be2dc939a889d065f4a7Keisuke Kuroyanagi ((12, 20), 'put'), 13940b6d6cbd11f4cdcff2ffbb756e8a4e3fb148ab7Keisuke Kuroyanagi ((12, 21), 'get'), 140985b2c2e91485a0cb293bff35a80877b2be71eaeKeisuke Kuroyanagi ((12, 22), 'ifelse'), 14140b6d6cbd11f4cdcff2ffbb756e8a4e3fb148ab7Keisuke Kuroyanagi ((12, 23), 'random'), 14269732169cca572c9b3f0a48e434e4abcbb4c95baKeisuke Kuroyanagi ((12, 24), 'mul'), 14375d8c20d03f8300946c5e4a4832117530110910bKeisuke Kuroyanagi ((12, 26), 'sqrt'), 14429dcf97e7472bde94d591c743c5311b641fd8238Keisuke Kuroyanagi ((12, 27), 'dup'), 14578aea9f133f00706fd6281e093b99a07edf5b81dKeisuke Kuroyanagi ((12, 28), 'exch'), 14678aea9f133f00706fd6281e093b99a07edf5b81dKeisuke Kuroyanagi ((12, 29), 'index'), 147 ((12, 30), 'roll'), 148 ((12, 34), 'hflex'), 149 ((12, 35), 'flex'), 150 ((12, 36), 'hflex1'), 151 ((12, 37), 'flex1'), 152] 153 154 155def getIntEncoder(format): 156 if format == "cff": 157 fourByteOp = chr(29) 158 elif format == "t1": 159 fourByteOp = chr(255) 160 else: 161 assert format == "t2" 162 fourByteOp = None 163 164 def encodeInt(value, fourByteOp=fourByteOp, chr=chr, 165 pack=struct.pack, unpack=struct.unpack): 166 if -107 <= value <= 107: 167 code = chr(value + 139) 168 elif 108 <= value <= 1131: 169 value = value - 108 170 code = chr((value >> 8) + 247) + chr(value & 0xFF) 171 elif -1131 <= value <= -108: 172 value = -value - 108 173 code = chr((value >> 8) + 251) + chr(value & 0xFF) 174 elif fourByteOp is None: 175 # T2 only supports 2 byte ints 176 if -32768 <= value <= 32767: 177 code = chr(28) + pack(">h", value) 178 else: 179 # Backwards compatible hack: due to a previous bug in FontTools, 180 # 16.16 fixed numbers were written out as 4-byte ints. When 181 # these numbers were small, they were wrongly written back as 182 # small ints instead of 4-byte ints, breaking round-tripping. 183 # This here workaround doesn't do it any better, since we can't 184 # distinguish anymore between small ints that were supposed to 185 # be small fixed numbers and small ints that were just small 186 # ints. Hence the warning. 187 import sys 188 sys.stderr.write("Warning: 4-byte T2 number got passed to the " 189 "IntType handler. This should happen only when reading in " 190 "old XML files.\n") 191 code = chr(255) + pack(">l", value) 192 else: 193 code = fourByteOp + pack(">l", value) 194 return code 195 196 return encodeInt 197 198 199encodeIntCFF = getIntEncoder("cff") 200encodeIntT1 = getIntEncoder("t1") 201encodeIntT2 = getIntEncoder("t2") 202 203def encodeFixed(f, pack=struct.pack): 204 # For T2 only 205 return "\xff" + pack(">l", int(round(f * 65536))) 206 207def encodeFloat(f): 208 # For CFF only, used in cffLib 209 s = str(f).upper() 210 if s[:2] == "0.": 211 s = s[1:] 212 elif s[:3] == "-0.": 213 s = "-" + s[2:] 214 nibbles = [] 215 while s: 216 c = s[0] 217 s = s[1:] 218 if c == "E" and s[:1] == "-": 219 s = s[1:] 220 c = "E-" 221 nibbles.append(realNibblesDict[c]) 222 nibbles.append(0xf) 223 if len(nibbles) % 2: 224 nibbles.append(0xf) 225 d = chr(30) 226 for i in range(0, len(nibbles), 2): 227 d = d + chr(nibbles[i] << 4 | nibbles[i+1]) 228 return d 229 230 231class CharStringCompileError(Exception): pass 232 233 234class T2CharString(ByteCodeBase): 235 236 operandEncoding = t2OperandEncoding 237 operators, opcodes = buildOperatorDict(t2Operators) 238 239 def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None): 240 if program is None: 241 program = [] 242 self.bytecode = bytecode 243 self.program = program 244 self.private = private 245 self.globalSubrs = globalSubrs 246 247 def __repr__(self): 248 if self.bytecode is None: 249 return "<%s (source) at %x>" % (self.__class__.__name__, id(self)) 250 else: 251 return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self)) 252 253 def getIntEncoder(self): 254 return encodeIntT2 255 256 def getFixedEncoder(self): 257 return encodeFixed 258 259 def decompile(self): 260 if not self.needsDecompilation(): 261 return 262 subrs = getattr(self.private, "Subrs", []) 263 decompiler = SimpleT2Decompiler(subrs, self.globalSubrs) 264 decompiler.execute(self) 265 266 def draw(self, pen): 267 subrs = getattr(self.private, "Subrs", []) 268 extractor = T2OutlineExtractor(pen, subrs, self.globalSubrs, 269 self.private.nominalWidthX, self.private.defaultWidthX) 270 extractor.execute(self) 271 self.width = extractor.width 272 273 def compile(self): 274 if self.bytecode is not None: 275 return 276 assert self.program, "illegal CharString: decompiled to empty program" 277 assert self.program[-1] in ("endchar", "return", "callsubr", "callgsubr", 278 "seac"), "illegal CharString" 279 bytecode = [] 280 opcodes = self.opcodes 281 program = self.program 282 encodeInt = self.getIntEncoder() 283 encodeFixed = self.getFixedEncoder() 284 i = 0 285 end = len(program) 286 while i < end: 287 token = program[i] 288 i = i + 1 289 tp = type(token) 290 if tp == types.StringType: 291 try: 292 bytecode.extend(map(chr, opcodes[token])) 293 except KeyError: 294 raise CharStringCompileError, "illegal operator: %s" % token 295 if token in ('hintmask', 'cntrmask'): 296 bytecode.append(program[i]) # hint mask 297 i = i + 1 298 elif tp == types.IntType: 299 bytecode.append(encodeInt(token)) 300 elif tp == types.FloatType: 301 bytecode.append(encodeFixed(token)) 302 else: 303 assert 0, "unsupported type: %s" % tp 304 try: 305 bytecode = "".join(bytecode) 306 except TypeError: 307 print bytecode 308 raise 309 self.setBytecode(bytecode) 310 311 def needsDecompilation(self): 312 return self.bytecode is not None 313 314 def setProgram(self, program): 315 self.program = program 316 self.bytecode = None 317 318 def setBytecode(self, bytecode): 319 self.bytecode = bytecode 320 self.program = None 321 322 def getToken(self, index, 323 len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType): 324 if self.bytecode is not None: 325 if index >= len(self.bytecode): 326 return None, 0, 0 327 b0 = ord(self.bytecode[index]) 328 index = index + 1 329 code = self.operandEncoding[b0] 330 handler = getattr(self, code) 331 token, index = handler(b0, self.bytecode, index) 332 else: 333 if index >= len(self.program): 334 return None, 0, 0 335 token = self.program[index] 336 index = index + 1 337 isOperator = type(token) == StringType 338 return token, isOperator, index 339 340 def getBytes(self, index, nBytes): 341 if self.bytecode is not None: 342 newIndex = index + nBytes 343 bytes = self.bytecode[index:newIndex] 344 index = newIndex 345 else: 346 bytes = self.program[index] 347 index = index + 1 348 assert len(bytes) == nBytes 349 return bytes, index 350 351 def do_operator(self, b0, data, index): 352 if b0 == 12: 353 op = (b0, ord(data[index])) 354 index = index+1 355 else: 356 op = b0 357 operator = self.operators[op] 358 return operator, index 359 360 def toXML(self, xmlWriter): 361 from fontTools.misc.textTools import num2binary 362 if self.bytecode is not None: 363 xmlWriter.dumphex(self.bytecode) 364 else: 365 index = 0 366 args = [] 367 while 1: 368 token, isOperator, index = self.getToken(index) 369 if token is None: 370 break 371 if isOperator: 372 args = map(str, args) 373 if token in ('hintmask', 'cntrmask'): 374 hintMask, isOperator, index = self.getToken(index) 375 bits = [] 376 for byte in hintMask: 377 bits.append(num2binary(ord(byte), 8)) 378 hintMask = string.join(bits, "") 379 line = string.join(args + [token, hintMask], " ") 380 else: 381 line = string.join(args + [token], " ") 382 xmlWriter.write(line) 383 xmlWriter.newline() 384 args = [] 385 else: 386 args.append(token) 387 388 def fromXML(self, (name, attrs, content)): 389 from fontTools.misc.textTools import binary2num, readHex 390 if attrs.get("raw"): 391 self.setBytecode(readHex(content)) 392 return 393 content = "".join(content) 394 content = content.split() 395 program = [] 396 end = len(content) 397 i = 0 398 while i < end: 399 token = content[i] 400 i = i + 1 401 try: 402 token = int(token) 403 except ValueError: 404 try: 405 token = float(token) 406 except ValueError: 407 program.append(token) 408 if token in ('hintmask', 'cntrmask'): 409 mask = content[i] 410 maskBytes = "" 411 for j in range(0, len(mask), 8): 412 maskBytes = maskBytes + chr(binary2num(mask[j:j+8])) 413 program.append(maskBytes) 414 i = i + 1 415 else: 416 program.append(token) 417 else: 418 program.append(token) 419 self.setProgram(program) 420 421 422t1Operators = [ 423# opcode name 424 (1, 'hstem'), 425 (3, 'vstem'), 426 (4, 'vmoveto'), 427 (5, 'rlineto'), 428 (6, 'hlineto'), 429 (7, 'vlineto'), 430 (8, 'rrcurveto'), 431 (9, 'closepath'), 432 (10, 'callsubr'), 433 (11, 'return'), 434 (13, 'hsbw'), 435 (14, 'endchar'), 436 (21, 'rmoveto'), 437 (22, 'hmoveto'), 438 (30, 'vhcurveto'), 439 (31, 'hvcurveto'), 440 ((12, 0), 'dotsection'), 441 ((12, 1), 'vstem3'), 442 ((12, 2), 'hstem3'), 443 ((12, 6), 'seac'), 444 ((12, 7), 'sbw'), 445 ((12, 12), 'div'), 446 ((12, 16), 'callothersubr'), 447 ((12, 17), 'pop'), 448 ((12, 33), 'setcurrentpoint'), 449] 450 451class T1CharString(T2CharString): 452 453 operandEncoding = t1OperandEncoding 454 operators, opcodes = buildOperatorDict(t1Operators) 455 456 def __init__(self, bytecode=None, program=None, subrs=None): 457 if program is None: 458 program = [] 459 self.bytecode = bytecode 460 self.program = program 461 self.subrs = subrs 462 463 def getIntEncoder(self): 464 return encodeIntT1 465 466 def getFixedEncoder(self): 467 def encodeFixed(value): 468 raise TypeError("Type 1 charstrings don't support floating point operands") 469 470 def decompile(self): 471 if self.program is not None: 472 return 473 program = [] 474 index = 0 475 while 1: 476 token, isOperator, index = self.getToken(index) 477 if token is None: 478 break 479 program.append(token) 480 self.setProgram(program) 481 482 def draw(self, pen): 483 extractor = T1OutlineExtractor(pen, self.subrs) 484 extractor.execute(self) 485 self.width = extractor.width 486 487 488class SimpleT2Decompiler: 489 490 def __init__(self, localSubrs, globalSubrs): 491 self.localSubrs = localSubrs 492 self.localBias = calcSubrBias(localSubrs) 493 self.globalSubrs = globalSubrs 494 self.globalBias = calcSubrBias(globalSubrs) 495 self.reset() 496 497 def reset(self): 498 self.callingStack = [] 499 self.operandStack = [] 500 self.hintCount = 0 501 self.hintMaskBytes = 0 502 503 def execute(self, charString): 504 self.callingStack.append(charString) 505 needsDecompilation = charString.needsDecompilation() 506 if needsDecompilation: 507 program = [] 508 pushToProgram = program.append 509 else: 510 pushToProgram = lambda x: None 511 pushToStack = self.operandStack.append 512 index = 0 513 while 1: 514 token, isOperator, index = charString.getToken(index) 515 if token is None: 516 break # we're done! 517 pushToProgram(token) 518 if isOperator: 519 handlerName = "op_" + token 520 if hasattr(self, handlerName): 521 handler = getattr(self, handlerName) 522 rv = handler(index) 523 if rv: 524 hintMaskBytes, index = rv 525 pushToProgram(hintMaskBytes) 526 else: 527 self.popall() 528 else: 529 pushToStack(token) 530 if needsDecompilation: 531 assert program, "illegal CharString: decompiled to empty program" 532 assert program[-1] in ("endchar", "return", "callsubr", "callgsubr", 533 "seac"), "illegal CharString" 534 charString.setProgram(program) 535 del self.callingStack[-1] 536 537 def pop(self): 538 value = self.operandStack[-1] 539 del self.operandStack[-1] 540 return value 541 542 def popall(self): 543 stack = self.operandStack[:] 544 self.operandStack[:] = [] 545 return stack 546 547 def push(self, value): 548 self.operandStack.append(value) 549 550 def op_return(self, index): 551 if self.operandStack: 552 pass 553 554 def op_endchar(self, index): 555 pass 556 557 def op_ignore(self, index): 558 pass 559 560 def op_callsubr(self, index): 561 subrIndex = self.pop() 562 subr = self.localSubrs[subrIndex+self.localBias] 563 self.execute(subr) 564 565 def op_callgsubr(self, index): 566 subrIndex = self.pop() 567 subr = self.globalSubrs[subrIndex+self.globalBias] 568 self.execute(subr) 569 570 def op_hstem(self, index): 571 self.countHints() 572 def op_vstem(self, index): 573 self.countHints() 574 def op_hstemhm(self, index): 575 self.countHints() 576 def op_vstemhm(self, index): 577 self.countHints() 578 579 def op_hintmask(self, index): 580 if not self.hintMaskBytes: 581 self.countHints() 582 self.hintMaskBytes = (self.hintCount + 7) / 8 583 hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes) 584 return hintMaskBytes, index 585 586 op_cntrmask = op_hintmask 587 588 def countHints(self): 589 args = self.popall() 590 self.hintCount = self.hintCount + len(args) / 2 591 592 593class T2OutlineExtractor(SimpleT2Decompiler): 594 595 def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX): 596 SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs) 597 self.pen = pen 598 self.nominalWidthX = nominalWidthX 599 self.defaultWidthX = defaultWidthX 600 601 def reset(self): 602 SimpleT2Decompiler.reset(self) 603 self.hints = [] 604 self.gotWidth = 0 605 self.width = 0 606 self.currentPoint = (0, 0) 607 self.sawMoveTo = 0 608 609 def _nextPoint(self, point): 610 x, y = self.currentPoint 611 point = x + point[0], y + point[1] 612 self.currentPoint = point 613 return point 614 615 def rMoveTo(self, point): 616 self.pen.moveTo(self._nextPoint(point)) 617 self.sawMoveTo = 1 618 619 def rLineTo(self, point): 620 if not self.sawMoveTo: 621 self.rMoveTo((0, 0)) 622 self.pen.lineTo(self._nextPoint(point)) 623 624 def rCurveTo(self, pt1, pt2, pt3): 625 if not self.sawMoveTo: 626 self.rMoveTo((0, 0)) 627 nextPoint = self._nextPoint 628 self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3)) 629 630 def closePath(self): 631 if self.sawMoveTo: 632 self.pen.closePath() 633 self.sawMoveTo = 0 634 635 def endPath(self): 636 # In T2 there are no open paths, so always do a closePath when 637 # finishing a sub path. 638 self.closePath() 639 640 def popallWidth(self, evenOdd=0): 641 args = self.popall() 642 if not self.gotWidth: 643 if evenOdd ^ (len(args) % 2): 644 self.width = self.nominalWidthX + args[0] 645 args = args[1:] 646 else: 647 self.width = self.defaultWidthX 648 self.gotWidth = 1 649 return args 650 651 def countHints(self): 652 args = self.popallWidth() 653 self.hintCount = self.hintCount + len(args) / 2 654 655 # 656 # hint operators 657 # 658 #def op_hstem(self, index): 659 # self.countHints() 660 #def op_vstem(self, index): 661 # self.countHints() 662 #def op_hstemhm(self, index): 663 # self.countHints() 664 #def op_vstemhm(self, index): 665 # self.countHints() 666 #def op_hintmask(self, index): 667 # self.countHints() 668 #def op_cntrmask(self, index): 669 # self.countHints() 670 671 # 672 # path constructors, moveto 673 # 674 def op_rmoveto(self, index): 675 self.endPath() 676 self.rMoveTo(self.popallWidth()) 677 def op_hmoveto(self, index): 678 self.endPath() 679 self.rMoveTo((self.popallWidth(1)[0], 0)) 680 def op_vmoveto(self, index): 681 self.endPath() 682 self.rMoveTo((0, self.popallWidth(1)[0])) 683 def op_endchar(self, index): 684 self.endPath() 685 args = self.popallWidth() 686 if args: 687 from fontTools.encodings.StandardEncoding import StandardEncoding 688 # endchar can do seac accent bulding; The T2 spec says it's deprecated, 689 # but recent software that shall remain nameless does output it. 690 adx, ady, bchar, achar = args 691 baseGlyph = StandardEncoding[bchar] 692 self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) 693 accentGlyph = StandardEncoding[achar] 694 self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) 695 696 # 697 # path constructors, lines 698 # 699 def op_rlineto(self, index): 700 args = self.popall() 701 for i in range(0, len(args), 2): 702 point = args[i:i+2] 703 self.rLineTo(point) 704 705 def op_hlineto(self, index): 706 self.alternatingLineto(1) 707 def op_vlineto(self, index): 708 self.alternatingLineto(0) 709 710 # 711 # path constructors, curves 712 # 713 def op_rrcurveto(self, index): 714 """{dxa dya dxb dyb dxc dyc}+ rrcurveto""" 715 args = self.popall() 716 for i in range(0, len(args), 6): 717 dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6] 718 self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc)) 719 720 def op_rcurveline(self, index): 721 """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline""" 722 args = self.popall() 723 for i in range(0, len(args)-2, 6): 724 dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6] 725 self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) 726 self.rLineTo(args[-2:]) 727 728 def op_rlinecurve(self, index): 729 """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve""" 730 args = self.popall() 731 lineArgs = args[:-6] 732 for i in range(0, len(lineArgs), 2): 733 self.rLineTo(lineArgs[i:i+2]) 734 dxb, dyb, dxc, dyc, dxd, dyd = args[-6:] 735 self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) 736 737 def op_vvcurveto(self, index): 738 "dx1? {dya dxb dyb dyc}+ vvcurveto" 739 args = self.popall() 740 if len(args) % 2: 741 dx1 = args[0] 742 args = args[1:] 743 else: 744 dx1 = 0 745 for i in range(0, len(args), 4): 746 dya, dxb, dyb, dyc = args[i:i+4] 747 self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc)) 748 dx1 = 0 749 750 def op_hhcurveto(self, index): 751 """dy1? {dxa dxb dyb dxc}+ hhcurveto""" 752 args = self.popall() 753 if len(args) % 2: 754 dy1 = args[0] 755 args = args[1:] 756 else: 757 dy1 = 0 758 for i in range(0, len(args), 4): 759 dxa, dxb, dyb, dxc = args[i:i+4] 760 self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0)) 761 dy1 = 0 762 763 def op_vhcurveto(self, index): 764 """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) 765 {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto 766 """ 767 args = self.popall() 768 while args: 769 args = self.vcurveto(args) 770 if args: 771 args = self.hcurveto(args) 772 773 def op_hvcurveto(self, index): 774 """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? 775 {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? 776 """ 777 args = self.popall() 778 while args: 779 args = self.hcurveto(args) 780 if args: 781 args = self.vcurveto(args) 782 783 # 784 # path constructors, flex 785 # 786 def op_hflex(self, index): 787 dx1, dx2, dy2, dx3, dx4, dx5, dx6 = self.popall() 788 dy1 = dy3 = dy4 = dy6 = 0 789 dy5 = -dy2 790 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 791 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 792 def op_flex(self, index): 793 dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6, fd = self.popall() 794 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 795 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 796 def op_hflex1(self, index): 797 dx1, dy1, dx2, dy2, dx3, dx4, dx5, dy5, dx6 = self.popall() 798 dy3 = dy4 = 0 799 dy6 = -(dy1 + dy2 + dy3 + dy4 + dy5) 800 801 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 802 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 803 def op_flex1(self, index): 804 dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, d6 = self.popall() 805 dx = dx1 + dx2 + dx3 + dx4 + dx5 806 dy = dy1 + dy2 + dy3 + dy4 + dy5 807 if abs(dx) > abs(dy): 808 dx6 = d6 809 dy6 = -dy 810 else: 811 dx6 = -dx 812 dy6 = d6 813 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 814 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 815 816 # 817 # MultipleMaster. Well... 818 # 819 def op_blend(self, index): 820 args = self.popall() 821 822 # misc 823 def op_and(self, index): 824 raise NotImplementedError 825 def op_or(self, index): 826 raise NotImplementedError 827 def op_not(self, index): 828 raise NotImplementedError 829 def op_store(self, index): 830 raise NotImplementedError 831 def op_abs(self, index): 832 raise NotImplementedError 833 def op_add(self, index): 834 raise NotImplementedError 835 def op_sub(self, index): 836 raise NotImplementedError 837 def op_div(self, index): 838 num2 = self.pop() 839 num1 = self.pop() 840 d1 = num1/num2 841 d2 = float(num1)/num2 842 if d1 == d2: 843 self.push(d1) 844 else: 845 self.push(d2) 846 def op_load(self, index): 847 raise NotImplementedError 848 def op_neg(self, index): 849 raise NotImplementedError 850 def op_eq(self, index): 851 raise NotImplementedError 852 def op_drop(self, index): 853 raise NotImplementedError 854 def op_put(self, index): 855 raise NotImplementedError 856 def op_get(self, index): 857 raise NotImplementedError 858 def op_ifelse(self, index): 859 raise NotImplementedError 860 def op_random(self, index): 861 raise NotImplementedError 862 def op_mul(self, index): 863 raise NotImplementedError 864 def op_sqrt(self, index): 865 raise NotImplementedError 866 def op_dup(self, index): 867 raise NotImplementedError 868 def op_exch(self, index): 869 raise NotImplementedError 870 def op_index(self, index): 871 raise NotImplementedError 872 def op_roll(self, index): 873 raise NotImplementedError 874 875 # 876 # miscelaneous helpers 877 # 878 def alternatingLineto(self, isHorizontal): 879 args = self.popall() 880 for arg in args: 881 if isHorizontal: 882 point = (arg, 0) 883 else: 884 point = (0, arg) 885 self.rLineTo(point) 886 isHorizontal = not isHorizontal 887 888 def vcurveto(self, args): 889 dya, dxb, dyb, dxc = args[:4] 890 args = args[4:] 891 if len(args) == 1: 892 dyc = args[0] 893 args = [] 894 else: 895 dyc = 0 896 self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc)) 897 return args 898 899 def hcurveto(self, args): 900 dxa, dxb, dyb, dyc = args[:4] 901 args = args[4:] 902 if len(args) == 1: 903 dxc = args[0] 904 args = [] 905 else: 906 dxc = 0 907 self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc)) 908 return args 909 910 911class T1OutlineExtractor(T2OutlineExtractor): 912 913 def __init__(self, pen, subrs): 914 self.pen = pen 915 self.subrs = subrs 916 self.reset() 917 918 def reset(self): 919 self.flexing = 0 920 self.width = 0 921 self.sbx = 0 922 T2OutlineExtractor.reset(self) 923 924 def endPath(self): 925 if self.sawMoveTo: 926 self.pen.endPath() 927 self.sawMoveTo = 0 928 929 def popallWidth(self, evenOdd=0): 930 return self.popall() 931 932 def exch(self): 933 stack = self.operandStack 934 stack[-1], stack[-2] = stack[-2], stack[-1] 935 936 # 937 # path constructors 938 # 939 def op_rmoveto(self, index): 940 if self.flexing: 941 return 942 self.endPath() 943 self.rMoveTo(self.popall()) 944 def op_hmoveto(self, index): 945 if self.flexing: 946 # We must add a parameter to the stack if we are flexing 947 self.push(0) 948 return 949 self.endPath() 950 self.rMoveTo((self.popall()[0], 0)) 951 def op_vmoveto(self, index): 952 if self.flexing: 953 # We must add a parameter to the stack if we are flexing 954 self.push(0) 955 self.exch() 956 return 957 self.endPath() 958 self.rMoveTo((0, self.popall()[0])) 959 def op_closepath(self, index): 960 self.closePath() 961 def op_setcurrentpoint(self, index): 962 args = self.popall() 963 x, y = args 964 self.currentPoint = x, y 965 966 def op_endchar(self, index): 967 self.endPath() 968 969 def op_hsbw(self, index): 970 sbx, wx = self.popall() 971 self.width = wx 972 self.sbx = sbx 973 self.currentPoint = sbx, self.currentPoint[1] 974 def op_sbw(self, index): 975 self.popall() # XXX 976 977 # 978 def op_callsubr(self, index): 979 subrIndex = self.pop() 980 subr = self.subrs[subrIndex] 981 self.execute(subr) 982 def op_callothersubr(self, index): 983 subrIndex = self.pop() 984 nArgs = self.pop() 985 #print nArgs, subrIndex, "callothersubr" 986 if subrIndex == 0 and nArgs == 3: 987 self.doFlex() 988 self.flexing = 0 989 elif subrIndex == 1 and nArgs == 0: 990 self.flexing = 1 991 # ignore... 992 def op_pop(self, index): 993 pass # ignore... 994 995 def doFlex(self): 996 finaly = self.pop() 997 finalx = self.pop() 998 self.pop() # flex height is unused 999 1000 p3y = self.pop() 1001 p3x = self.pop() 1002 bcp4y = self.pop() 1003 bcp4x = self.pop() 1004 bcp3y = self.pop() 1005 bcp3x = self.pop() 1006 p2y = self.pop() 1007 p2x = self.pop() 1008 bcp2y = self.pop() 1009 bcp2x = self.pop() 1010 bcp1y = self.pop() 1011 bcp1x = self.pop() 1012 rpy = self.pop() 1013 rpx = self.pop() 1014 1015 # call rrcurveto 1016 self.push(bcp1x+rpx) 1017 self.push(bcp1y+rpy) 1018 self.push(bcp2x) 1019 self.push(bcp2y) 1020 self.push(p2x) 1021 self.push(p2y) 1022 self.op_rrcurveto(None) 1023 1024 # call rrcurveto 1025 self.push(bcp3x) 1026 self.push(bcp3y) 1027 self.push(bcp4x) 1028 self.push(bcp4y) 1029 self.push(p3x) 1030 self.push(p3y) 1031 self.op_rrcurveto(None) 1032 1033 # Push back final coords so subr 0 can find them 1034 self.push(finalx) 1035 self.push(finaly) 1036 1037 def op_dotsection(self, index): 1038 self.popall() # XXX 1039 def op_hstem3(self, index): 1040 self.popall() # XXX 1041 def op_seac(self, index): 1042 "asb adx ady bchar achar seac" 1043 from fontTools.encodings.StandardEncoding import StandardEncoding 1044 asb, adx, ady, bchar, achar = self.popall() 1045 baseGlyph = StandardEncoding[bchar] 1046 self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) 1047 accentGlyph = StandardEncoding[achar] 1048 adx = adx + self.sbx - asb # seac weirdness 1049 self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) 1050 def op_vstem3(self, index): 1051 self.popall() # XXX 1052 1053 1054class DictDecompiler(ByteCodeBase): 1055 1056 operandEncoding = cffDictOperandEncoding 1057 1058 def __init__(self, strings): 1059 self.stack = [] 1060 self.strings = strings 1061 self.dict = {} 1062 1063 def getDict(self): 1064 assert len(self.stack) == 0, "non-empty stack" 1065 return self.dict 1066 1067 def decompile(self, data): 1068 index = 0 1069 lenData = len(data) 1070 push = self.stack.append 1071 while index < lenData: 1072 b0 = ord(data[index]) 1073 index = index + 1 1074 code = self.operandEncoding[b0] 1075 handler = getattr(self, code) 1076 value, index = handler(b0, data, index) 1077 if value is not None: 1078 push(value) 1079 1080 def pop(self): 1081 value = self.stack[-1] 1082 del self.stack[-1] 1083 return value 1084 1085 def popall(self): 1086 all = self.stack[:] 1087 del self.stack[:] 1088 return all 1089 1090 def do_operator(self, b0, data, index): 1091 if b0 == 12: 1092 op = (b0, ord(data[index])) 1093 index = index+1 1094 else: 1095 op = b0 1096 operator, argType = self.operators[op] 1097 self.handle_operator(operator, argType) 1098 return None, index 1099 1100 def handle_operator(self, operator, argType): 1101 if type(argType) == type(()): 1102 value = () 1103 for i in range(len(argType)-1, -1, -1): 1104 arg = argType[i] 1105 arghandler = getattr(self, "arg_" + arg) 1106 value = (arghandler(operator),) + value 1107 else: 1108 arghandler = getattr(self, "arg_" + argType) 1109 value = arghandler(operator) 1110 self.dict[operator] = value 1111 1112 def arg_number(self, name): 1113 return self.pop() 1114 def arg_SID(self, name): 1115 return self.strings[self.pop()] 1116 def arg_array(self, name): 1117 return self.popall() 1118 def arg_delta(self, name): 1119 out = [] 1120 current = 0 1121 for v in self.popall(): 1122 current = current + v 1123 out.append(current) 1124 return out 1125 1126 1127def calcSubrBias(subrs): 1128 nSubrs = len(subrs) 1129 if nSubrs < 1240: 1130 bias = 107 1131 elif nSubrs < 33900: 1132 bias = 1131 1133 else: 1134 bias = 32768 1135 return bias 1136