mork 0.8.1 → 0.9.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/README.md +107 -35
  4. data/lib/mork/coord.rb +1 -1
  5. data/lib/mork/extensions.rb +3 -0
  6. data/lib/mork/grid.rb +13 -6
  7. data/lib/mork/grid_const.rb +11 -9
  8. data/lib/mork/grid_omr.rb +15 -5
  9. data/lib/mork/grid_pdf.rb +1 -0
  10. data/lib/mork/magicko.rb +46 -28
  11. data/lib/mork/mimage.rb +113 -52
  12. data/lib/mork/mimage_list.rb +6 -5
  13. data/lib/mork/npatch.rb +16 -18
  14. data/lib/mork/sheet_omr.rb +210 -125
  15. data/lib/mork/sheet_pdf.rb +15 -8
  16. data/lib/mork/version.rb +1 -1
  17. data/mork.gemspec +2 -0
  18. data/mork.sublime-project +1 -1
  19. data/spec/mork/extensions_spec.rb +3 -3
  20. data/spec/mork/grid_omr_spec.rb +2 -1
  21. data/spec/mork/grid_spec.rb +34 -35
  22. data/spec/mork/magicko_spec.rb +2 -2
  23. data/spec/mork/mimage_spec.rb +22 -63
  24. data/spec/mork/sheet_omr_spec.rb +96 -201
  25. data/spec/mork/sheet_pdf_spec.rb +9 -10
  26. data/spec/out/{.gitignore → barcode/.gitignore} +0 -0
  27. data/spec/out/highlight/.gitignore +4 -0
  28. data/spec/out/mark/.gitignore +4 -0
  29. data/spec/out/outline/.gitignore +4 -0
  30. data/spec/out/pdf/.gitignore +4 -0
  31. data/spec/out/registration/.gitignore +4 -0
  32. data/spec/out/text/.gitignore +4 -0
  33. data/spec/samples/angolo.jpg +0 -0
  34. data/spec/samples/angolo2.jpg +0 -0
  35. data/spec/samples/angolo3.jpg +0 -0
  36. data/spec/samples/boxy.yml +0 -0
  37. data/spec/samples/grid.yml +0 -0
  38. data/spec/samples/grid160.yml +0 -0
  39. data/spec/samples/grid_omr_layout.yml +56 -0
  40. data/spec/samples/info.yml +15 -39
  41. data/spec/samples/jdoe/JohnDoe1.jpeg +0 -0
  42. data/spec/samples/jdoe/JohnDoe2.jpeg +0 -0
  43. data/spec/samples/jdoe/JohnDoe3.jpeg +0 -0
  44. data/spec/samples/jdoe/layout.yml +58 -0
  45. data/spec/samples/layout.yml +2 -2
  46. data/spec/samples/lucrezia/border1.pdf +0 -0
  47. data/spec/samples/lucrezia/border2.pdf +0 -0
  48. data/spec/samples/lucrezia/bw1.pdf +0 -0
  49. data/spec/samples/lucrezia/bw2.pdf +0 -0
  50. data/spec/samples/lucrezia/gray1.pdf +0 -0
  51. data/spec/samples/lucrezia/gray2.pdf +0 -0
  52. data/spec/samples/{syst/IMG_20150104_0004.jpg → marisol/marisol1.jpg} +0 -0
  53. data/spec/samples/{syst/IMG_20150104_0009.jpg → marisol/marisol2.jpg} +0 -0
  54. data/spec/samples/{syst/IMG_20150104_0011.jpg → marisol/marisol3.jpg} +0 -0
  55. data/spec/samples/reg_mark.jpg +0 -0
  56. data/spec/samples/rm00.jpeg +0 -0
  57. data/spec/samples/rm01.jpeg +0 -0
  58. data/spec/samples/rm02.jpeg +0 -0
  59. data/spec/samples/rm03.jpeg +0 -0
  60. data/spec/samples/rm04.jpeg +0 -0
  61. data/spec/samples/rm05.jpeg +0 -0
  62. data/spec/samples/syst/barr0.jpg +0 -0
  63. data/spec/samples/syst/barr1.jpg +0 -0
  64. data/spec/samples/syst/barr2.jpg +0 -0
  65. data/spec/samples/syst/bell0.jpg +0 -0
  66. data/spec/samples/syst/bell1.jpg +0 -0
  67. data/spec/samples/syst/bell2.jpg +0 -0
  68. data/spec/samples/syst/bila0.jpg +0 -0
  69. data/spec/samples/syst/bila1.jpg +0 -0
  70. data/spec/samples/syst/bila2.jpg +0 -0
  71. data/spec/samples/syst/bila3.jpg +0 -0
  72. data/spec/samples/syst/bila4.jpg +0 -0
  73. data/spec/samples/syst/bone0.jpg +0 -0
  74. data/spec/samples/syst/bone1.jpg +0 -0
  75. data/spec/samples/syst/bone2.jpg +0 -0
  76. data/spec/samples/syst/cost0.jpg +0 -0
  77. data/spec/samples/syst/cost1.jpg +0 -0
  78. data/spec/samples/syst/cost2.jpg +0 -0
  79. data/spec/samples/syst/cost3.jpg +0 -0
  80. data/spec/samples/syst/cost4.jpg +0 -0
  81. data/spec/samples/syst/dald0.jpg +0 -0
  82. data/spec/samples/syst/dald1.jpg +0 -0
  83. data/spec/samples/syst/dald2.jpg +0 -0
  84. data/spec/samples/syst/dald3.jpg +0 -0
  85. data/spec/samples/syst/dald4.jpg +0 -0
  86. data/spec/samples/syst/dign0.jpg +0 -0
  87. data/spec/samples/syst/dign1.jpg +0 -0
  88. data/spec/samples/syst/dign2.jpg +0 -0
  89. data/spec/samples/syst/dive0.jpg +0 -0
  90. data/spec/samples/syst/dive1.jpg +0 -0
  91. data/spec/samples/syst/dive2.jpg +0 -0
  92. data/spec/samples/syst/histo.m +0 -0
  93. data/spec/samples/{syst_grid.yml → syst/layout.yml} +0 -0
  94. data/spec/spec_helper.rb +29 -29
  95. metadata +49 -62
  96. data/auto.txt +0 -0
  97. data/spec/samples/out-1.jpg +0 -0
  98. data/spec/samples/qzc013.jpg +0 -0
  99. data/spec/samples/sample02.jpg +0 -0
  100. data/spec/samples/sample_gray.jpg +0 -0
  101. data/spec/samples/sheet.jpg +0 -0
  102. data/spec/samples/sheet666.jpg +0 -0
  103. data/spec/samples/slanted.jpg +0 -0
  104. data/spec/samples/slanted.yml +0 -54
  105. data/spec/samples/syst/IMG_20150104_0004.txt +0 -4955
  106. data/spec/samples/syst/IMG_20150104_0009.txt +0 -4955
  107. data/spec/samples/syst/IMG_20150104_0011.txt +0 -4955
  108. data/spec/samples/syst/SCN_0001.jpg +0 -0
  109. data/spec/samples/syst/SCN_0001.txt +0 -4955
  110. data/spec/samples/syst/barr0.txt +0 -4955
  111. data/spec/samples/syst/barr1.txt +0 -4955
  112. data/spec/samples/syst/barr2.txt +0 -4955
  113. data/spec/samples/syst/bell0.txt +0 -4955
  114. data/spec/samples/syst/bell1.txt +0 -4955
  115. data/spec/samples/syst/bell2.txt +0 -4955
  116. data/spec/samples/syst/bila0.txt +0 -4955
  117. data/spec/samples/syst/bila1.txt +0 -4955
  118. data/spec/samples/syst/bila2.txt +0 -4955
  119. data/spec/samples/syst/bila3.txt +0 -4955
  120. data/spec/samples/syst/bila4.txt +0 -4955
  121. data/spec/samples/syst/bone0.txt +0 -4955
  122. data/spec/samples/syst/bone1.txt +0 -4955
  123. data/spec/samples/syst/bone2.txt +0 -4955
  124. data/spec/samples/syst/cost0.txt +0 -4955
  125. data/spec/samples/syst/cost1.txt +0 -4955
  126. data/spec/samples/syst/cost2.txt +0 -4955
  127. data/spec/samples/syst/cost3.txt +0 -4955
  128. data/spec/samples/syst/cost4.txt +0 -4955
  129. data/spec/samples/syst/dald0.txt +0 -4955
  130. data/spec/samples/syst/dald1.txt +0 -4955
  131. data/spec/samples/syst/dald2.txt +0 -4955
  132. data/spec/samples/syst/dald3.txt +0 -4955
  133. data/spec/samples/syst/dald4.txt +0 -4955
  134. data/spec/samples/syst/dign0.txt +0 -4955
  135. data/spec/samples/syst/dign1.txt +0 -4955
  136. data/spec/samples/syst/dign2.txt +0 -4955
  137. data/spec/samples/syst/dive0.txt +0 -4955
  138. data/spec/samples/syst/dive1.txt +0 -4955
  139. data/spec/samples/syst/dive2.txt +0 -4955
  140. data/spec/samples/syst/out0000.jpg +0 -0
  141. data/spec/samples/syst/out0000.txt +0 -4955
  142. data/spec/samples/syst/out0001.jpg +0 -0
  143. data/spec/samples/syst/out0001.txt +0 -4955
  144. data/spec/samples/syst/out0002.jpg +0 -0
  145. data/spec/samples/syst/out0002.txt +0 -4955
  146. data/spec/samples/syst/qzc013.jpg +0 -0
  147. data/spec/samples/syst/qzc013.txt +0 -4955
  148. data/spec/samples/syst/sample_gray.jpg +0 -0
  149. data/spec/samples/syst/sample_gray.txt +0 -4955
  150. data/spec/samples/two_pages.pdf +0 -0
