14

I would like to use the full width of the 18mm strips in my Brother P950NW printer for an image. At the moment, I am using ESC/P (not ESC/POS, which this printer does not seem to support), but if it's not possible with that I'm fine with any other protocol this printer supports. (Update: with Brother's Windows software, full-width printing is possible, but it uses the LPR protocol, for which there don't seem to be any Python libraries.)

I'm using the ESC/P command ESC* with density 72 (the highest available according to the printer's documentation), which only allows filling up the width in steps of 48 dots.

How do I print 200 pixels wide on a strip in ESC/P-speak an image with height 200? That should easily fit onto the strip. However, due to ESC*72 only accepting blocks of 48, everything beyond 192 is output on another strip.

Here's my demo code:

import socket
import struct

def escp(density_code=72):
    stack_size_in_bytes = {72: 6}[density_code]
    height = 200
    width = 130

    yield b'\x1bia\x00'  # ESC/P command mode: ESC/P standard
    yield b'\x1b@'  # Initialize
    yield b'\x1bim\x00\x00'  # margin: 0
    yield b'\x1biXE2\x00\x00\x00'  # barcode margin: 0
    yield b'\x1b3' + struct.pack('!B', 24)  # line feed length: 24 dots (i.e. no space between lines)

    for y_offset in range(0, height, 8 * stack_size_in_bytes):
        yield b'\x1b*' + struct.pack('!B', density_code) + struct.pack('<H', width)
        yield b'\xff' * width * stack_size_in_bytes
        yield b'\x0a'  # linefeed (move position 24 dots down)
    yield b'\x0c' # Print start

c = socket.create_connection(('10.222.2.206', 9100))
c.sendall(b''.join(escp()))
c.close()

I'm fine with solutions in raw binary; here is the binary file and shortened hexdump generated by the above program.

phihag
  • 278,196
  • 72
  • 453
  • 469
  • I don't have the printer, so can only provide pointers that you will have to test. https://github.com/zakx/png2ptouch and https://github.com/max-weller/python-ptouch-pc and https://github.com/pklaus/brother_ql. The last one has support for raster, which your printer also support, so I assume it may still work for you – Tarun Lalwani Apr 23 '18 at 12:58
  • Which label size are you using? – Ethan Furman Apr 24 '18 at 05:51
  • @EthanFurman Updated the question: 18mm. – phihag Apr 24 '18 at 10:44
  • can you use (ESC J) when you print img?can not test because no printer. – obgnaw Apr 27 '18 at 14:47

2 Answers2

1

Base the page 8 of the DOC,we can specify the print position before print one line, and even with image ,it will print one line by one line.ESC $can specify absolute horizontal position and ESC J will Finishes input of the current line, then moves the vertical print position forward by n/180 inch.combine this two,maybe you can using all 234 printable area.

import socket
import struct

def escp(density_code=72):
    stack_size_in_bytes = {72: 6}[density_code]
    height = 200
    width = 130

    yield b'\x1bia\x00'  # ESC/P command mode: ESC/P standard
    yield b'\x1b@'  # Initialize
    yield b'\x1bim\x00\x00'  # margin: 0
    yield b'\x1biXE2\x00\x00\x00'  # barcode margin: 0
    yield b'\x1b3' + struct.pack('!B', 24)  # line feed length: 24 dots (i.e. no space between lines)

    for y_offset in range(0, height, 8 * stack_size_in_bytes):
        yield b'\x1b*' + struct.pack('!B', density_code) + struct.pack('<H', width)
        yield b'\xff' * width * stack_size_in_bytes
        # the added command ECS J
        yield b'\x1b4a' + struct.pack('!B', 42)
        # ESC $ is b'1b24'+struct.pack('!B', 0)+struct.pack('!B', 0)
        yield b'\x0a'  # linefeed (move position 24 dots down)
    yield b'\x0c' # Print start

c = socket.create_connection(('10.222.2.206', 9100))
c.sendall(b''.join(escp()))
c.close()

b'\x1b4a' + struct.pack('!B', 42)isESC J Forward paper feed

ASCII: ESC J n

Decimal: 27 74 n

Hexadecimal: 1B 4A n

Parameters

