Coverage for /usr/lib/python3/dist-packages/fontTools/misc/psCharStrings.py: 25%

957 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1"""psCharStrings.py -- module implementing various kinds of CharStrings: 

2CFF dictionary data and Type1/Type2 CharStrings. 

3""" 

4 

5from fontTools.misc.fixedTools import ( 

6 fixedToFloat, 

7 floatToFixed, 

8 floatToFixedToStr, 

9 strToFixedToFloat, 

10) 

11from fontTools.misc.textTools import bytechr, byteord, bytesjoin, strjoin 

12from fontTools.pens.boundsPen import BoundsPen 

13import struct 

14import logging 

15 

16 

17log = logging.getLogger(__name__) 

18 

19 

20def read_operator(self, b0, data, index): 

21 if b0 == 12: 

22 op = (b0, byteord(data[index])) 

23 index = index + 1 

24 else: 

25 op = b0 

26 try: 

27 operator = self.operators[op] 

28 except KeyError: 

29 return None, index 

30 value = self.handle_operator(operator) 

31 return value, index 

32 

33 

34def read_byte(self, b0, data, index): 

35 return b0 - 139, index 

36 

37 

38def read_smallInt1(self, b0, data, index): 

39 b1 = byteord(data[index]) 

40 return (b0 - 247) * 256 + b1 + 108, index + 1 

41 

42 

43def read_smallInt2(self, b0, data, index): 

44 b1 = byteord(data[index]) 

45 return -(b0 - 251) * 256 - b1 - 108, index + 1 

46 

47 

48def read_shortInt(self, b0, data, index): 

49 (value,) = struct.unpack(">h", data[index : index + 2]) 

50 return value, index + 2 

51 

52 

53def read_longInt(self, b0, data, index): 

54 (value,) = struct.unpack(">l", data[index : index + 4]) 

55 return value, index + 4 

56 

57 

58def read_fixed1616(self, b0, data, index): 

59 (value,) = struct.unpack(">l", data[index : index + 4]) 

60 return fixedToFloat(value, precisionBits=16), index + 4 

61 

62 

63def read_reserved(self, b0, data, index): 

64 assert NotImplementedError 

65 return NotImplemented, index 

66 

67 

68def read_realNumber(self, b0, data, index): 

69 number = "" 

70 while True: 

71 b = byteord(data[index]) 

72 index = index + 1 

73 nibble0 = (b & 0xF0) >> 4 

74 nibble1 = b & 0x0F 

75 if nibble0 == 0xF: 

76 break 

77 number = number + realNibbles[nibble0] 

78 if nibble1 == 0xF: 

79 break 

80 number = number + realNibbles[nibble1] 

81 return float(number), index 

82 

83 

84t1OperandEncoding = [None] * 256 

85t1OperandEncoding[0:32] = (32) * [read_operator] 

86t1OperandEncoding[32:247] = (247 - 32) * [read_byte] 

87t1OperandEncoding[247:251] = (251 - 247) * [read_smallInt1] 

88t1OperandEncoding[251:255] = (255 - 251) * [read_smallInt2] 

89t1OperandEncoding[255] = read_longInt 

90assert len(t1OperandEncoding) == 256 

91 

92t2OperandEncoding = t1OperandEncoding[:] 

93t2OperandEncoding[28] = read_shortInt 

94t2OperandEncoding[255] = read_fixed1616 

95 

96cffDictOperandEncoding = t2OperandEncoding[:] 

97cffDictOperandEncoding[29] = read_longInt 

98cffDictOperandEncoding[30] = read_realNumber 

99cffDictOperandEncoding[255] = read_reserved 

100 

101 

102realNibbles = [ 

103 "0", 

104 "1", 

105 "2", 

106 "3", 

107 "4", 

108 "5", 

109 "6", 

110 "7", 

111 "8", 

112 "9", 

113 ".", 

114 "E", 

115 "E-", 

116 None, 

117 "-", 

118] 

119realNibblesDict = {v: i for i, v in enumerate(realNibbles)} 

120 

121maxOpStack = 193 

122 

123 

124def buildOperatorDict(operatorList): 

125 oper = {} 

126 opc = {} 

127 for item in operatorList: 

128 if len(item) == 2: 

129 oper[item[0]] = item[1] 

130 else: 

131 oper[item[0]] = item[1:] 

132 if isinstance(item[0], tuple): 

133 opc[item[1]] = item[0] 

134 else: 

135 opc[item[1]] = (item[0],) 

136 return oper, opc 

137 

138 

139t2Operators = [ 

140 # opcode name 

141 (1, "hstem"), 

142 (3, "vstem"), 

143 (4, "vmoveto"), 

144 (5, "rlineto"), 

145 (6, "hlineto"), 

146 (7, "vlineto"), 

147 (8, "rrcurveto"), 

148 (10, "callsubr"), 

149 (11, "return"), 

150 (14, "endchar"), 

151 (15, "vsindex"), 

152 (16, "blend"), 

153 (18, "hstemhm"), 

154 (19, "hintmask"), 

155 (20, "cntrmask"), 

156 (21, "rmoveto"), 

157 (22, "hmoveto"), 

158 (23, "vstemhm"), 

159 (24, "rcurveline"), 

160 (25, "rlinecurve"), 

161 (26, "vvcurveto"), 

162 (27, "hhcurveto"), 

163 # (28, 'shortint'), # not really an operator 

164 (29, "callgsubr"), 

165 (30, "vhcurveto"), 

166 (31, "hvcurveto"), 

167 ((12, 0), "ignore"), # dotsection. Yes, there a few very early OTF/CFF 

168 # fonts with this deprecated operator. Just ignore it. 

169 ((12, 3), "and"), 

170 ((12, 4), "or"), 

171 ((12, 5), "not"), 

172 ((12, 8), "store"), 

173 ((12, 9), "abs"), 

174 ((12, 10), "add"), 

175 ((12, 11), "sub"), 

176 ((12, 12), "div"), 

177 ((12, 13), "load"), 

178 ((12, 14), "neg"), 

179 ((12, 15), "eq"), 

180 ((12, 18), "drop"), 

181 ((12, 20), "put"), 

182 ((12, 21), "get"), 

183 ((12, 22), "ifelse"), 

184 ((12, 23), "random"), 

185 ((12, 24), "mul"), 

186 ((12, 26), "sqrt"), 

187 ((12, 27), "dup"), 

188 ((12, 28), "exch"), 

189 ((12, 29), "index"), 

190 ((12, 30), "roll"), 

191 ((12, 34), "hflex"), 

192 ((12, 35), "flex"), 

193 ((12, 36), "hflex1"), 

194 ((12, 37), "flex1"), 

195] 