@@ -3,186 +3,165 @@ require 'mork/mimage'
3
3
  require 'mork/mimage_list'
4
4
 
5
5
  module Mork
6
+ # Optical mark recognition of a response sheet that was: 1) generated
7
+ # with SheetPDF, 2) printed on plain paper, 3) filled out by a responder,
8
+ # and 4) acquired as a image file.
9
+ #
10
+ # The sheet is automatically registered upon object creation, after which it
11
+ # is possible to perform queries, as well as save a copy of the scanned
12
+ # image with various overlays superimposed, highlighting the expected correc
13
+ # choices, the actually marked ones, etc.
6
14
  class SheetOMR
7
-
8
- def initialize(path, nitems=nil, grom=nil)
15
+ # @param path [String] the required path/filename to the saved image
16
+ # (.jpg, .jpeg, .png, or .pdf)
17
+ # @param choices [Fixnum, Array] the questions/choices we want scored, as
18
+ # an optional named argument. Scoring is done on all available questions,
19
+ # if the argument is omitted, on the first N questions, If an integer
20
+ # is passed, or on the indicated questions, if the argument is an array.
21
+ # @param layout [String, Hash] the sheet description. Use a string to
22
+ # specify the path/filename of a YAML file containing the parameters,
23
+ # or directly a hash of parameters. See the README file for a full listing
24
+ # of the available parameters.
25
+ def initialize(path, choices: nil, layout: nil)
9
26
  raise IOError, "File '#{path}' not found" unless File.exists? path
