USPS-intelligent-barcode 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/Gemfile.lock +33 -0
- data/LICENSE.md +11 -0
- data/README.md +56 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/examples/example.rb +17 -0
- data/lib/USPS-intelligent-barcode.rb +5 -0
- data/lib/USPS-intelligent-barcode/BarMap.rb +40 -0
- data/lib/USPS-intelligent-barcode/BarPosition.rb +26 -0
- data/lib/USPS-intelligent-barcode/Barcode.rb +110 -0
- data/lib/USPS-intelligent-barcode/BarcodeId.rb +66 -0
- data/lib/USPS-intelligent-barcode/CharacterPosition.rb +16 -0
- data/lib/USPS-intelligent-barcode/CodewordMap.rb +28 -0
- data/lib/USPS-intelligent-barcode/Crc.rb +42 -0
- data/lib/USPS-intelligent-barcode/MailerId.rb +70 -0
- data/lib/USPS-intelligent-barcode/NumericConversions.rb +13 -0
- data/lib/USPS-intelligent-barcode/RoutingCode.rb +77 -0
- data/lib/USPS-intelligent-barcode/SerialNumber.rb +61 -0
- data/lib/USPS-intelligent-barcode/ServiceType.rb +51 -0
- data/lib/USPS-intelligent-barcode/autoload.rb +61 -0
- data/lib/USPS-intelligent-barcode/bar_to_character_mapping.yml +66 -0
- data/lib/USPS-intelligent-barcode/codeword_to_character_mapping.yml +1366 -0
- data/spec/BarMap_spec.rb +30 -0
- data/spec/BarPosition_spec.rb +52 -0
- data/spec/BarcodeId_spec.rb +118 -0
- data/spec/Barcode_spec.rb +213 -0
- data/spec/CharacterPosition_spec.rb +25 -0
- data/spec/CodewordMap_spec.rb +22 -0
- data/spec/Crc_spec.rb +21 -0
- data/spec/MailerId_spec.rb +114 -0
- data/spec/NumericConversions_spec.rb +23 -0
- data/spec/RoutingCode_spec.rb +180 -0
- data/spec/SerialNumber_spec.rb +117 -0
- data/spec/ServiceType_spec.rb +93 -0
- data/spec/spec_helper.rb +1 -0
- metadata +160 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
andand (1.3.3)
|
5
|
+
diff-lcs (1.1.3)
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.8.4)
|
8
|
+
bundler (~> 1.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
rdoc
|
12
|
+
json (1.7.5)
|
13
|
+
memoizer (1.0.1)
|
14
|
+
rake (10.0.3)
|
15
|
+
rdoc (3.12)
|
16
|
+
json (~> 1.4)
|
17
|
+
rspec (2.12.0)
|
18
|
+
rspec-core (~> 2.12.0)
|
19
|
+
rspec-expectations (~> 2.12.0)
|
20
|
+
rspec-mocks (~> 2.12.0)
|
21
|
+
rspec-core (2.12.2)
|
22
|
+
rspec-expectations (2.12.1)
|
23
|
+
diff-lcs (~> 1.1.3)
|
24
|
+
rspec-mocks (2.12.1)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
andand (~> 1.3)
|
31
|
+
jeweler (~> 1.8)
|
32
|
+
memoizer (~> 1.0)
|
33
|
+
rspec (~> 2.12)
|
data/LICENSE.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Copyright 2012 Ryan Taylor Long
|
2
|
+
Copyright 2012 Wayne Conrad
|
3
|
+
|
4
|
+
This software is distributed under the [MIT License](http://opensource.org/licenses/MIT):
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
7
|
+
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
9
|
+
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
11
|
+
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# USPS-intelligent-barcode
|
2
|
+
|
3
|
+
USPS-intelligent-barcode is a pure ruby gem to generate a USPS
|
4
|
+
Intelligent Mail Barcode. More specifically, it generates the string
|
5
|
+
of characters you should print using one of the [USPS Intelligent
|
6
|
+
Barcode fonts](https://ribbs.usps.gov/onecodesolution/download.cfm).
|
7
|
+
|
8
|
+
## FORKED FROM
|
9
|
+
|
10
|
+
This project was forked from :
|
11
|
+
|
12
|
+
[rtlong/USPS-intellient-barcode](https://github.com/rtlong/USPS-intelligent-barcode)
|
13
|
+
(github) by Ryan Taylor, in order to add tests and refactor. It is
|
14
|
+
_not_ a drop-in replacement: I renamed most methods and classes, and
|
15
|
+
eliminated the #draw method.
|
16
|
+
|
17
|
+
## INSTALL
|
18
|
+
|
19
|
+
$ gem install USPS-intelligent-barcode
|
20
|
+
|
21
|
+
## EXAMPLE
|
22
|
+
|
23
|
+
#!/usr/bin/env ruby
|
24
|
+
|
25
|
+
require 'imb'
|
26
|
+
|
27
|
+
barcode_id = '01'
|
28
|
+
service_type = '234'
|
29
|
+
mailer_id = '567094'
|
30
|
+
serial_number = '987654321'
|
31
|
+
routing_code = '01234567891'
|
32
|
+
barcode = Imb::Barcode.new(barcode_id,
|
33
|
+
service_type,
|
34
|
+
mailer_id,
|
35
|
+
serial_number,
|
36
|
+
routing_code)
|
37
|
+
p barcode.barcode_letters
|
38
|
+
# => "AADTFFDFTDADTAADAATFDTDDAAADDTDTTDAFADADDDTFFFDDTTTADFAAADFTDAADA"
|
39
|
+
|
40
|
+
## STANDARD
|
41
|
+
|
42
|
+
This gem is based upon standard
|
43
|
+
[USPS-B-3200G](https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf)
|
44
|
+
|
45
|
+
## RUBY VERSIONS
|
46
|
+
|
47
|
+
The tests are known to pass in MRI 1.8.7 and MRI 1.9.3
|
48
|
+
|
49
|
+
## WHOAMI
|
50
|
+
|
51
|
+
Wayne Conrad <wconrad@yagni.com>
|
52
|
+
|
53
|
+
## CREDITS
|
54
|
+
|
55
|
+
Thanks to Ryan Taylor for his original work, without which I would
|
56
|
+
have been lost in the USPS specification.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
require 'bundler'
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
require 'rake'
|
14
|
+
|
15
|
+
require 'jeweler'
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
18
|
+
gem.name = "USPS-intelligent-barcode"
|
19
|
+
gem.homepage = "http://github.com/wconrad/USPS-intelligent-barcode"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{Generates a USPS Intelligent Mail Barcode.}
|
22
|
+
gem.description =
|
23
|
+
("A pure Ruby gem to generate a USPS Intelligent "\
|
24
|
+
"Mail barcode. It generates the string of characters "\
|
25
|
+
"to print with one of the USPS Intelligent Mail barcode "\
|
26
|
+
"fonts.")
|
27
|
+
gem.email = "wayne@databill.com"
|
28
|
+
gem.authors = ["Wayne Conrad"]
|
29
|
+
# dependencies defined in Gemfile
|
30
|
+
end
|
31
|
+
Jeweler::RubygemsDotOrgTasks.new
|
32
|
+
|
33
|
+
require 'rspec/core/rake_task'
|
34
|
+
RSpec::Core::RakeTask.new(:spec)
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rdoc/task'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "USPS-intelligent-barcode #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/examples/example.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'USPS-intelligent-barcode'
|
5
|
+
|
6
|
+
barcode_id = '01'
|
7
|
+
service_type = '234'
|
8
|
+
mailer_id = '567094'
|
9
|
+
serial_number = '987654321'
|
10
|
+
routing_code = '01234567891'
|
11
|
+
barcode = Imb::Barcode.new(barcode_id,
|
12
|
+
service_type,
|
13
|
+
mailer_id,
|
14
|
+
serial_number,
|
15
|
+
routing_code)
|
16
|
+
p barcode.barcode_letters
|
17
|
+
# => "AADTFFDFTDADTAADAATFDTDDAAADDTDTTDAFADADDDTFFFDDTTTADFAAADFTDAADA"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Imb
|
2
|
+
|
3
|
+
class BarMap
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@mapping = load_mapping
|
7
|
+
end
|
8
|
+
|
9
|
+
def barcode(characters)
|
10
|
+
@mapping.map do |bar_position|
|
11
|
+
bar_position.map(characters)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def load_mapping
|
18
|
+
convert_mapping_data(load_mapping_data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def convert_mapping_data(mapping_data)
|
22
|
+
mapping_data.map do |descender, ascender|
|
23
|
+
descender_character_position = CharacterPosition.new(*descender)
|
24
|
+
ascender_character_position = CharacterPosition.new(*ascender)
|
25
|
+
BarPosition.new(descender_character_position,
|
26
|
+
ascender_character_position)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_mapping_data
|
31
|
+
YAML.load_file(mapping_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def mapping_path
|
35
|
+
File.expand_path('bar_to_character_mapping.yml', File.dirname(__FILE__))
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Imb
|
2
|
+
|
3
|
+
class BarPosition
|
4
|
+
|
5
|
+
def initialize(descender_character_position, ascender_character_position)
|
6
|
+
@descender_character_position = descender_character_position
|
7
|
+
@ascender_character_position = ascender_character_position
|
8
|
+
end
|
9
|
+
|
10
|
+
def map(characters)
|
11
|
+
2 * ascender_bit(characters) + descender_bit(characters)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def descender_bit(characters)
|
17
|
+
@descender_character_position.extract_bit_from_characters(characters)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ascender_bit(characters)
|
21
|
+
@ascender_character_position.extract_bit_from_characters(characters)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Imb
|
2
|
+
|
3
|
+
class Barcode
|
4
|
+
|
5
|
+
include Memoizer
|
6
|
+
|
7
|
+
attr_reader :barcode_id
|
8
|
+
attr_reader :service_type
|
9
|
+
attr_reader :mailer_id
|
10
|
+
attr_reader :serial_number
|
11
|
+
attr_reader :routing_code
|
12
|
+
|
13
|
+
def initialize(barcode_id,
|
14
|
+
service_type,
|
15
|
+
mailer_id,
|
16
|
+
serial_number,
|
17
|
+
routing_code)
|
18
|
+
@barcode_id = BarcodeId.coerce(barcode_id)
|
19
|
+
@service_type = ServiceType.coerce(service_type)
|
20
|
+
@mailer_id = MailerId.coerce(mailer_id)
|
21
|
+
@serial_number = SerialNumber.coerce(serial_number)
|
22
|
+
@routing_code = RoutingCode.coerce(routing_code)
|
23
|
+
validate_components
|
24
|
+
end
|
25
|
+
|
26
|
+
def barcode_letters
|
27
|
+
barcode.map { |bar| "TDAF"[bar..bar] }.join
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
BAR_MAP = BarMap.new
|
33
|
+
CODEWORD_MAP = CodewordMap.new
|
34
|
+
|
35
|
+
def validate_components
|
36
|
+
components.each do |component|
|
37
|
+
component.validate(long_mailer_id?)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def components
|
42
|
+
[
|
43
|
+
@routing_code,
|
44
|
+
@barcode_id,
|
45
|
+
@service_type,
|
46
|
+
@mailer_id,
|
47
|
+
@serial_number,
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def long_mailer_id?
|
52
|
+
@mailer_id.long?
|
53
|
+
end
|
54
|
+
|
55
|
+
def binary_data
|
56
|
+
components.inject(0) do |data, component|
|
57
|
+
component.shift_and_add_to(data, long_mailer_id?)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
memoize :binary_data
|
61
|
+
|
62
|
+
def frame_check_sequence
|
63
|
+
Crc.crc(binary_data)
|
64
|
+
end
|
65
|
+
memoize :frame_check_sequence
|
66
|
+
|
67
|
+
def codewords
|
68
|
+
codewords = []
|
69
|
+
data = binary_data
|
70
|
+
data, codewords[9] = data.divmod 636
|
71
|
+
8.downto(0) do |i|
|
72
|
+
data, codewords[i] = data.divmod 1365
|
73
|
+
end
|
74
|
+
codewords
|
75
|
+
end
|
76
|
+
memoize :codewords
|
77
|
+
|
78
|
+
def codewords_with_orientation_in_character_j
|
79
|
+
result = codewords.dup
|
80
|
+
result[9] *= 2
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
def codewords_with_fcs_bit_in_character_a
|
85
|
+
result = codewords_with_orientation_in_character_j.dup
|
86
|
+
result[0] += 659 if frame_check_sequence[10] == 1
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
def characters
|
91
|
+
CODEWORD_MAP.characters(codewords_with_fcs_bit_in_character_a)
|
92
|
+
end
|
93
|
+
|
94
|
+
def characters_with_fcs_bits_0_through_9
|
95
|
+
characters.each_with_index.map do |character, i|
|
96
|
+
if frame_check_sequence[i] == 1
|
97
|
+
character ^ 0b1111111111111
|
98
|
+
else
|
99
|
+
character
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def barcode
|
105
|
+
BAR_MAP.barcode(characters_with_fcs_bits_0_through_9)
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Imb
|
2
|
+
|
3
|
+
class BarcodeId
|
4
|
+
|
5
|
+
def self.coerce(o)
|
6
|
+
case o
|
7
|
+
when BarcodeId
|
8
|
+
o
|
9
|
+
when String
|
10
|
+
new(o.to_i)
|
11
|
+
when Integer
|
12
|
+
new(o)
|
13
|
+
else
|
14
|
+
raise ArgumentError, 'Cannot coerce to BarcodeId'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(value)
|
19
|
+
@value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate(long_mailer_id)
|
23
|
+
unless RANGE === @value
|
24
|
+
raise ArgumentError, "Must be #{RANGE}"
|
25
|
+
end
|
26
|
+
unless LSB_RANGE === least_significant_digit
|
27
|
+
raise ArgumentError, "Least significant digit must be #{LSB_RANGE}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(o)
|
32
|
+
BarcodeId.coerce(o).to_i == to_i
|
33
|
+
rescue ArgumentError
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def most_significant_digit
|
38
|
+
@value / 10
|
39
|
+
end
|
40
|
+
|
41
|
+
def least_significant_digit
|
42
|
+
@value % 10
|
43
|
+
end
|
44
|
+
|
45
|
+
def shift_and_add_to(target, long_mailer_id)
|
46
|
+
target *= 10
|
47
|
+
target += most_significant_digit
|
48
|
+
target *= 5
|
49
|
+
target += least_significant_digit
|
50
|
+
target
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def to_i
|
56
|
+
@value
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
RANGE = 0..94
|
62
|
+
LSB_RANGE = 0..4
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Imb
|
2
|
+
|
3
|
+
class CharacterPosition
|
4
|
+
|
5
|
+
def initialize(character_index, bit_number)
|
6
|
+
@character_index = character_index
|
7
|
+
@bit_number = bit_number
|
8
|
+
end
|
9
|
+
|
10
|
+
def extract_bit_from_characters(characters)
|
11
|
+
characters[@character_index][@bit_number]
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|