USPS-intelligent-barcode 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/Gemfile +9 -0
  2. data/Gemfile.lock +33 -0
  3. data/LICENSE.md +11 -0
  4. data/README.md +56 -0
  5. data/Rakefile +46 -0
  6. data/VERSION +1 -0
  7. data/examples/example.rb +17 -0
  8. data/lib/USPS-intelligent-barcode.rb +5 -0
  9. data/lib/USPS-intelligent-barcode/BarMap.rb +40 -0
  10. data/lib/USPS-intelligent-barcode/BarPosition.rb +26 -0
  11. data/lib/USPS-intelligent-barcode/Barcode.rb +110 -0
  12. data/lib/USPS-intelligent-barcode/BarcodeId.rb +66 -0
  13. data/lib/USPS-intelligent-barcode/CharacterPosition.rb +16 -0
  14. data/lib/USPS-intelligent-barcode/CodewordMap.rb +28 -0
  15. data/lib/USPS-intelligent-barcode/Crc.rb +42 -0
  16. data/lib/USPS-intelligent-barcode/MailerId.rb +70 -0
  17. data/lib/USPS-intelligent-barcode/NumericConversions.rb +13 -0
  18. data/lib/USPS-intelligent-barcode/RoutingCode.rb +77 -0
  19. data/lib/USPS-intelligent-barcode/SerialNumber.rb +61 -0
  20. data/lib/USPS-intelligent-barcode/ServiceType.rb +51 -0
  21. data/lib/USPS-intelligent-barcode/autoload.rb +61 -0
  22. data/lib/USPS-intelligent-barcode/bar_to_character_mapping.yml +66 -0
  23. data/lib/USPS-intelligent-barcode/codeword_to_character_mapping.yml +1366 -0
  24. data/spec/BarMap_spec.rb +30 -0
  25. data/spec/BarPosition_spec.rb +52 -0
  26. data/spec/BarcodeId_spec.rb +118 -0
  27. data/spec/Barcode_spec.rb +213 -0
  28. data/spec/CharacterPosition_spec.rb +25 -0
  29. data/spec/CodewordMap_spec.rb +22 -0
  30. data/spec/Crc_spec.rb +21 -0
  31. data/spec/MailerId_spec.rb +114 -0
  32. data/spec/NumericConversions_spec.rb +23 -0
  33. data/spec/RoutingCode_spec.rb +180 -0
  34. data/spec/SerialNumber_spec.rb +117 -0
  35. data/spec/ServiceType_spec.rb +93 -0
  36. data/spec/spec_helper.rb +1 -0
  37. metadata +160 -0
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'andand', '~> 1.3'
4
+ gem 'memoizer', '~> 1.0'
5
+
6
+ group :development do
7
+ gem 'rspec', '~> 2.12'
8
+ gem 'jeweler', '~> 1.8'
9
+ end
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
@@ -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,5 @@
1
+ require 'andand'
2
+ require 'memoizer'
3
+ require 'yaml'
4
+
5
+ require 'USPS-intelligent-barcode/autoload.rb'
@@ -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