Coverage for pyilper/pilhp2225b.py: 93%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1098 statements  

1 #!/usr/bin/python3 

2# -*- coding: utf-8 -*- 

3# pyILPER 1.2.4 for Linux 

4# 

5# An emulator for virtual HP-IL devices for the PIL-Box 

6# derived from ILPER 1.4.5 for Windows 

7# Copyright (c) 2008-2013 Jean-Francois Garnier 

8# C++ version (c) 2013 Christoph Gießelink 

9# Python Version (c) 2015 Joachim Siebold 

10# 

11# This program is free software; you can redistribute it and/or 

12# modify it under the terms of the GNU General Public License 

13# as published by the Free Software Foundation; either version 2 

14# of the License, or (at your option) any later version. 

15# 

16# This program is distributed in the hope that it will be useful, 

17# but WITHOUT ANY WARRANTY; without even the implied warranty of 

18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19# GNU General Public License for more details. 

20# 

21# You should have received a copy of the GNU General Public License 

22# along with this program; if not, write to the Free Software 

23# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

24# 

25# HP2225B virtual device classes --------------------------------------------- 

26# 

27# Changelog 

28# 30.12.2018 jsi: 

29# - initial version 

30# 02.01.2018 jsi: 

31# - added support for ^N and ^O for bold mode on and off 

32# - allow ESC&kS in addition to ESC&k0S to switch to normal character pitch 

33# 04.01.2018 jsi: 

34# - support zero length graphics chunks 

35# - switchable print color 

36# 

37import copy 

38import queue 

39import threading 

40import re 

41from math import floor 

42from PySide6 import QtCore, QtWidgets, QtGui, QtPrintSupport 

43from .pilcore import UPDATE_TIMER, PDF_ORIENTATION_PORTRAIT 

44from .pilconfig import PILCONFIG 

45from .pilcharconv import charconv, barrconv, CHARSET_HP2225 

46from .pildevbase import cls_pildevbase 

47from .pilwidgets import cls_tabgeneric, LogCheckboxWidget, T_INTEGER, O_DEFAULT, T_STRING 

48from .pilcore import * 

49 

50# 

51# constants -------------------------------------------------------------- 

52# 

53 

54PDF_LINES=70 # number of lines in pdf output 

55PDF_MARGINS=50 # margins (top,bot,left,right) of pdf output 

56PDF_MAX_COLS=3 # max number of columns in pdf output 

57PDF_COLUMN_SPACING=80 # spacing between columns 

58PDF_LINE_SPACING=0 # linespacing in (relative) pixel 

59 

60# GUI commands 

61CMD_LF_PRESSED= 0 

62CMD_LF_RELEASED= 1 

63CMD_FF_PRESSED= 2 

64CMD_CLEAR= 3 

65 

66# HPIL-Thread commands 

67REMOTECMD_CLEAR=0 # clear device 

68REMOTECMD_LOG=1 # log something 

69REMOTECMD_STATUS=2 # printer status information:  

70REMOTECMD_TEXT=3 # print characters according to current status 

71REMOTECMD_GRAPHICS=4 # print graphics according to current status 

72REMOTECMD_CR=5 # carriage return 

73REMOTECMD_LF=6 # line feed 

74REMOTECMD_HLF=7 # half line feed 

75REMOTECMD_BS=8 # backspace 

76REMOTECMD_FF=9 # form feed 

77REMOTECMD_TERMGRAPHICS=10 # end of graphics 

78 

79ELEMENT_FF=0 

80ELEMENT_TEXT=1 

81ELEMENT_GRAPHICS=2 

82 

83# Printer constants 

84BUFFER_LINE_H=2 # number of dots for a buffer line height 

85PRINTER_WIDTH_HIGH= 1280 # width in dots for high resolution 

86PRINTER_WIDTH_LOW= 640 # width in dots for low resolution 

87HP2225B_FONT_PIXELSIZE=28 # pixel size of the font used 

88HP2225B_MAX_LINES= 69 # maximum number of lines for a page 

89 

90# Font widths 

91# Print mode norm : 80 chars/line, character width 16 dots 

92# Print mode expand : 40 chars/line, character width 32 dots 

93# Print mode compressed : 142 chars/line, character width 9 dots 

94# Print mode expand/compressed : 71 chars/line, character width 18 dots 

95FONT_WIDTH= [16, 32, 9, 18 ] 

96# 

97# this is a hack because Qt on Macos does not display expanded fonts correctly 

98# 

99if isMACOS(): 

100 FONT_STRETCH= [100, 110, 56, 110] 

101else: 

102 FONT_STRETCH= [100, 200, 56, 113] 

103 

104BUFFER_SIZE_NAMES=["5 Pages", "10 Pages","20 Pages","50 Pages"] 

105BUFFER_SIZE_VALUES=[2500, 5000, 10000, 25000] 

106# 

107# Print colors 

108# 

109HP2225_COLOR_BLACK=0 

110HP2225_COLOR_RED=1 

111HP2225_COLR_BLUE=2 

112HP2225_COLOR_GREEN=3 

113COLOR_NAMES= [ "black", "red", "blue", "green" ] 

114HP2225_COLORS=[QtCore.Qt.black, QtCore.Qt.red, QtCore.Qt.blue, QtCore.Qt.green] 

115 

116# 

117# HP2225B tab widget --------------------------------------------------------- 

118# 

119class cls_tabhp2225b(cls_tabgeneric): 

120 

121 

122 def __init__(self,parent,name): 

123 super().__init__(parent,name) 

124 self.name=name 

125# 

126# this parameter is global 

127# 

128 self.papersize=PILCONFIG.get("pyilper","papersize") 

129# 

130# init local parameter 

131# 

132 self.screenwidth=PILCONFIG.get(self.name,"hp2225b_screenwidth",-1) 

133 self.scrollupbuffersize=PILCONFIG.get(self.name,"hp2225b_scrollupbuffersize",1) 

134 self.printcolor=PILCONFIG.get(self.name,"hp2225b_printcolor",HP2225_COLOR_BLACK) 

135# 

136# create Printer GUI object 

137# 

138 self.guiobject=cls_hp2225bWidget(self,self.name,self.papersize) 

139# 

140# add gui object  

141# 

142 self.add_guiobject(self.guiobject) 

143# 

144# add cascading config menu 

145# 

146 self.add_configwidget() 

147# 

148# add local config option 

149# 

150 self.cBut.add_option("Screen width","hp2225b_screenwidth",T_INTEGER,[O_DEFAULT,640,960,1280]) 

151 self.cBut.add_option("Buffer size","hp2225b_scrollupbuffersize",T_STRING,BUFFER_SIZE_NAMES) 

152 self.cBut.add_option("Print color","hp2225b_printcolor",T_STRING,COLOR_NAMES) 

153# 

154# add logging control widget 

155# 

156 self.add_logging() 

157# 

158# create IL-Interface object, notify printer processor object 

159# 

160 self.pildevice= cls_pilhp2225b(self.guiobject) 

161 self.guiobject.set_pildevice(self.pildevice) 

162 self.cBut.config_changed_signal.connect(self.do_tabconfig_changed) 

163# 

164# handle changes of tab config options 

165# 

166 def do_tabconfig_changed(self): 

167 self.loglevel= PILCONFIG.get(self.name,"loglevel",0) 

168 self.guiobject.reconfigure() 

169 super().do_tabconfig_changed() 

170# 

171# reconfigure: reconfigure the gui object 

172# 

173 def reconfigure(self): 

174 self.guiobject.reconfigure() 

175# 

176# enable pildevice and gui object 

177# 

178 def enable(self): 

179 super().enable() 

180 self.parent.commthread.register(self.pildevice,self.name) 

181 self.pildevice.setactive(self.active) 

182 self.pildevice.enable() 

183 self.guiobject.enable() 

184# 

185# disable pildevice and gui object 

186# 

187 def disable(self): 

188 self.pildevice.disable() 

189 self.guiobject.disable() 

190 super().disable() 

191# 

192# active/inactive: enable/disable GUI controls 

193# 

194 def toggle_active(self): 

195 super().toggle_active() 

196 self.guiobject.toggle_active() 

197# 

198# becomes visible, refresh content, activate update 

199# 

200 def becomes_visible(self): 

201 self.guiobject.becomes_visible() 

202 return 

203# 

