Ptouch - Brother Label Printers - your own "driver" for Linux

Requirements: Ruby langugage interpreter and ImageMagic convert program to generate images.

For Debian based Linux system (Ubunutu) run as root

aptitude install ruby imagemagick

Tested printers PT-2420Pc, PT-18R and PT-P750W

Copy the following ruby sourcecode file and place it wherever. Name the file however you like. For example ptprint.rb Add add the running privileges to the file

chmod +x ptprint.rb

Also see the instructions on the beginning of the source ruby file and edit the printer's name first.

And finally print the labels.

ptprint.rb "This is a text I want to be printed on a label"

If you want to print on more then one line do

ptprint.rb "This is a first line\nthis a second line\nthird line"

here is the source code of ptprint.rb.

#!/usr/bin/env ruby
# encoding: ASCII-8BIT
 
# Install the printer using CUPS as a raw printer - no driver will be used,
# The manufacture of Raw printer is Raw and the printer gets what is sent. 
# If the CUPS can not find the printer, press the Plite button for few seconds to disable flash drive mode and enable printer mode.
 
# Set the printer name you have chosen 
PRINTER_NAME="Brother_raw"
# See installed printers and its names by issuing command lpstat -p -d
 
if ARGV[0]==nil
  puts "I expect one parameter which is text to be printed on a P-touch Label printer"
  puts 'If you want to print a text over more then one line, use "\n" as new line marker.'
  puts
  puts "For more information about this simple script see http://www.odorik.cz/w/ptouch "
  exit
end
 
 
# The width of the image (in pixels) should be divisible by eight to be a whole byte.
# The parameter -size x128 is suitable 24mm tape. If your use smaller tape lower the image width
# If you change the tape width,  you may want to change the image width.
# For example 6mm tape may be best with 32,40 or 48 pixels (bits). 
# The maximal width depends on printer model and for tape PT printers is usually 128 bits.
 
`convert  +antialias  -background white -fill black  -size x128 -gravity South -rotate 90 label:"#{ARGV[0]}"    image_to_be_printed.pbm`
# +antialias turns antialiasing off (not on as one would guess). The rendering is more smooth with certain versions of ImageMagick package.
 
# Of course you can create image with any program you wish. Or create a pdf or PostScript file and convert 
# it with ghostcsript to image.
# gs -sOutputFile=label.pbm -sDEVICE=pbmraw -g720x$::LABELLENGTH -dNOPAUSE -dBATCH label.ps
 
def readImage(image_name)
#  reads the width, lenght and  pbm image data into memory
 obrazek=File.open(image_name,"rb").read  # we read the whole image content
 # now we start to parse the content
 puts "if the image is in pbm format, first two bytes of this binary file should by characters P4:#{obrazek[0..1]}"
 # after P4 there should be at least one white space, so we can continue with char no 3
 n=3
 # If we create the pbm file with Ghostrcips, we may expect a note starting with #  
 # gs -sOutputFile=label.pbm -sDEVICE=pbmraw -g720x$::LABELLENGTH -dNOPAUSE -dBATCH label.ps
 if obrazek[n]=="#"
   puts "poznamka #{poznamka_zacatek=n}"
   while obrazek[n]!="\n" do  n=n+1; end
   puts "poznamka konec#{poznamka_konec=n}"
   puts "poznamka=#{obrazek[poznamka_zacatek..poznamka_konec]}"
   n=n+1
  end   
 puts "zacatek_sirky:#{zacatek_sirky=n}"
 while  !  /\s/.match(obrazek[n]) do  n=n+1; end
 puts "konc_sirky #{konec_sirky=n}"
 puts "image width:#{sirka=obrazek[zacatek_sirky..konec_sirky].to_i}"
 puts "sirka_v_bytech:#{sirka_v_bytech=sirka/8}"
    while /\s/.match(obrazek[n]) do  n=n+1; end
 
 puts "zacatek_delky:#{zacatek_delky=n}"
    while ! /\s/.match(obrazek[n]) do  n=n+1; end
 puts "konec_delky:#{konec_delky=n}"
 puts "image length:#{delka=obrazek[zacatek_delky..konec_delky].to_i}"
    while /\s/.match(obrazek[n]) do  n=n+1; end
 
 puts "image bitmap start #{zacatek_obrazku=n}"
 
 return obrazek,sirka,sirka_v_bytech,delka,zacatek_obrazku  
# orazek- picture, sirka- width, sirka_v_bytech - width in bytes, delke - length,  zacatek_obrazku - the beggining of the picture
end
 