10
- @grom = GridOMR.new grom
11
- @nitems = case nitems
12
- when nil
13
- [@grom.max_choices_per_question] * @grom.max_questions
27
+ grom = GridOMR.new layout
28
+ nitems = case choices
29
+ when NilClass
30
+ [grom.max_choices_per_question] * grom.max_questions
14
31
  when Fixnum
15
- [@grom.max_choices_per_question] * nitems
32
+ [grom.max_choices_per_question] * choices
16
33
  when Array
17
- nitems
34
+ choices
18
35
  end
19
- @mim = Mimage.new path, @nitems, @grom
36
+ @mim = Mimage.new path, nitems, grom
20
37
  end
21
38
 
39
+ # True if sheet registration completed successfully
40
+ #
41
+ # @return [Boolean]
22
42
  def valid?
23
43
  @mim.valid?
24
44
  end
25
45
 
46
+ # Registration status for each of the four corners
47
+ #
48
+ # @return [Hash] { tl: Symbol, tr: Symbol, br: Symbol, bl: Symbol } where
49
+ # symbol is either `:ok` or `:edgy`, meaning that the centroid was found
50
+ # to be too close to the edge of the search area to be considered reliable
26
51
  def status
27
52
  @mim.status
28
53
  end
29
54
 
30
- # barcode
55
+ # Sheet barcode as an integer
31
56
  #