204# becomes invisible, deactivate update 

205# 

206 def becomes_invisible(self): 

207 self.guiobject.becomes_invisible() 

208 return 

209# 

210# 

211# hp2225b widget classes - GUI component of the HP2225B HP-IL printer 

212# 

213class cls_hp2225bWidget(QtWidgets.QWidget): 

214 

215 def __init__(self,parent,name,papersize): 

216 super().__init__() 

217 self.name= name 

218 self.parent= parent 

219 self.papersize= papersize 

220 self.pildevice= None 

221# 

222# printer status that controls the appearance of the printer output  

223# 

224 self.pdf_rows=480 # text length in rows 

225 self.char_attr=0 # character pitch 

226 self.char_bold=False # bold mode 

227 self.char_underline=False # underline mode 

228 self.hiRes=False # high resolution of graphics output 

229 self.lpi6=True # lines/inch 

230 self.wrapEOL=False # EOL wrap 

231# 

232# line coordinates 

233# 

234 self.pos_y=0 # in 4 dots (1280dpi) steps 

235 self.pos_x=0 # in 1280dpi steps 

236 

237 self.graphics_counter=0 # number of graphics lines  

238# 

239# create user interface of printer widget 

240# 

241 self.hbox=QtWidgets.QHBoxLayout() 

242 self.hbox.addStretch(1) 

243# 

244# scrolled printer view 

245# 

246 self.printview=cls_ScrolledHp2225bView(self,self.name,self.papersize) 

247 self.hbox.addWidget(self.printview) 

248 self.vbox=QtWidgets.QVBoxLayout() 

249# 

250# Clear Button 

251# 

252 self.clearButton= QtWidgets.QPushButton("Clear") 

253 self.clearButton.setEnabled(False) 

254 self.clearButton.setAutoDefault(False) 

255 self.vbox.addWidget(self.clearButton) 

256 self.clearButton.clicked.connect(self.do_clear) 

257# 

258# LF Button 

259# 

260 self.LFButton= QtWidgets.QPushButton("LF") 

261 self.LFButton.setEnabled(False) 

262 self.LFButton.setAutoDefault(False) 

263 self.vbox.addWidget(self.LFButton) 

264 self.LFButton.pressed.connect(self.do_LF_pressed) 

265 self.LFButton.released.connect(self.do_LF_released) 

266# 

267# FF Button 

268# 

269 self.FFButton= QtWidgets.QPushButton("FF") 

270 self.FFButton.setEnabled(False) 

271 self.FFButton.setAutoDefault(False) 

272 self.vbox.addWidget(self.FFButton) 

273 self.FFButton.pressed.connect(self.do_FF_pressed) 

274# 

275# PDF Button 

276# 

277 self.pdfButton= QtWidgets.QPushButton("PDF") 

278 self.pdfButton.setEnabled(False) 

279 self.pdfButton.setAutoDefault(False) 

280 self.vbox.addWidget(self.pdfButton) 

281 self.pdfButton.clicked.connect(self.do_pdf) 

282 

283 self.vbox.addStretch(1) 

284 self.hbox.addLayout(self.vbox) 

285 self.hbox.addStretch(1) 

286 self.setLayout(self.hbox) 

287# 

288# initialize GUI command queue and lock 

289# 

290 self.gui_queue= queue.Queue() 

291 self.gui_queue_lock= threading.Lock() 

292# 

293# initialize refresh timer 

294# 

295 self.UpdateTimer=QtCore.QTimer() 

296 self.UpdateTimer.setSingleShot(True) 

297 self.UpdateTimer.timeout.connect(self.process_queue) 

298# 

299# initialize timer for the repeated pressed LF action 

300# 

301 self.repeatedLFpressedTimer=QtCore.QTimer() 

302 self.repeatedLFpressedTimer.timeout.connect(self.repeated_LFpressed) 

303 self.repeatedLFpressedTimer.setInterval(1500) 

304# 

305# set HP-IL device object 

306# 

307 def set_pildevice(self,pildevice): 

308 self.pildevice=pildevice 

309# 

310# enable: start timer, send mode to virtual device, update check boxes 

311# 

312 def enable(self): 

313 self.UpdateTimer.start(UPDATE_TIMER) 

314 self.toggle_active() 

315 return 

316# 

317# disable, clear the GUI queue, stop the timer 

318# 

319 def disable(self): 

320 self.gui_queue_lock.acquire() 

321 while True: 

322 try: 

323 self.gui_queue.get_nowait() 

324 self.gui_queue.task_done() 

325 except queue.Empty: 

326 break 

327 self.gui_queue_lock.release() 

328 self.UpdateTimer.stop() 

329 return 

330# 

331# becomes visible 

332# 

333 def becomes_visible(self): 

334 self.printview.becomes_visible() 

335# 

336# becomes invisible, do nothing 

337# 

338 def becomes_invisible(self): 

339 pass 

340# 

341# active/inactive: enable/disable GUI controls 

342# 

343 def toggle_active(self): 

344 if self.parent.active: 

345 self.clearButton.setEnabled(True) 

346 self.LFButton.setEnabled(True) 

347 self.FFButton.setEnabled(True) 

348 self.pdfButton.setEnabled(True) 

349 else: 

350 self.clearButton.setEnabled(False) 

351 self.LFButton.setEnabled(False) 

352 self.FFButton.setEnabled(False) 

353 self.pdfButton.setEnabled(False) 

354# 

355# reconfigure 

356# 

357 def reconfigure(self): 

358 self.printview.reconfigure() 

359 return 

360# 

361# action scripts 

362# 

363 def do_clear(self): 

364 self.printview.reset() 

365 self.pildevice.put_cmd(CMD_CLEAR) 

366 return 

367 

368 def do_FF_pressed(self): 

369 self.put_cmd([REMOTECMD_FF]) 

370 return 

371 

372 def do_LF_pressed(self): 

373 self.repeatedLFpressedTimer.start() 

374 self.put_cmd([REMOTECMD_LF]) 

375 return 

376 

377 def do_LF_released(self): 

378 self.repeatedLFpressedTimer.stop() 

379 return 

380 

381 def do_pdf(self): 

382 filename=cls_PdfOptions.getPdfOptions() 

383 if filename== "": 

384 return 

385 self.printview.pdf(filename,self.pdf_rows) 

386 return 

387# 

388# put command into the GUI-command queue, this is called by the thread component 

389# 

390 def put_cmd(self,item): 

391 self.gui_queue_lock.acquire() 

392 self.gui_queue.put(item) 

393 self.gui_queue_lock.release() 

394# 

395# repeated LF pressed action 

396# 

397 def repeated_LFpressed(self): 

398 self.put_cmd([REMOTECMD_LF]) 

399# 

400# process commands in the GUI command queue, this is called by a timer event 

401# 

402 def process_queue(self): 

403 items=[] 

404 self.gui_queue_lock.acquire() 

405 while True: 

406 try: 

407 i=self.gui_queue.get_nowait() 

408 items.append(i) 

409 self.gui_queue.task_done() 

410 except queue.Empty: 

411 break 

412 self.gui_queue_lock.release() 

413 if len(items): 

414 for c in items: 

415 self.process(c) 

416 self.UpdateTimer.start(UPDATE_TIMER) 

417 return 

418# 

419# GUI command processing, commands issued by the HP-IL thread 

420# 

421# Printer coordinate system 

422# 

423# The internal scene coordinate systems of this program is dots at 

424# a resolution of 192 dpi. The HP2225B operates 

425# either at 96x96dpi or 96x192dpi resolution. Thus we have always even 

426# values for the y coordinate.  

427# 

428# Constants for movement and positioning 

429# Dots per line: 1280 

430# Print mode norm : 80 chars/line, character width 16 dots 

431# Print mode expand : 40 chars/line, character width 32 dots 

432# Print mode compressed : 142 chars/line, character width 9 dots 

433# Print mode expand/compressed : 71 chars/line, character width 18 dots 

434# Character height always 16 dots 

435# Line spacing 8 dots at 8 lines/inch, 16 dots at 6 lines/inch 

436# Line feed is 24 dots at 8 lines/inch, 32 dots at 6 lines/inc 

437# Half line feed is 12 dots at 8 lines/inch and 16 dots at 6 lines/inch 

438# Graphics line: 16 dots height, 1280 dots width 

