1 from __future__ import print_function, division, absolute_import 2 from fontTools.misc.py23 import * 3 4 _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"} 5 6 7 class ps_object: 8 9 literal = 1 10 access = 0 11 value = None 12 13 def __init__(self, value): 14 self.value = value 15 self.type = self.__class__.__name__[3:] + "type" 16 17 def __repr__(self): 18 return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value)) 19 20 21 class ps_operator(ps_object): 22 23 literal = 0 24 25 def __init__(self, name, function): 26 self.name = name 27 self.function = function 28 self.type = self.__class__.__name__[3:] + "type" 29 def __repr__(self): 30 return "<operator %s>" % self.name 31 32 class ps_procedure(ps_object): 33 literal = 0 34 def __repr__(self): 35 return "<procedure>" 36 def __str__(self): 37 psstring = '{' 38 for i in range(len(self.value)): 39 if i: 40 psstring = psstring + ' ' + str(self.value[i]) 41 else: 42 psstring = psstring + str(self.value[i]) 43 return psstring + '}' 44 45 class ps_name(ps_object): 46 literal = 0 47 def __str__(self): 48 if self.literal: 49 return '/' + self.value 50 else: 51 return self.value 52 53 class ps_literal(ps_object): 54 def __str__(self): 55 return '/' + self.value 56 57 class ps_array(ps_object): 58 def __str__(self): 59 psstring = '[' 60 for i in range(len(self.value)): 61 item = self.value[i] 62 access = _accessstrings[item.access] 63 if access: 64 access = ' ' + access 65 if i: 66 psstring = psstring + ' ' + str(item) + access 67 else: 68 psstring = psstring + str(item) + access 69 return psstring + ']' 70 def __repr__(self): 71 return "<array>" 72 73 _type1_pre_eexec_order = [ 74 "FontInfo", 75 "FontName", 76 "Encoding", 77 "PaintType", 78 "FontType", 79 "FontMatrix", 80 "FontBBox", 81 "UniqueID", 82 "Metrics", 83 "StrokeWidth" 84 ] 85 86 _type1_fontinfo_order = [ 87 "version", 88 "Notice", 89 "FullName", 90 "FamilyName", 91 "Weight", 92 "ItalicAngle", 93 "isFixedPitch", 94 "UnderlinePosition", 95 "UnderlineThickness" 96 ] 97 98 _type1_post_eexec_order = [ 99 "Private", 100 "CharStrings", 101 "FID" 102 ] 103 104 def _type1_item_repr(key, value): 105 psstring = "" 106 access = _accessstrings[value.access] 107 if access: 108 access = access + ' ' 109 if key == 'CharStrings': 110 psstring = psstring + "/%s %s def\n" % (key, _type1_CharString_repr(value.value)) 111 elif key == 'Encoding': 112 psstring = psstring + _type1_Encoding_repr(value, access) 113 else: 114 psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access) 115 return psstring 116 117 def _type1_Encoding_repr(encoding, access): 118 encoding = encoding.value 119 psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n" 120 for i in range(256): 121 name = encoding[i].value 122 if name != '.notdef': 123 psstring = psstring + "dup %d /%s put\n" % (i, name) 124 return psstring + access + "def\n" 125 126 def _type1_CharString_repr(charstrings): 127 items = sorted(charstrings.items()) 128 return 'xxx' 129 130 class ps_font(ps_object): 131 def __str__(self): 132 psstring = "%d dict dup begin\n" % len(self.value) 133 for key in _type1_pre_eexec_order: 134 try: 135 value = self.value[key] 136 except KeyError: 137 pass 138 else: 139 psstring = psstring + _type1_item_repr(key, value) 140 items = sorted(self.value.items()) 141 for key, value in items: 142 if key not in _type1_pre_eexec_order + _type1_post_eexec_order: 143 psstring = psstring + _type1_item_repr(key, value) 144 psstring = psstring + "currentdict end\ncurrentfile eexec\ndup " 145 for key in _type1_post_eexec_order: 146 try: 147 value = self.value[key] 148 except KeyError: 149 pass 150 else: 151 psstring = psstring + _type1_item_repr(key, value) 152 return psstring + 'dup/FontName get exch definefont pop\nmark currentfile closefile\n' + \ 153 8 * (64 * '0' + '\n') + 'cleartomark' + '\n' 154 def __repr__(self): 155 return '<font>' 156 157 class ps_file(ps_object): 158 pass 159 160 class ps_dict(ps_object): 161 def __str__(self): 162 psstring = "%d dict dup begin\n" % len(self.value) 163 items = sorted(self.value.items()) 164 for key, value in items: 165 access = _accessstrings[value.access] 166 if access: 167 access = access + ' ' 168 psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access) 169 return psstring + 'end ' 170 def __repr__(self): 171 return "<dict>" 172 173 class ps_mark(ps_object): 174 def __init__(self): 175 self.value = 'mark' 176 self.type = self.__class__.__name__[3:] + "type" 177 178 class ps_procmark(ps_object): 179 def __init__(self): 180 self.value = 'procmark' 181 self.type = self.__class__.__name__[3:] + "type" 182 183 class ps_null(ps_object): 184 def __init__(self): 185 self.type = self.__class__.__name__[3:] + "type" 186 187 class ps_boolean(ps_object): 188 def __str__(self): 189 if self.value: 190 return 'true' 191 else: 192 return 'false' 193 194 class ps_string(ps_object): 195 def __str__(self): 196 return "(%s)" % repr(self.value)[1:-1] 197 198 class ps_integer(ps_object): 199 def __str__(self): 200 return repr(self.value) 201 202 class ps_real(ps_object): 203 def __str__(self): 204 return repr(self.value) 205 206 207 class PSOperators: 208 209 def ps_def(self): 210 obj = self.pop() 211 name = self.pop() 212 self.dictstack[-1][name.value] = obj 213 214 def ps_bind(self): 215 proc = self.pop('proceduretype') 216 self.proc_bind(proc) 217 self.push(proc) 218 219 def proc_bind(self, proc): 220 for i in range(len(proc.value)): 221 item = proc.value[i] 222 if item.type == 'proceduretype': 223 self.proc_bind(item) 224 else: 225 if not item.literal: 226 try: 227 obj = self.resolve_name(item.value) 228 except: 229 pass 230 else: 231 if obj.type == 'operatortype': 232 proc.value[i] = obj 233 234 def ps_exch(self): 235 if len(self.stack) < 2: 236 raise RuntimeError('stack underflow') 237 obj1 = self.pop() 238 obj2 = self.pop() 239 self.push(obj1) 240 self.push(obj2) 241 242 def ps_dup(self): 243 if not self.stack: 244 raise RuntimeError('stack underflow') 245 self.push(self.stack[-1]) 246 247 def ps_exec(self): 248 obj = self.pop() 249 if obj.type == 'proceduretype': 250 self.call_procedure(obj) 251 else: 252 self.handle_object(obj) 253 254 def ps_count(self): 255 self.push(ps_integer(len(self.stack))) 256 257 def ps_eq(self): 258 any1 = self.pop() 259 any2 = self.pop() 260 self.push(ps_boolean(any1.value == any2.value)) 261 262 def ps_ne(self): 263 any1 = self.pop() 264 any2 = self.pop() 265 self.push(ps_boolean(any1.value != any2.value)) 266 267 def ps_cvx(self): 268 obj = self.pop() 269 obj.literal = 0 270 self.push(obj) 271 272 def ps_matrix(self): 273 matrix = [ps_real(1.0), ps_integer(0), ps_integer(0), ps_real(1.0), ps_integer(0), ps_integer(0)] 274 self.push(ps_array(matrix)) 275 276 def ps_string(self): 277 num = self.pop('integertype').value 278 self.push(ps_string('\0' * num)) 279 280 def ps_type(self): 281 obj = self.pop() 282 self.push(ps_string(obj.type)) 283 284 def ps_store(self): 285 value = self.pop() 286 key = self.pop() 287 name = key.value 288 for i in range(len(self.dictstack)-1, -1, -1): 289 if name in self.dictstack[i]: 290 self.dictstack[i][name] = value 291 break 292 self.dictstack[-1][name] = value 293 294 def ps_where(self): 295 name = self.pop() 296 # XXX 297 self.push(ps_boolean(0)) 298 299 def ps_systemdict(self): 300 self.push(ps_dict(self.dictstack[0])) 301 302 def ps_userdict(self): 303 self.push(ps_dict(self.dictstack[1])) 304 305 def ps_currentdict(self): 306 self.push(ps_dict(self.dictstack[-1])) 307 308 def ps_currentfile(self): 309 self.push(ps_file(self.tokenizer)) 310 311 def ps_eexec(self): 312 f = self.pop('filetype').value 313 f.starteexec() 314 315 def ps_closefile(self): 316 f = self.pop('filetype').value 317 f.skipwhite() 318 f.stopeexec() 319 320 def ps_cleartomark(self): 321 obj = self.pop() 322 while obj != self.mark: 323 obj = self.pop() 324 325 def ps_readstring(self, 326 ps_boolean = ps_boolean, 327 len = len): 328 s = self.pop('stringtype') 329 oldstr = s.value 330 f = self.pop('filetype') 331 #pad = file.value.read(1) 332 # for StringIO, this is faster 333 f.value.pos = f.value.pos + 1 334 newstr = f.value.read(len(oldstr)) 335 s.value = newstr 336 self.push(s) 337 self.push(ps_boolean(len(oldstr) == len(newstr))) 338 339 def ps_known(self): 340 key = self.pop() 341 d = self.pop('dicttype', 'fonttype') 342 self.push(ps_boolean(key.value in d.value)) 343 344 def ps_if(self): 345 proc = self.pop('proceduretype') 346 if self.pop('booleantype').value: 347 self.call_procedure(proc) 348 349 def ps_ifelse(self): 350 proc2 = self.pop('proceduretype') 351 proc1 = self.pop('proceduretype') 352 if self.pop('booleantype').value: 353 self.call_procedure(proc1) 354 else: 355 self.call_procedure(proc2) 356 357 def ps_readonly(self): 358 obj = self.pop() 359 if obj.access < 1: 360 obj.access = 1 361 self.push(obj) 362 363 def ps_executeonly(self): 364 obj = self.pop() 365 if obj.access < 2: 366 obj.access = 2 367 self.push(obj) 368 369 def ps_noaccess(self): 370 obj = self.pop() 371 if obj.access < 3: 372 obj.access = 3 373 self.push(obj) 374 375 def ps_not(self): 376 obj = self.pop('booleantype', 'integertype') 377 if obj.type == 'booleantype': 378 self.push(ps_boolean(not obj.value)) 379 else: 380 self.push(ps_integer(~obj.value)) 381 382 def ps_print(self): 383 str = self.pop('stringtype') 384 print('PS output --->', str.value) 385 386 def ps_anchorsearch(self): 387 seek = self.pop('stringtype') 388 s = self.pop('stringtype') 389 seeklen = len(seek.value) 390 if s.value[:seeklen] == seek.value: 391 self.push(ps_string(s.value[seeklen:])) 392 self.push(seek) 393 self.push(ps_boolean(1)) 394 else: 395 self.push(s) 396 self.push(ps_boolean(0)) 397 398 def ps_array(self): 399 num = self.pop('integertype') 400 array = ps_array([None] * num.value) 401 self.push(array) 402 403 def ps_astore(self): 404 array = self.pop('arraytype') 405 for i in range(len(array.value)-1, -1, -1): 406 array.value[i] = self.pop() 407 self.push(array) 408 409 def ps_load(self): 410 name = self.pop() 411 self.push(self.resolve_name(name.value)) 412 413 def ps_put(self): 414 obj1 = self.pop() 415 obj2 = self.pop() 416 obj3 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype') 417 tp = obj3.type 418 if tp == 'arraytype' or tp == 'proceduretype': 419 obj3.value[obj2.value] = obj1 420 elif tp == 'dicttype': 421 obj3.value[obj2.value] = obj1 422 elif tp == 'stringtype': 423 index = obj2.value 424 obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:] 425 426 def ps_get(self): 427 obj1 = self.pop() 428 if obj1.value == "Encoding": 429 pass 430 obj2 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype', 'fonttype') 431 tp = obj2.type 432 if tp in ('arraytype', 'proceduretype'): 433 self.push(obj2.value[obj1.value]) 434 elif tp in ('dicttype', 'fonttype'): 435 self.push(obj2.value[obj1.value]) 436 elif tp == 'stringtype': 437 self.push(ps_integer(ord(obj2.value[obj1.value]))) 438 else: 439 assert False, "shouldn't get here" 440 441 def ps_getinterval(self): 442 obj1 = self.pop('integertype') 443 obj2 = self.pop('integertype') 444 obj3 = self.pop('arraytype', 'stringtype') 445 tp = obj3.type 446 if tp == 'arraytype': 447 self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value])) 448 elif tp == 'stringtype': 449 self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value])) 450 451 def ps_putinterval(self): 452 obj1 = self.pop('arraytype', 'stringtype') 453 obj2 = self.pop('integertype') 454 obj3 = self.pop('arraytype', 'stringtype') 455 tp = obj3.type 456 if tp == 'arraytype': 457 obj3.value[obj2.value:obj2.value + len(obj1.value)] = obj1.value 458 elif tp == 'stringtype': 459 newstr = obj3.value[:obj2.value] 460 newstr = newstr + obj1.value 461 newstr = newstr + obj3.value[obj2.value + len(obj1.value):] 462 obj3.value = newstr 463 464 def ps_cvn(self): 465 self.push(ps_name(self.pop('stringtype').value)) 466 467 def ps_index(self): 468 n = self.pop('integertype').value 469 if n < 0: 470 raise RuntimeError('index may not be negative') 471 self.push(self.stack[-1-n]) 472 473 def ps_for(self): 474 proc = self.pop('proceduretype') 475 limit = self.pop('integertype', 'realtype').value 476 increment = self.pop('integertype', 'realtype').value 477 i = self.pop('integertype', 'realtype').value 478 while 1: 479 if increment > 0: 480 if i > limit: 481 break 482 else: 483 if i < limit: 484 break 485 if type(i) == type(0.0): 486 self.push(ps_real(i)) 487 else: 488 self.push(ps_integer(i)) 489 self.call_procedure(proc) 490 i = i + increment 491 492 def ps_forall(self): 493 proc = self.pop('proceduretype') 494 obj = self.pop('arraytype', 'stringtype', 'dicttype') 495 tp = obj.type 496 if tp == 'arraytype': 497 for item in obj.value: 498 self.push(item) 499 self.call_procedure(proc) 500 elif tp == 'stringtype': 501 for item in obj.value: 502 self.push(ps_integer(ord(item))) 503 self.call_procedure(proc) 504 elif tp == 'dicttype': 505 for key, value in obj.value.items(): 506 self.push(ps_name(key)) 507 self.push(value) 508 self.call_procedure(proc) 509 510 def ps_definefont(self): 511 font = self.pop('dicttype') 512 name = self.pop() 513 font = ps_font(font.value) 514 self.dictstack[0]['FontDirectory'].value[name.value] = font 515 self.push(font) 516 517 def ps_findfont(self): 518 name = self.pop() 519 font = self.dictstack[0]['FontDirectory'].value[name.value] 520 self.push(font) 521 522 def ps_pop(self): 523 self.pop() 524 525 def ps_dict(self): 526 self.pop('integertype') 527 self.push(ps_dict({})) 528 529 def ps_begin(self): 530 self.dictstack.append(self.pop('dicttype').value) 531 532 def ps_end(self): 533 if len(self.dictstack) > 2: 534 del self.dictstack[-1] 535 else: 536 raise RuntimeError('dictstack underflow') 537 538 notdef = '.notdef' 539 from fontTools.encodings.StandardEncoding import StandardEncoding 540 ps_StandardEncoding = list(map(ps_name, StandardEncoding)) 541 542