usps_intelligent_barcode 0.3.1 → 1.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.
Files changed (60) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +6 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +2 -1
  5. data/{CHANGELOG.markdown → CHANGELOG.md} +27 -12
  6. data/Gemfile +2 -12
  7. data/Gemfile.lock +74 -81
  8. data/LICENSE.md +7 -0
  9. data/README.md +92 -0
  10. data/Rakefile +4 -28
  11. data/VERSION +1 -1
  12. data/doc/font_installation.md +55 -0
  13. data/doc/migration.md +47 -0
  14. data/doc/publishing.md +75 -0
  15. data/examples/example.rb +4 -4
  16. data/examples/generate_pdf.rb +70 -0
  17. data/fonts/LICENSE +47 -0
  18. data/fonts/USPSIMBCompact.ttf +0 -0
  19. data/fonts/USPSIMBStandard.ttf +0 -0
  20. data/lib/usps_intelligent_barcode/bar_map.rb +10 -8
  21. data/lib/usps_intelligent_barcode/bar_position.rb +14 -10
  22. data/lib/usps_intelligent_barcode/bar_symbol.rb +42 -12
  23. data/lib/usps_intelligent_barcode/bar_to_character_mapping.yml +24 -0
  24. data/lib/usps_intelligent_barcode/barcode.rb +83 -39
  25. data/lib/usps_intelligent_barcode/barcode_id.rb +19 -12
  26. data/lib/usps_intelligent_barcode/character_position.rb +10 -8
  27. data/lib/usps_intelligent_barcode/codeword_map.rb +0 -1
  28. data/lib/usps_intelligent_barcode/codeword_to_character_mapping.yml +7 -0
  29. data/lib/usps_intelligent_barcode/crc.rb +3 -3
  30. data/lib/usps_intelligent_barcode/mailer_id.rb +18 -14
  31. data/lib/usps_intelligent_barcode/numeric_conversions.rb +7 -6
  32. data/lib/usps_intelligent_barcode/project_dirs.rb +19 -0
  33. data/lib/usps_intelligent_barcode/routing_code.rb +38 -27
  34. data/lib/usps_intelligent_barcode/serial_number.rb +16 -11
  35. data/lib/usps_intelligent_barcode/service_type.rb +14 -12
  36. data/lib/usps_intelligent_barcode/usps_fonts.rb +48 -0
  37. data/lib/usps_intelligent_barcode.rb +2 -0
  38. data/rake/bundler.rb +1 -0
  39. data/rake/default.rb +1 -0
  40. data/rake/rspec.rb +2 -0
  41. data/rake/version.rb +33 -0
  42. data/rake/yard.rb +9 -0
  43. data/usps_intelligent_barcode.gemspec +24 -99
  44. metadata +68 -71
  45. data/README.markdown +0 -69
  46. data/USPS-intelligent-barcode.gemspec +0 -95
  47. data/spec/bar_map_spec.rb +0 -30
  48. data/spec/bar_position_spec.rb +0 -40
  49. data/spec/bar_symbol_spec.rb +0 -39
  50. data/spec/barcode_id_spec.rb +0 -106
  51. data/spec/barcode_spec.rb +0 -213
  52. data/spec/character_position_spec.rb +0 -25
  53. data/spec/codeword_map_spec.rb +0 -22
  54. data/spec/crc_spec.rb +0 -21
  55. data/spec/mailer_id_spec.rb +0 -124
  56. data/spec/numeric_conversions_spec.rb +0 -23
  57. data/spec/routing_code_spec.rb +0 -180
  58. data/spec/serial_number_spec.rb +0 -117
  59. data/spec/service_type_spec.rb +0 -93
  60. data/spec/spec_helper.rb +0 -8
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_dir = File.expand_path('../lib', __dir__)
4
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
5
+ require 'usps_intelligent_barcode'
6
+
7
+ begin
8
+ require 'prawn'
9
+ rescue LoadError
10
+ puts "This example requires the 'prawn' gem."
11
+ puts "Install it with: gem install prawn"
12
+ exit 1
13
+ end
14
+
15
+ # Generate a PDF using a USPS Intelligent Mail Barcode font
16
+ #
17
+ # Uses bundled USPS IMB fonts - no installation required.
18
+
19
+ class BarcodeToPDFFont
20
+ OUTPUT_FILENAME = 'barcode_font.pdf'
21
+ FONT_NAME = 'USPSIMBStandard'
22
+ FONT_FILE = Imb::UspsFonts.standard_font_path
23
+ FONT_SIZE = Imb::UspsFonts.font_size
24
+
25
+ def initialize(barcode)
26
+ @barcode = barcode
27
+ @letters = barcode.barcode_letters
28
+ end
29
+
30
+ def generate(path)
31
+ Prawn::Document.generate(path, page_size: 'LETTER') do |pdf|
32
+ pdf.text "USPS Intelligent Mail Barcode (Font-Based)", size: 16, style: :bold
33
+ pdf.move_down 10
34
+ pdf.text "Barcode ID: #{@barcode.barcode_id.to_s}"
35
+ pdf.text "Service Type: #{@barcode.service_type.to_s}"
36
+ pdf.text "Mailer ID: #{@barcode.mailer_id.to_s}"
37
+ pdf.text "Serial Number: #{@barcode.serial_number.to_s}"
38
+ pdf.text "Routing Code: #{@barcode.routing_code.to_s}"
39
+ pdf.text "\n"
40
+ pdf.text "Barcode string: #{@letters}", size: 8
41
+ pdf.text "\n"
42
+ pdf.text "Barcode string printed with font #{FONT_NAME} #{FONT_SIZE}"
43
+ render_barcode(pdf)
44
+ pdf.move_down 10
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def render_barcode(pdf)
51
+ pdf.font_families.update(FONT_NAME => { normal: FONT_FILE })
52
+ pdf.font(FONT_NAME) do
53
+ pdf.text @letters, size: FONT_SIZE
54
+ end
55
+ end
56
+ end
57
+
58
+ # Example usage
59
+ barcode = Imb::Barcode.new(
60
+ '01', # barcode_id
61
+ '234', # service_type
62
+ '567094', # mailer_id
63
+ '987654321', # serial_number
64
+ '01234567891' # routing_code
65
+ )
66
+
67
+ pdf_generator = BarcodeToPDFFont.new(barcode)
68
+ path = "/tmp/barcode_to_pdf.pdf"
69
+ pdf_generator.generate(path)
70
+ puts "Generated #{path}"
data/fonts/LICENSE ADDED
@@ -0,0 +1,47 @@
1
+ USPS Intelligent Mail Barcode Fonts License
2
+ ============================================
3
+
4
+ Copyright © US Postal Service 2009
5
+
6
+ LICENSE GRANT
7
+
8
+ The Postal Service grants you a world-wide, royalty-free, non-exclusive license
9
+ to use the Software, Documentation, and Fonts in the mailing industry without
10
+ paying a royalty or other fee, including rights to reproduce, display, merge,
11
+ sell, distribute, sublicense, and translate them; and the right to create
12
+ derivative works.
13
+
14
+ CONDITIONS
15
+
16
+ Redistribution of the Software, Documentation, and/or Fonts must contain:
17
+ (a) the copyright notice set forth above
18
+ (b) this list of conditions
19
+ (c) the disclaimer noted below
20
+
21
+ You may redistribute your versions of the Software and/or Fonts if your versions
22
+ were created by making modifications or improvements for a legitimate purpose in
23
+ furtherance of your or another's business.
24
+
25
+ The US Postal Service reserves the right to change this License in any manner
26
+ within reason at its discretion.
27
+
28
+ DISCLAIMER
29
+
30
+ THE POSTAL SERVICE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
31
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32
+ PURPOSE. IN NO EVENT SHALL THE POSTAL SERVICE BE LIABLE FOR ANY SPECIAL,
33
+ INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
34
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
35
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
36
+ THIS SOFTWARE.
37
+
38
+ FONTS INCLUDED
39
+
40
+ - USPSIMBStandard.ttf - Primary USPS Intelligent Mail Barcode font
41
+ - USPSIMBCompact.ttf - Compact variant of the USPS Intelligent Mail Barcode font
42
+
43
+ OFFICIAL SOURCE
44
+
45
+ Official USPS font downloads and documentation:
46
+ https://postalpro.usps.com/mailing/encoder-software-and-fonts
47
+ https://postalpro.usps.com/onecodesolution
Binary file
Binary file
@@ -4,9 +4,7 @@ require 'usps_intelligent_barcode/character_position'
4
4
 