32
- # returns the sheet barcode as an integer
57
+ # @return [Fixnum]
33
58
  def barcode
34
59
  return if not_registered
35
60
  barcode_string.to_i(2)
36
61
  end
37
62
 
38
- # barcode_string
63
+ # Sheet barcode as a binary-like string
39
64
  #
40
- # returns the sheet barcode as a string of 0s and 1s. The string is barcode_bits
41
- # bits long, with most significant bits to the left
65
+ # @return [String] a string of 0s and 1s; the string is `barcode_bits`
66
+ # bits long, with most significant bits to the left
42
67
  def barcode_string
43
68
  return if not_registered
44
- cs = @grom.barcode_bits.times.inject("") { |c, v| c << barcode_bit_string(v) }
45
- cs.reverse
69
+ @mim.barcode_bits.map do |b|
70
+ b ? '1' : '0'
71
+ end.join.reverse
46
72
  end
47
73
 
48
- # marked?(question, choice)
74
+ # True if the specified question/choice cell has been marked
49
75
  #
50
- # returns true if the specified question/choice cell has been darkened
51
- # false otherwise
52
- def marked?(q, c)
76
+ # @param question [Fixnum] the question number, zero-based
77
+ # @param choice [Fixnum] the choice number, zero-based
78
+ # @return [Boolean]
79
+ def marked?(question, choice)
53
80
  return if not_registered
54
- @mim.marked? q, c
81
+ @mim.marked[question][choice]
55
82
  end
56
83
 
57
- # TODO: define method ‘mark’ to retrieve the choice array for a single item
58
-
59
-
60
- # mark_array(range)
84
+ # The set of choice indices marked on the response sheet
61
85
  #
62
- # returns an array of arrays of marked choices.
63
- # takes either a range of questions, an array of questions, or a fixnum,
64
- # in which case the choices for the first n questions will be returned.
65
- # if called without arguments, all available choices will be evaluated.
66
- def mark_array(r = nil)
67
- return if not_registered
68
- question_range(r).collect do |q|
69
- cho = []
70
- (0...@grom.max_choices_per_question).each do |c|
71
- cho << c if marked?(q, c)
72
- end
73
- cho
74
- end
75
- end
76
-
77
- # mark_char_array(range)
86
+ # @return [Array] an array of arrays of integers; each element contains
87
+ # the (zero-based) list of marked choices for the corresponding question.
88
+ # For example, the following `marked_choices` array: `[[0], [], [3,4]]`
89
+ # indicates that the responder has marked the first choice for the first
90
+ # question, none for the second, and the fourth and fifth choices for the
91
+ # third question.
78
92
  #
79
- # returns an array of arrays of the characters corresponding to marked choices.
80
- # WARNING: at this time, only the latin sequence 'A, B, C...' is supported.
81
- # takes either a range of questions, an array of questions, or a fixnum,
82
- # in which case the choices for the first n questions will be returned.
83
- # if called without arguments, all available choices will be evaluated.
84
- def mark_char_array(r = nil)
93
+ # Note that only the questions/choices indicated via the `choices` argument
94
+ # during object creation are evaluated.
95
+ def marked_choices
85
96
  return if not_registered
86
- question_range(r).collect do |q|
87
- cho = []
88
- (0...@grom.max_choices_per_question).each do |c|
89
- cho << (65+c).chr if marked?(q, c)
90
- end
91
- cho
92
- end
97
+ @mim.marked_int
93
98
  end
94
99
 
95
- def mark_logical_array(r = nil)
100
+ # The set of letters marked on the response sheet. At this time, only the
101
+ # latin sequence 'A, B, C...' is supported.
102
+ #
103
+ # @return [Array] an array of arrays of 1-character strings; each element
104
+ # contains the list of letters marked for the corresponding question.
105
+ #
106
+ # Note that only the questions/choices indicated via the `choices` argument
107
+ # during object creation are evaluated.
108
+ def marked_letters
96
109
  return if not_registered