439# A graphics dot is 2x2 dots at low res an 2x1 dot at high res 

440# Graphics data is 80 bytes at low res and 160 bytes at high res 

441# 

442# Here we use the following coordinate system: 

443# x= 1 dot (resolution 1280) 

444# y= 4 dots (resolution 1280) 

445# LF= 6 / 8 , half LF= 3 / 4 

446#  

447 def process(self,item): 

448 cmd= item[0] 

449# 

450# clear graphhics views  

451# 

452 if cmd== REMOTECMD_CLEAR: 

453 self.printview.reset() 

454 self.pos_x=0 

455# print("GUI: reset") 

456 self.graphics_counter=0 

457# 

458# carriage return go to beginning of the current line 

459# 

460 elif cmd== REMOTECMD_CR: 

461# print("GUI: cr") 

462 self.pos_x=0 

463 self.graphics_counter=0 

464# 

465# line feed advance according to line spacing 

466# 

467 elif cmd== REMOTECMD_LF: 

468# print("GUI: lf") 

469 if self.lpi6: 

470 self.printview.advance(8) 

471 else: 

472 self.printview.advance(6) 

473 self.graphics_counter=0 

474# 

475# Form feed, we need that for the PDF output later 

476# 

477 elif cmd== REMOTECMD_FF: 

478# print("GUI: ff") 

479 if self.lpi6: 

480 self.printview.advance(8) 

481 else: 

482 self.printview.advance(6) 

483 self.graphics_counter=0 

484 self.printview.add([ELEMENT_FF]) 

485# 

486# advance one half line feed 

487# 

488 elif cmd== REMOTECMD_HLF: 

489# print("GUI: half lf") 

490 self.graphics_counter=0 

491 if self.lpi6: 

492 self.printview.advance(4) 

493 else: 

494 self.printview.advance(3) 

495# 

496# Backspace, go back one character, use current font width 

497# 

498 elif cmd== REMOTECMD_BS: 

499# print("GUI: bs") 

500 self.graphics_counter=0 

501 l= FONT_WIDTH[self.char_attr] 

502 self.pos_x-=l 

503 if self.pos_x < 0: 

504 self.pos_x=0 

505# 

506# update configuration triggered by an escape sequence 

507# 

508 elif cmd== REMOTECMD_STATUS: 

509# print("GUI status", item[1]) 

510 self.pdf_rows=item[1][0] 

511 self.char_attr=item[1][1] 

512 self.char_bold=item[1][2] 

513 self.char_underline=item[1][3] 

514 self.hiRes=item[1][4] 

515 self.lpi6=item[1][5] 

516 self.wrapEOL=item[1][6] 

517# 

518# text element, we do not support EOL wrap at the moment and ignore any text 

519# that exceeds a sinlge line 

520# 

521 elif cmd== REMOTECMD_TEXT: 

522 self.graphics_counter=0 

523# print("GUI text", self.pos_x, item[1]) 

524 txt_list= item[1] 

525 

526 while txt_list is not None: 

527# 

528# new length of row  

529# 

530 newlen=self.pos_x+len(txt_list)*FONT_WIDTH[self.char_attr] 

531# 

532# exceeds row 

533# 

534 if newlen> PRINTER_WIDTH_HIGH: 

535 fit_in_row=len(txt_list)- round((newlen-PRINTER_WIDTH_HIGH)/ 

536 FONT_WIDTH[self.char_attr]) 

537# 

538# txt contains the characters that fit in the current row 

539# 

540 txt=bytearray(txt_list[:fit_in_row]) 

541# 

542# if eolWrap is off we throw away the remaining content, otherwise 

543# keep it 

544# 

545 if self.wrapEOL: 

546 txt_list= txt_list[fit_in_row:] 

547 else: 

548 txt_list= None 

549 else: 

550# 

551# text fits into current row 

552# 

553 fit_in_row= len(txt_list) 

554 txt=bytearray(txt_list) 

555 txt_list= None 

556# 

557# add it to the current line in the view 

558# 

559 self.printview.add([ELEMENT_TEXT,self.pos_x,self.char_attr, 

560 self.char_bold,self.char_underline,barrconv(txt,CHARSET_HP2225)]) 

561 self.pos_x+= fit_in_row* FONT_WIDTH[self.char_attr] 

562# 

563# if we have remaining text in txt_list then do a cr/lf 

564# 

565 if txt_list is not None: 

566 if self.lpi6: 

567 self.printview.advance(8) 

568 else: 

569 self.printview.advance(6) 

570 self.graphics_counter=0 

571 self.pos_x=0 

572# 

573# graphics, we can have only 2 graphics lines at a single printer rows 

574# 

575 elif cmd== REMOTECMD_GRAPHICS: 

576 self.pos_x=0 

577# print("GUI: graphics",self.graphics_counter, item[1]) 

578 self.printview.add([ELEMENT_GRAPHICS,self.graphics_counter,self.hiRes,item[1]]) 

579 self.graphics_counter+=1 

580 if self.graphics_counter == 2: 

581 self.graphics_counter=0 

582 self.printview.advance(1) 

583# 

584# terminate graphics, advance 4 dots 

585# 

586 elif cmd== REMOTECMD_TERMGRAPHICS: 

587 self.graphics_counter=0 

588 self.printview.advance(1) 

589# 

590# log line 

591# 

592 elif cmd== REMOTECMD_LOG: 

593 self.parent.cbLogging.logWrite(item[1]) 

594 self.parent.cbLogging.logFlush() 

595# 

596# custom class for scrolled hp2225b output widget ---------------------------- 

597# 

598class cls_ScrolledHp2225bView(QtWidgets.QWidget): 

599 

600 def __init__(self,parent,name,papersize): 

601 super().__init__(parent) 

602 self.parent=parent 

603 self.name=name 

604#  

605# create window and scrollbars 

606# 

607 self.hbox= QtWidgets.QHBoxLayout() 

608 self.scrollbar= QtWidgets.QScrollBar() 

609 self.hp2225bwidget= cls_hp2225bView(self,self.name,papersize) 

610 self.hp2225bwidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 

611 self.hp2225bwidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 

612 self.hbox.addWidget(self.hp2225bwidget) 

613 self.hbox.addWidget(self.scrollbar) 

614 self.setLayout(self.hbox) 

615# 

616# Initialize scrollbar 

617# 

618 self.scrollbar.valueChanged.connect(self.do_scrollbar) 

619 self.scrollbar.setEnabled(True) 

620 self.reset() 

621# 

622# scrollbar value changed action 

623# 

624 def do_scrollbar(self): 

625 self.hp2225bwidget.do_scroll(self.scrollbar.value()) 

626# 

627# reset output window 

628# 

629 def reset(self): 

630 self.hp2225bwidget.reset() 

631 self.scrollbar.setMinimum(0) 

632 self.scrollbar.setMaximum(0) 

633 self.scrollbar.setSingleStep(1) 

634# 

635# generate pdf output 

636# 

637 def pdf(self,filename,pdf_rows): 

638 self.hp2225bwidget.pdf(filename,pdf_rows) 

639# 

640# becomes visible/invisible: nothing to do 

641# 

642 def becomes_visible(self): 

643 return 

644 

645 def becomes_invisible(self): 

646 return 

647# 

648# reconfigure 

649# 

650 def reconfigure(self): 

651 self.hp2225bwidget.reconfigure() 

652 return 

653# 

654# add elements 

655# 

656 def add(self,element): 

657 self.hp2225bwidget.add(element) 

658# 

659# advance 

660# 

661 def advance(self,n): 

662 self.hp2225bwidget.advance(n) 

663 

664# 

665# custom class for hp2225b output ----------------------------------------- 

666# 

667class cls_hp2225bView(QtWidgets.QGraphicsView): 

668 

669 def __init__(self,parent,name,papersize): 

670 super().__init__() 

671 self.parent=parent 

672 self.name= name 

673 self.screenwidth= -1 

674 self.printcolor= QtCore.Qt.black 

675 self.w=-1 

676 self.h=-1 

677 self.rows= 0 

678 

679 self.linebuffersize= -1 

680 self.papersize= papersize 

681# 

682# set the font and font size 

683 

684 self.font=QtGui.QFont(FONT) 

685 

686 self.font.setPixelSize(HP2225B_FONT_PIXELSIZE) 

687 metrics=QtGui.QFontMetrics(self.font) 

