mork 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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