97
- question_range(r).collect do |q|
98
- (0...@grom.max_choices_per_question).collect {|c| marked?(q, c)}
110
+ marked_choices.map do |q|
111
+ q.map { |cho| (65+cho).chr }
99
112
  end
100
113
  end
101
114
 
102
- # ================
103
- # = HIGHLIGHTING =
104
- # ================
105
-
106
- def outline(cells)
107
- return if not_registered
108
- raise "Invalid ‘cells’ argument" unless cells.kind_of? Array
109
- @mim.outline cells
110
- end
111
-
112
- def cross(cells)
113
- return if not_registered
114
- raise "Invalid ‘cells’ argument" unless cells.kind_of? Array
115
- @mim.cross cells
116
- end
117
-
118
- def cross_marked
119
- return if not_registered
120
- @mim.cross mark_array
121
- end
122
-
123
- def highlight_marked
124
- return if not_registered
125
- @mim.highlight_cells mark_array
126
- end
127
-
128
- def highlight_all_choices
115
+ # Marked choices as boolean values
116
+ #
117
+ # @return [Array] an array of arrays of true/false values corresponding to
118
+ # marked vs unmarked choice cells.
119
+ def marked_logicals
129
120
  return if not_registered
130
- @mim.highlight_all_choices
121
+ @mim.marked
131
122
  end
132
123
 
133
- def highlight_barcode
124
+ # Apply an overlay on the image
125
+ #
126
+ # @param what [Symbol] the overlay type, choose from `:outline`, `:check`,
127
+ # `:highlight`
128
+ # @param where [Array, Symbol] where to apply the overlay. Either an array
129
+ # of arrays of (zero-based) indices to specify target cells, or one of
130
+ # the following symbols: `:marked`: all marked cells, among those
131
+ # specified by the `choices` argument during object creation
132
+ # (this is the default); `:all`: all cells in `choices`;
133
+ # `:max`: maximum number of cells allowed by the layout (can be larger
134
+ # than `:all`); `:barcode`: the dark barcode elements; `:cal` the
135
+ # calibration cells
136
+ def overlay(what, where=:marked)
134
137
  return if not_registered
135
- @mim.highlight_barcode barcode_string
138
+ @mim.overlay what, where
136
139
  end
137
140
 
138
- # write(output_path_file_name)
141
+ # Saves a copy of the source image after registration;
142
+ # the output image will also contain any previously applied overlays.
139
143
  #
140
- # writes out a copy of the source image after registration;
141
- # the output image will also contain any previously applied overlays;
142
- # if the argument is omitted, the image is created in-place,
143
- # i.e. the original source image is overwritten.
144
- def write(fname=nil)
144
+ # @param fname [String] the path/filename of the target image, including
145
+ # the extension (`.jpg`, `.png`)
146
+ def save(fname)
145
147
  return if not_registered
146
- @mim.write(fname)
148
+ @mim.save(fname, true)
147
149
  end
148
150
 
149
- # # write_raw(output_path_file_name)
150
- # #
151
- # # writes out a copy of the source image before registration;
152
- # # the output image will also contain any previously applied overlays
153
- # # if the argument is omitted, the image is created in-place,
154
- # # i.e. the original source image is overwritten.
155
- # def write_raw(fname=nil)
156
- # @mim.write(fname, false)
157
- # end
158
-
159
- def write_registration(fname)
160
- @mim.highlight_rm_areas
161
- @mim.highlight_rm_centers
162
- @mim.write fname, false
151
+ # Saves a copy of the original image with overlays showing the crop areas
152
+ # used to localize the registration marks and the detected registration
153
+ # mark centers.
154
+ #
155
+ # @param fname [String] the path/filename of the target image, including
156
+ # the extension (`.jpg`, `.png`)
157
+ def save_registration(fname)
158
+ @mim.save_registration fname
163
159
  end
164
160
 
165
161
  # ============================================================#
166
162
  private #
167
163
  # ============================================================#
168
164
 