688# 

689# Initialize line bitmap buffer 

690# 

691 self.lb= [ ] 

692 self.lb_current= 0 

693 self.lb_anz=0 

694 self.lb_position=0 

695 

696 self.printscene=None 

697 self.reconfigure() 

698 return 

699 

700 

701 def reconfigure(self): 

702# 

703# re/configure the printview widget 

704# 

705 tmp=BUFFER_SIZE_VALUES[PILCONFIG.get(self.name,"hp2225b_scrollupbuffersize")] 

706 if tmp != self.linebuffersize: 

707 self.linebuffersize=tmp 

708 if self.printscene is not None: 

709 self.printscene.reset() 

710 self.lb= [None]* self.linebuffersize 

711 self.lb_current= 0 

712 self.lb_anz=0 

713 self.lb_position=0 

714 

715 tmp=PILCONFIG.get_dual(self.name,"hp2225b_screenwidth") 

716 if tmp != self.screenwidth: 

717 self.screenwidth=tmp 

718 self.w=self.screenwidth 

719# 

720# set fixed width 

721# 

722 self.setFixedWidth(self.w) 

723# 

724# reconfigure scene if it exists 

725# 

726 if self.printscene is not None: 

727 self.printscene.reconfigure(self.screenwidth,self.printcolor) 

728 self.do_resize() 

729# 

730# print color 

731# 

732 tmp=HP2225_COLORS[PILCONFIG.get(self.name,"hp2225b_printcolor")] 

733 if tmp != self.printcolor: 

734 self.printcolor=tmp 

735 if self.printscene is not None: 

736 self.printscene.reconfigure(self.screenwidth,self.printcolor) 

737 self.do_resize() 

738# 

739# initialize scene if it does not exist 

740# 

741 if self.printscene is None: 

742 self.printscene= cls_hp2225b_scene(self,self.font, self.screenwidth,self.printcolor) 

743 self.setScene(self.printscene) 

744 self.reset() 

745 return 

746# 

747# reset output window 

748# 

749 def reset(self): 

750 for i in range(0,self.linebuffersize): 

751 if self.lb[i] is not None: 

752 self.lb[i]= None 

753 self.lb_current= 0 

754 self.lb_anz=0 

755 self.lb_position=0 

756 self.printscene.reset() 

757# 

758# resize event, adjust the scene size, reposition everything and redraw 

759# 

760 def resizeEvent(self,event): 

761 self.do_resize() 

762 

763 def do_resize(self): 

764 h=self.height() 

765# 

766# compute the number of rows that will fit into the current window size 

767# 

768 self.rows= floor(h /BUFFER_LINE_H /2 * PRINTER_WIDTH_HIGH /self.screenwidth) 

769 

770# print("resize view dimensions ",self.screenwidth,h); 

771# print("resize view rows: ",self.rows) 

772# print("resize view: fit in view", PRINTER_WIDTH_HIGH, self.rows*2*BUFFER_LINE_H)) 

773# 

774# adjust the size of the print scene 

775# 

776 self.printscene.set_scenesize(self.rows) 

777# 

778# now transform the scene into the current view, force transformation 

779# to identity if we use a screen width of 1280 

780# 

781 if self.screenwidth != PRINTER_WIDTH_HIGH: 

782 self.fitInView(0,0,PRINTER_WIDTH_HIGH,self.rows*2*BUFFER_LINE_H) 

783 else: 

784 self.resetTransform() 

785# 

786# now adjust the scroll bar parameters 

787# 

788 scroll_max=self.lb_current- self.rows 

789 if scroll_max < 0: 

790 scroll_max=0 

791# print("scrollbar adjustment: ", self.lb_current,scroll_max) 

792# print("---") 

793 self.parent.scrollbar.setMaximum(scroll_max) 

794 self.parent.scrollbar.setPageStep(self.rows) 

795 self.printscene.update_scene() 

796 return 

797# 

798# PDF output. Text length configuration is not supported at the moment 

799# 

800 def pdf(self,filename,pdf_rows): 

801 

802 self.printer=QtPrintSupport.QPrinter (QtPrintSupport.QPrinter.HighResolution) 

803 self.printer.setPageOrientation(QtGui.QPageLayout.Portrait) 

804 self.printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat) 

805 self.pdfscene=QtWidgets.QGraphicsScene() 

806# 

807# page set up, we use 192 dpi dots as scene units and set the left 

808# and right margins so that we get a print width of 6.7 inches 

809# The height of 60 lines is 10 inches 

810# DINA4: 0,79 inches= 151 dots 

811# Letter: 0.9 inches = 173 dots 

812# 

813# 

814# A4 format is 8,27 inches x 11,7 inches 

815# 

816 if self.papersize== PDF_FORMAT_A4: 

817 self.printer.setPageSize(QtGui.QPageSize.A4) 

818 lmargin= 151 

819 tmargin= 163 

820 scene_w= 1280 + lmargin*2 

821 scene_h= 1920 + tmargin*2 

822 self.pdfscene.setSceneRect(0,0,scene_w,scene_h) 

823 else: 

824# 

825# Letter format is 8.5 inches x 11 inches 

826# 

827 self.printer.setPageSize(QtGui.QPageSize.Letter) 

828 lmargin= 173 

829 tmargin= 96 

830 scene_w= 1280 + lmargin*2 

831 scene_h= 1920 + tmargin*2 

832 self.pdfscene.setSceneRect(0,0,scene_w,scene_h) 

833 

834 self.painter= QtGui.QPainter() 

835 

836 self.printer.setOutputFileName(filename) 

837 self.painter.begin(self.printer) 

838 

839 pdfitems=[] 

840 anzitems=0 

841 delta= BUFFER_LINE_H*2 

842 horizontal_margin=floor((480-pdf_rows)/2)*delta 

843 

844 rowcount=0 

845 y=tmargin + horizontal_margin 

846# 

847# print all items to pdf 

848# 

849 for i in range(0,self.lb_anz): 

850 s= self.lb[i] 

851# 

852# we have a form feed element, issue new page 

853# 

854 if s is not None: 

855 if s[0][0]== ELEMENT_FF: 

856# print("FF ",rowcount, pdf_rows) 

857 self.pdfscene.render(self.painter) 

858 self.printer.newPage() 

859 for l in reversed(range(anzitems)): 

860 self.pdfscene.removeItem(pdfitems[l]) 

861 del pdfitems[-1] 

862 anzitems=0 

863 y=tmargin + horizontal_margin 

864 rowcount=0 

865# print("reset y to ",y,tmargin,horizontal_margin) 

866 item=cls_hp2225b_line(s,self.font,self.printcolor) 

867 pdfitems.append(item) 

868 self.pdfscene.addItem(item) 

869 item.setPos(lmargin,y) 

870# print("pdf item added ",rowcount,y,s) 

871 anzitems+=1 

872# else: 

873# print("none element") 

874 rowcount+=1 

875 y+= delta 

876# 

877# does the next line fit into the page, if not issue page break 

878# The character height is always 16px. 

879# 

880 if rowcount > pdf_rows: 

881# print("page break ",rowcount, pdf_rows) 

882 self.pdfscene.render(self.painter) 

883 self.printer.newPage() 

884 for l in reversed(range(anzitems)): 

885 self.pdfscene.removeItem(pdfitems[l]) 

886 del pdfitems[-1] 

887 anzitems=0 

888 rowcount=0 

889 y=tmargin + horizontal_margin 

890# print("reset y to ",y,tmargin,horizontal_margin) 

891# 

892# output remaining data and terminate printing 

893# 

894 if anzitems > 0: 

895 self.pdfscene.render(self.painter) 

896 for l in reversed(range(anzitems)): 

897 self.pdfscene.removeItem(pdfitems[l]) 

898 del pdfitems[-1] 

899 self.painter.end() 

900# 

901# 

902# Mouse wheel event 

903# 

904 def wheelEvent(self,event): 

905 numDegrees= event.angleDelta()/8 

906 delta=0 

907 step= round (8 * self.w / PRINTER_WIDTH_HIGH) 

908 if numDegrees.y() is not None: 

909 if numDegrees.y() < 0: 

910 delta=step 

911 if numDegrees.y() > 0: 

912 delta=-step 

913 event.accept() 

914 if self.lb_current < self.rows: 

915 return 