5
5
  module Imb
6
6
 
7
- # Maps intelligent barcode "characters" to codes that indicate what
8
- # type of bar to print at each given position.
9
-
7
+ # Maps intelligent barcode "characters" to the actual barcode.
10
8
  class BarMap
11
9
 
12
10
  def initialize
@@ -15,9 +13,11 @@ module Imb
15
13
 
16
14
  # Given an array of intelligent barcode "characters", return an
17
15
  # the symbols for each position.
18
- # @param [[Integer]] characters array of characters
19
- # @return [[BarSymbol]] array of symbols
20
-
16
+ #
17
+ # @param characters [Array<Integer>] array of 13-bit "characters"
18
+ # between 0 and 1364
19
+ # @return [Array<BarSymbol>] array of symbols,
20
+ # e.g. [BarSymbol::TRACKER, BarSymbol::ASCENDER, ...]
21
21
  def symbols(characters)
22
22
  @mapping.map do |bar_position|
23
23
  bar_position.map(characters)
@@ -34,8 +34,10 @@ module Imb
34
34
  mapping_data.map do |descender, ascender|
35
35
  descender_character_position = CharacterPosition.new(*descender)
36
36
  ascender_character_position = CharacterPosition.new(*ascender)
37
- BarPosition.new(descender_character_position,
38
- ascender_character_position)
37
+ BarPosition.new(
38
+ descender_character_position,
39
+ ascender_character_position,
40
+ )
39
41
  end