# Lets initialize the printer
data = "\x00"*100  # It seems that sending zeros before we start may help if the last printing operation did not finish correctly. 
data << "\x1B@"  # Initialize
#data << "\x1BiS"   # 69H 53H the printer returns its state. We don't need this
data << "\x1BiM\x40"  # auto cut
data << "\x1BiK\x08"  # half cut off, no chain printing (cut at the end of printing).
data << "M\x02"  # Compression? Don't understand this but the web checker at https://9cb21392cee7550e9d82f3f871ce806316582628.googledrive.com/host/0B7Vet6dn3-Gwd3BKVmwzR0pjU0E/index.html works better when I set the compression on.
 
 
obrazek,sirka,sirka_v_bytech,delka,zacatek_obrazku=readImage("image_to_be_printed.pbm")
 
radek=0  # we start sending the pixel data line by line, starting from top going down 
while radek<delka do
 data_od=zacatek_obrazku+radek*sirka_v_bytech # count the offset of first byte in current line
 data_po=zacatek_obrazku+radek*sirka_v_bytech+sirka_v_bytech-1 # count the offset of last byte in current line
 
 bytu_na_vycentrovani_dole=(16-sirka_v_bytech)/2  # padding bytes from one side
 bytu_na_vycentrovani_nahore=16-sirka_v_bytech-bytu_na_vycentrovani_dole # padding bytes from other side
 
 # The printer expects the bits to be from right to left. So we need to reverse the byte order. Also we need to reverse each bit in every byte.
 # We always send 128 bits of image data per line. If there is narrow tape, not all pixels will be printed. Only the pixels in the center will be printed.
# We try to center the image in the middle of the 128 bit (pixels). 
# If the image width is less then 128bits we add pudding 00 bytes.
 data <<  "G\x11\x00\x0F"+"\x00"*bytu_na_vycentrovani_dole+obrazek[data_od..data_po].chars.map{|s| [s.unpack('b*')[0].reverse].pack('b*')}.join().reverse + "\x00"*bytu_na_vycentrovani_nahore
 # If would go from button of the image to top, we would not need to reverse the bit's order. Then we would use the line below
 #data <<  "G\x11\x00\x0F"+"\x00"*bytu_na_vycentrovani_dole+obrazek[data_od..data_po]+"\x00"*bytu_na_vycentrovani_nahore
 
 # sending uncopressed line does not work. I do not know why. My attempt is below
 #data <<  "g\x10\x00"+"\x00"*bytu_na_vycentrovani_L+obrazek[data_od..data_po].chars.map{|s| [s.unpack('b*')[0].reverse].pack('b*')}.join().reverse + "\x00"*bytu_na_vycentrovani_P
 
 radek=radek+1  # increase the line counter
end
 
# Simple comented testing lines below
#data << "G\x02\x00\xF0\xFF"*55  # to print one line - 128 dots - 16 bytes of black dots
#data << "G\x11\x00\x0F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF"*45  # Lets print one line by hend to understand the speudocompression format
#data << "G\x02\x00\xF0\xFF"*55  # to print one line - 128 dots - 16 bytes of black dots
#data << "\x5A"*9  # empty line
 
data << "\x1A"  # end of this print
 
 
File.binwrite("to_be_printed.prn", data)
 
# You can have a look at the image to be printed  on https://9cb21392cee7550e9d82f3f871ce806316582628.googledrive.com/host/0B7Vet6dn3-Gwd3BKVmwzR0pjU0E/index.html
# Just upload the file to_be_printed.prn to the Javascript and let it display the image. You can correct obvious mistake and waste your tape.
 
`lp -d #{PRINTER_NAME} to_be_printed.prn`
 
# Alternatively you could copy the file to the usb device 
# `cp to_be_printed.prn  /dev/usb/lp0`
# File.binwrite("/dev/labelprinter0", data)
#`cp to_be_printed.prn /dev/labelprinter0`
# In this case you do not need the CUPS to be installed. You should be able to read the information like status and tape type from printer using this file. 

The image to be printed is created using program “convert”. However you can create your initial image by any program, for example inkscape or convert it from postcript using gs. Just change the code above a little.

There is also an opensource ptouch driver for cups , which is probably distributed with your Linux distribution. However the driver does not always work, is very difficult to troubleshoot and seems not to support continuous roll - varying page length. The driver was not updated since 2009 and did not work for me.

The Ruby script above is very easy to trouble shoot as anything which may be wrong is located only on few lines of code.

Trouble shooting

See the unfilial short, but very transparent documentation.

Or see the official, not so transparent and not complete documentation of some models. (similar to other models where official documentation is missing)

PT-E550w/P750w QL series PT-9500PC

See instructions for similar QL-500/QL-550 printers