196 

197 

198def getIntEncoder(format): 

199 if format == "cff": 

200 twoByteOp = bytechr(28) 

201 fourByteOp = bytechr(29) 

202 elif format == "t1": 

203 twoByteOp = None 

204 fourByteOp = bytechr(255) 

205 else: 

206 assert format == "t2" 

207 twoByteOp = bytechr(28) 

208 fourByteOp = None 

209 

210 def encodeInt( 

211 value, 

212 fourByteOp=fourByteOp, 

213 bytechr=bytechr, 

214 pack=struct.pack, 

215 unpack=struct.unpack, 

216 twoByteOp=twoByteOp, 

217 ): 

218 if -107 <= value <= 107: 

219 code = bytechr(value + 139) 

220 elif 108 <= value <= 1131: 

221 value = value - 108 

222 code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF) 

223 elif -1131 <= value <= -108: 

224 value = -value - 108 

225 code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF) 

226 elif twoByteOp is not None and -32768 <= value <= 32767: 

227 code = twoByteOp + pack(">h", value) 

228 elif fourByteOp is None: 

229 # Backwards compatible hack: due to a previous bug in FontTools, 

230 # 16.16 fixed numbers were written out as 4-byte ints. When 

231 # these numbers were small, they were wrongly written back as 

232 # small ints instead of 4-byte ints, breaking round-tripping. 

233 # This here workaround doesn't do it any better, since we can't 

234 # distinguish anymore between small ints that were supposed to 

235 # be small fixed numbers and small ints that were just small 

236 # ints. Hence the warning. 

237 log.warning( 

238 "4-byte T2 number got passed to the " 

239 "IntType handler. This should happen only when reading in " 

240 "old XML files.\n" 

241 ) 

242 code = bytechr(255) + pack(">l", value) 

243 else: 

244 code = fourByteOp + pack(">l", value) 

245 return code 

246 

247 return encodeInt 

248 

249 

250encodeIntCFF = getIntEncoder("cff") 

251encodeIntT1 = getIntEncoder("t1") 

252encodeIntT2 = getIntEncoder("t2") 

253 

254 

255def encodeFixed(f, pack=struct.pack): 

256 """For T2 only""" 

257 value = floatToFixed(f, precisionBits=16) 

258 if value & 0xFFFF == 0: # check if the fractional part is zero 

259 return encodeIntT2(value >> 16) # encode only the integer part 

260 else: 

261 return b"\xff" + pack(">l", value) # encode the entire fixed point value 

262 

263 

264realZeroBytes = bytechr(30) + bytechr(0xF) 

265 

266 

267def encodeFloat(f): 

268 # For CFF only, used in cffLib 

269 if f == 0.0: # 0.0 == +0.0 == -0.0 

270 return realZeroBytes 

271 # Note: 14 decimal digits seems to be the limitation for CFF real numbers 

272 # in macOS. However, we use 8 here to match the implementation of AFDKO. 

273 s = "%.8G" % f 

274 if s[:2] == "0.": 

275 s = s[1:] 

276 elif s[:3] == "-0.": 

277 s = "-" + s[2:] 

278 nibbles = [] 

279 while s: 

280 c = s[0] 

281 s = s[1:] 

282 if c == "E": 

283 c2 = s[:1] 

284 if c2 == "-": 

285 s = s[1:] 

286 c = "E-" 

287 elif c2 == "+": 

288 s = s[1:] 

289 nibbles.append(realNibblesDict[c]) 

290 nibbles.append(0xF) 

291 if len(nibbles) % 2: 

292 nibbles.append(0xF) 

293 d = bytechr(30) 

294 for i in range(0, len(nibbles), 2): 

295 d = d + bytechr(nibbles[i] << 4 | nibbles[i + 1]) 

296 return d 

297 

298 

299class CharStringCompileError(Exception): 

300 pass 

301 

302 

303class SimpleT2Decompiler(object): 

304 def __init__(self, localSubrs, globalSubrs, private=None, blender=None): 

305 self.localSubrs = localSubrs 

306 self.localBias = calcSubrBias(localSubrs) 

307 self.globalSubrs = globalSubrs 

308 self.globalBias = calcSubrBias(globalSubrs) 

309 self.private = private 

310 self.blender = blender 

311 self.reset() 

312 

313 def reset(self): 

314 self.callingStack = [] 

315 self.operandStack = [] 

316 self.hintCount = 0 

317 self.hintMaskBytes = 0 

318 self.numRegions = 0 

319 self.vsIndex = 0 

320 

321 def execute(self, charString): 

322 self.callingStack.append(charString) 

323 needsDecompilation = charString.needsDecompilation() 

324 if needsDecompilation: 

325 program = [] 

326 pushToProgram = program.append 

327 else: 

328 pushToProgram = lambda x: None 

329 pushToStack = self.operandStack.append 

330 index = 0 

331 while True: 

332 token, isOperator, index = charString.getToken(index) 

333 if token is None: 

334 break # we're done! 

335 pushToProgram(token) 

336 if isOperator: 

337 handlerName = "op_" + token 

338 handler = getattr(self, handlerName, None) 

339 if handler is not None: 

340 rv = handler(index) 

341 if rv: 

342 hintMaskBytes, index = rv 