916 if self.lb_position+delta < 0: 

917 delta=-self.lb_position 

918 if self.lb_position+delta+self.rows > self.lb_current: 

919 delta=self.lb_current-(self.lb_position + self.rows ) 

920 self.lb_position+=delta 

921 self.parent.scrollbar.setValue(self.lb_position) 

922 self.printscene.update_scene() 

923 return 

924# 

925# external methods 

926# 

927# add element 

928# 

929 def add(self,elem): 

930# print("View add element: ",self.lb_current,elem) 

931 if self.lb[self.lb_current] is None: 

932 self.lb[self.lb_current]= [ ] 

933 self.lb[self.lb_current].append(elem) 

934 self.printscene.update_scene() 

935 return 

936# 

937# advance 

938# 

939 def advance(self,n): 

940 if self.lb_anz+n < self.linebuffersize: 

941 self.lb_anz+=n 

942 self.lb_current+=n 

943 else: 

944 self.lb=self.lb[n:] + self.lb[:n] 

945 for i in range (0,n): 

946 self.lb[i-n]=None 

947 

948 self.lb_position= self.lb_current- (self.rows) 

949 if self.lb_position < 0: 

950 self.lb_position=0 

951# print("View advance: ",n,self.lb_current, self.lb_position) 

952 self.parent.scrollbar.setMaximum(self.lb_position) 

953 self.parent.scrollbar.setValue(self.lb_position) 

954 self.printscene.update_scene() 

955 return 

956# 

957# scroll bar action 

958# 

959 def do_scroll(self,value): 

960 self.lb_position=value 

961 self.printscene.update_scene() 

962# 

963# pdf output 

964# 

965 def do_pdf(self,filename): 

966 return 

967# 

968# custom class for HP2225B graphics scene 

969# 

970class cls_hp2225b_scene(QtWidgets.QGraphicsScene): 

971 

972 def __init__(self,parent,font,screenwidth,printcolor): 

973 super().__init__() 

974 self.rows= 0 

975 self.w=0 

976 self.h=0 

977 self.parent=parent 

978 self.si= None 

979 self.font=font 

980 self.reconfigure(screenwidth,printcolor) 

981 return 

982# 

983# re/configure graphics scene 

984# 

985 def reconfigure(self,screenwidth,printcolor): 

986 self.screenwidth=screenwidth 

987 self.w= PRINTER_WIDTH_HIGH 

988 self.h= BUFFER_LINE_H *2 

989 self.printcolor=printcolor 

990 return 

991# 

992# set or change the size of the scene 

993# 

994 def set_scenesize(self,rows): 

995 self.reset() 

996 self.rows= rows 

997 self.si= [None] * rows 

998 self.setSceneRect(0,0,self.w,(self.h*(self.rows))) 

999# print("Scene size ",self.w,self.h*self.rows) 

1000# 

1001# clear window and reset 

1002# 

1003 def reset(self): 

1004 for i in range(0,self.rows): 

1005 if self.si[i] is not None: 

1006 self.removeItem(self.si[i]) 

1007 self.si[i]=None 

1008# 

1009# update graphics scene 

1010# 

1011 def update_scene(self): 

1012 for i in range(0,self.rows): 

1013 if self.si[i] is not None: 

1014 self.removeItem(self.si[i]) 

1015 self.si[i]=None 

1016 start= self.parent.lb_position 

1017 end= start+self.rows 

1018 if end >= self.parent.lb_anz: 

1019 end=self.parent.lb_anz 

1020 y=0 

1021 j=0 

1022 for i in range(start,end): 

1023 self.si[j]=self.parent.lb[i] 

1024 if self.parent.lb[i] is not None: 

1025 self.si[j]=cls_hp2225b_line(self.parent.lb[i], self.font,self.printcolor) 

1026 self.addItem(self.si[j]) 

1027 self.si[j].setPos(0,y) 

1028 y+=self.h 

1029 j+=1 

1030# print("Scene updated: ",start,end) 

1031 

1032# 

1033# custum class HP2225 print line 

1034# 

1035class cls_hp2225b_line(QtWidgets.QGraphicsItem): 

1036 

1037 def __init__(self,itemlist, font, color): 

1038 super().__init__() 

1039 self.itemlist= itemlist 

1040 self.font=font 

1041 self.color=color 

1042 metrics=QtGui.QFontMetrics(self.font) 

1043 self.font_height=metrics.height() 

1044 self.rect= QtCore.QRectF(0,0,PRINTER_WIDTH_HIGH,self.font_height) 

1045# self.flags=QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop | QtCore.Qt.TextDontClip 

1046# self.flags=QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop  

1047 

1048 

1049 def setPos(self,x,y): 

1050 super().setPos(x,y) 

1051 

1052 def boundingRect(self): 

1053 return self.rect 

1054# 

1055# paint elements 

1056# 

1057 def paint(self,painter,option,widget): 

1058 

1059 posx=0 

1060 for item in self.itemlist: 

1061# 

1062# Ignore element form feed 

1063# 

1064 if item[0]== ELEMENT_FF: 

1065 continue 

1066# 

1067# Paint text, align each character so that we get exactly the 

1068# number of characters per row as the original printer 

1069# 

1070 elif item[0]== ELEMENT_TEXT: 

1071 painter.setPen(self.color) 

1072 posx=item[1] 

1073 self.font.setBold(item[3]) 

1074 self.font.setUnderline(item[4]) 

1075 self.font.setStretch(FONT_STRETCH[item[2]]) 

1076 posy=self.font_height-12 

1077 

1078 painter.setFont(self.font) 

1079 for c in item[5]: 

1080 painter.drawText(posx,posy,c) 

1081# bounding_rect= QtCore.QRect(posx,0,posx+ (FONT_WIDTH[item[2]]),self.font_height) 

1082# painter.drawText(bounding_rect, self.flags, c) 

1083 posx+= FONT_WIDTH[item[2]] 

1084 continue 

1085# 

1086# Paint raster graphics elements. They always begin at column 0 

1087# We have at most two graphics row elements. The y resolution 

1088# is always 96dpi and the x resolution may bei either 96 or 192 

1089# dpi according to the hiRes mode 

1090# 

1091 elif item[0]==ELEMENT_GRAPHICS: 

1092 painter.setPen(self.color) 

1093 posy=item[1]*2 

1094 hiRes=item[2] 

1095 posx=0 

1096 for i in item[3]: 

1097 mask=0x80 

1098 if posx>=PRINTER_WIDTH_HIGH: 

1099 break 

1100 for j in range (0,8): 

1101 if hiRes: 

1102 if i & mask: 

1103 painter.fillRect(posx,posy,1,2,self.color) 

1104 posx+=1 

1105 else: 

1106 if i & mask: 

1107 painter.fillRect(posx,posy,2,2,self.color) 

1108 posx+=2 

1109 mask= mask >> 1 

1110 return 

1111 

1112# 

1113# custom class open pdf output file and set options 

1114# 

1115class cls_PdfOptions(QtWidgets.QDialog): 

1116 

1117 def __init__(self): 

1118 super().__init__() 

1119 self.filename="hp2225b.pdf" 

1120 self.setWindowTitle('HP2225B PDF output') 

1121 self.vlayout = QtWidgets.QVBoxLayout() 

1122 self.setLayout(self.vlayout) 

1123 self.glayout = QtWidgets.QGridLayout() 

1124 self.vlayout.addLayout(self.glayout) 

1125 

1126 self.glayout.addWidget(QtWidgets.QLabel("PDF Output Options"),0,0,1,3) 

1127 self.glayout.addWidget(QtWidgets.QLabel("Output file:"),1,0) 

1128 self.filename="hp2225b.pdf" 

1129 self.lfilename=QtWidgets.QLabel(self.filename) 

1130 self.glayout.addWidget(self.lfilename,1,1) 

1131 self.butchange=QtWidgets.QPushButton("Change") 

1132 self.butchange.setFixedWidth(60) 

1133 self.glayout.addWidget(self.butchange,1,2) 

1134 

1135 self.buttonBox = QtWidgets.QDialogButtonBox() 

1136 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

1137 self.buttonBox.setCenterButtons(True) 

1138 self.buttonBox.accepted.connect(self.do_ok) 

1139 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) 

1140 self.buttonBox.rejected.connect(self.do_cancel) 

