mirror of
https://github.com/emi420/esc-pos-server-print.git
synced 2025-12-13 08:02:24 +00:00
Initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
ENV
|
||||
*.DS_Store
|
||||
*.pyc
|
||||
BIN
OpenSans-Bold.ttf
Executable file
BIN
OpenSans-Bold.ttf
Executable file
Binary file not shown.
BIN
OpenSans-Regular.ttf
Executable file
BIN
OpenSans-Regular.ttf
Executable file
Binary file not shown.
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ESC-POS Server Print
|
||||
|
||||
## Simple web server for print using ESC-POS.
|
||||
|
||||
## Install and run
|
||||
|
||||
* Install pip requirements
|
||||
* Run 'python server.py'
|
||||
|
||||
## License
|
||||
|
||||
You may use any Mootor project under the terms of either the MIT License or the GNU General Public License (GPL) Version 3.
|
||||
|
||||
(c) 2016 Emilio Mariscal
|
||||
88
image.py
Normal file
88
image.py
Normal file
@@ -0,0 +1,88 @@
|
||||
""" Image format handling class
|
||||
This module contains the image format handler :py:class:`EscposImage`.
|
||||
:author: `Michael Billington <michael.billington@gmail.com>`_
|
||||
:organization: `python-escpos <https://github.com/python-escpos>`_
|
||||
:copyright: Copyright (c) 2016 Michael Billington <michael.billington@gmail.com>
|
||||
:license: GNU GPL v3
|
||||
"""
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
|
||||
class EscposImage(object):
|
||||
"""
|
||||
Load images in, and output ESC/POS formats.
|
||||
The class is designed to efficiently delegate image processing to
|
||||
PIL, rather than spend CPU cycles looping over pixels.
|
||||
"""
|
||||
|
||||
def __init__(self, img_source):
|
||||
"""
|
||||
Load in an image
|
||||
|
||||
:param img_source: PIL.Image, or filename to load one from.
|
||||
"""
|
||||
if isinstance(img_source, Image.Image):
|
||||
img_original = img_source
|
||||
else:
|
||||
img_original = Image.open(img_source)
|
||||
|
||||
# Convert to white RGB background, paste over white background
|
||||
# to strip alpha.
|
||||
img_original = img_original.convert('RGBA')
|
||||
im = Image.new("RGB", img_original.size, (255, 255, 255))
|
||||
im.paste(img_original, mask=img_original.split()[3])
|
||||
# Convert down to greyscale
|
||||
im = im.convert("L")
|
||||
# Invert: Only works on 'L' images
|
||||
im = ImageOps.invert(im)
|
||||
# Pure black and white
|
||||
self._im = im.convert("1")
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""
|
||||
Width of image in pixels
|
||||
"""
|
||||
width_pixels, _ = self._im.size
|
||||
return width_pixels
|
||||
|
||||
@property
|
||||
def width_bytes(self):
|
||||
"""
|
||||
Width of image if you use 8 pixels per byte and 0-pad at the end.
|
||||
"""
|
||||
return (self.width + 7) >> 3
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""
|
||||
Height of image in pixels
|
||||
"""
|
||||
_, height_pixels = self._im.size
|
||||
return height_pixels
|
||||
|
||||
def to_column_format(self, high_density_vertical=True):
|
||||
"""
|
||||
Extract slices of an image as equal-sized blobs of column-format data.
|
||||
:param high_density_vertical: Printed line height in dots
|
||||
"""
|
||||
im = self._im.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT)
|
||||
line_height = 24 if high_density_vertical else 8
|
||||
width_pixels, height_pixels = im.size
|
||||
top = 0
|
||||
left = 0
|
||||
while left < width_pixels:
|
||||
box = (left, top, left + line_height, top + height_pixels)
|
||||
im_slice = im.transform((line_height, height_pixels), Image.EXTENT, box)
|
||||
im_bytes = im_slice.tobytes()
|
||||
yield(im_bytes)
|
||||
left += line_height
|
||||
|
||||
def to_raster_format(self):
|
||||
"""
|
||||
Convert image to raster-format binary
|
||||
"""
|
||||
return self._im.tobytes()
|
||||
|
||||
|
||||
4
reqs.txt
Normal file
4
reqs.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Pillow==3.3.0
|
||||
pyserial==3.1.1
|
||||
six==1.10.0
|
||||
wheel==0.24.0
|
||||
176
server.py
Normal file
176
server.py
Normal file
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
ESC-POS Server Print - a web service for print using ESC-POS.
|
||||
|
||||
You may use any Server Print project under the terms
|
||||
of the GNU General Public License (GPL) Version 3.
|
||||
|
||||
(c) 2016 Emilio Mariscal (emi420 [at] gmail.com)
|
||||
|
||||
Module description:
|
||||
|
||||
Server Print
|
||||
|
||||
Simple web server for print using ESC-POS.
|
||||
|
||||
'''
|
||||
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
import os
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from urlparse import urlparse, parse_qs
|
||||
import textwrap
|
||||
import serial
|
||||
import time
|
||||
import image
|
||||
import six
|
||||
|
||||
# Set to false if you want to test the image without printing
|
||||
DEBUG = False
|
||||
|
||||
PORT = 8001
|
||||
|
||||
SERIAL = '/dev/ttyUSB0'
|
||||
SPEED = 38400
|
||||
DENSITY = 3
|
||||
|
||||
H1_FONT = ImageFont.truetype("OpenSans-Bold.ttf", 70)
|
||||
H2_FONT = ImageFont.truetype("OpenSans-Regular.ttf", 30)
|
||||
P_FONT = ImageFont.truetype("OpenSans-Regular.ttf", 23)
|
||||
TMP_FILE = "page.png"
|
||||
LOGO_FILE = "logo.png"
|
||||
W = 300
|
||||
GS = b'\x1d'
|
||||
|
||||
|
||||
def _int_low_high(inp_number, out_bytes):
|
||||
"""
|
||||
Generate multiple bytes for a number: In lower and higher parts, or more parts as needed.
|
||||
Function from python-escpos library (https://github.com/python-escpos/python-escpos)
|
||||
|
||||
: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 bytes")
|
||||
if not 0 <= inp_number <= max_input:
|
||||
raise ValueError("Number too large. Can only output up to {0} in {1} bytes".format(max_input, out_bytes))
|
||||
outp = b''
|
||||
for _ in range(0, out_bytes):
|
||||
outp += six.int2byte(inp_number % 256)
|
||||
inp_number //= 256
|
||||
return outp
|
||||
|
||||
|
||||
'''
|
||||
APIServer create a simple web server to generate and print images
|
||||
'''
|
||||
class APIServer(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
|
||||
mime = "text/html"
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", mime)
|
||||
self.send_header('Allow', 'GET, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
|
||||
height = 150
|
||||
|
||||
imgtmp = Image.new("RGBA", (W,100), (255,255,255))
|
||||
drawtmp = ImageDraw.Draw(imgtmp)
|
||||
|
||||
params = parse_qs(urlparse(self.path).query)
|
||||
|
||||
if params:
|
||||
|
||||
h1 = unicode(params.get("h1")[0],"utf8")
|
||||
h2 = unicode(params.get("h2")[0],"utf8")
|
||||
p = unicode(params.get("p")[0],"utf8")
|
||||
|
||||
linesh2 = textwrap.wrap(h2, width=20)
|
||||
lines = textwrap.wrap(p, width=20)
|
||||
|
||||
imglogo = Image.open(LOGO_FILE, 'r')
|
||||
img_w, img_h = imglogo.size
|
||||
|
||||
wh1, hh1 = drawtmp.textsize(h1, font=H1_FONT)
|
||||
wh2, hh2 = drawtmp.textsize(h2, font=H2_FONT)
|
||||
|
||||
height += img_h + hh1 + hh2
|
||||
|
||||
for line in lines:
|
||||
w, h = drawtmp.textsize(line, font=P_FONT)
|
||||
height += h
|
||||
|
||||
for line in linesh2:
|
||||
w, h = drawtmp.textsize(line, font=H2_FONT)
|
||||
height += h
|
||||
|
||||
del drawtmp
|
||||
|
||||
img = Image.new("RGBA", (W,height), (255,255,255))
|
||||
|
||||
img.paste(imglogo, (((W - img_w) / 2),30))
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
draw.text(((W-wh1)/2, img_h + 50), h1, (0,0,0), font=H1_FONT)
|
||||
|
||||
y_text = img_h + hh1 + 80
|
||||
|
||||
for line in linesh2:
|
||||
w, h = draw.textsize(line, font=H2_FONT)
|
||||
draw.text(((W-w)/2, y_text), line, (0,0,0), font=H2_FONT)
|
||||
y_text += h
|
||||
|
||||
y_text += 25
|
||||
|
||||
for line in lines:
|
||||
w, h = draw.textsize(line, font=P_FONT)
|
||||
draw.text(((W-w)/2, y_text), line, (0,0,0), font=P_FONT)
|
||||
y_text += h
|
||||
|
||||
del draw
|
||||
|
||||
img.save(TMP_FILE, "PNG")
|
||||
|
||||
if not DEBUG:
|
||||
conn = serial.Serial(SERIAL, SPEED, timeout=10)
|
||||
im = image.EscposImage(TMP_FILE)
|
||||
out = im.to_raster_format()
|
||||
header = GS + b"v0" + six.int2byte(DENSITY) + _int_low_high(im.width_bytes, 2) + _int_low_high(im.height, 2)
|
||||
conn.write(header + out)
|
||||
conn.write("\x0a\x0a\x0a\x1d\x56\x00\x0a\x0a")
|
||||
time.sleep(1)
|
||||
conn.close()
|
||||
printer_returns = 1
|
||||
else:
|
||||
printer_returns = 0
|
||||
|
||||
self.wfile.write(printer_returns)
|
||||
|
||||
return
|
||||
|
||||
def main():
|
||||
|
||||
try:
|
||||
server = HTTPServer(('', PORT), APIServer)
|
||||
print 'Started httpserver on port ' + str(PORT)
|
||||
server.serve_forever()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print '^C received, shutting down server'
|
||||
server.socket.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user