343 pushToProgram(hintMaskBytes) 

344 else: 

345 self.popall() 

346 else: 

347 pushToStack(token) 

348 if needsDecompilation: 

349 charString.setProgram(program) 

350 del self.callingStack[-1] 

351 

352 def pop(self): 

353 value = self.operandStack[-1] 

354 del self.operandStack[-1] 

355 return value 

356 

357 def popall(self): 

358 stack = self.operandStack[:] 

359 self.operandStack[:] = [] 

360 return stack 

361 

362 def push(self, value): 

363 self.operandStack.append(value) 

364 

365 def op_return(self, index): 

366 if self.operandStack: 

367 pass 

368 

369 def op_endchar(self, index): 

370 pass 

371 

372 def op_ignore(self, index): 

373 pass 

374 

375 def op_callsubr(self, index): 

376 subrIndex = self.pop() 

377 subr = self.localSubrs[subrIndex + self.localBias] 

378 self.execute(subr) 

379 

380 def op_callgsubr(self, index): 

381 subrIndex = self.pop() 

382 subr = self.globalSubrs[subrIndex + self.globalBias] 

383 self.execute(subr) 

384 

385 def op_hstem(self, index): 

386 self.countHints() 

387 

388 def op_vstem(self, index): 

389 self.countHints() 

390 

391 def op_hstemhm(self, index): 

392 self.countHints() 

393 

394 def op_vstemhm(self, index): 

395 self.countHints() 

396 

397 def op_hintmask(self, index): 

398 if not self.hintMaskBytes: 

399 self.countHints() 

400 self.hintMaskBytes = (self.hintCount + 7) // 8 

401 hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes) 

402 return hintMaskBytes, index 

403 

404 op_cntrmask = op_hintmask 

405 

406 def countHints(self): 

407 args = self.popall() 

408 self.hintCount = self.hintCount + len(args) // 2 

409 

410 # misc 

411 def op_and(self, index): 

412 raise NotImplementedError 

413 

414 def op_or(self, index): 

415 raise NotImplementedError 

416 

417 def op_not(self, index): 

418 raise NotImplementedError 

419 

420 def op_store(self, index): 

421 raise NotImplementedError 

422 

423 def op_abs(self, index): 

424 raise NotImplementedError 

425 

426 def op_add(self, index): 

427 raise NotImplementedError 

428 

429 def op_sub(self, index): 

430 raise NotImplementedError 

431 

432 def op_div(self, index): 

433 raise NotImplementedError 

434 

435 def op_load(self, index): 

436 raise NotImplementedError 

437 

438 def op_neg(self, index): 

439 raise NotImplementedError 

440 

441 def op_eq(self, index): 

442 raise NotImplementedError 

443 

444 def op_drop(self, index): 

445 raise NotImplementedError 

446 

447 def op_put(self, index): 

448 raise NotImplementedError 

449 

450 def op_get(self, index): 

451 raise NotImplementedError 

452 

453 def op_ifelse(self, index): 

454 raise NotImplementedError 

455 

456 def op_random(self, index): 

457 raise NotImplementedError 

458 

459 def op_mul(self, index): 

460 raise NotImplementedError 

461 

462 def op_sqrt(self, index): 

463 raise NotImplementedError 

464 

465 def op_dup(self, index): 

466 raise NotImplementedError 

467 

468 def op_exch(self, index): 

469 raise NotImplementedError 

470 

471 def op_index(self, index): 

472 raise NotImplementedError 

473 

474 def op_roll(self, index): 

475 raise NotImplementedError 

476 

477 def op_blend(self, index): 

478 if self.numRegions == 0: 

479 self.numRegions = self.private.getNumRegions() 

480 numBlends = self.pop() 

481 numOps = numBlends * (self.numRegions + 1) 

482 if self.blender is None: 

483 del self.operandStack[ 

484 -(numOps - numBlends) : 

485 ] # Leave the default operands on the stack. 

486 else: 

487 argi = len(self.operandStack) - numOps 

488 end_args = tuplei = argi + numBlends 

489 while argi < end_args: 

490 next_ti = tuplei + self.numRegions 

491 deltas = self.operandStack[tuplei:next_ti] 

492 delta = self.blender(self.vsIndex, deltas) 

493 self.operandStack[argi] += delta 

494 tuplei = next_ti 

495 argi += 1 

496 self.operandStack[end_args:] = [] 

497 

498 def op_vsindex(self, index): 

499 vi = self.pop() 

500 self.vsIndex = vi 

501 self.numRegions = self.private.getNumRegions(vi) 

502 

503 

504t1Operators = [ 

505 # opcode name 

506 (1, "hstem"), 

507 (3, "vstem"), 

508 (4, "vmoveto"), 

509 (5, "rlineto"), 

510 (6, "hlineto"), 

511 (7, "vlineto"), 

512 (8, "rrcurveto"), 

513 (9, "closepath"), 

514 (10, "callsubr"), 

515 (11, "return"), 

516 (13, "hsbw"), 

517 (14, "endchar"), 

518 (21, "rmoveto"), 

519 (22, "hmoveto"), 

520 (30, "vhcurveto"), 

521 (31, "hvcurveto"), 

522 ((12, 0), "dotsection"), 

523 ((12, 1), "vstem3"), 

524 ((12, 2), "hstem3"), 

525 ((12, 6), "seac"), 

526 ((12, 7), "sbw"), 

527 ((12, 12), "div"), 

528 ((12, 16), "callothersubr"), 

529 ((12, 17), "pop"), 

530 ((12, 33), "setcurrentpoint"), 

531] 

532 

533 

534class T2WidthExtractor(SimpleT2Decompiler): 

535 def __init__( 

536 self, 

537 localSubrs, 

538 globalSubrs, 

539 nominalWidthX, 

540 defaultWidthX, 

541 private=None, 

542 blender=None, 

543 ): 

544 SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private, blender) 

545 self.nominalWidthX = nominalWidthX 