40
42
  end
41
43
 
@@ -2,14 +2,17 @@ module Imb
2
2
 
3
3
  # @!group Internal
4
4
 
5
- # Represents a position (one line) in the barcode. This class is
6
- # internal and may change.
7
-
5
+ # Represents a position (a vertical bar) in the barcode. This class
6
+ # is internal and may change.
7
+ #
8
+ # Each bar represents two bits, but which two bits it represents is
9
+ # determined by the "Bar to Character Mapping" table in the
10
+ # specification (see Table 22, "Bar to Character Mapping", appendix
11
+ # E) in the specification linked to in the README.
8
12
  class BarPosition
9
13
 
10
- # @param [CharacterPosition] descender_character_position
11
- # @param [CharacterPosition] ascender_character_position
12
-
14
+ # @param descender_character_position [CharacterPosition]
15
+ # @param ascender_character_position [CharacterPosition]
13
16
  def initialize(descender_character_position, ascender_character_position)
14
17
  @descender_character_position = descender_character_position
15
18
  @ascender_character_position = ascender_character_position
@@ -17,12 +20,13 @@ module Imb
17
20
 
18
21
  # Given an array of characters, return a symbol for this
19
22
  # barcode position.
20
- # @param [[Integer]] characters character codes
23
+ # @param characters [Array<Integer>] character codes
21
24
  # @return [BarSymbol] symbol code
22
-
23
25
  def map(characters)
24
- BarSymbol.make(ascender_bit(characters),
25
- descender_bit(characters))
26
+ BarSymbol.make(
27
+ ascender_bit(characters),
28
+ descender_bit(characters),
29
+ )
26
30
  end
27
31
 
28
32
  private
@@ -2,14 +2,34 @@ module Imb
2
2
 
3
3
  # @!group Internal
4
4
 
