Skip to content

Commit ec92543

Browse files
committed
Fixed issue #2
Add function printASCII, printlnASCII that will safety handle print requests
1 parent 1ab98f3 commit ec92543

3 files changed

Lines changed: 174 additions & 21 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ Newline is required to fush the internal printer buffer and force it to print al
150150

151151
View this [python notebook](https://github.com/bitrate16/ppa6-python/blob/main/notebooks/ppa6-tutorial.ipynb) for tutorial
152152

153+
## Printer disassembly
154+
155+
View this [photos](https://github.com/bitrate16/ppa6-python/blob/main/disassembly)
156+
153157
## TODO
154158

155159
* Implement CLI QR code printing
@@ -177,4 +181,4 @@ SOFTWARE.**
177181

178182
## License
179183

180-
[MIT License](LICENSE)
184+
[MIT License](https://github.com/bitrate16/ppa6-python/blob/main/LICENSE)

ppa6/__init__.py

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def __init__(self, mac, printertype=PrinterType.A6, timeout=1.0):
6565
self.mac = mac
6666
self.timeout = timeout
6767
self.printerType = printertype
68+
69+
# This buffer is used for continuous printing
70+
self.printBuffer = ''
6871

6972
def isConnected(self):
7073
"""
@@ -295,6 +298,20 @@ def getRowWidth(self):
295298
return 576
296299
else:
297300
raise ValueError('Unsupported printer type')
301+
302+
def getRowCharacters(self):
303+
"""
304+
Returns amount of characters that may fit in a single row.
305+
By default A6+ can fit up to 48 characters, A6 can fit up to 32 characters.
306+
"""
307+
308+
if self.printerType == PrinterType.A6:
309+
# TODO: Measure amount of characters that can fit in the line
310+
return 32
311+
elif self.printerType == PrinterType.A6p:
312+
return 48
313+
else:
314+
raise ValueError('Unsupported printer type')
298315

299316
def getHeightLimit(self):
300317
"""
@@ -397,14 +414,17 @@ def printBreak(self, size=0x40):
397414

398415
def writeASCII(self, text='\n', wait=False):
399416
"""
417+
Deprecated.
400418
Write raw ASCII string to the printer.
401419
By default this printer accepts an ascii string for printing it with raw monospace
402-
font. Printer has internal buffer (48 characters in Peripage A6+) that will
420+
font. Printer has internal buffer (getRowCharacters()) that will
403421
accumulate the received characters. Printer will print out the buffer if meets a '\n'
404422
character or buffer overflows.
405423
This function expects only ASCII characters without control codes (0x00-0x20, 0xFF).
406424
This function is not recommended to use while printer is in byte stream printing mode
407425
or while it expects arguments for some of it's opcodes.
426+
If string contains sequently repeating '\n' characters, the printer may freeze. So
427+
it's recommended to use printASCII() instead.
408428
409429
:param text: string containing ASCII characters
410430
:type text: str
@@ -417,6 +437,148 @@ def writeASCII(self, text='\n', wait=False):
417437
else:
418438
self.tellPrinter(bytes(text, 'ascii'))
419439

440+
def printlnASCII(self, text='\n', delay=0.25):
441+
"""
442+
Write raw ASCII string to the printer.
443+
By default this printer accepts an ascii string for printing it with raw monospace
444+
font. Printer has internal buffer (getRowCharacters()) that will
445+
accumulate the received characters. Printer will print out the buffer if meets a '\n'
446+
character or buffer overflows.
447+
This function expects only ASCII characters without control codes (0x00-0x20, 0xFF).
448+
This function is not recommended to use while printer is in byte stream printing mode
449+
or while it expects arguments for some of it's opcodes.
450+
If string contains sequently repeating '\n' characters, they will be replaced with
451+
printBreak(30) which matches the length of the '\n\n'. This function automatically
452+
slices string into pieces of size getRowCharacters() and waits till new piece being
453+
printed.
454+
This function acts as println. This function will print out the data stored in the
455+
buffer of printASCII()
456+
457+
:param text: string containing ASCII characters
458+
:type text: str
459+
:param delay: delay between sending each line
460+
:type delay: float
461+
"""
462+
463+
# Remove non-ASCII & control (except \n)
464+
text = ''.join([i for i in text if (31 < ord(i) or ord(i) == 10) and ord(i) < 127])
465+
466+
# Check for empty and print out newline
467+
text = self.printBuffer + text
468+
if len(text) == 0:
469+
self.printBreak(30)
470+
time.sleep(delay)
471+
return
472+
473+
lines = text.split('\n')
474+
self.printBuffer = ''
475+
476+
for l in lines:
477+
# Replace every empty line with break matching newline height
478+
if len(l) == 0:
479+
self.printBreak(30)
480+
time.sleep(delay)
481+
else:
482+
# Split to lines
483+
parts = [l[i:i+self.getRowCharacters()] for i in range(0, len(l), self.getRowCharacters())]
484+
485+
for i, p in enumerate(parts):
486+
self.tellPrinter(bytes(p, 'ascii'))
487+
if i != 0:
488+
time.sleep(delay)
489+
490+
# Push last line from the buffer
491+
self.tellPrinter(bytes('\n', 'ascii'))
492+
time.sleep(delay)
493+
494+
def printASCII(self, text='\n', delay=0.25):
495+
"""
496+
Write raw ASCII string to the printer.
497+
By default this printer accepts an ascii string for printing it with raw monospace
498+
font. Printer has internal buffer (getRowCharacters()) that will
499+
accumulate the received characters. Printer will print out the buffer if meets a '\n'
500+
character or buffer overflows.
501+
This function expects only ASCII characters without control codes (0x00-0x20, 0xFF).
502+
This function is not recommended to use while printer is in byte stream printing mode
503+
or while it expects arguments for some of it's opcodes.
504+
If string contains sequently repeating '\n' characters, they will be replaced with
505+
printBreak(30) which matches the length of the '\n\n'. This function automatically
506+
slices string into pieces of size getRowCharacters() and waits till new piece being
507+
printed.
508+
This function uses in class buffer to store tail of the text if text didn't end with
509+
'\n'.
510+
511+
:param text: string containing ASCII characters
512+
:type text: str
513+
:param delay: delay between sending each line
514+
:type delay: float
515+
"""
516+
517+
# Remove non-ASCII & control (except \n)
518+
text = ''.join([i for i in text if (31 < ord(i) or ord(i) == 10) and ord(i) < 127])
519+
520+
# Check for empty and print out newline
521+
text = self.printBuffer + text
522+
self.printBuffer = ''
523+
if len(text) == 0:
524+
return
525+
526+
endLineBreak = text[-1] == '\n'
527+
lines = text.split('\n')
528+
529+
for i, l in enumerate(lines):
530+
# Replace every empty line with break matching newline height
531+
if len(l) == 0:
532+
self.printBreak(30)
533+
time.sleep(delay)
534+
else:
535+
# Split to lines
536+
parts = [l[i:i+self.getRowCharacters()] for i in range(0, len(l), self.getRowCharacters())]
537+
538+
for j, p in enumerate(parts):
539+
# If this is the last part of the text and it ends with '\n', push it
540+
if j == len(parts)-1:
541+
if i == len(lines)-1:
542+
if endLineBreak:
543+
self.tellPrinter(bytes(p, 'ascii'))
544+
time.sleep(delay)
545+
self.tellPrinter(bytes('\n', 'ascii'))
546+
time.sleep(delay)
547+
else:
548+
self.printBuffer = p
549+
550+
# Push out the string that is a full row
551+
if len(p) == self.getRowCharacters():
552+
self.tellPrinter(bytes(p, 'ascii'))
553+
time.sleep(delay)
554+
self.tellPrinter(bytes('\n', 'ascii'))
555+
time.sleep(delay)
556+
self.printBuffer = ''
557+
else:
558+
self.tellPrinter(bytes(p, 'ascii'))
559+
time.sleep(delay)
560+
self.tellPrinter(bytes('\n', 'ascii'))
561+
time.sleep(delay)
562+
else:
563+
self.tellPrinter(bytes(p, 'ascii'))
564+
if j != 0:
565+
time.sleep(delay)
566+
567+
def flushASCII(self, delay=0.25):
568+
"""
569+
Prints out the buffer used in printASCII() followed by newline.
570+
571+
:param delay: delay between sending each line
572+
:type delay: float
573+
"""
574+
575+
if len(self.printBuffer) > 0:
576+
self.tellPrinter(bytes(self.printBuffer, 'ascii'))
577+
time.sleep(delay)
578+
self.tellPrinter(bytes('\n', 'ascii'))
579+
time.sleep(delay)
580+
self.printBuffer = ''
581+
420582
def printRow(self, rowbytes):
421583
"""
422584
Send array of pixels represented with rowbytes bytes to the printer.

ppa6/__main__.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def main():
2424
# 4. Requist printer information
2525
group = parser.add_mutually_exclusive_group(required=True)
2626
group.add_argument('-t', '--text', help='ASCII text that should be printed. Add a line break at the end of the string to avoid it being cut. String can be empty, so just page break will be printed', type=str)
27-
group.add_argument('-s', '--stream', help='Reads an input from stdin and prints as ASCII text')
27+
group.add_argument('-s', '--stream', help='Reads an input from stdin and prints as ASCII text', action='store_true')
2828
group.add_argument('-i', '--image', help='Path to the image that should be printed', type=str)
2929
group.add_argument('-e', '--introduce', help='Ask the printer to introduce himself', action='store_true')
3030

@@ -56,16 +56,9 @@ def main():
5656

5757
while True:
5858
try:
59-
line = input() + '\n'
59+
line = input()
6060

61-
# Split line if it is too long
62-
parts = [line[i:i+0xff] for i in range(0, len(line), 0xff)]
63-
64-
for p in parts:
65-
printer.writeASCII(p, wait=False)
66-
67-
# TODO: Measure timing to avoid internal buffer overflow
68-
time.sleep(0.25)
61+
printer.printlnASCII(line)
6962

7063
except EOFError:
7164
# Input closed ^d^d
@@ -84,17 +77,11 @@ def main():
8477
printer.reset()
8578

8679
line = args.text
80+
if args.newline:
81+
line += '\n'
8782

8883
if len(line) > 0:
89-
if args.newline:
90-
line += '\n'
91-
92-
# Split text if it is too long
93-
parts = [line[i:i+0xff] for i in range(0, len(line), 0xff)]
94-
95-
for p in parts:
96-
printer.writeASCII(p, wait=True)
97-
# time.sleep(0.25)
84+
printer.printASCII(line)
9885

9986
if 'breaksize' in args and args.breaksize > 0:
10087
printer.printBreak(args.breaksize)

0 commit comments

Comments
 (0)