546 self.defaultWidthX = defaultWidthX 

547 

548 def reset(self): 

549 SimpleT2Decompiler.reset(self) 

550 self.gotWidth = 0 

551 self.width = 0 

552 

553 def popallWidth(self, evenOdd=0): 

554 args = self.popall() 

555 if not self.gotWidth: 

556 if evenOdd ^ (len(args) % 2): 

557 # For CFF2 charstrings, this should never happen 

558 assert ( 

559 self.defaultWidthX is not None 

560 ), "CFF2 CharStrings must not have an initial width value" 

561 self.width = self.nominalWidthX + args[0] 

562 args = args[1:] 

563 else: 

564 self.width = self.defaultWidthX 

565 self.gotWidth = 1 

566 return args 

567 

568 def countHints(self): 

569 args = self.popallWidth() 

570 self.hintCount = self.hintCount + len(args) // 2 

571 

572 def op_rmoveto(self, index): 

573 self.popallWidth() 

574 

575 def op_hmoveto(self, index): 

576 self.popallWidth(1) 

577 

578 def op_vmoveto(self, index): 

579 self.popallWidth(1) 

580 

581 def op_endchar(self, index): 

582 self.popallWidth() 

583 

584 

585class T2OutlineExtractor(T2WidthExtractor): 

586 def __init__( 

587 self, 

588 pen, 

589 localSubrs, 

590 globalSubrs, 

591 nominalWidthX, 

592 defaultWidthX, 

593 private=None, 

594 blender=None, 

595 ): 

596 T2WidthExtractor.__init__( 

597 self, 

598 localSubrs, 

599 globalSubrs, 

600 nominalWidthX, 

601 defaultWidthX, 

602 private, 

603 blender, 

604 ) 

605 self.pen = pen 

606 self.subrLevel = 0 

607 

608 def reset(self): 

609 T2WidthExtractor.reset(self) 

610 self.currentPoint = (0, 0) 

611 self.sawMoveTo = 0 

612 self.subrLevel = 0 

613 

614 def execute(self, charString): 

615 self.subrLevel += 1 

616 super().execute(charString) 

617 self.subrLevel -= 1 

618 if self.subrLevel == 0: 

619 self.endPath() 

620 

621 def _nextPoint(self, point): 

622 x, y = self.currentPoint 

623 point = x + point[0], y + point[1] 

624 self.currentPoint = point 

625 return point 

626 

627 def rMoveTo(self, point): 

628 self.pen.moveTo(self._nextPoint(point)) 

629 self.sawMoveTo = 1 

630 

631 def rLineTo(self, point): 

632 if not self.sawMoveTo: 

633 self.rMoveTo((0, 0)) 

634 self.pen.lineTo(self._nextPoint(point)) 

635 

636 def rCurveTo(self, pt1, pt2, pt3): 

637 if not self.sawMoveTo: 

638 self.rMoveTo((0, 0)) 

639 nextPoint = self._nextPoint 

640 self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3)) 

641 

642 def closePath(self): 

643 if self.sawMoveTo: 

644 self.pen.closePath() 

645 self.sawMoveTo = 0 

646 

647 def endPath(self): 

648 # In T2 there are no open paths, so always do a closePath when 

649 # finishing a sub path. We avoid spurious calls to closePath() 

650 # because its a real T1 op we're emulating in T2 whereas 

651 # endPath() is just a means to that emulation 

652 if self.sawMoveTo: 

653 self.closePath() 

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 

678 def op_hmoveto(self, index): 

679 self.endPath() 

680 self.rMoveTo((self.popallWidth(1)[0], 0)) 

681 

682 def op_vmoveto(self, index): 

683 self.endPath() 

684 self.rMoveTo((0, self.popallWidth(1)[0])) 

685 

686 def op_endchar(self, index): 

687 self.endPath() 

688 args = self.popallWidth() 

689 if args: 

690 from fontTools.encodings.StandardEncoding import StandardEncoding 

691 

692 # endchar can do seac accent bulding; The T2 spec says it's deprecated, 

693 # but recent software that shall remain nameless does output it. 

694 adx, ady, bchar, achar = args 

695 baseGlyph = StandardEncoding[bchar] 

696 self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) 

697 accentGlyph = StandardEncoding[achar] 

698 self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) 

699 

700 # 

701 # path constructors, lines 

702 # 

703 def op_rlineto(self, index): 

704 args = self.popall() 

705 for i in range(0, len(args), 2): 

706 point = args[i : i + 2] 

707 self.rLineTo(point) 

708 

709 def op_hlineto(self, index): 

710 self.alternatingLineto(1) 

711 

712 def op_vlineto(self, index): 

713 self.alternatingLineto(0) 

714 

715 # 

716 # path constructors, curves 

717 # 

718 def op_rrcurveto(self, index): 

719 """{dxa dya dxb dyb dxc dyc}+ rrcurveto""" 

720 args = self.popall() 

721 for i in range(0, len(args), 6): 

722 ( 

723 dxa, 

724 dya, 

725 dxb, 

726 dyb, 

727 dxc, 

728 dyc, 

729 ) = args[i : i + 6] 

730 self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc)) 

731 

732 def op_rcurveline(self, index): 

733 """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline""" 

734 args = self.popall() 

735 for i in range(0, len(args) - 2, 6): 

736 dxb, dyb, dxc, dyc, dxd, dyd = args[i : i + 6] 

737 self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) 

738 self.rLineTo(args[-2:]) 

739 

740 def op_rlinecurve(self, index): 

741 """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve""" 

742 args = self.popall() 

743 lineArgs = args[:-6] 

744 for i in range(0, len(lineArgs), 2): 

745 self.rLineTo(lineArgs[i : i + 2]) 

746 dxb, dyb, dxc, dyc, dxd, dyd = args[-6:] 

747 self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) 

748 

749 def op_vvcurveto(self, index): 

750 "dx1? {dya dxb dyb dyc}+ vvcurveto" 

751 args = self.popall() 