5
- # Represents a symbol in the barcode.
6
-
5
+ # Represents a symbol in the barcode. A symbol encodes two bits,
6
+ # and is represented as one of four characters to be printed using
7
+ # one of the USPS Intelligent Barcode fonts. Each character, when
8
+ # printed using that font, results in a vertical bar having three
9
+ # part: An ascender, which may be present or missing; a descender,
10
+ # which may be present or missing; and between the ascender and the
11
+ # descender, a tracker which is always present.
12
+ #
13
+ # This chart shows the bits being encoded (ascender bit, then
14
+ # descender bit), the code (ASCII character) used for the barcode
15
+ # font, and an ASCII art repsentation of the bar that is printed by
16
+ # that code:
17
+ #
18
+ # bits: 00 01 10 11
19
+ #
20
+ # ascender: | |
21
+ # tracker: | | | |
22
+ # descender: | |
23
+ #
24
+ # code: T D A F
25
+ # mnemonic: tracker descender ascender full
7
26
  class BarSymbol
8
27
 
9
- # @param [Integer] ascender_bit (0..1)
10
- # @param [Integer] descender_bit (0..1)
28
+ # Return the symbol for a given ascender bit and descender bit.
29
+ #
30
+ # @param ascender_bit [Integer] 0 or 1
31
+ # @param descender_bit [Integer] 0 or 1
11
32
  # @return [BarSymbol]
12
-
13
33
  def self.make(ascender_bit, descender_bit)
14
34
  case [ascender_bit, descender_bit]
15
35
  when [0, 0]
@@ -29,9 +49,12 @@ module Imb
29
49
  # @return [String] the letter for this symbol
30
50
  attr_reader :letter
31
51
 
32
- # @param [Integer] code (0..3)
33
- # @param [String] letter
34
-
52
+ # Make an instance.
53
+ #
54
+ # @param code [Integer] Binary number from 0b00 to 0b11. Bit 1
55
+ # controls the ascender; bit 0 controls the descender.
56
+ # @param letter [String] The character to print in the barcode
57
+ # font.
35
58
  def initialize(code, letter)
36
59
  @code = code
37
60
  @letter = letter
@@ -39,10 +62,17 @@ module Imb
39
62
 
40
63
  private
41
64
 
42
- TRACKER = BarSymbol.new(0, 'T')
43
- DESCENDER = BarSymbol.new(1, 'D')
44
- ASCENDER = BarSymbol.new(2, 'A')
45
- FULL = BarSymbol.new(3, 'F')
65
+ TRACKER = BarSymbol.new(0b00, 'T')
66
+ private_constant :TRACKER
67
+
68
+ DESCENDER = BarSymbol.new(0b01, 'D')
69
+ private_constant :DESCENDER
70
+
71
+ ASCENDER = BarSymbol.new(0b10, 'A')
72
+ private_constant :ASCENDER
73
+
74
+ FULL = BarSymbol.new(0b11, 'F')
75
+ private_constant :FULL
46
76
 
47
77
  end
48
78
 
@@ -1,3 +1,27 @@
1
+ # Represents the table from the spec. that controls how the bits of
2
+ # the "characters" are mapped to barcode positions. Each line of this
3
+ # file represents a single barcode position (or vertical line), with
4
+ # the leftmost position first.
5
+ #
6
+ # Let's decode the first line:
7
+ #
8
+ # [
9
+ # [
10
+ # 7, # descender character index (0..)
11
+ # 2, # descender bit number (0..)
12
+ # ],
13
+ # [
14
+ # 4, # ascender character index (0..)
15
+ # 3, #ascender bit number (0..)
16
+ # ]
17
+ # ]
18
+ #
19
+ # This means that the first barcode position's ascender is controlled
20
+ # by bit 2 of character 7; the descender is controlled by bit 3 of
21
+ # character 4.
22
+ #
23
+ # See spec. section 10.0 ("Appendix E -- Tables for Converting
24
+ # Characters"), Table 22 ("Bar to Character Mapping")
1
25
  ---
2
26
  - [[7, 2], [4, 3]]
3
27
  - [[1, 10], [0, 0]]
@@ -1,12 +1,13 @@
1
+ # coding: utf-8
2
+
1
3
  require 'usps_intelligent_barcode/codeword_map'
2
4
  require 'usps_intelligent_barcode/crc'
3
5
 
4
6
  # The namespace for everything in this library.
5
-
6
7
  module Imb
7
8
 