1141 self.hlayout = QtWidgets.QHBoxLayout() 

1142 self.hlayout.addWidget(self.buttonBox) 

1143 self.vlayout.addLayout(self.hlayout) 

1144 self.butchange.clicked.connect(self.change_pdffile) 

1145 

1146 def get_pdfFilename(self): 

1147 dialog=QtWidgets.QFileDialog() 

1148 dialog.setWindowTitle("Enter PDF file name") 

1149 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave) 

1150 dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) 

1151 dialog.setDefaultSuffix("pdf") 

1152 dialog.setNameFilters( ["PDF (*.pdf )", "All Files (*)"] ) 

1153 dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog) 

1154 if dialog.exec(): 

1155 return dialog.selectedFiles() 

1156 

1157 def change_pdffile(self): 

1158 flist= self.get_pdfFilename() 

1159 if flist is None: 

1160 return 

1161 self.filename= flist [0] 

1162 self.lfilename.setText(self.filename) 

1163 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True) 

1164 

1165 

1166 def do_ok(self): 

1167 super().accept() 

1168 

1169 def do_cancel(self): 

1170 super().reject() 

1171 

1172 @staticmethod 

1173 def getPdfOptions(): 

1174 dialog= cls_PdfOptions() 

1175 result= dialog.exec() 

1176 if result== QtWidgets.QDialog.Accepted: 

1177 return dialog.lfilename.text() 

1178 else: 

1179 return "" 

1180# 

1181# HP2225B emulator (thread component) -------------------------------------- 

1182# 

1183 

1184class cls_hp2225b(QtCore.QObject): 

1185 

1186 BUF_EMPTY=0 

1187 BUF_TEXT=1 

1188 BUF_GRAPHICS=2 

1189 

1190 def __init__(self,parent,guiobject): 

1191 super().__init__() 

1192 self.pildevice=parent 

1193 self.guiobject= guiobject 

1194 

1195 self.esc= False # escape mode 

1196 self.esc_seq="" # escape sequence 

1197 self.esc_prefix="" # prefix of combined esc sequences 

1198 self.num_graphics=-1 # number of graphics bytes 

1199 self.ignore_crlf=False # flag to ignore cr/lf between graphics chunks 

1200 self.apgot=False # flag avoid printing graphics over text 

1201# 

1202# printer status that controls the appearance of the printer output and 

1203# is therefore handled in the GUI component 

1204# 

1205 self.text_legnth= 60 # text length given in lines 

1206 self.char_attr=0 # character pitch 

1207 self.char_bold=False # bold mode 

1208 self.char_underline=False # underline mode 

1209 self.hiRes=False # high resolution of graphics output 

1210 self.lpi6=False # lines/inch 

1211 self.pdf_rows=480 # number of rows for pdf output 

1212 self.wrapEOL=False # EOL wrap 

1213 self.empty_line=False # detect empty text lines, this disables 

1214 # ignoring cr/lf. 

1215# 

1216# printer status which is handled here 

1217# 

1218 self.ltermMode=0 # line termination mode 

1219 self.altMode= False # alternate control mode 

1220 self.displayFunctions=False # display functions mode 

1221# 

1222# buffer for accumulated text and graphics data b e t w e e n control  

1223# characters or escape sequences 

1224# 

1225 self.buf_status=self.BUF_EMPTY # buffer status 

1226 self.buf_data= [ ] 

1227 self.log_line="" 

1228 self.reset() 

1229# 

1230# 

1231 def reset(self): 

1232# 

1233# reset variables to default 

1234# 

1235 self.esc= False 

1236 self.esc_seq="" 

1237 self.esc_prefix="" 

1238 self.num_graphics=-1 

1239 self.ignore_crlf=False 

1240 self.apgot=False 

1241 self.text_length=60 

1242 self.pdf_rows=480 

1243 self.char_attr=0 

1244 self.char_bold= False 

1245 self.char_underline=False 

1246 self.ltermMode=0 

1247 self.hiRes=False 

1248 self.lpi6=True 

1249 self.wrapEOL=False 

1250 

1251 self.displayFunctions=False 

1252 self.log_line="" 

1253 self.empty_line=False 

1254 self.buf_clear() 

1255 

1256# 

1257# send clear command to GUI 

1258# 

1259 self.guiobject.put_cmd([REMOTECMD_CLEAR]) 

1260 self.put_status() 

1261 return 

1262# 

1263# clear data buffer 

1264# 

1265 def buf_clear(self): 

1266 self.buf_status= self.BUF_EMPTY 

1267 self.buf_data= [ ] 

1268# 

1269# flush buffer, send data to printer 

1270# 

1271 def buf_flush(self): 

1272 if self.buf_status == self.BUF_EMPTY: 

1273 return 

1274 data_copy= copy.deepcopy(self.buf_data) 

1275 if self.buf_status == self.BUF_TEXT: 

1276 self.guiobject.put_cmd([REMOTECMD_TEXT,data_copy]) 

1277# print("put cmd text",data_copy) 

1278 else: 

1279 self.guiobject.put_cmd([REMOTECMD_GRAPHICS,data_copy]) 

1280# print("put cmd graphics",data_copy) 

1281 self.buf_clear() 

1282#  

1283# send status 

1284# 

1285 def put_status(self): 

1286# 

1287# if we have data in the buffer, clear it first 

1288# 

1289 self.buf_flush() 

1290# 

1291# send printer status to GUI 

1292# 

1293 self.guiobject.put_cmd([REMOTECMD_STATUS,[self.pdf_rows,self.char_attr,self.char_bold,self.char_underline,self.hiRes,self.lpi6,self.wrapEOL]]) 

1294 

1295# 

1296# set/clear alternate control mode (has no effect) 

1297# 

1298 def setAltMode(self,mode): 

1299 self.altMode= mode 

1300 if self.altMode: 

1301 print("hp2225: entering ESC/P command mode. This mode is not supported and the emulator will ignore all data.") 

1302 else: 

1303 print("hp2225: returning to PCL command mode.") 

1304 return 

1305# 

1306# process escaape sequences HP command set 

1307# 

1308 def process_esc_hp(self): 

1309# 

1310# normal width 

1311# 

1312 if self.esc_seq=="&k0S" or self.esc_seq=="&kS": 

1313 self.char_attr= (self.char_attr & 0x0C) 

1314 self.put_status() 

1315 return 

1316# 

1317# expanded width 

1318# 

1319 elif self.esc_seq=="&k1S": 

1320 self.char_attr= (self.char_attr & 0x0C) | 0x01 

1321 self.put_status() 

1322 return 

1323# 

1324# compressed width 

1325# 

1326 elif self.esc_seq=="&k2S": 

1327 self.char_attr= (self.char_attr & 0x0C) | 0x02 

1328 self.put_status() 

1329 return 

1330# 

1331# expanded-compressed width 

1332# 

1333 elif self.esc_seq=="&k3S": 

1334 self.char_attr= (self.char_attr & 0x0C) | 0x03 

1335 self.put_status() 

1336 return 

1337# 

1338# bold mode on 

1339# 

1340 elif self.esc_seq=="(s1B": 

1341 self.char_bold= True 

1342 self.put_status() 

1343 return 

1344# 

1345# bold mode off 

1346# 

1347 elif self.esc_seq=="(s0B": 

1348 self.char_bold=False 

1349 self.put_status() 

1350 return 

1351# 

1352# underline mode on 

1353# 

1354 elif self.esc_seq=="&dD": 

1355 self.char_underline=True 

1356 self.put_status() 

1357 return 

1358# 

1359# underline mode off 

1360# 

1361 elif self.esc_seq=="&d@": 

1362 self.char_underline=False 

1363 self.put_status() 

1364 return 

1365# 

1366# 6 lines/inch 

1367# 

1368 elif self.esc_seq=="&l6D": 

1369 self.lpi6=True 

1370 self.put_status() 

1371 return 

1372# 

1373# 8 lines/inch 

1374# 

1375 elif self.esc_seq=="&l8D": 

1376 self.lpi6=False 

1377 self.put_status() 

1378 return 

1379# 

1380# perforation skip on (has no effect) 

1381# 

1382 elif self.esc_seq=="&l1L": 

1383 return 

1384# 

1385# perforation skip off (has no effect) 

1386# 

1387 elif self.esc_seq=="&l0L": 

1388 return 