752 if len(args) % 2: 

753 dx1 = args[0] 

754 args = args[1:] 

755 else: 

756 dx1 = 0 

757 for i in range(0, len(args), 4): 

758 dya, dxb, dyb, dyc = args[i : i + 4] 

759 self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc)) 

760 dx1 = 0 

761 

762 def op_hhcurveto(self, index): 

763 """dy1? {dxa dxb dyb dxc}+ hhcurveto""" 

764 args = self.popall() 

765 if len(args) % 2: 

766 dy1 = args[0] 

767 args = args[1:] 

768 else: 

769 dy1 = 0 

770 for i in range(0, len(args), 4): 

771 dxa, dxb, dyb, dxc = args[i : i + 4] 

772 self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0)) 

773 dy1 = 0 

774 

775 def op_vhcurveto(self, index): 

776 """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) 

777 {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto 

778 """ 

779 args = self.popall() 

780 while args: 

781 args = self.vcurveto(args) 

782 if args: 

783 args = self.hcurveto(args) 

784 

785 def op_hvcurveto(self, index): 

786 """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? 

787 {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? 

788 """ 

789 args = self.popall() 

790 while args: 

791 args = self.hcurveto(args) 

792 if args: 

793 args = self.vcurveto(args) 

794 

795 # 

796 # path constructors, flex 

797 # 

798 def op_hflex(self, index): 

799 dx1, dx2, dy2, dx3, dx4, dx5, dx6 = self.popall() 

800 dy1 = dy3 = dy4 = dy6 = 0 

801 dy5 = -dy2 

802 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 

803 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 

804 

805 def op_flex(self, index): 

806 dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6, fd = self.popall() 

807 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 

808 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 

809 

810 def op_hflex1(self, index): 

811 dx1, dy1, dx2, dy2, dx3, dx4, dx5, dy5, dx6 = self.popall() 

812 dy3 = dy4 = 0 

813 dy6 = -(dy1 + dy2 + dy3 + dy4 + dy5) 

814 

815 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 

816 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 

817 

818 def op_flex1(self, index): 

819 dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, d6 = self.popall() 

820 dx = dx1 + dx2 + dx3 + dx4 + dx5 

821 dy = dy1 + dy2 + dy3 + dy4 + dy5 

822 if abs(dx) > abs(dy): 

823 dx6 = d6 

824 dy6 = -dy 

825 else: 

826 dx6 = -dx 

827 dy6 = d6 

828 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 

829 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 

830 

831 # misc 

832 def op_and(self, index): 

833 raise NotImplementedError 

834 

835 def op_or(self, index): 

836 raise NotImplementedError 

837 

838 def op_not(self, index): 

839 raise NotImplementedError 

840 

841 def op_store(self, index): 

842 raise NotImplementedError 

843 

844 def op_abs(self, index): 

845 raise NotImplementedError 

846 

847 def op_add(self, index): 

848 raise NotImplementedError 

849 

850 def op_sub(self, index): 

851 raise NotImplementedError 

852 

853 def op_div(self, index): 

854 num2 = self.pop() 

855 num1 = self.pop() 

856 d1 = num1 // num2 

857 d2 = num1 / num2 

858 if d1 == d2: 

859 self.push(d1) 

860 else: 

861 self.push(d2) 

862 

863 def op_load(self, index): 

864 raise NotImplementedError 

865 

866 def op_neg(self, index): 

867 raise NotImplementedError 

868 

869 def op_eq(self, index): 

870 raise NotImplementedError 

871 

872 def op_drop(self, index): 

873 raise NotImplementedError 

874 

875 def op_put(self, index): 

876 raise NotImplementedError 

877 

878 def op_get(self, index): 

879 raise NotImplementedError 

880 

881 def op_ifelse(self, index): 

882 raise NotImplementedError 

883 

884 def op_random(self, index): 

885 raise NotImplementedError 

886 

887 def op_mul(self, index): 

888 raise NotImplementedError 

889 

890 def op_sqrt(self, index): 

891 raise NotImplementedError 

892 

893 def op_dup(self, index): 

894 raise NotImplementedError 

895 

896 def op_exch(self, index): 

897 raise NotImplementedError 

898 

899 def op_index(self, index): 

900 raise NotImplementedError 

901 

902 def op_roll(self, index): 

903 raise NotImplementedError 

904 

905 # 

906 # miscellaneous helpers 

907 # 

908 def alternatingLineto(self, isHorizontal): 

909 args = self.popall() 

910 for arg in args: 

911 if isHorizontal: 

912 point = (arg, 0) 

913 else: 

914 point = (0, arg) 

915 self.rLineTo(point) 

916 isHorizontal = not isHorizontal 

917 

918 def vcurveto(self, args): 

919 dya, dxb, dyb, dxc = args[:4] 

920 args = args[4:] 

921 if len(args) == 1: 

922 dyc = args[0] 

923 args = [] 

924 else: 

925 dyc = 0 

926 self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc)) 

927 return args 

928 

929 def hcurveto(self, args): 

930 dxa, dxb, dyb, dyc = args[:4] 

931 args = args[4:] 

932 if len(args) == 1: 

933 dxc = args[0] 

934 args = [] 

935 else: 

936 dxc = 0 

937 self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc)) 

938 return args 

939 

940 

941class T1OutlineExtractor(T2OutlineExtractor): 

942 def __init__(self, pen, subrs): 

943 self.pen = pen 

944 self.subrs = subrs 

945 self.reset() 

946 

947 def reset(self): 

948 self.flexing = 0 

949 self.width = 0 

950 self.sbx = 0 

951 T2OutlineExtractor.reset(self) 

952 

953 def endPath(self): 

954 if self.sawMoveTo: 

955 self.pen.endPath() 

956 self.sawMoveTo = 0 

957 

958 def popallWidth(self, evenOdd=0): 

959 return self.popall() 

960 

961 def exch(self): 

962 stack = self.operandStack 

963 stack[-1], stack[-2] = stack[-2], stack[-1] 