169
- def barcode_bit_string(i)
170
- @mim.barcode_bit?(i) ? "1" : "0"
171
- end
172
-
173
- def question_range(r)
174
- # TODO: help text: although not API, people need to know this!
175
- if r.nil?
176
- (0...@nitems.length)
177
- elsif r.is_a? Fixnum
178
- (0...r)
179
- elsif r.is_a? Array
180
- r
181
- else
182
- raise "Invalid argument"
183
- end
184
- end
185
-
186
165
  def not_registered
187
166
  unless valid?
188
167
  puts "---=={ Unregistered image. Reason: '#{@mim.status.inspect}' }==---"
@@ -191,3 +170,109 @@ module Mork
191
170
  end
192
171
  end
193
172
  end
173
+
174
+
175
+ # # write_raw(output_path_file_name)
176
+ # #
177
+ # # writes out a copy of the source image before registration;
178
+ # # the output image will also contain any previously applied overlays
179
+ # # if the argument is omitted, the image is created in-place,
180
+ # # i.e. the original source image is overwritten.
181
+ # def write_raw(fname=nil)
182
+ # @mim.write(fname, false)
183
+ # end
184
+
185
+ # # Array of arrays of marked choices.
186
+ # #
187
+ # # @param questions [Fixnum, Range, or Array] look for the first n questions
188
+ # # If the argument is omitted, all available choices are evaluated.
189
+ # # @return [Array] The list of marked choices as an array (one element per
190
+ # # question) of arrays (the indices of all marked choices for the question)
191
+ # def mark_array(questions = nil)
192
+ # return if not_registered
193
+ # x = question_range questions
194
+ # byebug
195
+ # x.collect do |q|
196
+ # [].tap do |cho|
197
+ # (0...@grom.max_choices_per_question).each do |c|
198
+ # cho << c if marked?(q, c)
199
+ # end
200
+ # end
201
+ # end
202
+ # end
203
+
204
+ # # Array of arrays of the characters corresponding to marked choices.
205
+ # # At this time, only the latin sequence 'A, B, C...' is supported.
206
+ # #
207
+ # # @param questions [Fixnum, Range, Array] same as for `mark_array`
208
+ # # @return [Array] The list of marked choices as an array (one element per
209
+ # # question) of arrays (the indices of all marked choices for the question)
210
+ # def mark_char_array(questions = nil)
211
+ # return if not_registered
212
+ # question_range(questions).collect do |q|
213
+ # [].tap do |cho|
214
+ # (0...@grom.max_choices_per_question).each do |c|
215
+ # cho << (65+c).chr if marked?(q, c)
216
+ # end
217
+ # end
218
+ # end
219
+ # end
220
+
221
+ # # Array of logical arrays of marked choices
222
+ # #
223
+ # # @param [Fixnum, Range, Array]
224
+ # def mark_logical_array(r = nil)
225
+ # return if not_registered
226
+ # question_range(r).collect do |q|
227
+ # (0...@grom.max_choices_per_question).collect {|c| marked?(q, c)}
228
+ # end
229
+ # end
230
+
231
+ # def question_range(r)
232
+ # # TODO: help text: although not API, people need to know this!
233
+ # if r.nil?
234
+ # (0...@nitems.length)
235
+ # elsif r.is_a? Fixnum
236
+ # (0...r)
237
+ # elsif r.is_a? Array
238
+ # r
239
+ # else
240
+ # raise "Invalid argument"
241
+ # end
242
+ # end
243
+
244
+ # def outline(cells)
245
+ # return if not_registered
246
+ # raise "Invalid ‘cells’ argument" unless cells.kind_of? Array
247
+ # @mim.outline cells
248
+ # end
249
+
250
+ # def cross(cells)
251
+ # return if not_registered
252
+ # raise "Invalid ‘cells’ argument" unless cells.kind_of? Array
253
+ # @mim.cross cells
254
+ # end
255
+
256
+ # def cross_marked
257
+ # return if not_registered
258
+ # @mim.cross mark_array
259
+ # end
260
+
261
+ # def highlight_all_choices
262
+ # return if not_registered
263
+ # @mim.highlight_all_choices
264
+ # end
265
+
266
+ # def highlight_marked
267
+ # return if not_registered
268
+ # @mim.highlight_cells mark_array
269
+ # end
270
+
271
+ # def highlight_barcode
272
+ # return if not_registered
273
+ # @mim.highlight_barcode barcode_string
274
+ # end
275
+
276
+ # def barcode_bit_string(i)
277
+ # @mim.barcode_bit?(i) ? "1" : "0"
278
+ # end
@@ -3,13 +3,14 @@ require 'prawn'
3
3
 
