116 lines
3.7 KiB
Ruby
116 lines
3.7 KiB
Ruby
require 'tmpdir'
|
|
require 'rqrcode'
|
|
|
|
DPI = 203
|
|
SRC_PDF = ARGV[0] || '/home/randy/EPH266757312_adresne_stitky_a6.pdf'
|
|
OUT_PS = ARGV[1] || '/tmp/test-label-v2.ps'
|
|
|
|
# barcode crop region in the source label, in native pixels at 203dpi
|
|
BARCODE_BOX = { x0: 388, y0: 222, x1: 772, y1: 322 }
|
|
|
|
def read_pbm(path)
|
|
bytes = File.binread(path)
|
|
header = /\AP4\s+(?:#[^\n]*\n\s*)*(\d+)\s+(\d+)\s/.match(bytes)
|
|
raise "bad pbm #{path}" unless header
|
|
|
|
width, height = header[1].to_i, header[2].to_i
|
|
bytes_per_row = (width + 7) / 8
|
|
body = bytes.byteslice(header.end(0), bytes_per_row * height)
|
|
{ width: width, height: height, bytes_per_row: bytes_per_row, body: body }
|
|
end
|
|
|
|
def pbm_bit(pbm, x, y)
|
|
byte = pbm[:body].getbyte(y * pbm[:bytes_per_row] + (x / 8))
|
|
(byte >> (7 - (x % 8))) & 1
|
|
end
|
|
|
|
# Packs a [x,y] => 0/1 (1 = black) pixel getter into row-padded, bit-inverted
|
|
# (PostScript DeviceGray 1bpc: 0=black) bytes ready for the `image` operator.
|
|
def pack_for_ps_image(width, height)
|
|
bytes_per_row = (width + 7) / 8
|
|
rows = Array.new(height) { "\x00".b * bytes_per_row }
|
|
height.times do |y|
|
|
row = rows[y].dup.b
|
|
width.times do |x|
|
|
next unless yield(x, y) == 1
|
|
|
|
byte_index = x / 8
|
|
bit = 0x80 >> (x % 8)
|
|
row.setbyte(byte_index, row.getbyte(byte_index) | bit)
|
|
end
|
|
rows[y] = row
|
|
end
|
|
packed = rows.join
|
|
inverted = packed.bytes.map { |b| (~b) & 0xFF }.pack('C*')
|
|
{ width: width, height: height, hex: inverted.unpack1('H*').upcase }
|
|
end
|
|
|
|
def ps_image_block(x_pt, y_pt, w_pt, h_pt, img)
|
|
<<~PS
|
|
gsave
|
|
#{x_pt} #{y_pt} translate
|
|
#{w_pt} #{h_pt} scale
|
|
#{img[:width]} #{img[:height]} 1
|
|
[#{img[:width]} 0 0 -#{img[:height]} 0 #{img[:height]}]
|
|
{ <#{img[:hex]}> } image
|
|
grestore
|
|
PS
|
|
end
|
|
|
|
Dir.mktmpdir do |dir|
|
|
pbm_path = File.join(dir, 'label.pbm')
|
|
system('gs', '-q', '-dNOPAUSE', '-dBATCH', '-dSAFER',
|
|
'-sDEVICE=pbmraw', "-r#{DPI}", '-dFirstPage=1', '-dLastPage=1',
|
|
"-sOutputFile=#{pbm_path}", SRC_PDF) or raise 'gs failed'
|
|
|
|
pbm = read_pbm(pbm_path)
|
|
bw = BARCODE_BOX[:x1] - BARCODE_BOX[:x0]
|
|
bh = BARCODE_BOX[:y1] - BARCODE_BOX[:y0]
|
|
barcode_img = pack_for_ps_image(bw, bh) do |x, y|
|
|
pbm_bit(pbm, BARCODE_BOX[:x0] + x, BARCODE_BOX[:y0] + y)
|
|
end
|
|
|
|
qr = RQRCode::QRCode.new('TEST-LABEL-PRINTSERVER')
|
|
modules = qr.modules
|
|
quiet = 4
|
|
qsize = modules.size + quiet * 2
|
|
qr_img = pack_for_ps_image(qsize, qsize) do |x, y|
|
|
mx, my = x - quiet, y - quiet
|
|
next 0 if mx.negative? || my.negative? || mx >= modules.size || my >= modules.size
|
|
|
|
modules[my][mx] ? 1 : 0
|
|
end
|
|
|
|
# physical sizes on the A6 (298x420pt) page
|
|
barcode_pt_w = 220
|
|
barcode_pt_h = barcode_pt_w * bh / bw
|
|
qr_pt = 100
|
|
|
|
ps = <<~PS
|
|
%!PS
|
|
<< /PageSize [298 420] >> setpagedevice
|
|
1 setlinewidth
|
|
10 10 278 400 rectstroke
|
|
/Helvetica-Bold findfont 24 scalefont setfont
|
|
30 380 moveto (TEST LABEL) show
|
|
/Helvetica findfont 9 scalefont setfont
|
|
30 362 moveto (Generic print-quality test page - safe to reprint anytime) show
|
|
/Helvetica-Bold findfont 13 scalefont setfont
|
|
30 330 moveto (Odosielatel: Test Sender, BA) show
|
|
30 312 moveto (Adresat: Test Recipient, KE) show
|
|
/Helvetica findfont 8 scalefont setfont
|
|
30 292 moveto (Fine print 8pt: quick brown fox 0123456789) show
|
|
#{ps_image_block(30, 210, barcode_pt_w, barcode_pt_h, barcode_img)}
|
|
/Helvetica findfont 8 scalefont setfont
|
|
30 198 moveto (1D: reused known-good Code128 from a real courier label) show
|
|
#{ps_image_block(30, 60, qr_pt, qr_pt, qr_img)}
|
|
/Helvetica findfont 8 scalefont setfont
|
|
140 100 moveto (QR: TEST-LABEL-PRINTSERVER) show
|
|
140 88 moveto (rendered natively via rqrcode) show
|
|
showpage
|
|
PS
|
|
|
|
File.write(OUT_PS, ps)
|
|
puts "wrote #{OUT_PS}"
|
|
end
|