964 

965 # 

966 # path constructors 

967 # 

968 def op_rmoveto(self, index): 

969 if self.flexing: 

970 return 

971 self.endPath() 

972 self.rMoveTo(self.popall()) 

973 

974 def op_hmoveto(self, index): 

975 if self.flexing: 

976 # We must add a parameter to the stack if we are flexing 

977 self.push(0) 

978 return 

979 self.endPath() 

980 self.rMoveTo((self.popall()[0], 0)) 

981 

982 def op_vmoveto(self, index): 

983 if self.flexing: 

984 # We must add a parameter to the stack if we are flexing 

985 self.push(0) 

986 self.exch() 

987 return 

988 self.endPath() 

989 self.rMoveTo((0, self.popall()[0])) 

990 

991 def op_closepath(self, index): 

992 self.closePath() 

993 

994 def op_setcurrentpoint(self, index): 

995 args = self.popall() 

996 x, y = args 

997 self.currentPoint = x, y 

998 

999 def op_endchar(self, index): 

1000 self.endPath() 

1001 

1002 def op_hsbw(self, index): 

1003 sbx, wx = self.popall() 

1004 self.width = wx 

1005 self.sbx = sbx 

1006 self.currentPoint = sbx, self.currentPoint[1] 

1007 

1008 def op_sbw(self, index): 

1009 self.popall() # XXX 

1010 

1011 # 

1012 def op_callsubr(self, index): 

1013 subrIndex = self.pop() 

1014 subr = self.subrs[subrIndex] 

1015 self.execute(subr) 

1016 

1017 def op_callothersubr(self, index): 

1018 subrIndex = self.pop() 

1019 nArgs = self.pop() 

1020 # print nArgs, subrIndex, "callothersubr" 

1021 if subrIndex == 0 and nArgs == 3: 

1022 self.doFlex() 

1023 self.flexing = 0 

1024 elif subrIndex == 1 and nArgs == 0: 

1025 self.flexing = 1 

1026 # ignore... 

1027 

1028 def op_pop(self, index): 

1029 pass # ignore... 

1030 

1031 def doFlex(self): 

1032 finaly = self.pop() 

1033 finalx = self.pop() 

1034 self.pop() # flex height is unused 

1035 

1036 p3y = self.pop() 

1037 p3x = self.pop() 

1038 bcp4y = self.pop() 

1039 bcp4x = self.pop() 

1040 bcp3y = self.pop() 

1041 bcp3x = self.pop() 

1042 p2y = self.pop() 

1043 p2x = self.pop() 

1044 bcp2y = self.pop() 

1045 bcp2x = self.pop() 

1046 bcp1y = self.pop() 

1047 bcp1x = self.pop() 

1048 rpy = self.pop() 

1049 rpx = self.pop() 

1050 

1051 # call rrcurveto 

1052 self.push(bcp1x + rpx) 

1053 self.push(bcp1y + rpy) 

1054 self.push(bcp2x) 

1055 self.push(bcp2y) 

1056 self.push(p2x) 

1057 self.push(p2y) 

1058 self.op_rrcurveto(None) 

1059 

1060 # call rrcurveto 

1061 self.push(bcp3x) 

1062 self.push(bcp3y) 

1063 self.push(bcp4x) 

1064 self.push(bcp4y) 

1065 self.push(p3x) 

1066 self.push(p3y) 

1067 self.op_rrcurveto(None) 

1068 

1069 # Push back final coords so subr 0 can find them 

1070 self.push(finalx) 

1071 self.push(finaly) 

1072 

1073 def op_dotsection(self, index): 

1074 self.popall() # XXX 

1075 

1076 def op_hstem3(self, index): 

1077 self.popall() # XXX 

1078 

1079 def op_seac(self, index): 

1080 "asb adx ady bchar achar seac" 

1081 from fontTools.encodings.StandardEncoding import StandardEncoding 

1082 

1083 asb, adx, ady, bchar, achar = self.popall() 

1084 baseGlyph = StandardEncoding[bchar] 

1085 self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) 

1086 accentGlyph = StandardEncoding[achar] 

1087 adx = adx + self.sbx - asb # seac weirdness 

1088 self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) 

1089 

1090 def op_vstem3(self, index): 

1091 self.popall() # XXX 

1092 

1093 

1094class T2CharString(object): 

1095 operandEncoding = t2OperandEncoding 

1096 operators, opcodes = buildOperatorDict(t2Operators) 

1097 decompilerClass = SimpleT2Decompiler 

1098 outlineExtractor = T2OutlineExtractor 

1099 

1100 def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None): 

1101 if program is None: 

1102 program = [] 

1103 self.bytecode = bytecode 

1104 self.program = program 

1105 self.private = private 

1106 self.globalSubrs = globalSubrs if globalSubrs is not None else [] 

1107 self._cur_vsindex = None 

1108 

1109 def getNumRegions(self, vsindex=None): 

1110 pd = self.private 

1111 assert pd is not None 

1112 if vsindex is not None: 

1113 self._cur_vsindex = vsindex 

1114 elif self._cur_vsindex is None: 

1115 self._cur_vsindex = pd.vsindex if hasattr(pd, "vsindex") else 0 

1116 return pd.getNumRegions(self._cur_vsindex) 

1117 

1118 def __repr__(self): 

1119 if self.bytecode is None: 

1120 return "<%s (source) at %x>" % (self.__class__.__name__, id(self)) 

1121 else: 

1122 return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self)) 

1123 

1124 def getIntEncoder(self): 

1125 return encodeIntT2 

1126 

1127 def getFixedEncoder(self): 

1128 return encodeFixed 

1129 

1130 def decompile(self): 

1131 if not self.needsDecompilation(): 

1132 return 

1133 subrs = getattr(self.private, "Subrs", []) 

1134 decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private) 

1135 decompiler.execute(self) 

1136 

1137 def draw(self, pen, blender=None): 

1138 subrs = getattr(self.private, "Subrs", []) 