8
- # This class represents a barcode.
9
-
9
+ # This class turns a collection of fields into a string of symbols
10
+ # for printing using a barcode font.
10
11
  class Barcode
11
12
 
12
13
  include Memoizer
@@ -26,8 +27,6 @@ module Imb
26
27
  # @return [RoutingCode]
27
28
  attr_reader :routing_code
28
29
 
29
- # @param
30
-
31
30
  # Create a new barcode
32
31
  #
33
32
  # @param barcode_id [String] Nominally a String, but can be
@@ -40,12 +39,13 @@ module Imb
40
39
  # anything that {SerialNumber.coerce} will accept.
41
40
  # @param routing_code [String] Nominally a String, but can be
42
41
  # anything that {RoutingCode.coerce} will accept.
43
-
44
- def initialize(barcode_id,
45
- service_type,
46
- mailer_id,
47
- serial_number,
48
- routing_code)
42
+ def initialize(
43
+ barcode_id,
44
+ service_type,
45
+ mailer_id,
46
+ serial_number,
47
+ routing_code
48
+ )
49
49
  @barcode_id = BarcodeId.coerce(barcode_id)
50
50
  @service_type = ServiceType.coerce(service_type)
51
51
  @mailer_id = MailerId.coerce(mailer_id)
@@ -61,39 +61,18 @@ module Imb
61
61
  # * 'D' for a descender mark
62
62
  # * 'F' for a full mark (both ascender and descender)
63
63
  # @return [String] A string that represents the barcode.
64
-
65
64
  def barcode_letters
66
65
  symbols.map(&:letter).join
67
66
  end
68
-
69
- private
70
-
71
- # :stopdoc:
72
- BAR_MAP = BarMap.new
73
- CODEWORD_MAP = CodewordMap.new
74
- CRC = Crc.new
75
- # :startdoc:
76
67
 
77
- def validate_components
78
- components.each do |component|
79
- component.validate(long_mailer_id?)
80
- end
81
- end
82
-
83
- def components
84
- [
85
- @routing_code,
86
- @barcode_id,
87
- @service_type,
88
- @mailer_id,
89
- @serial_number,
90
- ]
91
- end
92
-
93
- def long_mailer_id?
94
- @mailer_id.long?
95
- end
68
+ # @!group Algorithm Steps - Public for testing
96
69
 
70
+ # The components ("fields" in the spec) are turned into a single
71
+ # number that the spec calls "binary_data"). This is done through
72
+ # a series of multiplications and additions. See spec. section
73
+ # 2.2.1 ("Step 1--Conversion of Data Fields into Binary Data").
74
+ #
75
+ # @return [Integer]
97
76
  def binary_data
98
77
  components.inject(0) do |data, component|
99
78
  component.shift_and_add_to(data, long_mailer_id?)
@@ -101,11 +80,20 @@ module Imb
101
80
  end
102
81
  memoize :binary_data
103
82
 
83
+ # Compute the "frame check sequence." See spec. section 2.2.2
84
+ # ("Step 2--Generation of 11-Bit CRC on Binary Data").
85
+ #
86
+ # @return [Integer]
104
87
  def frame_check_sequence
105
88
  CRC.crc(binary_data)
106
89
  end
107
90
  memoize :frame_check_sequence
108
91
 
92
+ # Compute the "code words." This is an array of 10 integers
93
+ # computed from the binary data. See spec. section 2.2.3 ("Step
94
+ # 3--Conversion from Binary Data to Codewords").
95
+ #
96
+ # @return [Array<Integer>] 10 "characters."
109
97
  def codewords
110
98
  codewords = []
111
99
  data = binary_data
@@ -117,22 +105,45 @@ module Imb
117
105
  end
118
106
  memoize :codewords
119
107
 
108
+ # Insert the orientation into the codewords. The spec. doesn't
109
+ # say much about this, other than to multiply codeword "J" by two.
110
+ # This will cause the LSB to be zero, which is presumably the
111
+ # orientation. See spec. section 2.4.4 ("Step 4--Inserting
112
+ # Additional Information into Codewords").
113
+ #
114
+ # @return [Array<Integer>] 10 "characters."
120
115
  def codewords_with_orientation_in_character_j
