From 5feb8a2ccaf2a19cb0034038ecb5a8013e42be92 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 19 May 2019 23:22:03 +0200 Subject: [PATCH] first commit --- .gitignore | 4 ++ README.md | 9 +++++ convertimage.py | 38 ++++++++++++++++++ pattern | 1 + print.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ print_old.py | 87 +++++++++++++++++++++++++++++++++++++++++ run.py | 7 ++++ run.sh | 19 +++++++++ testbasic.bas | 13 +++++++ 9 files changed, 278 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 convertimage.py create mode 100644 pattern create mode 100644 print.py create mode 100644 print_old.py create mode 100644 run.py create mode 100755 run.sh create mode 100644 testbasic.bas diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4836677 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.jpg +*.png +*.gif +*.bmp diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d33770 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +#Pin Print Python + +takes an image (currently img.img) converts it to bitmap and prints it on the pin printer + +run + +```bash +python3 print.py > /dev/usb/lp0 +``` diff --git a/convertimage.py b/convertimage.py new file mode 100644 index 0000000..219cc49 --- /dev/null +++ b/convertimage.py @@ -0,0 +1,38 @@ +from PIL import Image +import random +im = Image.open("img.png") #Can be many different formats. +new = im.copy() +pix = im.load() +newpix = new.load() +width,height=im.size +print([width,height]) +print(pix[1,1]) + +window = 3 # input parameter 'n' + +area = window*window +for i in range(width//window): #loop over pixels + for j in range(height//window):#loop over pixels + avg = 0 + area_pix = [] + for k in range(window): + for l in range(window): + area_pix.append((k,l))#make a list of coordinates within the tile + try: + avg += pix[window*i+k,window*j+l][0] + newpix[window*i+k,window*j+l] = (0,0,0) #set everything to black + except IndexError: + avg += 255/2 #just an arbitrary mean value (when were outside of the image) + # this is just a dirty trick for coping with images that have + # sides that are not multiples of window + avg = avg/area + # val = v is the number of pixels within the tile that will be turned white + val = round(avg/255 * (area+0.99) - 0.5)#0.99 due to rounding errors + assert val<=area,'something went wrong with the val' + print(val) + random.shuffle(area_pix) #randomize pixel coordinates + for m in range(val): + rel_coords = area_pix.pop()#find random pixel within tile and turn it white + newpix[window*i+rel_coords[0],window*j+rel_coords[1]] = (255,255,255) + +new.save('dog_dithered'+str(window)+'.jpg') diff --git a/pattern b/pattern new file mode 100644 index 0000000..55d6234 --- /dev/null +++ b/pattern @@ -0,0 +1 @@ +^[@^[5^[9^[*1\4\161\363\300\317\55\222\257\356\172\62\133\365\340\106\276\206\40\252\2\42\275\2\232\123\267\46\373\356\134\251\376\320\34\164\260\240\120\324\233\222\324\322\255\120\141\233\252\342\64\250\265\330\150\214\337\263\205\240\237\214\110\210\35\35\32\326\115\162\1\131\246\72\73\226\151\367\53\107\143\121\161\246\34\353\174\46\46\213\273\52\301\36\100\213\127\257\245\214\333\261\226\251\255\224\244\164\21\173\35\74\115\243\216\70\300\111\64\2\113\363\212\23\342\150\255\373\320\26\327\326\212\231\205\316\324\320\165\321\64\220\333\323\377\17\346\244\241\216\341\250\321\230\177\356\64\23\272\361\47\64\176\310\132\227\276\3\51\72\4\213\260\123\262\144\316\34\104\170\270\33\314\13\140\120\154\5\225\366\342\263\245\221\153\116\227\55\11\331\375\46\200\32\42\300\40\245\364\244\133\140\200\276\273\157\260\240\56\35\23\226\305\100\352\270\55\144\346\302\260\22\203\355\212\36\65\326\233\73\240\326\211\351\256\120\232\357\155\53\343\330\226\37\170\150\52^[@ \ No newline at end of file diff --git a/print.py b/print.py new file mode 100644 index 0000000..b511af7 --- /dev/null +++ b/print.py @@ -0,0 +1,100 @@ +import sys +import os +import six +from random import randint +from PIL import Image, ImageOps + +ESC = b'\x1B' +ETX = b'\x03' +SO = b'\x0E' + +GRAPHIC = b'\x1B'+b'\x2A'+b'\x01' #1B 2A +LINE_FEED = b'\n' +CARIDGE_RET = b'\x0D' +DATA = bytearray() +COLUMNS = 2000 +H = COLUMNS // 256 +L = COLUMNS % 256 + + + +def _to_column_format(im, line_height): + """ + Extract slices of an image as equal-sized blobs of column-format data. + + :param im: Image to extract from + :param line_height: Printed line height in dots + """ + width_pixels, height_pixels = im.size + top = 0 + left = 0 + blobs = [] + while left < width_pixels: + remaining_pixels = width_pixels - left + box = (left, top, left + line_height, top + height_pixels) + #transform: (size, method, data=None, resample=0, fill=1) + slice = im.transform((line_height, height_pixels), Image.EXTENT, box) + bytes = slice.tobytes() + blobs.append(bytes) + left += line_height + return blobs + +def _int_low_high(inp_number, out_bytes): + """ Generate multiple bytes for a number: In lower and higher parts, or more parts as needed. + to generate the H and L value + :param inp_number: Input number + :param out_bytes: The number of bytes to output (1 - 4). + """ + max_input = (256 << (out_bytes * 8) - 1); + if not 1 <= out_bytes <= 4: + raise ValueError("Can only output 1-4 byes") + if not 0 <= inp_number <= max_input: + raise ValueError("Number too large. Can only output up to {0} in {1} byes".format(max_input, out_bytes)) + outp = b''; + for _ in range(0, out_bytes): + outp += six.int2byte(inp_number % 256) + inp_number = inp_number // 256 + return outp + + +filename = u"img.jpg" +im = Image.open(filename) + +basewidth = 600 +# Initial rotate. mirror, and extract blobs for each 8 or 24-pixel row +# Convert to black & white via greyscale (so that bits can be inverted) + +im = im.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT) +width_pixels,height_pixels = im.size + +if width_pixels > basewidth: + wpercent = (basewidth/float(im.size[0])) + hsize = int((float(im.size[1])*float(wpercent))) + im = im.resize((basewidth,hsize), Image.NEAREST) + +height_pixels, width_pixels = im.size +im = im.resize((height_pixels,int(width_pixels*1.5)), Image.ANTIALIAS) + +im = im.convert("L") # Invert: Only works on 'L' images +im = ImageOps.invert(im) # Bits are sent with 0 = white, 1 = black in ESC/POS +im = im.convert("1") # Pure black and white + +line_height = 1 +blobs = _to_column_format (im, line_height * 8); + + +#generate random Data +for i in range(COLUMNS): + DATA.append(randint(0,255)) + +height_pixels, width_pixels = im.size + +with os.fdopen(sys.stdout.fileno(), 'wb') as fp: + fp.write(ESC+b"@") + fp.write(ESC + b"3" + six.int2byte(22)); # Adjust line-feed size + fp.write(CARIDGE_RET) + for blob in blobs: + fp.write(GRAPHIC + _int_low_high( width_pixels, 2 ) + blob) + fp.write(LINE_FEED) + #fp.write(GRAPHIC + bytes([L,H]) + DATA) + fp.write(ESC + b"2"); # Reset line-feed size diff --git a/print_old.py b/print_old.py new file mode 100644 index 0000000..b45f229 --- /dev/null +++ b/print_old.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +""" +This is a minimal ESC/POS printing script which uses the 'column format' +of image output. + +The snippet is designed to efficiently delegate image processing to +PIL, rather than spend CPU cycles looping over pixels. + +Do not attempt to use this snippet in production, get a copy of python-escpos instead! +""" + +from PIL import Image, ImageOps +import six +import sys +import os + +def _to_column_format(im, line_height): + """ + Extract slices of an image as equal-sized blobs of column-format data. + + :param im: Image to extract from + :param line_height: Printed line height in dots + """ + width_pixels, height_pixels = im.size + top = 0 + left = 0 + blobs = [] + while left < width_pixels: + remaining_pixels = width_pixels - left + box = (left, top, left + line_height, top + height_pixels) + slice = im.transform((line_height, height_pixels), Image.EXTENT, box) + bytes = slice.tobytes() + blobs.append(bytes) + left += line_height + return blobs + +def _int_low_high(inp_number, out_bytes): + """ Generate multiple bytes for a number: In lower and higher parts, or more parts as needed. + + :param inp_number: Input number + :param out_bytes: The number of bytes to output (1 - 4). + """ + max_input = (256 << (out_bytes * 8) - 1); + if not 1 <= out_bytes <= 4: + raise ValueError("Can only output 1-4 byes") + if not 0 <= inp_number <= max_input: + raise ValueError("Number too large. Can only output up to {0} in {1} byes".format(max_input, out_bytes)) + outp = b''; + for _ in range(0, out_bytes): + outp += six.int2byte(inp_number % 256) + inp_number = inp_number // 256 + return outp + +if __name__ == "__main__": + # Configure + high_density_horizontal = True + high_density_vertical = True + if len(sys.argv) > 1: + filename = sys.argv[1] + else: + filename = u"img.png" + + # Load Image + im = Image.open(filename) + + # Initial rotate. mirror, and extract blobs for each 8 or 24-pixel row + # Convert to black & white via greyscale (so that bits can be inverted) + im = im.convert("L") # Invert: Only works on 'L' images + im = ImageOps.invert(im) # Bits are sent with 0 = white, 1 = black in ESC/POS + im = im.convert("1") # Pure black and white + im = im.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT) + line_height = 3 if high_density_vertical else 1 + blobs = _to_column_format (im, line_height * 8); + + # Generate ESC/POS header and print image + ESC = b"\x1b"; + # Height and width refer to output size here, image is rotated in memory so coordinates are swapped + height_pixels, width_pixels = im.size + density_byte = (1 if high_density_horizontal else 0) + (32 if high_density_vertical else 0); + header = ESC + b"*" + six.int2byte(density_byte) + _int_low_high( width_pixels, 2 ); + + with os.fdopen(sys.stdout.fileno(), 'wb') as fp: + fp.write(ESC + b"3" + six.int2byte(16)); # Adjust line-feed size + for blob in blobs: + fp.write(header + blob + b"\n") + fp.write(ESC + b"2"); # Reset line-feed size + diff --git a/run.py b/run.py new file mode 100644 index 0000000..f6a0d52 --- /dev/null +++ b/run.py @@ -0,0 +1,7 @@ +from escpos.connections import getFilePrinter + + +printer = getFilePrinter()(dev='/dev/usb/lp0') + +printer.text("Hello World") +printer.lf() diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..9b8f426 --- /dev/null +++ b/run.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# OKI 320 ML tests +# this test is to generate (crap) random 8 pin mode patterns +#PRNG +function rand { +echo "obase=8;`expr $RANDOM % 256`" | bc +} +function randpat { +n=1 +while [ $n -le 256 ]; do +RND=`rand` +echo -ne '\'$RND +n=$((n+1)) +done +} +PATTERN=`randpat` +# noise +# PATTERN2=`head /dev/urandom -c 1024` +echo -e '^[@^[5^[9^[*1\4'$PATTERN'^[@' | lp -o raw diff --git a/testbasic.bas b/testbasic.bas new file mode 100644 index 0000000..357d50c --- /dev/null +++ b/testbasic.bas @@ -0,0 +1,13 @@ +10 WIDTH “LPT1:,”255 ‘Set printer for maximum line width +20 LPRINT CHR$(27);”*”;CHR$(113);CHR$(80);”:”;CHR$(3); +30 REM CHR$(113) and CHR$(80) are the attribute codes for normal speed,quadruple density graphics +40 REM CHR$(3) Begins graphics printing +50 FOR I=1 TO 6 ‘Repeat triangle pattern six times +60 FOR J=1 TO 16 ‘Each triangle has 16 columns +70 READ A ‘Read the column +80 LPRINT CHR$(A); ‘Send the byte to the printer +90 NEXT J ‘Now print the next column +100 RESTORE ‘Go back to the beginning of the DATA statements +120 LPRINT CHR$(3);CHR$(2); ‘End graphics printing +130 END +140 DATA 128,192,224,240,248,252,254,255,255,254,252,248,240,224,192,128