0≤n≤255

Description

 Finishes input of the current line, then moves the vertical print position forward by n/180 inch.

 If n is less than 24, the feed amount is 24/180 inch (approximately 0.34 cm).

b'1b24'+struct.pack('!B', 0)+struct.pack('!B', 0)is ESC $ Specify absolute horizontal position

ASCII: ESC $ n1 n2

Decimal: 27 36 n1 n2

Hexadecimal: 1B 24 n1 n2

Parameters

0≤n1≤255, 0≤n2≤255

Description

 Specifies an absolute print position (in units of 1/60 inch) for the next data.

 An absolute print position specifies the horizontal print position from the left margin.

 The next character is printed at a position (n1 + 256 * n2) / 60 inch from the left margin.

 The maximum number of dots can be specified by both n1 and n2 is 1023/60 inches.

obgnaw
  • 3,007
  • 11
  • 25
  • Can you describe which commands to send exactly? Unfortunately I can only test on Monday, but I'd happily award you the bounty for effort either way now. – phihag Apr 29 '18 at 07:04
  • Thanks,I add the code,i think it's` b'\x1b4a' + struct.pack('!B', 42)`,but maybe you can try `b'\x1b4a' + struct.pack('!B', 200)` test the function.Please post the result . – obgnaw Apr 29 '18 at 13:05
  • Thanks! Awarded the +500 bounty. I'll test this tomorrow! – phihag Apr 29 '18 at 16:19
  • This exact code just adds garbage text. What command is `ESC 4a`? Adding the commented-out `b'\x1b\x24\x00\x00'` (which can be writen as `b'\x1b$\x00\x00'`) has no impact. – phihag Apr 30 '18 at 10:06
  • please post a pic of the result,and test with other param,if this command cannot work,i will give the bounty back by starting a new bounty.@phihag – obgnaw Apr 30 '18 at 14:14
  • no, the bounty is yours for answering, that is fine. When you write `b'\x1b4a'`, this is three bytes: `1B 34 61`. You probably mean `'\x1b\x4a'`. I previously tried using these commands and do not understand how the code in this answer helps fixing the problem. Can you elaborate *why* they help? I can't access the printer right now, but will post pictures asap. The main problem is that I'm getting two slips of paper, but only want one. – phihag Apr 30 '18 at 15:53
  • It's like painting with a 48mm pen in a 234mm paper,we cannot control the size of the pen,but we can control the position of the pen.only let some 6mm area overlaps will let you final drawing fit the paper by only down the pen 42mm inside of 48mm. – obgnaw May 02 '18 at 13:56
  • I'm sorry, I totally forgot that I promised to send you photos. Will do tomorrow. However, since the ESC/P code in this answer is malformed, the result will be two slips (not one), plus some of the raw ASCII as actual text. I solved the problem by using an undocumented mode other than ESC/P - for more information, see [my answer](https://stackoverflow.com/a/50317680/35070). Thanks for your help though! – phihag May 13 '18 at 15:27
  • [Here is a photo of the printer output](https://phihag.de/2018/so-printer.jpg). The code in this answer outputs 2 strips and some ASCII characters at the bottom. On the right the desired output, rendered by rasteprrynt. On the bottom, the example with multiple images per strip (with cut lines in between). – phihag May 14 '18 at 10:57
  • @phihag thanks for your photo and bounty,glad that you find a solution. – obgnaw May 16 '18 at 14:39
1

Both the Brother PT-P950NW and Borther PT-9800PCN support a "raster protocol" (code 1 instead of 0 after ESC iA). With this protocol, it is possible to print raster graphics at full width.

However, I could not find any documentation (the closest was this PDF for another printer), so I reverse-engineered it (and tried out a lot). The result is the project rasterprynt, available as a PyPi package. With rasterprynt, you can print arbitrary images, like this:

import rasterprynt

import PIL.Image

# Enter the IP address of your printer below
printer_ip = '192.168.1.123'

img1 = PIL.Image.open('example1.png')
img2 = PIL.Image.open('example2.png')
data = rasterprynt.prynt([img1, img2, img1], printer_ip)
phihag
  • 278,196
  • 72
  • 453
  • 469