121
116
  result = codewords.dup
122
117
  result[9] *= 2
123
118
  result
124
119
  end
125
120
 
121
+ # Insert the most significant bit of the FCS into codeword A. See
122
+ # spec. section 2.4.4 ("Step 4--Inserting Additional Information
123
+ # into Codewords").
124
+ #
125
+ # @return [Array<Integer>] 10 "characters."
126
126
  def codewords_with_fcs_bit_in_character_a
127
127
  result = codewords_with_orientation_in_character_j.dup
128
128
  result[0] += 659 if frame_check_sequence[10] == 1
129
129
  result
130
130
  end
131
131
 
132
+ # Convert the codewords to "characters". Each character is a
133
+ # 13-bit integer; there are 10 of them, labeled "A" through "J" by
134
+ # the spec. See spec. section 2.2.5 ("Step 5--Conversion from
135
+ # Codewords to Characters"), para. "A".
136
+ #
137
+ # @return [Array<Integer>] 10 "characters."
132
138
  def characters
133
139
  CODEWORD_MAP.characters(codewords_with_fcs_bit_in_character_a)
134
140
  end
135
141
 
142
+ # Fold the least-significant 10 bits of the FCS into the
143
+ # "characters". See spec. section 2.2.5 ("Step 5--Conversion from
144
+ # Codewords to Characters"), para. "B".
145
+ #
146
+ # @return [Array<Integer>] 10 "characters".
136
147
  def characters_with_fcs_bits_0_through_9
137
148
  characters.each_with_index.map do |character, i|
138
149
  if frame_check_sequence[i] == 1
@@ -143,6 +154,39 @@ module Imb
143
154
  end
144
155
  end
145
156
 
157
+ # @!endgroup
158
+
159
+ private
160
+
161
+ # :stopdoc:
162
+ BAR_MAP = BarMap.new
163
+ CODEWORD_MAP = CodewordMap.new
164
+ CRC = Crc.new
165
+ # :startdoc:
166
+
167
+ def validate_components
168
+ components.each do |component|
169
+ component.validate(long_mailer_id?)
170
+ end
171
+ end
172
+
173
+ def components
174
+ [
175
+ @routing_code,
176
+ @barcode_id,
177
+ @service_type,
178
+ @mailer_id,
179
+ @serial_number,
180
+ ]
181
+ end
182
+
183
+ def long_mailer_id?
184
+ @mailer_id.long?
185
+ end
186
+
187
+ # Map the "characters" to symbols. Here is where the barcode is made.
188
+ #
189
+ # @return [Array<BarSymbol>]
146
190
  def symbols
147
191
  BAR_MAP.symbols(characters_with_fcs_bits_0_through_9)
148
192
  end
@@ -1,7 +1,7 @@
1
1
  module Imb
2
2
 
3
- # This class represents a Barcode ID
4
-
3
+ # This class represents a Barcode ID, one of the fields that is used
4
+ # to generate a barcode.
5
5
  class BarcodeId
6
6
 
7
7
  # The allowable range of a barcode ID
@@ -14,6 +14,7 @@ module Imb
14
14
  # * {BarcodeId}
15
15
  # * String
16
16
  # * Integer
17
+ #
17
18
  # @return [BarcodeId]
18
19
  # @raise [ArgumentError] If the argument cannot be coerced
19
20
 
@@ -31,15 +32,15 @@ module Imb
31
32
  end
32
33
 
33
34
  # Create a new BarcodeId
34
- # @param [Integer] value The barcode ID
35
-
35
+ #
36
+ # @param value [Integer] The barcode ID
36
37
  def initialize(value)
37
38
  @value = value
38
39
  end
39
40
 
40
41
  # Return true if this object is equal to o
41
- # @param [Object] o Any object acceptable to {.coerce}
42
-
42
+ #
43
+ # @param o [Object] Any object acceptable to {.coerce}
43
44
  def ==(o)
44
45
  BarcodeId.coerce(o).to_i == to_i
45
46
  rescue ArgumentError