1389# 

1390# EOL wrap on 

1391# 

1392 elif self.esc_seq=="&s0C": 

1393 self.wrapEOL=True 

1394 self.put_status() 

1395 return 

1396# 

1397# EOL wrap off 

1398# 

1399 elif self.esc_seq=="&s1C": 

1400 self.wrapEOL=False 

1401 self.put_status() 

1402 return 

1403# 

1404# display functions on 

1405# 

1406 elif self.esc_seq=="Y": 

1407 self.displayFunctions=True 

1408 return 

1409# 

1410# display functions off 

1411# 

1412 elif self.esc_seq=="Z": 

1413 self.displayFunctions=False 

1414 return 

1415# 

1416# unidirectional printing (has no effect) 

1417# 

1418 elif self.esc_seq=="&k0W": 

1419 return 

1420# 

1421# biidirectional printing (has no effect) 

1422# 

1423 elif self.esc_seq=="&k1W": 

1424 return 

1425# 

1426# half line feed 

1427# 

1428 elif self.esc_seq=="=": 

1429 self.half_lf() 

1430 return 

1431# 

1432# avoid printing graphics over text 

1433# 

1434 elif self.esc_seq=="*rA": 

1435 self.apgot= True 

1436 return 

1437# 

1438# terminate graphics 

1439# 

1440 elif self.esc_seq=="*rB": 

1441 self.ignore_crlf= False 

1442 self.guiobject.put_cmd([REMOTECMD_TERMGRAPHICS]) 

1443 return 

1444# 

1445# line termination mode 0 

1446# 

1447 elif self.esc_seq=="&k0G": 

1448 self.ltermMode=0 

1449 return 

1450# 

1451# line termination mode 1 

1452# 

1453 elif self.esc_seq=="&k1G": 

1454 self.ltermMode=1 

1455 return 

1456# 

1457# line termination mode 2 

1458# 

1459 elif self.esc_seq=="&k2G": 

1460 self.ltermMode=2 

1461 return 

1462# 

1463# line termination mode 3 

1464# 

1465 elif self.esc_seq=="&k3G": 

1466 self.ltermMode=3 

1467 return 

1468# 

1469# self test (has no effect) 

1470# 

1471 elif self.esc_seq=="z": 

1472 return 

1473# 

1474# reset printer 

1475# 

1476 elif self.esc_seq=="E": 

1477 self.reset() 

1478 return 

1479# 

1480# Graphics dot row 

1481# 

1482 elif self.esc_seq.startswith("*b") and self.esc_seq.endswith("W"): 

1483 ret=re.findall(r"\d+",self.esc_seq) 

1484 if ret== []: 

1485 return 

1486 try: 

1487 n=int(ret[0]) 

1488 except ValueError: 

1489 return 

1490 if n<0 or n> 255: 

1491 return 

1492 self.num_graphics=n 

1493 self.begin_graphics() 

1494 return 

1495# 

1496# graphics resolution 

1497# 

1498 elif self.esc_seq.startswith("*r") and self.esc_seq.endswith("S"): 

1499 ret=re.findall(r"\d+",self.esc_seq) 

1500 if ret== []: 

1501 return 

1502 try: 

1503 n=int(ret[0]) 

1504 except ValueError: 

1505 return 

1506 if n<=640: 

1507 self.hiRes=False 

1508 else: 

1509 self.hiRes=True 

1510 self.put_status() 

1511 return 

1512# 

1513# page length (has no effect) 

1514# 

1515 elif self.esc_seq.startswith("&l") and self.esc_seq.endswith("P"): 

1516 return 

1517# 

1518# Text length 

1519# 

1520 elif self.esc_seq.startswith("&l") and self.esc_seq.endswith("F"): 

1521 ret=re.findall(r"\d+",self.esc_seq) 

1522 if ret== []: 

1523 return 

1524 try: 

1525 n=int(ret[0]) 

1526 except ValueError: 

1527 return 

1528 self.text_length=n 

1529# 

1530# the text length can not exceed 80 lines at 8lpi or 60 lines at 

1531# 6lpi. The maximum print area is limited to 10 inches in this 

1532# emulator. We now compute the numbes of rows the new text length 

1533# will occupy in the pdf file 

1534# 

1535 if self.text_length < 1: 

1536 self.text_length=1 

1537 if self.lpi6: 

1538 if self.text_length> 60: 

1539 self.text_length=60 

1540 self.pdf_rows= self.text_length* 8 

1541 else: 

1542 if self.text_length> 80: 

1543 self.text_length=80 

1544 self.pdf_rows= self.text_length * 6 

1545 self.pdf_rows-=1 

1546 self.put_status() 

1547 return 

1548 else: 

1549 print("hp2225: illegal escape sequence ignored: ", self.esc_seq) 

1550 return 

1551# 

1552# begin graphics 

1553# 

1554 def begin_graphics(self): 

1555# 

1556# flush any pending text and go to BOL 

1557# 

1558# print("begin new graphics ",self.num_graphics) 

1559 if self.buf_status== self.BUF_TEXT: 

1560 self.cr() 

1561# 

1562# if the avoid printing graphics over text command was issued do a linefeed 

1563# 

1564 if self.apgot: 

1565 self.lf() 

1566 self.apgot= False 

1567 self.ignore_crlf= True 

1568 self.buf_status= self.BUF_GRAPHICS 

1569 self.empty_line= False 

1570 return 

1571 

1572# 

1573# printer data processor HP command set 

1574# 

1575 def process_char_hp(self,ch): 

1576# 

1577# if there are graphics data, append it to buffer and return 

1578# 

1579 if self.num_graphics > 0: 

1580 self.num_graphics-=1 

1581 self.buf_data.append(ch) 

1582 return 

1583# 

1584# last byte of graphics received, flush buffer and proceed 

1585# 

1586 if self.num_graphics==0: 

1587 self.buf_flush() 

1588# print("graphics flushed ", self.buf_status) 

1589 self.num_graphics= -1 

1590# 

1591# process ESC sequences 

1592# 

1593 if (self.esc== False) and (ch== 0x1B): 

1594 self.esc= True 

1595 self.esc_seq="" 

1596 self.esc_prefix="" 

1597 self.empty_line=False 

1598 return 

1599 if self.esc: 

1600# 

1601# ESC | or escape sequence terminated with capital letter 

1602# 

1603# if ch == 0x7c or (ch >= 0x41 and ch <= 0x5A): 

1604 self.empty_line=False 

1605 if chr(ch) in "SBD@LPFCYZW=AGzE": 

1606 self.esc_seq+= chr(ch) 

1607 if self.esc_prefix!="": 

1608 self.esc_seq= self.esc_prefix+self.esc_seq 

1609 self.process_esc_hp() 

1610 self.esc= False 

1611 self.esc_seq="" 

1612 self.esc_prefix="" 

1613 return 

1614# 

1615# repeated escape sequence terminated with lowercase letter 

1616# 

1617 if chr(ch) in "sdlpfcwabg" and len(self.esc_seq)>2: 

1618 if self.esc_prefix == "": 

1619 self.esc_prefix= self.esc_seq[:2] 

1620 self.esc_seq= self.esc_seq[2:] 

1621 self.esc_seq= self.esc_prefix+self.esc_seq+chr(ch).upper() 

1622 self.process_esc_hp() 

1623 self.esc_seq="" 

1624 return 

1625# 

1626# still in escape sequence, accumulate characters 

1627#  

1628 self.esc_seq+= chr(ch) 

1629 return 

1630# 

1631# not in escape sequence, process control and data characters 

1632# 

1633# Backspace: 

1634# 

1635 if (ch == 0x08): 

1636 if self.ignore_crlf: 

1637 return 

1638 self.buf_flush() 

1639 self.guiobject.put_cmd([REMOTECMD_BS]) 

1640# 

1641# CR 

1642# 

1643 elif (ch == 0x0D): 

1644 if self.ignore_crlf: 

1645 return 

1646 self.cr() 

1647 if self.ltermMode & 0x01: 

1648 self.lf() 

1649# 

1650# LF 

1651# 

1652 elif (ch == 0x0A): 

1653 if self.empty_line: 

1654 if self.ignore_crlf: 

1655 self.ignore_crlf= False 

1656 self.empty_line= True 

1657 if self.ignore_crlf: 

1658 return 