1139 extractor = self.outlineExtractor( 

1140 pen, 

1141 subrs, 

1142 self.globalSubrs, 

1143 self.private.nominalWidthX, 

1144 self.private.defaultWidthX, 

1145 self.private, 

1146 blender, 

1147 ) 

1148 extractor.execute(self) 

1149 self.width = extractor.width 

1150 

1151 def calcBounds(self, glyphSet): 

1152 boundsPen = BoundsPen(glyphSet) 

1153 self.draw(boundsPen) 

1154 return boundsPen.bounds 

1155 

1156 def compile(self, isCFF2=False): 

1157 if self.bytecode is not None: 

1158 return 

1159 opcodes = self.opcodes 

1160 program = self.program 

1161 

1162 if isCFF2: 

1163 # If present, remove return and endchar operators. 

1164 if program and program[-1] in ("return", "endchar"): 

1165 program = program[:-1] 

1166 elif program and not isinstance(program[-1], str): 

1167 raise CharStringCompileError( 

1168 "T2CharString or Subr has items on the stack after last operator." 

1169 ) 

1170 

1171 bytecode = [] 

1172 encodeInt = self.getIntEncoder() 

1173 encodeFixed = self.getFixedEncoder() 

1174 i = 0 

1175 end = len(program) 

1176 while i < end: 

1177 token = program[i] 

1178 i = i + 1 

1179 if isinstance(token, str): 

1180 try: 

1181 bytecode.extend(bytechr(b) for b in opcodes[token]) 

1182 except KeyError: 

1183 raise CharStringCompileError("illegal operator: %s" % token) 

1184 if token in ("hintmask", "cntrmask"): 

1185 bytecode.append(program[i]) # hint mask 

1186 i = i + 1 

1187 elif isinstance(token, int): 

1188 bytecode.append(encodeInt(token)) 

1189 elif isinstance(token, float): 

1190 bytecode.append(encodeFixed(token)) 

1191 else: 

1192 assert 0, "unsupported type: %s" % type(token) 

1193 try: 

1194 bytecode = bytesjoin(bytecode) 

1195 except TypeError: 

1196 log.error(bytecode) 

1197 raise 

1198 self.setBytecode(bytecode) 

1199 

1200 def needsDecompilation(self): 

1201 return self.bytecode is not None 

1202 

1203 def setProgram(self, program): 

1204 self.program = program 

1205 self.bytecode = None 

1206 

1207 def setBytecode(self, bytecode): 

1208 self.bytecode = bytecode 

1209 self.program = None 

1210 

1211 def getToken(self, index, len=len, byteord=byteord, isinstance=isinstance): 

1212 if self.bytecode is not None: 

1213 if index >= len(self.bytecode): 

1214 return None, 0, 0 

1215 b0 = byteord(self.bytecode[index]) 

1216 index = index + 1 

1217 handler = self.operandEncoding[b0] 

1218 token, index = handler(self, b0, self.bytecode, index) 

1219 else: 

1220 if index >= len(self.program): 

1221 return None, 0, 0 

1222 token = self.program[index] 

1223 index = index + 1 

1224 isOperator = isinstance(token, str) 

1225 return token, isOperator, index 

1226 

1227 def getBytes(self, index, nBytes): 

1228 if self.bytecode is not None: 

1229 newIndex = index + nBytes 

1230 bytes = self.bytecode[index:newIndex] 

1231 index = newIndex 

1232 else: 

1233 bytes = self.program[index] 

1234 index = index + 1 

1235 assert len(bytes) == nBytes 

1236 return bytes, index 

1237 

1238 def handle_operator(self, operator): 

1239 return operator 

1240 

1241 def toXML(self, xmlWriter, ttFont=None): 

1242 from fontTools.misc.textTools import num2binary 

1243 

1244 if self.bytecode is not None: 

1245 xmlWriter.dumphex(self.bytecode) 

1246 else: 

1247 index = 0 

1248 args = [] 

1249 while True: 

1250 token, isOperator, index = self.getToken(index) 

1251 if token is None: 

1252 break 

1253 if isOperator: 

1254 if token in ("hintmask", "cntrmask"): 

1255 hintMask, isOperator, index = self.getToken(index) 

1256 bits = [] 

1257 for byte in hintMask: 

1258 bits.append(num2binary(byteord(byte), 8)) 

1259 hintMask = strjoin(bits) 

1260 line = " ".join(args + [token, hintMask]) 

1261 else: 

1262 line = " ".join(args + [token]) 

1263 xmlWriter.write(line) 

1264 xmlWriter.newline() 

1265 args = [] 

1266 else: 

1267 if isinstance(token, float): 

1268 token = floatToFixedToStr(token, precisionBits=16) 

1269 else: 

1270 token = str(token) 

1271 args.append(token) 

1272 if args: 

1273 # NOTE: only CFF2 charstrings/subrs can have numeric arguments on 

1274 # the stack after the last operator. Compiling this would fail if 

1275 # this is part of CFF 1.0 table. 

1276 line = " ".join(args) 

1277 xmlWriter.write(line) 

1278 

1279 def fromXML(self, name, attrs, content): 

1280 from fontTools.misc.textTools import binary2num, readHex 

1281 

1282 if attrs.get("raw"): 

1283 self.setBytecode(readHex(content)) 

1284 return 

1285 content = strjoin(content) 

1286 content = content.split() 

1287 program = [] 

1288 end = len(content) 

1289 i = 0 

1290 while i < end: 

1291 token = content[i] 

1292 i = i + 1 

1293 try: 

1294 token = int(token) 

1295 except ValueError: 

1296 try: 

1297 token = strToFixedToFloat(token, precisionBits=16) 

1298 except ValueError: 

1299 program.append(token) 

1300 if token in ("hintmask", "cntrmask"): 

1301 mask = content[i] 

1302 maskBytes = b"" 

1303 for j in range(0, len(mask), 8): 