@@ -47,17 +48,22 @@ module Imb
47
48
  end
48
49
 
49
50
  # @return [Integer] The integer value of the barcode ID
50
-
51
51
  def to_i
52
52
  @value
53
53
  end
54
54
 
55
+ # @return [String] The string value of the barcode ID
56
+ def to_s
57
+ "%02d" % @value
58
+ end
59
+
55
60
  # @!group Internal
56
61
 
57
62
  # Validate the value.
58
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
63
+ #
64
+ # @param long_mailer_id [boolean] truthy if the mailer ID is long
65
+ # (9 digits).
59
66
  # @raise ArgumentError if invalid
60
-
61
67
  def validate(long_mailer_id)
62
68
  unless RANGE === @value
63
69
  raise ArgumentError, "Must be #{RANGE}"
@@ -69,10 +75,11 @@ module Imb
69
75
 
70
76
  # Add this object's value to target, shifting it left as many
71
77
  # digts as are needed to make room.
72
- # @param [Integer] target The target to be shifted and added to
73
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
78
+ #
79
+ # @param target [Integer] The target to be shifted and added to
80
+ # @param long_mailer_id [boolean] truthy if the mailer ID is long
81
+ # (9 digits).
74
82
  # @return [Integer] The new value of the target
75
-
76
83
  def shift_and_add_to(target, long_mailer_id)
77
84
  target *= 10
78
85
  target += most_significant_digit
@@ -2,23 +2,25 @@ module Imb
2
2
 
3
3
  # @!group Internal
4
4
 
5
- # Represents the position of one bit in the array of intelligent
6
- # barcode "characters".
7
-
5
+ # For a given barcode position, maps to the "character" index and
6
+ # bit number used to drive either the ascender or descender of that
7
+ # position.
8
8
  class CharacterPosition
9
9
 
10
- # @param [Integer] character_index
11
- # @param [Integer] bit_number
12
-
10
+ # Construct an instance.
11
+ #
12
+ # @param character_index [Integer] The character's index within an
13
+ # array of characters.
14
+ # @param bit_number [Integer] The character's bit number
13
15
  def initialize(character_index, bit_number)
14
16
  @character_index = character_index
15
17
  @bit_number = bit_number
16
18
  end
17
19
 
18
20
  # Given an array of characters, return the bit for this position.
19
- # @param [[Integer]] characters
21
+ #
22
+ # @param characters [Array<Integer>]
20
23
  # @return [Integer] bit (0 or 1)
21
-
22
24
  def extract_bit_from_characters(characters)
23
25
  characters[@character_index][@bit_number]
24
26
  end
@@ -3,7 +3,6 @@ module Imb
3
3
  # @!group Internal
4
4
 
5
5
  # Maps codewords to characters.
6
-
7
6
  class CodewordMap
8
7
 
9
8
  # Constructor
@@ -1,3 +1,10 @@
1
+ # Represents the table from the spec. that maps from codewords to
2
+ # "characters." A codeword is an integer between 0 and 1364, so this
3
+ # table is an array of 1365 entries.
4
+ #
5
+ # See spec. section 10.0 ("Appendix E -- Tables for Converting
6
+ # Characters"), Tables 19 ("5 of 13 Characters") and 20 ("2 of 13
7
+ # Characters")
1
8
  ---
2
9
  - 31
3
10
  - 7936
@@ -5,15 +5,15 @@ module Imb
5
5
  # @!group Internal
6
6
 
7
7
  # Calculates the Intelligent Mail Barcode CRC.
8
-
8
+ #
9
+ # See spec. section 9.1 ("CRC Generating Code")
9
10
  class Crc
10
11
 
11
12
  include NumericConversions
12
13
 
13
14
  # Calculate a CRC.
14
- # @param [Integer] binary_data A 102-bit integer
15
+ # @param binary_data [Integer] A 102-bit integer
15
16
  # @return [Integer] An 11-bit CRC
16
-
17
17
  def crc(binary_data)
18
18
  crc = MASK
19
19
  bytes = numeric_to_bytes(binary_data, NUM_INPUT_BYTES)