zebra-zpl 1.0.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/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE +21 -0
- data/LICENSE_ORIGINAL.txt +22 -0
- data/README.md +180 -0
- data/Rakefile +1 -0
- data/lib/zebra/print_job.rb +35 -0
- data/lib/zebra/zpl/barcode.rb +55 -0
- data/lib/zebra/zpl/barcode_type.rb +34 -0
- data/lib/zebra/zpl/box.rb +50 -0
- data/lib/zebra/zpl/character_set.rb +45 -0
- data/lib/zebra/zpl/country_code.rb +36 -0
- data/lib/zebra/zpl/font.rb +47 -0
- data/lib/zebra/zpl/justification.rb +21 -0
- data/lib/zebra/zpl/label.rb +97 -0
- data/lib/zebra/zpl/language.rb +76 -0
- data/lib/zebra/zpl/multipliers.rb +42 -0
- data/lib/zebra/zpl/print_mode.rb +18 -0
- data/lib/zebra/zpl/printable.rb +58 -0
- data/lib/zebra/zpl/qrcode.rb +37 -0
- data/lib/zebra/zpl/rotation.rb +20 -0
- data/lib/zebra/zpl/text.rb +80 -0
- data/lib/zebra/zpl/version.rb +5 -0
- data/lib/zebra/zpl.rb +18 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/zebra/print_job_spec.rb +36 -0
- data/spec/zebra/zpl/barcode_spec.rb +196 -0
- data/spec/zebra/zpl/box_spec.rb +71 -0
- data/spec/zebra/zpl/character_set_spec.rb +100 -0
- data/spec/zebra/zpl/label_spec.rb +138 -0
- data/spec/zebra/zpl/qrcode_spec.rb +101 -0
- data/spec/zebra/zpl/text_spec.rb +139 -0
- data/spec/zebra/zpl/zpl_spec.rb +5 -0
- data/zebra-zpl.gemspec +28 -0
- metadata +174 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Zebra
|
4
|
+
module Zpl
|
5
|
+
class Label
|
6
|
+
class InvalidPrintSpeedError < StandardError; end
|
7
|
+
class InvalidPrintDensityError < StandardError; end
|
8
|
+
class PrintSpeedNotInformedError < StandardError; end
|
9
|
+
|
10
|
+
attr_writer :copies
|
11
|
+
attr_reader :elements, :tempfile
|
12
|
+
attr_accessor :width, :length, :gap, :print_speed, :print_density
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
options.each_pair { |key, value| self.__send__("#{key}=", value) if self.respond_to?("#{key}=") }
|
16
|
+
@elements = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def length_and_gap=(length_and_gap)
|
20
|
+
self.length = length_and_gap[0]
|
21
|
+
self.gap = length_and_gap[1]
|
22
|
+
end
|
23
|
+
|
24
|
+
def print_speed=(s)
|
25
|
+
raise InvalidPrintSpeedError unless (0..6).include?(s)
|
26
|
+
@print_speed = s
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_density=(d)
|
30
|
+
raise InvalidPrintDensityError unless (0..6).include?(d)
|
31
|
+
@print_density = d
|
32
|
+
end
|
33
|
+
|
34
|
+
def copies
|
35
|
+
@copies || 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def <<(element)
|
39
|
+
element.width = self.width
|
40
|
+
elements << element
|
41
|
+
end
|
42
|
+
|
43
|
+
def dump_contents(io = STDOUT)
|
44
|
+
check_required_configurations
|
45
|
+
# Start format
|
46
|
+
io << "^XA"
|
47
|
+
# ^LL<label height in dots>,<space between labels in dots>
|
48
|
+
# io << "^LL#{length},#{gap}\n" if length && gap
|
49
|
+
io << "^LL#{length}" if length
|
50
|
+
# ^LH<label home - x,y coordinates of top left label>
|
51
|
+
io << "^LH0,0"
|
52
|
+
# ^LS<shift the label to the left(or right)>
|
53
|
+
io << "^LS10"
|
54
|
+
# ^PW<label width in dots>
|
55
|
+
io << "^PW#{width}" if width
|
56
|
+
# Print Rate(speed) (^PR command)
|
57
|
+
io << "^PR#{print_speed}"
|
58
|
+
# Density (D command) "Carried over from EPL, does this exist in ZPL ????"
|
59
|
+
# io << "D#{print_density}\n" if print_density
|
60
|
+
|
61
|
+
# TEST ZPL (comment everything else out)...
|
62
|
+
# io << "^XA^WD*:*.FNT*^XZ"
|
63
|
+
# TEST ZPL SEGMENT
|
64
|
+
# io << "^WD*:*.FNT*"
|
65
|
+
# TEST AND GET CONFIGS
|
66
|
+
# io << "^HH"
|
67
|
+
|
68
|
+
elements.each do |element|
|
69
|
+
io << element.to_zpl
|
70
|
+
end
|
71
|
+
# Specify how many copies to print
|
72
|
+
io << "^PQ#{copies}"
|
73
|
+
# End format
|
74
|
+
io << "^XZ"
|
75
|
+
end
|
76
|
+
|
77
|
+
def persist
|
78
|
+
# debugger
|
79
|
+
tempfile = Tempfile.new "zebra_label"
|
80
|
+
dump_contents tempfile
|
81
|
+
tempfile.close
|
82
|
+
@tempfile = tempfile
|
83
|
+
tempfile
|
84
|
+
end
|
85
|
+
|
86
|
+
def persisted?
|
87
|
+
!!self.tempfile
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def check_required_configurations
|
93
|
+
raise PrintSpeedNotInformedError unless print_speed
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Zebra
|
3
|
+
module Zpl
|
4
|
+
class Language
|
5
|
+
class InvalidLanguageError < StandardError; end
|
6
|
+
class InvalidLanguageForNumberOfDataBitsError < StandardError; end
|
7
|
+
|
8
|
+
# 8 bits languages
|
9
|
+
ENGLISH_US = "0"
|
10
|
+
LATIN_1 = "1"
|
11
|
+
LATIN_2 = "2"
|
12
|
+
PORTUGUESE = "3"
|
13
|
+
FRENCH_CANADIAN = "4"
|
14
|
+
NORDIC = "5"
|
15
|
+
TURKISH = "6"
|
16
|
+
ICELANDIC = "7"
|
17
|
+
HEBREW = "8"
|
18
|
+
CYRILLIC = "9"
|
19
|
+
CYRILLIC_CIS_1 = "10"
|
20
|
+
GREEK = "11"
|
21
|
+
GREEK_1 = "12"
|
22
|
+
GREEK_2 = "13"
|
23
|
+
LATIN_1_WINDOWS = "A"
|
24
|
+
LATIN_2_WINDOWS = "B"
|
25
|
+
CYRILLIC_WINDOWS = "C"
|
26
|
+
GREEK_WINDOWS = "D"
|
27
|
+
TURKISH_WINDOWS = "E"
|
28
|
+
HEBREW_WINDOWS = "F"
|
29
|
+
|
30
|
+
# 7 bits languages
|
31
|
+
USA = "0"
|
32
|
+
BRITISH = "1"
|
33
|
+
GERMAN = "2"
|
34
|
+
FRENCH = "3"
|
35
|
+
DANISH = "4"
|
36
|
+
ITALIAN = "5"
|
37
|
+
SPANISH = "6"
|
38
|
+
SWEDISH = "7"
|
39
|
+
SWISS = "8"
|
40
|
+
|
41
|
+
def self.valid_language?(language)
|
42
|
+
("0".."13").include?(language) || ("A".."F").include?(language)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.validate_language(language)
|
46
|
+
raise InvalidLanguageError unless valid_language?(language)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.validate_language_for_number_of_data_bits(language, number_of_data_bits)
|
50
|
+
if number_of_data_bits == 8
|
51
|
+
validate_8_data_bits_language language
|
52
|
+
elsif number_of_data_bits == 7
|
53
|
+
validate_7_data_bits_language language
|
54
|
+
else
|
55
|
+
raise ArgumentError.new("Unknown number of data bits")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def self.validate_8_data_bits_language(language)
|
62
|
+
raise InvalidLanguageForNumberOfDataBitsError unless [ENGLISH_US,
|
63
|
+
LATIN_1, LATIN_2, PORTUGUESE, FRENCH_CANADIAN, NORDIC,
|
64
|
+
TURKISH, ICELANDIC, HEBREW, CYRILLIC, CYRILLIC_CIS_1, GREEK,
|
65
|
+
GREEK_1, GREEK_2, LATIN_1_WINDOWS, LATIN_2_WINDOWS, CYRILLIC_WINDOWS,
|
66
|
+
GREEK_WINDOWS, TURKISH_WINDOWS, HEBREW_WINDOWS].include?(language)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.validate_7_data_bits_language(language)
|
70
|
+
raise InvalidLanguageForNumberOfDataBitsError unless [USA, BRITISH,
|
71
|
+
GERMAN, FRENCH, DANISH, ITALIAN, SPANISH, SWEDISH, SWISS].include?(language)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Zebra
|
2
|
+
module Zpl
|
3
|
+
module BaseMultiplier
|
4
|
+
class InvalidMultiplierError < StandardError; end
|
5
|
+
|
6
|
+
VALUE_1 = 1
|
7
|
+
VALUE_2 = 2
|
8
|
+
VALUE_3 = 3
|
9
|
+
VALUE_4 = 4
|
10
|
+
VALUE_5 = 5
|
11
|
+
VALUE_6 = 6
|
12
|
+
VALUE_7 = 7
|
13
|
+
VALUE_8 = 8
|
14
|
+
|
15
|
+
def self.included(base_module)
|
16
|
+
base_module.instance_eval do
|
17
|
+
def validate_multiplier(multiplier)
|
18
|
+
raise InvalidMultiplierError unless valid_multiplier?(multiplier)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module HorizontalMultiplier
|
25
|
+
include BaseMultiplier
|
26
|
+
|
27
|
+
def self.valid_multiplier?(multiplier)
|
28
|
+
(1..8).include? multiplier
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module VerticalMultiplier
|
33
|
+
include BaseMultiplier
|
34
|
+
|
35
|
+
VALUE_9 = 9
|
36
|
+
|
37
|
+
def self.valid_multiplier?(multiplier)
|
38
|
+
(1..9).include? multiplier
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Zebra
|
2
|
+
module Zpl
|
3
|
+
module PrintMode
|
4
|
+
class InvalidPrintModeError < StandardError; end
|
5
|
+
|
6
|
+
NORMAL = "N"
|
7
|
+
REVERSE = "R"
|
8
|
+
|
9
|
+
def self.valid_mode?(mode)
|
10
|
+
%w(N R).include? mode
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.validate_mode(mode)
|
14
|
+
raise InvalidPrintModeError unless valid_mode?(mode)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Zebra
|
2
|
+
module Zpl
|
3
|
+
module Printable
|
4
|
+
class MissingAttributeError < StandardError
|
5
|
+
def initialize(message)
|
6
|
+
super("Can't print if #{message}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :position, :x, :y, :margin
|
11
|
+
attr_accessor :data
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
options.each_pair { |attribute, value| self.__send__ "#{attribute}=", value }
|
15
|
+
end
|
16
|
+
|
17
|
+
def position=(coords)
|
18
|
+
@position = coords
|
19
|
+
@x = (@margin.nil? || @margin == 0) ? coords[0] : (@margin + coords[0])
|
20
|
+
@y = coords[1]
|
21
|
+
end
|
22
|
+
|
23
|
+
def justification=(just)
|
24
|
+
Justification.validate_justification just
|
25
|
+
@justification = just
|
26
|
+
end
|
27
|
+
|
28
|
+
def justification
|
29
|
+
@justification || Justification::LEFT
|
30
|
+
end
|
31
|
+
|
32
|
+
def margin=(margin)
|
33
|
+
@margin = margin || 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def rotation=(rot)
|
37
|
+
Rotation.validate_rotation rot
|
38
|
+
@rotation = rot
|
39
|
+
end
|
40
|
+
|
41
|
+
def rotation
|
42
|
+
@rotation || Rotation::NO_ROTATION
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def has_data?
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def check_attributes
|
52
|
+
raise MissingAttributeError.new("the X value is not given") unless @x
|
53
|
+
raise MissingAttributeError.new("the Y value is not given") unless @y
|
54
|
+
raise MissingAttributeError.new("the data to be printed is not given") unless @data || !has_data?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "zebra/zpl/printable"
|
2
|
+
|
3
|
+
module Zebra
|
4
|
+
module Zpl
|
5
|
+
class Qrcode
|
6
|
+
include Printable
|
7
|
+
|
8
|
+
class InvalidScaleFactorError < StandardError; end
|
9
|
+
class InvalidCorrectionLevelError < StandardError; end
|
10
|
+
|
11
|
+
attr_reader :scale_factor, :correction_level
|
12
|
+
|
13
|
+
def scale_factor=(value)
|
14
|
+
raise InvalidScaleFactorError unless (1..99).include?(value.to_i)
|
15
|
+
@scale_factor = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def correction_level=(value)
|
19
|
+
raise InvalidCorrectionLevelError unless %w[L M Q H].include?(value.to_s)
|
20
|
+
@correction_level = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_zpl
|
24
|
+
check_attributes
|
25
|
+
["b#{x}", y, "Q", "s#{scale_factor}", "e#{correction_level}", "\"#{data}\""].join(",")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def check_attributes
|
31
|
+
super
|
32
|
+
raise MissingAttributeError.new("the scale factor to be used is not given") unless @scale_factor
|
33
|
+
raise MissingAttributeError.new("the error correction level to be used is not given") unless @correction_level
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Zebra
|
2
|
+
module Zpl
|
3
|
+
module Rotation
|
4
|
+
class InvalidRotationError < StandardError; end
|
5
|
+
|
6
|
+
NO_ROTATION = 'N'
|
7
|
+
DEGREES_90 = 'R'
|
8
|
+
DEGREES_180 = 'I'
|
9
|
+
DEGREES_270 = 'B'
|
10
|
+
|
11
|
+
def self.valid_rotation?(rotation)
|
12
|
+
[NO_ROTATION, DEGREES_90, DEGREES_180, DEGREES_270].include? rotation
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.validate_rotation(rotation)
|
16
|
+
raise InvalidRotationError unless valid_rotation?(rotation)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "zebra/zpl/printable"
|
2
|
+
|
3
|
+
module Zebra
|
4
|
+
module Zpl
|
5
|
+
class Text
|
6
|
+
include Printable
|
7
|
+
|
8
|
+
attr_reader :font_size, :font_type, :width
|
9
|
+
|
10
|
+
def font_size=(f)
|
11
|
+
FontSize.validate_font_size f
|
12
|
+
@font_size = f
|
13
|
+
end
|
14
|
+
|
15
|
+
def width=(width)
|
16
|
+
unless (margin.nil? || margin < 1)
|
17
|
+
@width = (width - (margin*2))
|
18
|
+
else
|
19
|
+
@width = width || 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def font_type=(type)
|
24
|
+
FontType.validate_font_type type
|
25
|
+
@font_type = type
|
26
|
+
end
|
27
|
+
|
28
|
+
def font_type
|
29
|
+
@font_type || FontType::TYPE_0
|
30
|
+
end
|
31
|
+
|
32
|
+
def print_mode=(mode)
|
33
|
+
PrintMode.validate_mode mode
|
34
|
+
@print_mode = mode
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_mode
|
38
|
+
@print_mode || PrintMode::NORMAL
|
39
|
+
end
|
40
|
+
|
41
|
+
def h_multiplier
|
42
|
+
@h_multiplier || HorizontalMultiplier::VALUE_1
|
43
|
+
end
|
44
|
+
|
45
|
+
def v_multiplier
|
46
|
+
@v_multiplier || VerticalMultiplier::VALUE_1
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_mode
|
50
|
+
@print_mode || PrintMode::NORMAL
|
51
|
+
end
|
52
|
+
|
53
|
+
def h_multiplier=(multiplier)
|
54
|
+
HorizontalMultiplier.validate_multiplier multiplier
|
55
|
+
@h_multiplier = multiplier
|
56
|
+
end
|
57
|
+
|
58
|
+
def v_multiplier=(multiplier)
|
59
|
+
VerticalMultiplier.validate_multiplier multiplier
|
60
|
+
@v_multiplier = multiplier
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_zpl
|
64
|
+
check_attributes
|
65
|
+
# ["A#{x}", y, rotation, font_size, h_multiplier, v_multiplier, print_mode, "\"#{data}\""].join(",")
|
66
|
+
# "^FO25,25^FB600,100,0,C,0^FDFoo^FS"
|
67
|
+
|
68
|
+
# "^CF#{font_type},#{font_size}^FO#{x},#{y}^FB609,4,0,#{justification},0^FD#{data}^FS"
|
69
|
+
"^FW#{rotation}^CF#{font_type},#{font_size}^CI28^FO#{x},#{y}^FB#{width},4,0,#{justification},0^FD#{data}^FS"
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def check_attributes
|
75
|
+
super
|
76
|
+
raise MissingAttributeError.new("the font_size to be used is not given") unless @font_size
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/zebra/zpl.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "cups"
|
2
|
+
require "tempfile"
|
3
|
+
require "zebra/zpl/version"
|
4
|
+
require "zebra/zpl/language"
|
5
|
+
require "zebra/zpl/country_code"
|
6
|
+
require "zebra/zpl/character_set"
|
7
|
+
require "zebra/zpl/rotation"
|
8
|
+
require "zebra/zpl/multipliers"
|
9
|
+
require "zebra/zpl/print_mode"
|
10
|
+
require "zebra/zpl/font"
|
11
|
+
require "zebra/zpl/box"
|
12
|
+
require "zebra/zpl/label"
|
13
|
+
require "zebra/zpl/text"
|
14
|
+
require "zebra/zpl/barcode"
|
15
|
+
require "zebra/zpl/barcode_type"
|
16
|
+
require "zebra/print_job"
|
17
|
+
require "zebra/zpl/qrcode"
|
18
|
+
require "zebra/zpl/justification"
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require "zebra/zpl"
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
12
|
+
config.run_all_when_everything_filtered = true
|
13
|
+
config.filter_run :focus
|
14
|
+
|
15
|
+
# Run specs in random order to surface order dependencies. If you find an
|
16
|
+
# order dependency and want to debug it, you can fix the order by providing
|
17
|
+
# the seed, which is printed after each run.
|
18
|
+
# --seed 1234
|
19
|
+
config.order = 'random'
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zebra::PrintJob do
|
4
|
+
before do
|
5
|
+
Cups.stub(:show_destinations).and_return(["Zebra", "Foobar"])
|
6
|
+
end
|
7
|
+
|
8
|
+
it "receives the name of a printer" do
|
9
|
+
described_class.new("Zebra").printer.should == "Zebra"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises an error if the printer does not exists" do
|
13
|
+
expect {
|
14
|
+
described_class.new("Wrong")
|
15
|
+
}.to raise_error(Zebra::PrintJob::UnknownPrinter)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#print" do
|
19
|
+
let(:label) { stub :persist => tempfile }
|
20
|
+
let(:cups_job) { stub :print => true }
|
21
|
+
let(:tempfile) { stub(:path => "/foo/bar").as_null_object }
|
22
|
+
|
23
|
+
subject(:print_job) { described_class.new "Zebra" }
|
24
|
+
|
25
|
+
before { print_job.stub(:` => true) }
|
26
|
+
|
27
|
+
it "creates a cups print job with the correct arguments" do
|
28
|
+
print_job.print label
|
29
|
+
end
|
30
|
+
|
31
|
+
it "prints the label" do
|
32
|
+
print_job.should_receive(:`).with("lpr -P Zebra -o raw /foo/bar")
|
33
|
+
print_job.print label
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|