4
4
  module Mork
5
5
 
6
- #TODO: read the prawn manual, we should probably use views
7
-
6
+ # Generating response sheets as PDF files.
7
+ # See the README file for usage
8
8
  class SheetPDF < Prawn::Document
9
- def initialize(content, grip=GridPDF.new)
10
- @grip = case grip
11
- when String, Hash; GridPDF.new grip
12
- when Mork::GridPDF; grip
9
+ def initialize(content, layout=nil)
10
+ @grip = case layout
11
+ when NilClass; GridPDF.new
12
+ when String, Hash; GridPDF.new layout
13
+ when Mork::GridPDF; layout
13
14
  else raise ArgumentError, 'Invalid initialization parameter'
14
15
  end
15
16
  super my_page_params
@@ -19,15 +20,21 @@ module Mork
19
20
  process
20
21
  end
21
22
 
22
- def save(fn)
23
- render_file fn
23
+ # Saving the PDF file to disk
24
+ #
25
+ # @param fname [String] the path/filename for the target PDF document
26
+ def save(fname)
27
+ render_file fname
24
28
  end
25
29
 
30
+ # The PDF document as a string
26
31
  def to_pdf
27
32
  render
28
33
  end
29
34
 
35
+ ###########################################################################
30
36
  private
37
+ ###########################################################################
31
38
 
32
39
  def my_page_params
33
40
  {
data/lib/mork/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mork
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
data/mork.gemspec CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.add_dependency 'narray', '~> 0.6'
22
22
  s.add_dependency 'mini_magick', '~> 4.5'
23
23
  s.add_dependency 'prawn', '~> 2.1'
24
+ s.add_dependency 'deep_merge', '~> 1.0'
24
25
  s.add_development_dependency 'rake', '~> 10.3'
25
26
  s.add_development_dependency 'rspec', '~> 3.1'
26
27
  s.add_development_dependency 'guard', '~> 2.6'
@@ -28,4 +29,5 @@ Gem::Specification.new do |s|
28
29
  s.add_development_dependency 'guard-shell', '~> 0.6'
29
30
  # s.add_development_dependency 'rb-fsevent'
30
31
  s.add_development_dependency 'awesome_print', '~> 1.2'
32
+ s.add_development_dependency 'byebug', '~> 9.0'
31
33
  end
data/mork.sublime-project CHANGED
@@ -3,7 +3,7 @@
3
3
  [
4
4
  {
5
5
  "path": ".",
6
- "folder_exclude_patterns": ["tmp", "log"]
6
+ "folder_exclude_patterns": ["tmp", "log", "doc", ".yardoc"]
7
7
  }
8
8
  ]
9
9
  }
@@ -2,9 +2,9 @@ require 'spec_helper'
2
2
 
3
3
  module Mork
4
4
  describe Array do
5
- it 'should give the standard deviation' do
6
- a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ]
7
- a.stdev.should == 4.594682917363407
5
+ it 'computes the average' do
6
+ a = [ 20, 23, 23, 24, 25, 22, 12, 21, 28 ]
7
+ expect(a.mean).to eq 22
8
8
  end
9
9
  end
10
10
  end
@@ -3,12 +3,13 @@ require 'spec_helper'
3
3
  module Mork
4
4
  describe GridOMR do
5
5
  before(:each) do
6
- @grom = GridOMR.new 'spec/samples/layout.yml'
6
+ @grom = GridOMR.new 'spec/samples/grid_omr_layout.yml'
7
7
  @grom.set_page_size 1601, 2281
8
8
  end
9
9
 
10
10
  describe '#choice_cell_area' do
11
11
  it 'returns the coordinates of the first choice cell' do
12
+ puts
12
13
  expect(@grom.choice_cell_area(0,0)).to have_coords(63, 436, 51, 41)
13
14
  end
14
15
 
