mork 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/README.md +107 -35
- data/lib/mork/coord.rb +1 -1
- data/lib/mork/extensions.rb +3 -0
- data/lib/mork/grid.rb +13 -6
- data/lib/mork/grid_const.rb +11 -9
- data/lib/mork/grid_omr.rb +15 -5
- data/lib/mork/grid_pdf.rb +1 -0
- data/lib/mork/magicko.rb +46 -28
- data/lib/mork/mimage.rb +113 -52
- data/lib/mork/mimage_list.rb +6 -5
- data/lib/mork/npatch.rb +16 -18
- data/lib/mork/sheet_omr.rb +210 -125
- data/lib/mork/sheet_pdf.rb +15 -8
- data/lib/mork/version.rb +1 -1
- data/mork.gemspec +2 -0
- data/mork.sublime-project +1 -1
- data/spec/mork/extensions_spec.rb +3 -3
- data/spec/mork/grid_omr_spec.rb +2 -1
- data/spec/mork/grid_spec.rb +34 -35
- data/spec/mork/magicko_spec.rb +2 -2
- data/spec/mork/mimage_spec.rb +22 -63
- data/spec/mork/sheet_omr_spec.rb +96 -201
- data/spec/mork/sheet_pdf_spec.rb +9 -10
- data/spec/out/{.gitignore → barcode/.gitignore} +0 -0
- data/spec/out/highlight/.gitignore +4 -0
- data/spec/out/mark/.gitignore +4 -0
- data/spec/out/outline/.gitignore +4 -0
- data/spec/out/pdf/.gitignore +4 -0
- data/spec/out/registration/.gitignore +4 -0
- data/spec/out/text/.gitignore +4 -0
- data/spec/samples/angolo.jpg +0 -0
- data/spec/samples/angolo2.jpg +0 -0
- data/spec/samples/angolo3.jpg +0 -0
- data/spec/samples/boxy.yml +0 -0
- data/spec/samples/grid.yml +0 -0
- data/spec/samples/grid160.yml +0 -0
- data/spec/samples/grid_omr_layout.yml +56 -0
- data/spec/samples/info.yml +15 -39
- data/spec/samples/jdoe/JohnDoe1.jpeg +0 -0
- data/spec/samples/jdoe/JohnDoe2.jpeg +0 -0
- data/spec/samples/jdoe/JohnDoe3.jpeg +0 -0
- data/spec/samples/jdoe/layout.yml +58 -0
- data/spec/samples/layout.yml +2 -2
- data/spec/samples/lucrezia/border1.pdf +0 -0
- data/spec/samples/lucrezia/border2.pdf +0 -0
- data/spec/samples/lucrezia/bw1.pdf +0 -0
- data/spec/samples/lucrezia/bw2.pdf +0 -0
- data/spec/samples/lucrezia/gray1.pdf +0 -0
- data/spec/samples/lucrezia/gray2.pdf +0 -0
- data/spec/samples/{syst/IMG_20150104_0004.jpg → marisol/marisol1.jpg} +0 -0
- data/spec/samples/{syst/IMG_20150104_0009.jpg → marisol/marisol2.jpg} +0 -0
- data/spec/samples/{syst/IMG_20150104_0011.jpg → marisol/marisol3.jpg} +0 -0
- data/spec/samples/reg_mark.jpg +0 -0
- data/spec/samples/rm00.jpeg +0 -0
- data/spec/samples/rm01.jpeg +0 -0
- data/spec/samples/rm02.jpeg +0 -0
- data/spec/samples/rm03.jpeg +0 -0
- data/spec/samples/rm04.jpeg +0 -0
- data/spec/samples/rm05.jpeg +0 -0
- data/spec/samples/syst/barr0.jpg +0 -0
- data/spec/samples/syst/barr1.jpg +0 -0
- data/spec/samples/syst/barr2.jpg +0 -0
- data/spec/samples/syst/bell0.jpg +0 -0
- data/spec/samples/syst/bell1.jpg +0 -0
- data/spec/samples/syst/bell2.jpg +0 -0
- data/spec/samples/syst/bila0.jpg +0 -0
- data/spec/samples/syst/bila1.jpg +0 -0
- data/spec/samples/syst/bila2.jpg +0 -0
- data/spec/samples/syst/bila3.jpg +0 -0
- data/spec/samples/syst/bila4.jpg +0 -0
- data/spec/samples/syst/bone0.jpg +0 -0
- data/spec/samples/syst/bone1.jpg +0 -0
- data/spec/samples/syst/bone2.jpg +0 -0
- data/spec/samples/syst/cost0.jpg +0 -0
- data/spec/samples/syst/cost1.jpg +0 -0
- data/spec/samples/syst/cost2.jpg +0 -0
- data/spec/samples/syst/cost3.jpg +0 -0
- data/spec/samples/syst/cost4.jpg +0 -0
- data/spec/samples/syst/dald0.jpg +0 -0
- data/spec/samples/syst/dald1.jpg +0 -0
- data/spec/samples/syst/dald2.jpg +0 -0
- data/spec/samples/syst/dald3.jpg +0 -0
- data/spec/samples/syst/dald4.jpg +0 -0
- data/spec/samples/syst/dign0.jpg +0 -0
- data/spec/samples/syst/dign1.jpg +0 -0
- data/spec/samples/syst/dign2.jpg +0 -0
- data/spec/samples/syst/dive0.jpg +0 -0
- data/spec/samples/syst/dive1.jpg +0 -0
- data/spec/samples/syst/dive2.jpg +0 -0
- data/spec/samples/syst/histo.m +0 -0
- data/spec/samples/{syst_grid.yml → syst/layout.yml} +0 -0
- data/spec/spec_helper.rb +29 -29
- metadata +49 -62
- data/auto.txt +0 -0
- data/spec/samples/out-1.jpg +0 -0
- data/spec/samples/qzc013.jpg +0 -0
- data/spec/samples/sample02.jpg +0 -0
- data/spec/samples/sample_gray.jpg +0 -0
- data/spec/samples/sheet.jpg +0 -0
- data/spec/samples/sheet666.jpg +0 -0
- data/spec/samples/slanted.jpg +0 -0
- data/spec/samples/slanted.yml +0 -54
- data/spec/samples/syst/IMG_20150104_0004.txt +0 -4955
- data/spec/samples/syst/IMG_20150104_0009.txt +0 -4955
- data/spec/samples/syst/IMG_20150104_0011.txt +0 -4955
- data/spec/samples/syst/SCN_0001.jpg +0 -0
- data/spec/samples/syst/SCN_0001.txt +0 -4955
- data/spec/samples/syst/barr0.txt +0 -4955
- data/spec/samples/syst/barr1.txt +0 -4955
- data/spec/samples/syst/barr2.txt +0 -4955
- data/spec/samples/syst/bell0.txt +0 -4955
- data/spec/samples/syst/bell1.txt +0 -4955
- data/spec/samples/syst/bell2.txt +0 -4955
- data/spec/samples/syst/bila0.txt +0 -4955
- data/spec/samples/syst/bila1.txt +0 -4955
- data/spec/samples/syst/bila2.txt +0 -4955
- data/spec/samples/syst/bila3.txt +0 -4955
- data/spec/samples/syst/bila4.txt +0 -4955
- data/spec/samples/syst/bone0.txt +0 -4955
- data/spec/samples/syst/bone1.txt +0 -4955
- data/spec/samples/syst/bone2.txt +0 -4955
- data/spec/samples/syst/cost0.txt +0 -4955
- data/spec/samples/syst/cost1.txt +0 -4955
- data/spec/samples/syst/cost2.txt +0 -4955
- data/spec/samples/syst/cost3.txt +0 -4955
- data/spec/samples/syst/cost4.txt +0 -4955
- data/spec/samples/syst/dald0.txt +0 -4955
- data/spec/samples/syst/dald1.txt +0 -4955
- data/spec/samples/syst/dald2.txt +0 -4955
- data/spec/samples/syst/dald3.txt +0 -4955
- data/spec/samples/syst/dald4.txt +0 -4955
- data/spec/samples/syst/dign0.txt +0 -4955
- data/spec/samples/syst/dign1.txt +0 -4955
- data/spec/samples/syst/dign2.txt +0 -4955
- data/spec/samples/syst/dive0.txt +0 -4955
- data/spec/samples/syst/dive1.txt +0 -4955
- data/spec/samples/syst/dive2.txt +0 -4955
- data/spec/samples/syst/out0000.jpg +0 -0
- data/spec/samples/syst/out0000.txt +0 -4955
- data/spec/samples/syst/out0001.jpg +0 -0
- data/spec/samples/syst/out0001.txt +0 -4955
- data/spec/samples/syst/out0002.jpg +0 -0
- data/spec/samples/syst/out0002.txt +0 -4955
- data/spec/samples/syst/qzc013.jpg +0 -0
- data/spec/samples/syst/qzc013.txt +0 -4955
- data/spec/samples/syst/sample_gray.jpg +0 -0
- data/spec/samples/syst/sample_gray.txt +0 -4955
- data/spec/samples/two_pages.pdf +0 -0
data/lib/mork/sheet_omr.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
11
|
-
|
12
|
-
when
|
13
|
-
[
|
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
|
-
[
|
32
|
+
[grom.max_choices_per_question] * choices
|
16
33
|
when Array
|
17
|
-
|
34
|
+
choices
|
18
35
|
end
|
19
|
-
@mim = Mimage.new path,
|
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
|
-
#
|
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
|
-
#
|
63
|
+
# Sheet barcode as a binary-like string
|
39
64
|
#
|
40
|
-
#
|
41
|
-
#
|
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
|
-
|
45
|
-
|
69
|
+
@mim.barcode_bits.map do |b|
|
70
|
+
b ? '1' : '0'
|
71
|
+
end.join.reverse
|
46
72
|
end
|
47
73
|
|
48
|
-
#
|
74
|
+
# True if the specified question/choice cell has been marked
|
49
75
|
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
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
|
81
|
+
@mim.marked[question][choice]
|
55
82
|
end
|
56
83
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
# mark_array(range)
|
84
|
+
# The set of choice indices marked on the response sheet
|
61
85
|
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
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
|
-
#
|
80
|
-
#
|
81
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
98
|
-
|
110
|
+
marked_choices.map do |q|
|
111
|
+
q.map { |cho| (65+cho).chr }
|
99
112
|
end
|
100
113
|
end
|
101
114
|
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
|
106
|
-
def
|
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.
|
121
|
+
@mim.marked
|
131
122
|
end
|
132
123
|
|
133
|
-
|
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.
|
138
|
+
@mim.overlay what, where
|
136
139
|
end
|
137
140
|
|
138
|
-
#
|
141
|
+
# Saves a copy of the source image after registration;
|
142
|
+
# the output image will also contain any previously applied overlays.
|
139
143
|
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
|
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.
|
148
|
+
@mim.save(fname, true)
|
147
149
|
end
|
148
150
|
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
155
|
-
|
156
|
-
|
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
|
data/lib/mork/sheet_pdf.rb
CHANGED
@@ -3,13 +3,14 @@ require 'prawn'
|
|
3
3
|
|
4
4
|
module Mork
|
5
5
|
|
6
|
-
#
|
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,
|
10
|
-
@grip = case
|
11
|
-
when
|
12
|
-
when
|
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
|
-
|
23
|
-
|
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
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
@@ -2,9 +2,9 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Mork
|
4
4
|
describe Array do
|
5
|
-
it '
|
6
|
-
a = [ 20, 23, 23, 24, 25, 22, 12, 21,
|
7
|
-
a.
|
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
|
data/spec/mork/grid_omr_spec.rb
CHANGED
@@ -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/
|
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
|
|
data/spec/mork/grid_spec.rb
CHANGED
@@ -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
|
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
|
data/spec/mork/magicko_spec.rb
CHANGED
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Mork
|
4
4
|
describe Magicko do
|
5
|
-
let(:sh) { sample_img :
|
6
|
-
let(:ma) { Magicko.new sh.
|
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
|
|