You can also see similar but more complex and complicated program in perl, blabel in perl (did not work for me)

To see a working example of what exactly can be send to your exact printer model, install the SnoopyPro usb Sniffer for Windows. SnoopyPro is opensource.

Choose the appropriate item to be monitored

Also if you use option “print to file” under Windows you get the binary prn file you can examine. However ptouch editor does not support printing to file and using a different program may not be optimal. Therefore using the SnoopyPro may be your best choice. Print the empty small page or page with just one dot and see what has been send. To examine binary files under Linux you can use GUI HEX editor bless or console alternative dhex.

Which printer is /dev/usb/lp0 ?

For USB printer may work:

aptitude install foo2zjs
usb_printerid /dev/usb/lp0 

The command above returns for example:

GET_DEVICE_ID string:
MFG:Brother;CMD:PT-CBP;MDL:QL-500;CLS:PRINTER;

Some printers may not be detected.

CUPS does not detect my old printer

Brother USB printers do not have this problem, they are detected fine, you can skip this part if all your printers are detected fine.

If you have printer connected to “usb to parallel adapter”, or your printer is very old, CUPS will not recognize your printer as the printer may not send any identification details.

To see connected USB devices issue

lsusb

To see which printers can be detected on your system, issue

lpinfo -v

If CUPS does not detect your printer, you can add it manually.

You have to enter the printer URI manually. The printer will be one of the /dev/usb/lpX files, depending on the order as they got connected. See below how to create a special dev file for your special printer which des not depends on the turn on order. Then you can use this new file name instead.

For adding printer to CUPS manually you will use CUPS URI like : parallel:/dev/usb/lpX .

Go to http://localhost:631 CUPS add printer dialog: „Other Network Printers→LPD/LPR Host or Printer“ (weird or what?), pasted „parallel:/dev/usb/lp0“ into the „Connection“ (URI) field and followed the prompts.

However if you do not need to use the CUPS driver, you can avoid using CUPS all together and send data to be printed directly to the printer's dev file.

I want my printer to have always the same dev file name

If you have more then one printer, /dev/usb/lp0 will be the printer which was turned on as first. So the name of the automatically created dev created for your special printer may be different each time.

To have always the same dev file for your special printer you need to add an udev rule. Run.

udevadm info -a -p $(udevadm info -q path -n /dev/usb/lp0)>lp0_info.txt
udevadm info -a -p $(udevadm info -q path -n /dev/usb/lp1)>lp1_info.txt
vimdiff lp0_info.txt lp1_info.txt

See atributes which differs. Best are those which reminds you random hash. ATTRS{serial}==“0000:00:1d.0” may not be enough as I have find out that two different devices may have this same serial number.

To make sure one dev file represents always the same printer, you can add udev rule using one of the attribute. For example:

vim /etc/udev/rules.d/99-printer.rules

Příklad obsahu souboru:

SUBSYSTEM=="usb", ATTRS{modalias}=="usb:v067Bp2305d0200dc00dsc00dp00ic07isc01ip02" , SYMLINK+="dot0"
SUBSYSTEM=="usbmisc", ATTRS{product}=="PT-2420PC"  , SYMLINK+="labelprinter0"
SUBSYSTEM=="usbmisc", ATTRS{product}=="IEEE-1284 Controller" , SYMLINK+="dot0"

This creates file /dev/dot0 for our dotmatric printer connected to usb-parallel adapter. See other udev examples

To test new rules without rebooting

udevadm control --reload-rules && udevadm trigger

The printer which should have access to thouse special dev file needs to be added in the group lp. Run

usermod -a -G lp user_name_to_use_this_printer

And relogin. (su su user) Check that new group is loaded by cammand groups

Now you can send the printing data directly to the /dev/dot0 file instead of to a raw printer using cups.

Other example of changing udev rules: http://unix.stackexchange.com/questions/60154/udev-rule-file-for-modem-not-working

ls /dev/serial/by-path/

udevadm trigger

udevadm info /dev/ttyUSB4|grep ID_PATH

http://superuser.com/questions/536478/how-to-lock-device-ids-to-port-addresses

udevadm test $(udevadm info -q path -n <device>)

One click to print from your web page

If you want to be able to create links on your web page which will enable you to print just by clinking on them, create your special URL.

Your new URL, for example print:/ /text_to_be_printed will call your local script to the printing job.

See my working example how to create your own URL to run a script to activate Callback.

This simple driver is not enough?

Choose label printer manufacturer which fully supports Linux. Dymo printer seems to be fine.

Receipment thermal printer

 
ptouch.txt · Last modified: 2019/07/16 13:37 (external edit)