@@ -4,7 +4,7 @@ module Mork
4
4
  describe Grid do
5
5
  context 'init params' do
6
6
  it 'does not work with an integer' do
7
- expect {Grid.new 1}.to raise_error RuntimeError
7
+ expect {Grid.new 1}.to raise_error ArgumentError
8
8
  end
9
9
  end
10
10
 
@@ -25,39 +25,6 @@ module Mork
25
25
  end
26
26
  end
27
27
  end
28
-
29
-
30
- # describe "#cell_x" do
31
- # context "for 1st-column questions" do
32
- # it "returns the distance from the registration frame of the left edge of the 1st choice" do
33
- # grid.send(:cell_x,0,0).should == 7.5
34
- # end
35
- #
36
- # it "returns the distance from the registration frame of the left edge of the 2nd choice" do
37
- # grid.send(:cell_x,0,1).should == 14.5
38
- # end
39
- # end
40
- #
41
- # context "for 4th-column questions" do
42
- # it "returns the distance from the registration frame of the left edge of the 1st choice" do
43
- # grid.send(:cell_x,120,0).should == 157.5
44
- # end
45
- #
46
- # it "returns the distance from the registration frame of the left edge of the 2nd choice" do
47
- # grid.send(:cell_x,120,1).should == 164.5
48
- # end
49
- # end
50
- # end
51
- #
52
- # describe "#cell_y" do
53
- # it "returns the distance from the registration frame of the top edge of the 1st row of cells" do
54
- # grid.send(:cell_y,0).should == 33.5
55
- # end
56
- #
57
- # it "returns the distance from the registration frame of the 40th row of cells" do
58
- # grid.send(:cell_y,39).should == 267.5
59
- # end
60
- # end
61
28
  end
62
29
  end
63
30
 
@@ -98,9 +65,41 @@ end
98
65
  # @grid.ctrl_area_dark.should == {:x=>1479, :y=>329, :w=>51, :h=>41}
99
66
  # end
100
67
  # end
101
- #
68
+
102
69
  # describe '#ctrl_area_light' do
103
70
  # it 'returns the coordinates of the control cell used to set the darkened threshold' do
104
71
  # @grid.ctrl_area_light.should == {:x=>1538, :y=>329, :w=>51, :h=>41}
105
72
  # end
106
73
  # end
74
+
75
+ # describe "#cell_x" do
76
+ # context "for 1st-column questions" do
77
+ # it "returns the distance from the registration frame of the left edge of the 1st choice" do
78
+ # grid.send(:cell_x,0,0).should == 7.5
79
+ # end
80
+ #
81
+ # it "returns the distance from the registration frame of the left edge of the 2nd choice" do
82
+ # grid.send(:cell_x,0,1).should == 14.5
83
+ # end
84
+ # end
85
+ #
86
+ # context "for 4th-column questions" do
87
+ # it "returns the distance from the registration frame of the left edge of the 1st choice" do
88
+ # grid.send(:cell_x,120,0).should == 157.5
89
+ # end
90
+ #
91
+ # it "returns the distance from the registration frame of the left edge of the 2nd choice" do
92
+ # grid.send(:cell_x,120,1).should == 164.5
93
+ # end
94
+ # end
95
+ # end
96
+ #
97
+ # describe "#cell_y" do
98
+ # it "returns the distance from the registration frame of the top edge of the 1st row of cells" do
99
+ # grid.send(:cell_y,0).should == 33.5
100
+ # end
101
+ #
102
+ # it "returns the distance from the registration frame of the 40th row of cells" do
103
+ # grid.send(:cell_y,39).should == 267.5
104
+ # end
105
+ # end
@@ -2,8 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  module Mork
4
4
  describe Magicko do
5
- let(:sh) { sample_img :slanted }
6
- let(:ma) { Magicko.new sh.filename }
5
+ let(:sh) { sample_img :jdoe1 }
6
+ let(:ma) { Magicko.new sh.image_path }
7
7
  let(:co) { Coord.new 50}
8
8
  let(:pp) { { tl: {x: 10, y: 10}, tr: {x: 1000, y: 10}, bl: {x: 10, y: 1700}, br: {x: 1000, y: 1700}} }
9
9