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.
@@ -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
@@ -0,0 +1,5 @@
1
+ module Zebra
2
+ module Zpl
3
+ VERSION = "1.0.0"
4
+ end
5
+ 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"
@@ -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