1659 if self.ltermMode & 0x02: 

1660 self.cr() 

1661 self.lf() 

1662# 

1663# FF 

1664# 

1665 elif (ch == 0x0C): 

1666 if self.ltermMode & 0x02 or self.ltermMode & 0x03: 

1667 self.cr() 

1668 self.ff() 

1669# 

1670# bold mode on ^N 

1671# 

1672 elif (ch == 0x0E): 

1673 self.empty_line= False 

1674 self.char_bold=True 

1675 self.put_status() 

1676# 

1677# bold mode off ^O 

1678# 

1679 elif (ch == 0x0F): 

1680 self.empty_line= False 

1681 self.char_bold=False 

1682 self.put_status() 

1683# 

1684# normal character 

1685# 

1686 else: 

1687 self.empty_line= False 

1688 self.ignore_crlf=False 

1689 if ((ch >=0x20 and ch < 127) or (ch > 159 and ch< 255)): 

1690 self.buf_status= self.BUF_TEXT 

1691 assert self.buf_status== self.BUF_EMPTY or self.buf_status== self.BUF_TEXT 

1692 self.buf_data.append(ch) 

1693 self.log_line+= charconv(chr(ch), CHARSET_HP2225) 

1694# 

1695# process printer data for display functions mode 

1696# 

1697 def process_char_df(self,ch): 

1698 if self.esc: 

1699 self.esc= False 

1700 if ch== 0x5A: # ESC Z terminates display functions mode 

1701 self.displayFunctions= False 

1702 if ch == 0x1B: 

1703 self.esc=True 

1704 self.buf_status= self.BUF_TEXT 

1705 self.buf_data.append(ch) 

1706 self.log_line+= charconv(chr(ch), CHARSET_HP2225) 

1707 if (ch == 0x0A): 

1708 self.cr() 

1709 self.lf() 

1710 return 

1711# 

1712# process printer data for the alternate control mode (not implemented!) 

1713# 

1714 def process_char_alt(self,ch): 

1715 return 

1716# 

1717# process printer data  

1718# 

1719 def process_char(self,ch): 

1720 if self.altMode: 

1721 self.process_char_alt(ch) 

1722 else: 

1723 if self.displayFunctions: 

1724 self.process_char_df(ch) 

1725 else: 

1726 self.process_char_hp(ch) 

1727 return 

1728# 

1729# line positioning: cr, lf, ff 

1730# 

1731 def cr(self): 

1732 self.buf_flush() 

1733 self.guiobject.put_cmd([REMOTECMD_CR]) 

1734 return 

1735 

1736 def lf(self): 

1737 self.buf_flush() 

1738 self.guiobject.put_cmd([REMOTECMD_LF]) 

1739 if self.log_line != "": 

1740 self.log_line+="\n" 

1741 self.guiobject.put_cmd([REMOTECMD_LOG,self.log_line]) 

1742 self.log_line="" 

1743 return 

1744 

1745 def half_lf(self): 

1746 self.buf_flush() 

1747 self.guiobject.put_cmd([REMOTECMD_HLF]) 

1748 return 

1749 

1750 

1751 def ff(self): 

1752 self.buf_flush() 

1753 self.guiobject.put_cmd([REMOTECMD_FF]) 

1754 if self.log_line != "": 

1755 self.log_line+="\n" 

1756 self.guiobject.put_cmd([REMOTECMD_LOG,self.log_line]) 

1757 self.log_line="" 

1758 return 

1759# 

1760# process commands sent from the GUI 

1761# 

1762 def process(self,command): 

1763 

1764 if command== CMD_CLEAR: 

1765 self.reset() 

1766 return 

1767 

1768# 

1769# HP-IL virtual HP2225B object class --------------------------------------- 

1770# 

1771 

1772 

1773class cls_pilhp2225b(cls_pildevbase): 

1774 

1775 def __init__(self,guiobject): 

1776 super().__init__() 

1777 

1778# 

1779# overloaded variable initialization 

1780# 

1781 self.__aid__ = 0x23 # accessory id  

1782 self.__defaddr__ = 5 # default address alter AAU 

1783 self.__did__ = "HP2225B" # device id 

1784 self.__status_len__=2 # device status is 2 bytes 

1785# 

1786# private vars 

1787# 

1788 self.__ddlcmd__=False # ddl command was sent 

1789# 

1790# object specific variables 

1791# 

1792 self.__guiobject__= guiobject 

1793# 

1794# initialize remote command queue and lock 

1795# 

1796 self.__print_queue__= queue.Queue() 

1797 self.__print_queue_lock__= threading.Lock() 

1798# 

1799# printer processor 

1800# 

1801 self.__printer__=cls_hp2225b(self,self.__guiobject__) 

1802# 

1803# the printer status is a l w a y s: 

1804# - first byte : 0xA1 (everything is fine, no service request) 

1805# - second byte: 0x04 (buffer empty) 

1806# 

1807 self.set_status(0x04A1) 

1808 

1809# 

1810# public (overloaded) -------- 

1811# 

1812# enable: reset 

1813# 

1814 def enable(self): 

1815 self.__ddlcmd__= False 

1816 self.__printer__.reset() 

1817 return 

1818# 

1819# disable: clear the printer command queue 

1820# 

1821 def disable(self): 

1822 self.__print_queue_lock__.acquire() 

1823 while True: 

1824 try: 

1825 self.__print_queue__.get_nowait() 

1826 self.__print_queue__.task_done() 

1827 except queue.Empty: 

1828 break 

1829 self.__print_queue_lock__.release() 

1830# 

1831# process frames  

1832# 

1833 def process(self,frame): 

1834 

1835 if self.__isactive__: 

1836 self.process_print_queue() 

1837 frame= super().process(frame) 

1838 return frame 

1839# 

1840# process the printer command queue 

1841# 

1842 def process_print_queue(self): 

1843 items=[] 

1844 self.__print_queue_lock__.acquire() 

1845 while True: 

1846 try: 

1847 i=self.__print_queue__.get_nowait() 

1848 items.append(i) 

1849 self.__print_queue__.task_done() 

1850 except queue.Empty: 

1851 break 

1852 self.__print_queue_lock__.release() 

1853 if len(items): 

1854 for c in items: 

1855 self.__printer__.process(c) 

1856 return 

1857 

1858# 

1859# put command into the print-command queue 

1860# 

1861 def put_cmd(self,item): 

1862 self.__print_queue_lock__.acquire() 

1863 self.__print_queue__.put(item) 

1864 self.__print_queue_lock__.release() 

1865# 

1866# set status (2 byte status information) 

1867# 

1868 def set_status(self,s): 

1869 self.__setstatus__(s) 

1870 return s 

1871# 

1872# get status byte (2 byte status information) 

1873# 

1874 def get_status(self): 

1875 s=self.__getstatus__() 

1876 return s 

1877 

1878# 

1879# private (overloaded) -------- 

1880# 

1881# 

1882# output character to HP2225B 

1883# 

1884 def __indata__(self,frame): 

1885 n= frame & 0xFF 

1886# 

1887 if self.__ddlcmd__: 

1888 if n== 18: 

1889 self.__printer__.setAltMode(True) 

1890 if n==0: 

1891 self.__printer__.setAltMode(False) 

1892 self.__ddlcmd__= False 

1893 else: 

1894 self.__printer__.process_char(n) 

1895# 

1896# ddl command implementation 

1897# 

1898 def __cmd_ext__(self,frame): 

1899 n= frame & 0xFF 

1900 t= n>>5 

1901 if t==5: # DDL 

1902 n=n & 31 

1903 if (self.__ilstate__ & 0xC0) == 0x80: # are we listener? 

1904 self.__devl__= n & 0xFF 

1905 if n== 6: 

1906 self.__ddlcmd__= True 

1907 return(frame) 

1908# 

1909# clear device: reset printer 

1910# 

1911 def __clear_device__(self): 

1912 super().__clear_device__() 

1913# 

1914# clear printer queue 

1915# 

1916 self.__print_queue_lock__.acquire() 

1917 while True: 

1918 try: 

1919 self.__print_queue__.get_nowait() 

1920 self.__print_queue__.task_done() 

1921 except queue.Empty: 

1922 break 

1923 self.__print_queue_lock__.release() 

1924# 

1925# reset device  

1926# 

1927 self.__printer__.reset() 

1928