1304 maskBytes = maskBytes + bytechr(binary2num(mask[j : j + 8])) 

1305 program.append(maskBytes) 

1306 i = i + 1 

1307 else: 

1308 program.append(token) 

1309 else: 

1310 program.append(token) 

1311 self.setProgram(program) 

1312 

1313 

1314class T1CharString(T2CharString): 

1315 operandEncoding = t1OperandEncoding 

1316 operators, opcodes = buildOperatorDict(t1Operators) 

1317 

1318 def __init__(self, bytecode=None, program=None, subrs=None): 

1319 super().__init__(bytecode, program) 

1320 self.subrs = subrs 

1321 

1322 def getIntEncoder(self): 

1323 return encodeIntT1 

1324 

1325 def getFixedEncoder(self): 

1326 def encodeFixed(value): 

1327 raise TypeError("Type 1 charstrings don't support floating point operands") 

1328 

1329 def decompile(self): 

1330 if self.bytecode is None: 

1331 return 

1332 program = [] 

1333 index = 0 

1334 while True: 

1335 token, isOperator, index = self.getToken(index) 

1336 if token is None: 

1337 break 

1338 program.append(token) 

1339 self.setProgram(program) 

1340 

1341 def draw(self, pen): 

1342 extractor = T1OutlineExtractor(pen, self.subrs) 

1343 extractor.execute(self) 

1344 self.width = extractor.width 

1345 

1346 

1347class DictDecompiler(object): 

1348 operandEncoding = cffDictOperandEncoding 

1349 

1350 def __init__(self, strings, parent=None): 

1351 self.stack = [] 

1352 self.strings = strings 

1353 self.dict = {} 

1354 self.parent = parent 

1355 

1356 def getDict(self): 

1357 assert len(self.stack) == 0, "non-empty stack" 

1358 return self.dict 

1359 

1360 def decompile(self, data): 

1361 index = 0 

1362 lenData = len(data) 

1363 push = self.stack.append 

1364 while index < lenData: 

1365 b0 = byteord(data[index]) 

1366 index = index + 1 

1367 handler = self.operandEncoding[b0] 

1368 value, index = handler(self, b0, data, index) 

1369 if value is not None: 

1370 push(value) 

1371 

1372 def pop(self): 

1373 value = self.stack[-1] 

1374 del self.stack[-1] 

1375 return value 

1376 

1377 def popall(self): 

1378 args = self.stack[:] 

1379 del self.stack[:] 

1380 return args 

1381 

1382 def handle_operator(self, operator): 

1383 operator, argType = operator 

1384 if isinstance(argType, tuple): 

1385 value = () 

1386 for i in range(len(argType) - 1, -1, -1): 

1387 arg = argType[i] 

1388 arghandler = getattr(self, "arg_" + arg) 

1389 value = (arghandler(operator),) + value 

1390 else: 

1391 arghandler = getattr(self, "arg_" + argType) 

1392 value = arghandler(operator) 

1393 if operator == "blend": 

1394 self.stack.extend(value) 

1395 else: 

1396 self.dict[operator] = value 

1397 

1398 def arg_number(self, name): 

1399 if isinstance(self.stack[0], list): 

1400 out = self.arg_blend_number(self.stack) 

1401 else: 

1402 out = self.pop() 

1403 return out 

1404 

1405 def arg_blend_number(self, name): 

1406 out = [] 

1407 blendArgs = self.pop() 

1408 numMasters = len(blendArgs) 

1409 out.append(blendArgs) 

1410 out.append("blend") 

1411 dummy = self.popall() 

1412 return blendArgs 

1413 

1414 def arg_SID(self, name): 

1415 return self.strings[self.pop()] 

1416 

1417 def arg_array(self, name): 

1418 return self.popall() 

1419 

1420 def arg_blendList(self, name): 

1421 """ 

1422 There may be non-blend args at the top of the stack. We first calculate 

1423 where the blend args start in the stack. These are the last 

1424 numMasters*numBlends) +1 args. 

1425 The blend args starts with numMasters relative coordinate values, the BlueValues in the list from the default master font. This is followed by 

1426 numBlends list of values. Each of value in one of these lists is the 

1427 Variable Font delta for the matching region. 

1428 

1429 We re-arrange this to be a list of numMaster entries. Each entry starts with the corresponding default font relative value, and is followed by 

1430 the delta values. We then convert the default values, the first item in each entry, to an absolute value. 

1431 """ 

1432 vsindex = self.dict.get("vsindex", 0) 

1433 numMasters = ( 

1434 self.parent.getNumRegions(vsindex) + 1 

1435 ) # only a PrivateDict has blended ops. 

1436 numBlends = self.pop() 

1437 args = self.popall() 

1438 numArgs = len(args) 

1439 # The spec says that there should be no non-blended Blue Values,. 

1440 assert numArgs == numMasters * numBlends 

1441 value = [None] * numBlends 

1442 numDeltas = numMasters - 1 

1443 i = 0 

1444 prevVal = 0 

1445 while i < numBlends: 

1446 newVal = args[i] + prevVal 

1447 prevVal = newVal 

1448 masterOffset = numBlends + (i * numDeltas) 

1449 blendList = [newVal] + args[masterOffset : masterOffset + numDeltas] 

1450 value[i] = blendList 

1451 i += 1 

1452 return value 

1453 

1454 def arg_delta(self, name): 

1455 valueList = self.popall() 

1456 out = [] 

1457 if valueList and isinstance(valueList[0], list): 

1458 # arg_blendList() has already converted these to absolute values. 

1459 out = valueList 

1460 else: 

1461 current = 0 

1462 for v in valueList: 

1463 current = current + v 

1464 out.append(current) 

1465 return out 

1466 

1467 

1468def calcSubrBias(subrs): 

1469 nSubrs = len(subrs) 

1470 if nSubrs < 1240: 

1471 bias = 107 

1472 elif nSubrs < 33900: 

1473 bias = 1131 

1474 else: 

1475 bias = 32768 

1476 return bias