zpl_render 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +49 -0
- data/LICENSE.txt +21 -0
- data/README.md +145 -0
- data/exe/zpl_render +62 -0
- data/lib/zpl_render/barcode/code128.rb +332 -0
- data/lib/zpl_render/barcode/code39.rb +53 -0
- data/lib/zpl_render/barcode/interleaved2of5.rb +42 -0
- data/lib/zpl_render/barcode/pdf417.rb +201 -0
- data/lib/zpl_render/barcode/pdf417_tables.rb +354 -0
- data/lib/zpl_render/barcode/qr.rb +50 -0
- data/lib/zpl_render/canvas.rb +112 -0
- data/lib/zpl_render/font.rb +210 -0
- data/lib/zpl_render/font_data.rb +153 -0
- data/lib/zpl_render/fonts/LICENSE-LiberationSans.txt +102 -0
- data/lib/zpl_render/fonts/LiberationMono-Regular.ttf +0 -0
- data/lib/zpl_render/fonts/LiberationSans-Bold.ttf +0 -0
- data/lib/zpl_render/graphic_field.rb +112 -0
- data/lib/zpl_render/helvetica_metrics.rb +17 -0
- data/lib/zpl_render/output/pdf.rb +39 -0
- data/lib/zpl_render/output/png.rb +35 -0
- data/lib/zpl_render/parser.rb +90 -0
- data/lib/zpl_render/renderer.rb +627 -0
- data/lib/zpl_render/truetype.rb +428 -0
- data/lib/zpl_render/version.rb +5 -0
- data/lib/zpl_render.rb +71 -0
- metadata +127 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ZplRender
|
|
4
|
+
# Helvetica advance widths (1/1000 em) for ASCII 32-126, from the
|
|
5
|
+
# Adobe Core 14 AFM metrics. Used to approximate Zebra's scalable
|
|
6
|
+
# CG Triumvirate (font 0), which shares Helvetica's metrics closely.
|
|
7
|
+
HELVETICA_WIDTHS = [
|
|
8
|
+
278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584,
|
|
9
|
+
278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,
|
|
10
|
+
556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722,
|
|
11
|
+
722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
|
|
12
|
+
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278,
|
|
13
|
+
278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556,
|
|
14
|
+
556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500,
|
|
15
|
+
278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584
|
|
16
|
+
].freeze
|
|
17
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prawn"
|
|
4
|
+
require "stringio"
|
|
5
|
+
|
|
6
|
+
module ZplRender
|
|
7
|
+
module Output
|
|
8
|
+
module Pdf
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# Encode one page per label Canvas. Each page has the label's exact
|
|
12
|
+
# physical size (canvas dots / dpmm), and the label raster is placed
|
|
13
|
+
# 1:1 so barcode modules stay sharp when printed.
|
|
14
|
+
def encode(canvases, dpmm:)
|
|
15
|
+
raise ArgumentError, "no labels to render" if canvases.empty?
|
|
16
|
+
|
|
17
|
+
first = canvases.first
|
|
18
|
+
doc = Prawn::Document.new(
|
|
19
|
+
page_size: page_size(first, dpmm),
|
|
20
|
+
margin: 0,
|
|
21
|
+
skip_page_creation: false,
|
|
22
|
+
info: { Creator: "zpl_render #{ZplRender::VERSION}" }
|
|
23
|
+
)
|
|
24
|
+
canvases.each_with_index do |canvas, i|
|
|
25
|
+
doc.start_new_page(size: page_size(canvas, dpmm), margin: 0) if i.positive?
|
|
26
|
+
png = Png.encode(canvas)
|
|
27
|
+
doc.image StringIO.new(png), at: [0, doc.bounds.top],
|
|
28
|
+
width: doc.bounds.width, height: doc.bounds.height
|
|
29
|
+
end
|
|
30
|
+
doc.render
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def page_size(canvas, dpmm)
|
|
34
|
+
# dots -> mm -> points (72 pt / inch, 25.4 mm / inch)
|
|
35
|
+
[canvas.width / dpmm.to_f / 25.4 * 72.0, canvas.height / dpmm.to_f / 25.4 * 72.0]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "chunky_png"
|
|
4
|
+
|
|
5
|
+
module ZplRender
|
|
6
|
+
module Output
|
|
7
|
+
module Png
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Encode a Canvas as a PNG binary string. +scale+ is an integer
|
|
11
|
+
# nearest-neighbor upscale (keeps barcode modules pixel-exact).
|
|
12
|
+
def encode(canvas, scale: 1)
|
|
13
|
+
scale = [scale.to_i, 1].max
|
|
14
|
+
image = ChunkyPNG::Image.new(canvas.width * scale, canvas.height * scale, ChunkyPNG::Color::WHITE)
|
|
15
|
+
black = ChunkyPNG::Color::BLACK
|
|
16
|
+
(0...canvas.height).each do |y|
|
|
17
|
+
(0...canvas.width).each do |x|
|
|
18
|
+
next unless canvas.get(x, y)
|
|
19
|
+
|
|
20
|
+
if scale == 1
|
|
21
|
+
image[x, y] = black
|
|
22
|
+
else
|
|
23
|
+
(0...scale).each do |dy|
|
|
24
|
+
(0...scale).each do |dx|
|
|
25
|
+
image[x * scale + dx, y * scale + dy] = black
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
image.to_blob(:fast_rgb)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ZplRender
|
|
4
|
+
# Tokenizes a ZPL II stream into [command, params] pairs.
|
|
5
|
+
#
|
|
6
|
+
# Commands are introduced by the format prefix (^, changeable via ^CC/~CC)
|
|
7
|
+
# or the control prefix (~, changeable via ^CT/~CT). The command code is
|
|
8
|
+
# two characters, except ^A<f> where the second character is the font
|
|
9
|
+
# designator (^A@ remains its own command). Parameters run until the next
|
|
10
|
+
# prefix character.
|
|
11
|
+
class Parser
|
|
12
|
+
Command = Struct.new(:name, :params, :prefix, keyword_init: true) do
|
|
13
|
+
# Split comma-separated parameters, preserving empty slots.
|
|
14
|
+
def args
|
|
15
|
+
@args ||= params.split(",", -1).map(&:strip)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.parse(zpl)
|
|
20
|
+
new(zpl).parse
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(zpl)
|
|
24
|
+
@zpl = zpl.to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parse
|
|
28
|
+
commands = []
|
|
29
|
+
caret = "^"
|
|
30
|
+
tilde = "~"
|
|
31
|
+
i = 0
|
|
32
|
+
len = @zpl.length
|
|
33
|
+
|
|
34
|
+
while i < len
|
|
35
|
+
ch = @zpl[i]
|
|
36
|
+
unless ch == caret || ch == tilde
|
|
37
|
+
i += 1
|
|
38
|
+
next
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
prefix = ch == caret ? :caret : :tilde
|
|
42
|
+
code = @zpl[i + 1, 2].to_s
|
|
43
|
+
if code.empty?
|
|
44
|
+
i += 1
|
|
45
|
+
next
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
name = nil
|
|
49
|
+
font = nil
|
|
50
|
+
if code[0]&.upcase == "A" && code != "A@" && prefix == :caret
|
|
51
|
+
name = "A"
|
|
52
|
+
font = code[1]
|
|
53
|
+
else
|
|
54
|
+
name = code.upcase
|
|
55
|
+
end
|
|
56
|
+
i += 3 # prefix + two command characters (^A consumes its font char)
|
|
57
|
+
|
|
58
|
+
# ^CC/~CC and ^CT/~CT take effect immediately: their parameter is a
|
|
59
|
+
# single character that becomes the new prefix.
|
|
60
|
+
if name == "CC" || name == "CT"
|
|
61
|
+
new_prefix = @zpl[i]
|
|
62
|
+
if new_prefix
|
|
63
|
+
caret = new_prefix if name == "CC"
|
|
64
|
+
tilde = new_prefix if name == "CT"
|
|
65
|
+
i += 1
|
|
66
|
+
end
|
|
67
|
+
next
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# parameters: everything until the next prefix character
|
|
71
|
+
j = i
|
|
72
|
+
j += 1 while j < len && @zpl[j] != caret && @zpl[j] != tilde
|
|
73
|
+
params = @zpl[i...j].to_s
|
|
74
|
+
|
|
75
|
+
if name == "A"
|
|
76
|
+
params = "#{font}#{params}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# CR/LF are ignorable in ZPL streams
|
|
80
|
+
params = params.delete("\r\n") unless %w[GF DG DB].include?(name)
|
|
81
|
+
params = params.strip if %w[GF DG DB].include?(name)
|
|
82
|
+
|
|
83
|
+
commands << Command.new(name: name, params: params, prefix: prefix)
|
|
84
|
+
i = j
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
commands
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|