I need to send escpos to a thermal receipt printer. I am running into issues with specifying character size, which is described [https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=34]. In Python I write this command as
#ESC # for initiate the printer
string = b'\x1b\x40'
#GS ! command in the doc corresponding to 4 times character height and width
string = string + b'\x1d' + b'\x21' + b'\x30' + b'\x03'
string = string + bytes('hello world')
In the first line I initiated the printer corresponding to ESC #
In the second line I wanted to specify the character size to be 4x height and width (see the links to the doc).
In the third line I print out the text.
The problem is that the text printed out has 4x width, but not 4 times height. I have also tried to write the character size as two commands
string = string + b'\x1d' + b'\x21' + b'\x30'
string = string + b'\x1d' + b'\x21' + b'\x03'
In this case, my text is printed out with 4 times height but not 4 times width. I am pretty sure that I have misread the doc, but I don't know how else I should write the command in order to achieve both 4 times height and width.
Also examples exist for the GS ! syntax in escpos, and there it seems to be written as GS ! 0x11 to achieve both 2 times width and height. This doesn't seems to make sense from the table. I am aware that python-escpos exists, however it doesn't work on windows 10 for my usb printer.
From reading the docs, it seems to me that you would have to use
b'\x1d' + b'\x21' + b'\x33'
to get 4 times magnification in height as well as width. The two '3' indicate the magnifications minus one. The first is width, the second is height.
So the problem seems to be that you split width and height into two bytes. They should be collected into one byte.
So, in total:
#ESC # for initiate the printer
string = b'\x1b\x40'
#GS ! command in the doc corresponding to 4 times character height and width
string = string + b'\x1d' + b'\x21' + b'\x33'
string = string + bytes('hello world')
Or, in another way:
def initialize():
# Code for initialization of the printer.
return b'\x1b\x40'
def magnify(wm, hm):
# Code for magnification of characters.
# wm: Width magnification from 1 to 8. Normal width is 1, double is 2, etc.
# hm: Height magnification from 1 to 8. Normal height is 1, double is 2, etc.
return bytes([0x1d, 16*(wm-1) + (hm-1)])
def text(t, encoding="ascii"):
# Code for sending text.
return bytes(t, encoding)
string = initialize() + magnify(4, 4) + text('hello world')
Related
I wrote the following code, which attempts to take a list of strings, and fit them inside of a bounding box of a specified width and height. It does this by inserting line breaks in appropriate places, and if the height is still exceeded, then it scales the text down by half the size and then attempts to fit it again -- and so on, until eventually the text fits.
def tbox(slst, width, height):
if width < 0.01 or height < 0.01: raise (" Error: Box too small ")
if slst == []: raise(" Error: empty string list ")
nlst = [] # Word width list
t_height = 0 # Max word height
for s in slst:
t = Tex(s+" ")
nlst.append(t.width)
t_height = max(t_height, t.height)
t_ind = 0
size = 1
cur_line = ""
line_len = 0
out = []
while t_ind < len(slst): # While there are strings to process
# If a single string is too large or the height outside the max
if nlst[t_ind]*size > width or len(out)*t_height*size > height:
# then reset with a smaller text size.
t_ind, cur_line, line_len, out = 0, "", 0, []
size *= 0.5
continue
# If the current string fits, add it
if (line_len + nlst[t_ind])*size <= width:
cur_line += slst[t_ind]+" "
line_len += nlst[t_ind]
# otherwise log the line and start a fresh line
else:
out.append(cur_line)
cur_line = ""
line_len = 0
continue
t_ind += 1
# Log any line that remains at the end.
if line_len > 0:
out.append(cur_line)
# Fit the resulting strings into a scaled VGroup
vg = VGroup()
for s in out:
t = Tex(s).scale(size)
vg.add(t)
vg.arrange(DOWN)
return vg
Now when I try to use this on a long string, and allow it to have a wide bounding box, it seems that Manim somehow automatically adds line breaks for strings that are too long. (At least I think so -- I don't know what else could be causing the occurrence of line breaks that don't show up before passing the strings into the Tex constructor.)
Is there a way to turn this off? I tried parsing through the source code for Tex and its superclass, but can't identify a part of the code that would be inserting these line breaks.
You could not find the piece of code responsible for these line breaks because Manim does not insert them either: when Tex or MathTex is rendered, Manim basically compiles a LaTeX document, converts it to an SVG, and imports the result.
Just as every other LaTeX document, the one that Manim compiles also has a page width -- and if your string is too long, LaTeX breaks the line accordingly.
Take a look at these lines: https://github.com/behackl/manim-content/blob/dad382e6843700cf9afa64675d2be1d3cdb10bca/2022-06_four-gf-problems.py#L9-L13 -- I've modified the default LaTeX page width there to make some text break automatically in my video, and you can likely adapt that approach.
I want to wrap the string at 30,700 in this script. What is the best way of doing this, I have tried using textWrap but it does not seem to work. This is my code:
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
canvas = canvas.Canvas("Forensic Report.pdf", pagesize=letter)
canvas.setLineWidth(.3)
canvas.setFont('Helvetica', 12)
canvas.drawString(30,750,'LYIT MOBILE FORENSICS DIVISION')
canvas.drawString(500,750,"Date: 12/02/2018")
canvas.line(500,747,595,747)
canvas.drawString(500,725,'Case Number:')
canvas.drawString(580,725,"10")
canvas.line(500,723,595,723)
canvas.drawString(30, 700, 'This forensic report has been compiled by the forensic examiner in conclusion to the investigation into the RTA case which occured on 23/01/2018')
canvas.save()
print("Forensic Report Generated")
Perhaps you want to use the drawText?
Doing so, your code will be
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
canvas = canvas.Canvas("Forensic Report.pdf", pagesize=letter)
canvas.setLineWidth(.3)
canvas.setFont('Helvetica', 12)
canvas.drawString(30,750,'LYIT MOBILE FORENSICS DIVISION')
canvas.drawString(500,750,"Date: 12/02/2018")
canvas.line(500,747,595,747)
canvas.drawString(500,725,'Case Number:')
canvas.drawString(580,725,"10")
canvas.line(500,723,595,723)
line1 = 'This forensic report has been compiled by the forensic'
line2 = 'examiner in conclusion to the investigation into the RTA'
line3 = 'case which occured on 23/01/2018'
textobject = canvas.beginText(30, 700)
lines = [line1, line2, line3]
for line in lines:
textobject.textLine(line)
canvas.drawText(textobject)
canvas.save()
This is also the solution suggested here. Unfortunately, I do not see it as a valid solution for automatic text wrapping in new lines, i.e. you should manage how to split the string yourself.
You should wrap the string itself using a function and draw what's returned. To get a properly wrapped string you could specify a line length like so:
def wrap(string, length):
if len(string) < length:
return string
return string[:length:] + '\n' + wrap(string[length::], length)
The wrap function first checks if the string length is less than the specified length and immediately returns it if so. If it's longer then it appends a newline character to the end of a substring up to length, plus sends the remainder of the string to the wrap() function to check the rest.
Running:
string = "This is a really super duper long string from some forensic report and should take up a lot of space..."
length = 20
print(wrap(string, length))
Will print out:
This is a really sup
er duper long string
from some forensic
report and should ta
ke up a lot of space
...
Because you PROBABLY don't want every single line to be truncated at the line width, we can fix this by adding another recursive function to check for the most recent whitespace character like so:
def seek(string, index):
if string[index-1] == ' ':
return index
return seek(string, index-1)
The seek() will return the index of the last whitespace character of any string (substring in this case).
Note: seek() HAS to check the previous character string[index-1] otherwise will give you the index of the space character and wrap() will append it to each new line.
You can then modify the wrap() function like so:
def wrap(string, length):
if len(string) < length:
return string
pos = seek(string, length)
return string[:pos:] + '\n' + wrap(string[pos::], length)
Running:
string = "This is a really super duper long string from some forensic report and should take up a lot of space..."
length = 20
print(wrap(string, length))
Prints out:
This is a really
super duper long
string from some
forensic report and
should take up a
lot of space...
I am writing a python program to parse some user data from a txt file.
One of the rows in the text file will contain the user's height.
I have specified an order that the user is expected to follow like
First line of the file should contain name, the next line, date of birth,
3rd line, height etc.
I have also given a sample file to the user which looks like this
Name: First Name Last Name
DOB: 16.04.2000
Age: 16
Height: 5 feet 9 inch
When I read the file, I looked at each line and split it using ':' as a separator.
The first field is my column name like name, dob, age, height.
In some cases, users forget the ':' after Name or DOB, or they will simply send data like:
Height 5 feet 9 inch
5 feet 9 inch
5ft 9 in
5feet 9inches
The logic I have decided to use is:
Look for ':' on each line; if one is found, then I have my field.
Otherwise, try to find out what data it could be.
The logic for height is like this:
if any(heightword in file_line.upper() for heightword in ['FT', 'HEIGHT', 'FEET', 'INCH', 'CM'])
This if condition will look for words associated with height.
Once I have determined that the line from the file contains the height, I want to be able to convert that information to inches before I write it to the database.
Please can someone help me work out how to convert the following data to inches.
Height 5 feet 9 inch
5 feet 9 inch
5ft 9 in
5feet 9inches
I know since I am trying to cater to variety of user inputs. This list is not exhaustive; I am trying to use these as an example to understand, and then I will keep adding code if and when I find new patterns.
pyparsing is a nice module for simple parsing situations like this, especially when trying to process less-than-predictable-but-still-fairly-structured human input. You can compose your parser using some friendly-named classes (Keyword, Optional, OneOrMore, and so on) and arithmetic operators ('+' for sequence, '|' for alternatives, etc.), to assemble smaller parsers into larger ones. Here is a parser built up from bits for your example (also support ' and " for feet and inches, and fractional feet and inch values too). (This sample uses the latest version of pyparsing, version 2.1.4):
samples = """\
Height 5 feet 9 inch
5 feet 9 inch
5ft 9 in
5feet 9inches
5'-9-1/2"
5' 9-1/2"
5' 9 1/2"
6'
3/4"
3ft-6-1/4 in
"""
from pyparsing import CaselessKeyword, pyparsing_common, Optional
CK = CaselessKeyword
feet_units = CK("feet") | CK("ft") | "'"
inch_units = CK("inches") | CK("inch") | CK("in") | '"'
# pyparsing_common.number will parse an integer or real, and convert to float
integer = pyparsing_common.number
fraction = integer + '/' + integer
fraction.addParseAction(lambda t: t[0]/t[-1])
qty = fraction | (integer + Optional(fraction)).addParseAction(lambda t:sum(t))
# define whole Height feet-inches expression
HEIGHT = CK("height") | CK("ht")
inch_qty = qty("inches")
feet_qty = qty("feet")
height_parser = Optional(HEIGHT) + (inch_qty + inch_units |
feet_qty + feet_units + Optional(inch_qty + inch_units))
# use parse-time callback to convert feet-and-inches to inches
height_parser.addParseAction(lambda t: t.get("feet", 0.0)*12 + t.get("inches", 0.0))
height_parser.ignore("-")
height_parser.runTests(samples)
# how to use the parser in normal code
height_value = height_parser.parseString(samples.splitlines()[0])[0]
print(height_value, type(height_value))
Prints:
Height 5 feet 9 inch
[69.0]
5 feet 9 inch
[69.0]
5ft 9 in
[69.0]
5feet 9inches
[69.0]
5'-9-1/2"
[69.5]
5' 9-1/2"
[69.5]
5' 9 1/2"
[69.5]
6'
[72.0]
3/4"
[0.75]
3ft-6-1/4 in
[42.25]
69.0 <type 'float'>
In JavaScript, there is an operation called "computed access", done as object[key], where the object property read is determined through the result of a given expression, as an alternative to the normal . operator. Personally, I mostly use it for iteration and reading properties with hyphens and stuff, but it can also be used to get associated wanted results from an input string.
So after an entire afternoon of Googling and figuring out Python syntax, etc. I was able to write a short program to do this.
import re
import string
h = 0
r = re.compile(r'(\d+)\s*(\w+)\b')
def incr( m ):
h+=m.group(1)*({'in':1,'inches':1,'inch':1,'foot':12,'feet':12,'cm':0.3937,'centimeter':0.3937,'centimeters':0.3937}[string.lower(m.group(2))]||1) # etc. etc.
return ''
re.sub(r, incr, input)
print h
You may want to restrict the keywords usable to keep the dict from getting too big.
I tried out Stephen's code in the first comment on python 3.6 and had to tweak it to work for me:
import re
h = 0
input = '5 feet 9 inches'
r = re.compile(r'(\d)\s*(\w+)\b')
measures ={'in':1,'inches':1,'inch':1,'foot':12,'feet':12,'ft':12,'cm':0.3937,'centimeter':0.3937,'centimeters':0.3937}
def incr(m):
global h
h+=int(m.group(1))*measures[m.group(2)]
return ''
re.sub(r, incr, input)
print(h)
I have a python script that is writing text to images using the PIL. Everything this is working fine except for when I encounter strings with carriage returns in them. I need to preserve the carriage returns in the text. Instead of writing the carriage return to the image, I get a little box character where the return should be. Here is the code that is writing the text:
<code>
draw = ImageDraw.Draw(blankTemplate)
draw.text((35 + attSpacing, 570),str(attText),fill=0,font=attFont)
</code>
attText is the variable that I am having trouble with. I'm casting it to a string before writing it because in some cases it is a number.
Thanks for you help.
Let's think for a moment. What does a "return" signify? It means go to the left some distance, and down some distance and resume displaying characters.
You've got to do something like the following.
y, x = 35, 570
for line in attText.splitlines():
draw.text( (x,y), line, ... )
y = y + attSpacing
You could try the the following code which works perfectly good for my needs:
# Place Text on background
lineCnt = 0
for line in str(attText):
draw = ImageDraw.Draw(blankTemplate)
draw.text((35 + attSpacing,570 + 80 * lineCnt), line, font=attFont)
lineCnt = lineCnt +1
The expression "y+80*lineCnt" moves the text down y position depending on the line no. (the factor "80" for the shift must be adapted according the font).
I have this string: 500 Minutes + Unlimited Text + Your Choice of Data
I want to separate it in 3 different strings and store it in 3 different variables, having data as
1) 500 Minutes 2) Unlimited text 3) Your choice of data.
I want to use + as the separator in this process, whenever + comes, create a new variable for the following text, until one more + comes.
Please help me in solving this.
If you know there are exactly three variables:
text = "500 Minutes + Unlimited Text + Your Choice of Data"
a, b, c = [part.strip() for part in text.split('+')]
print a
print b
print c
prints:
500 Minutes
Unlimited Text
Your Choice of Data