mork 0.0.9 → 0.0.10
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/.ruby-version +1 -1
- data/README.md +130 -24
- data/lib/mork/extensions.rb +26 -0
- data/lib/mork/grid.rb +47 -325
- data/lib/mork/grid_const.rb +33 -35
- data/lib/mork/grid_omr.rb +94 -0
- data/lib/mork/grid_pdf.rb +107 -0
- data/lib/mork/mimage.rb +14 -2
- data/lib/mork/npatch.rb +0 -1
- data/lib/mork/{sheet.rb → sheet_omr.rb} +87 -89
- data/lib/mork/sheet_pdf.rb +60 -66
- data/lib/mork/version.rb +1 -1
- data/lib/mork.rb +2 -6
- data/spec/mork/extensions_spec.rb +10 -0
- data/spec/mork/grid_omr_spec.rb +122 -0
- data/spec/mork/grid_spec.rb +22 -138
- data/spec/mork/sheet_omr_spec.rb +165 -0
- data/spec/mork/sheet_pdf_spec.rb +53 -43
- data/spec/samples/grid01.yml +3 -4
- data/spec/samples/grid02.yml +57 -0
- data/spec/samples/grid160.yml +57 -0
- data/spec/samples/grid_omr_01.yml +28 -0
- data/spec/samples/sample_color.pdf +0 -0
- data/spec/samples/sample_gray.jpg +0 -0
- data/spec/samples/sheet666.jpg +0 -0
- data/spec/samples/sheet666.pdf +12566 -0
- data/spec/spec_helper.rb +3 -20
- metadata +50 -28
- data/spec/mork/sheet_spec.rb +0 -178
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdc12cd7f6628650dd014afd86ed58f415b3167b
|
4
|
+
data.tar.gz: f39a614c5641b14b63cf0c7ba2f5cbda76219aff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a17ad3a56ba66c46b7b1e583ce5d7a0184e365280814dc1144c4ff4594a63451a46344cf618e0ac07f87425ed88d92d2ec634434fd93ebcee3e6fa1a8ff802d0
|
7
|
+
data.tar.gz: adcdd8f1d6ca75d97dbd10f10e2195ee7203288196af55b85844352390a6275ff2c84d1464470b8619180116a8d6978b183a22ab29e5a77e1b02062149588c43
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.1.2
|
data/README.md
CHANGED
@@ -1,46 +1,152 @@
|
|
1
1
|
Mork
|
2
2
|
====
|
3
3
|
|
4
|
-
A ruby [optical mark recognition](http://en.wikipedia.org/wiki/Optical_mark_recognition) (OMR) library
|
4
|
+
A ruby [optical mark recognition](http://en.wikipedia.org/wiki/Optical_mark_recognition) (OMR) library aimed at accomplishing two tasks in the context of paper-based, multiple-choice tests and surveys:
|
5
5
|
|
6
|
-
1. generating [response sheets](/spec/samples/
|
7
|
-
2. capturing the responses provided on the [printed sheet](/spec/samples/
|
6
|
+
1. generating [response sheets](/spec/samples/sample01.pdf) in PDF format
|
7
|
+
2. capturing the responses provided on the [printed sheet](/spec/samples/sample01.jpg) by a human with a pen or a pencil.
|
8
8
|
|
9
9
|
Assumptions and limitations
|
10
10
|
---------------------------
|
11
11
|
Mork is a low-level library, and very much work in progress. It is not, and will likely never be a complete OMR solution. While suggestions and contributions are more than welcome, for the time being several assumptions and restrictions to what the library is capable of apply.
|
12
12
|
|
13
|
-
- the generated
|
14
|
-
- the
|
13
|
+
- the PDF files generated by Mork [such as this one](/spec/samples/sample01.pdf) are intended to be printed on regular printer paper
|
14
|
+
- all items must fit within the allocated response area of a single sheet
|
15
|
+
- after collecting the responses, a filled-out form can be acquired as a bitmap image by a normal optical scanner or camera (i.e., no specialized equipment is necessary)
|
16
|
+
- the response sheet contains the following items:
|
15
17
|
- registration marks at each page corner
|
16
18
|
- a bar code along the bottom margin to uniquely identify the sheet
|
17
19
|
- a header area to print arbitrary information
|
18
20
|
- a response area containing a list of numbered items (questions)
|
19
|
-
- each item contains an arbitrary number of choices, each marked with a capital letter (A, B, C, ...). In order to maximize contrast, choice
|
21
|
+
- each item contains an arbitrary number of choices, each marked with a capital letter (A, B, C, ...). In order to maximize contrast, assuming that responses will be given using a black or blue pen, choice cells are printed in red
|
22
|
+
- the far right side of the response area is reserved for a column of calibration cells that must remain blank
|
20
23
|
- with some restrictions, the number of columns in the response area, the numer of items, the number of choices per item, the size and shape of the choice cells can be set by the user
|
21
|
-
- all items must fit within the allocated response area of a single sheet
|
22
|
-
|
23
|
-
Installing
|
24
|
-
----------
|
25
|
-
|
26
|
-
Install the gem in your system:
|
27
|
-
|
28
|
-
$ gem install mork
|
29
|
-
|
30
|
-
If you are using bundler in your project, as you should, make sure your `Gemfile` contains the following, before running `bundle install`:
|
31
|
-
|
32
|
-
source 'http://rubygems.org'
|
33
|
-
gem 'mork'
|
34
|
-
|
35
|
-
Usage
|
36
|
-
-----
|
37
24
|
|
38
|
-
|
25
|
+
Creating response sheets
|
26
|
+
------------------------
|
27
|
+
|
28
|
+
To create a small ruby project for testing purposes, cd into a directory of choice, then execute the following shell commands:
|
29
|
+
|
30
|
+
```
|
31
|
+
mkdir mork_test
|
32
|
+
cd mork_test
|
33
|
+
echo "source 'http://rubygems.org'" > Gemfile
|
34
|
+
echo "gem 'mork'" >> Gemfile
|
35
|
+
echo "require 'mork'" > mork_test.rb
|
36
|
+
echo "include Mork" >> mork_test.rb
|
37
|
+
bundle install
|
38
|
+
```
|
39
|
+
|
40
|
+
Creating response sheets is done through the `Mork::SheetPDF` class.
|
41
|
+
|
42
|
+
[...work in progress...]
|
43
|
+
|
44
|
+
For example, add the following code to `mork_test.rb`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
content = {
|
48
|
+
# the response sheet's unique identifier; it is printed
|
49
|
+
# in binary form as a barcode at the bottom of the sheet
|
50
|
+
barcode: 123456,
|
51
|
+
# number of items and number of choices per item
|
52
|
+
# in this case: 100 items with 5 choices each
|
53
|
+
choices: [5] * 100,
|
54
|
+
# stuff to print in the header
|
55
|
+
header: {
|
56
|
+
name: 'John Doe UI354320',
|
57
|
+
title: 'Anatomy and Physiology test, Sept 20, 2014',
|
58
|
+
code: '201.48',
|
59
|
+
signature: 'Signature'
|
60
|
+
}
|
61
|
+
}
|
62
|
+
s = SheetPDF.new content
|
63
|
+
s.save 'sheet.pdf'
|
64
|
+
system 'open sheet.pdf' # this works in OSX
|
65
|
+
```
|
66
|
+
|
67
|
+
In the shell, execute the following and inspect the output PDF file
|
68
|
+
|
69
|
+
bundle exec ruby mork_test.rb
|
70
|
+
|
71
|
+
The “content” hash provides SheetPDF with the information necessary to create a meaningful response sheet. These are the key-value pairs that the hash should contain:
|
72
|
+
|
73
|
+
- `barcode`: an integer number, the sheet's unique identifier
|
74
|
+
- `choices`: an array of integers, specifiying the number of items in the test (the length of the array) and the number of choices available for each item (the array values)
|
75
|
+
- `header`: a hash of key-value pairs defining the content of named header elements; in each pair, the key is the name of one header element, while the value is the rendered content; the actually available elements are defined in the Grid object (see below)
|
76
|
+
|
77
|
+
It's easy to see that by iterating over a series of `content` hashes you can produce any number of sheets, all based on the same layout but each containing unique information (notably the barcode, but also names, dates, etc.)
|
78
|
+
|
79
|
+
But where is the layout itself defined? In the above example, creating the SheetPDF with a single initialization parameter (the `content` hash) caused a default boilerplate layout to be invoked. In real life, however, you will want to have full control of the layout. This is accomplished by passing a `Mork::Grid` object as a second argument in the SheetPDF constructor call.
|
80
|
+
|
81
|
+
The Grid is a layout descriptor, it tells the SheetPDF object in detail where to render what on the sheet. To get a rough idea of the information managed by a Grid, try this:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
g = Grid.new
|
85
|
+
g.show
|
86
|
+
```
|
87
|
+
|
88
|
+
The YAML-formatted output you see is the full list of parameters that describe the default Grid.
|
89
|
+
|
90
|
+
```yaml
|
91
|
+
---
|
92
|
+
:page_size:
|
93
|
+
:width: 210
|
94
|
+
:height: 297
|
95
|
+
:regmarks:
|
96
|
+
:margin: 10
|
97
|
+
:radius: 2.5
|
98
|
+
:search: 10
|
99
|
+
:offset: 2
|
100
|
+
:header:
|
101
|
+
:name:
|
102
|
+
:top: 5
|
103
|
+
:left: 7.5
|
104
|
+
:width: 170
|
105
|
+
:size: 14
|
106
|
+
|
107
|
+
...more...
|
108
|
+
```
|
109
|
+
|
110
|
+
Thus, if you replace the call to SheetPDF with the following:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
s = SheetPDF.new content, Grid.new
|
114
|
+
```
|
115
|
+
|
116
|
+
...you get exactly the same result as before. In order to actually customize the layout, the Grid object must be constructed according to your own specifications. One way to initialize a custom Grid is to send the constructor a YAML path/file name. Copy the file `grid01.yml` from the `spec/samples` directory to your working directory. Edit some values, save, and execute:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
s = SheetPDF.new content, Grid.new('grid01.yml')
|
120
|
+
s.save 'sheet.pdf'
|
121
|
+
```
|
122
|
+
|
123
|
+
Although this will work as well:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
s = SheetPDF.new content, 'grid01.yml'
|
127
|
+
s.save 'sheet.pdf'
|
128
|
+
```
|
129
|
+
|
130
|
+
Please note that currently there are no sanity checks on Grid parameters. The user is entirely responsible for providing values that actually produce the desired layout.
|
131
|
+
|
132
|
+
Scoring response sheets
|
133
|
+
-----------------------
|
134
|
+
|
135
|
+
Copy the file `sample01.jpg` from `spec/samples` to your working directory, along with a fresh copy of `grid01.yml` and run the following:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
s = SheetOMR.new 'sample01.jpg', 'grid01.yml'
|
139
|
+
s.highlight
|
140
|
+
s.write 'highlights.jpg'
|
141
|
+
system 'open highlights.jpg' # OSX only
|
142
|
+
```
|
143
|
+
|
144
|
+
Print out the PDF file you produced, and fill in a few response cells with a black pen. Be sure to also fill in the calibration ‘T’ cell. Now scan the sheet and save the image as a JPEG file (e.g. `img01.jpg`). A resolution of 150-200 dpi should be sufficient. Place the JPEG file in your working directory and run the following:
|
39
145
|
|
40
146
|
License
|
41
147
|
-------
|
42
148
|
|
43
|
-
Copyright (c)
|
149
|
+
Copyright (c) 2014 Giuseppe Bertini
|
44
150
|
|
45
151
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
46
152
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Array
|
2
|
+
def mean
|
3
|
+
@the_sample_mean ||= inject(:+)/length.to_f
|
4
|
+
end
|
5
|
+
|
6
|
+
def sample_variance
|
7
|
+
sum = inject(0){|accum, i| accum + (i-mean)**2 }
|
8
|
+
sum/(length - 1).to_f
|
9
|
+
end
|
10
|
+
|
11
|
+
def stdev
|
12
|
+
Math.sqrt sample_variance
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Fixnum
|
17
|
+
def mm
|
18
|
+
self * 2.83464566929134
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Float
|
23
|
+
def mm
|
24
|
+
self * 2.83464566929134
|
25
|
+
end
|
26
|
+
end
|
data/lib/mork/grid.rb
CHANGED
@@ -1,306 +1,60 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'mork/grid_const'
|
2
3
|
|
3
4
|
module Mork
|
4
5
|
# The Grid is a set of expectations on what the response sheet should look like
|
5
|
-
# It knows nothing about the actual scanned image
|
6
|
+
# It knows nothing about the actual scanned image.
|
6
7
|
# All returned values are in the arbitrary units given in the configuration file
|
7
8
|
class Grid
|
8
|
-
|
9
|
+
# Calling Grid.new without arguments creates the default boilerplate Grid
|
9
10
|
def initialize(options=nil)
|
10
11
|
@params = DGRID
|
11
|
-
case options
|
12
|
-
when
|
13
|
-
if File.exists?
|
12
|
+
case options
|
13
|
+
when NilClass
|
14
|
+
if File.exists?('config/grid.yml')
|
14
15
|
@params.merge! symbolize YAML.load_file('config/grid.yml')
|
15
16
|
end
|
16
|
-
when
|
17
|
+
when Hash
|
17
18
|
@params.merge! symbolize options
|
18
|
-
when
|
19
|
+
when String
|
19
20
|
@params.merge! symbolize YAML.load_file(options)
|
20
21
|
else
|
21
|
-
raise "Invalid
|
22
|
+
raise "Invalid parameter in the Grid constructor: #{options.class.inspect}"
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
return obj.inject([]){|memo,v | memo << symbolize(v); memo} if obj.is_a? Array
|
32
|
-
return obj
|
26
|
+
# Puts out the Grid parameters in YAML format; the entire hash is displayed
|
27
|
+
# if no arguments are given; you can specify what to show by passing one of:
|
28
|
+
# :page_size, :regmarks, :header, :items, :barcode, :control
|
29
|
+
def show(subset=nil)
|
30
|
+
out = subset ? @params[subset] : @params
|
31
|
+
puts out.to_yaml
|
33
32
|
end
|
34
33
|
|
35
34
|
def options
|
36
35
|
@params
|
37
36
|
end
|
38
37
|
|
39
|
-
def set_page_size(x, y)
|
40
|
-
@px = x.to_f
|
41
|
-
@py = y.to_f
|
42
|
-
end
|
43
|
-
|
44
38
|
def max_questions
|
45
39
|
columns * rows
|
46
40
|
end
|
47
41
|
|
48
|
-
def
|
49
|
-
@params[:
|
50
|
-
end
|
51
|
-
|
52
|
-
def code_bits
|
53
|
-
@params[:code][:bits].to_i
|
54
|
-
end
|
55
|
-
|
56
|
-
# ====================================================
|
57
|
-
# = Returning {x, y, w, h} hashes for area locations =
|
58
|
-
# ====================================================
|
59
|
-
# {} = rm_search_area(x, y)
|
60
|
-
#
|
61
|
-
# the 4 values needed to locate a single registration mark
|
62
|
-
def rm_search_area(corner, i)
|
63
|
-
{
|
64
|
-
x: (ppu_x * rmx(corner, i)).round,
|
65
|
-
y: (ppu_y * rmy(corner, i)).round,
|
66
|
-
w: (ppu_x * (reg_search + reg_step * i)).round,
|
67
|
-
h: (ppu_y * (reg_search + reg_step * i)).round
|
68
|
-
}
|
69
|
-
end
|
70
|
-
|
71
|
-
def rm_edgy_x
|
72
|
-
(ppu_x * reg_radius).round + 5
|
73
|
-
end
|
74
|
-
|
75
|
-
def rm_edgy_y
|
76
|
-
(ppu_y * reg_radius).round + 5
|
77
|
-
end
|
78
|
-
|
79
|
-
def rm_max_search_area_side
|
80
|
-
(ppu_x * page_width / 4).round
|
81
|
-
end
|
82
|
-
|
83
|
-
def choice_cell_area(q, c)
|
84
|
-
cell_area cell_x(q, c), cell_y(q)
|
85
|
-
end
|
86
|
-
|
87
|
-
def ctrl_area_dark
|
88
|
-
cell_area ctrl_cell_x, ctrl_cell_y
|
89
|
-
end
|
90
|
-
|
91
|
-
def ctrl_area_light
|
92
|
-
cell_area ctrl_cell_x + cell_spacing, ctrl_cell_y
|
93
|
-
end
|
94
|
-
|
95
|
-
def cal_area_white
|
96
|
-
code_cell_area -1
|
97
|
-
end
|
98
|
-
|
99
|
-
def cal_area_black
|
100
|
-
code_cell_area 0
|
101
|
-
end
|
102
|
-
|
103
|
-
def code_bit_area(i)
|
104
|
-
raise "Invalid code bit" if i >= code_bits
|
105
|
-
code_cell_area i+1
|
106
|
-
end
|
107
|
-
|
108
|
-
# ============
|
109
|
-
# = to Prawn =
|
110
|
-
# ============
|
111
|
-
def pdf_page_size
|
112
|
-
[page_width.mm, page_height.mm]
|
113
|
-
end
|
114
|
-
|
115
|
-
def pdf_margins
|
116
|
-
reg_margin.mm
|
117
|
-
end
|
118
|
-
|
119
|
-
def pdf_reg_marks
|
120
|
-
r = reg_radius.mm
|
121
|
-
[
|
122
|
-
{ p: [0, 0 ], r: r },
|
123
|
-
{ p: [0, reg_frame_height.mm], r: r },
|
124
|
-
{ p: [reg_frame_width.mm, reg_frame_height.mm], r: r },
|
125
|
-
{ p: [reg_frame_width.mm, 0 ], r: r }
|
126
|
-
]
|
127
|
-
end
|
128
|
-
|
129
|
-
def pdf_dark_calibration_area
|
130
|
-
pdf_code_cell_area 0
|
131
|
-
end
|
132
|
-
|
133
|
-
def pdf_code_areas_for(code)
|
134
|
-
a = []
|
135
|
-
code_bits.times do |bit|
|
136
|
-
a << pdf_code_cell_area(bit+1) if code[bit] == 1
|
137
|
-
end
|
138
|
-
a
|
139
|
-
end
|
140
|
-
|
141
|
-
def pdf_code_bit_areas
|
142
|
-
(1..code_bits).collect do |bit|
|
143
|
-
pdf_code_cell_area bit
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def pdf_code_cell_area(i)
|
148
|
-
{
|
149
|
-
p: [code_cell_x(i).mm, (reg_frame_height - code_y).mm],
|
150
|
-
w: code_width.mm,
|
151
|
-
h: code_height.mm * 2
|
152
|
-
}
|
153
|
-
end
|
154
|
-
|
155
|
-
def pdf_choice_cell_area(q, c)
|
156
|
-
{
|
157
|
-
p: [cell_x(q, c).mm, (reg_frame_height - cell_y(q)).mm],
|
158
|
-
w: cell_width.mm,
|
159
|
-
h: cell_height.mm
|
160
|
-
}
|
161
|
-
end
|
162
|
-
|
163
|
-
def pdf_choice_letter_xy(q, c)
|
164
|
-
[
|
165
|
-
cell_x(q, c).mm + 2.mm,
|
166
|
-
(reg_frame_height - cell_y(q)).mm - (cell_height*2)
|
167
|
-
]
|
168
|
-
end
|
169
|
-
|
170
|
-
def pdf_qnum_xy(q)
|
171
|
-
[
|
172
|
-
cell_x(q, 0).mm - pdf_qnum_width - @params[:items][:number_margin].to_f.mm,
|
173
|
-
(reg_frame_height - cell_y(q)).mm - cell_height
|
174
|
-
]
|
175
|
-
end
|
176
|
-
|
177
|
-
def pdf_qnum_size
|
178
|
-
@params[:items][:number_size].to_f
|
179
|
-
end
|
180
|
-
|
181
|
-
def pdf_chlett_size
|
182
|
-
@params[:items][:letter_size].to_f
|
183
|
-
end
|
184
|
-
|
185
|
-
def pdf_qnum_width
|
186
|
-
@params[:items][:number_width].to_f.mm
|
187
|
-
end
|
188
|
-
|
189
|
-
def pdf_header_xy(k)
|
190
|
-
[
|
191
|
-
@params[:header][k][:left].to_f.mm,
|
192
|
-
(reg_frame_height - @params[:header][k][:top].to_f).mm
|
193
|
-
]
|
194
|
-
end
|
195
|
-
|
196
|
-
def pdf_header_padding(k)
|
197
|
-
[
|
198
|
-
1.mm,
|
199
|
-
pdf_header_height(k) - 1.mm
|
200
|
-
]
|
201
|
-
end
|
202
|
-
|
203
|
-
def pdf_header_width(k)
|
204
|
-
@params[:header][k][:width].to_f.mm
|
205
|
-
end
|
206
|
-
|
207
|
-
def pdf_header_height(k)
|
208
|
-
@params[:header][k][:height].to_f.mm
|
209
|
-
end
|
210
|
-
|
211
|
-
def pdf_header_size(k)
|
212
|
-
@params[:header][k][:size].to_f
|
213
|
-
end
|
214
|
-
|
215
|
-
def pdf_header_boxed?(k)
|
216
|
-
@params[:header][k][:box] == true
|
217
|
-
end
|
218
|
-
|
219
|
-
def pdf_ctrl_area_dark
|
220
|
-
{
|
221
|
-
p: [pdf_control_xy[0]+pdf_control_width+ctrl_margin.mm, pdf_control_xy[1]],
|
222
|
-
w: cell_width.mm,
|
223
|
-
h: cell_height.mm
|
224
|
-
}
|
225
|
-
end
|
226
|
-
|
227
|
-
def pdf_ctrl_area_light
|
228
|
-
{
|
229
|
-
p: [
|
230
|
-
cell_spacing.mm +
|
231
|
-
pdf_control_xy[0] +
|
232
|
-
pdf_control_width +
|
233
|
-
ctrl_margin.mm,
|
234
|
-
pdf_control_xy[1]
|
235
|
-
],
|
236
|
-
w: cell_width.mm,
|
237
|
-
h: cell_height.mm
|
238
|
-
}
|
42
|
+
def barcode_bits
|
43
|
+
@params[:barcode][:bits].to_i
|
239
44
|
end
|
240
45
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
xy[0] + 2.mm,
|
245
|
-
xy[1] - 4.mm
|
246
|
-
]
|
247
|
-
end
|
248
|
-
|
249
|
-
def pdf_light_control_letter_xy
|
250
|
-
xy = pdf_ctrl_area_light[:p]
|
251
|
-
[
|
252
|
-
xy[0] + 2.mm,
|
253
|
-
xy[1] - 4.mm
|
254
|
-
]
|
255
|
-
end
|
256
|
-
|
257
|
-
def pdf_control_width
|
258
|
-
@params[:control][:width].to_f.mm
|
259
|
-
end
|
260
|
-
|
261
|
-
def pdf_control_xy
|
262
|
-
[
|
263
|
-
@params[:control][:left].to_f.mm,
|
264
|
-
(reg_frame_height - ctrl_cell_y).mm
|
265
|
-
]
|
266
|
-
end
|
46
|
+
#====================#
|
47
|
+
private
|
48
|
+
#====================#
|
267
49
|
|
268
|
-
|
269
|
-
|
50
|
+
# recursively turn hash keys into symbols. pasted from
|
51
|
+
# http://stackoverflow.com/questions/800122/best-way-to-convert-strings-to-symbols-in-hash
|
52
|
+
def symbolize(obj)
|
53
|
+
return obj.inject({}){|memo,(k,v)| memo[k.to_sym] = symbolize(v); memo} if obj.is_a? Hash
|
54
|
+
return obj.inject([]){|memo,v | memo << symbolize(v); memo} if obj.is_a? Array
|
55
|
+
return obj
|
270
56
|
end
|
271
|
-
|
272
|
-
private
|
273
57
|
|
274
|
-
def cx
|
275
|
-
@px / reg_frame_width
|
276
|
-
end
|
277
|
-
|
278
|
-
def cy
|
279
|
-
@py / reg_frame_height
|
280
|
-
end
|
281
|
-
|
282
|
-
def ppu_x
|
283
|
-
# horizontal pixels per unit of length (mm by def)
|
284
|
-
@px / page_width
|
285
|
-
end
|
286
|
-
|
287
|
-
def ppu_y
|
288
|
-
# vertical pixels per unit of length (mm by def)
|
289
|
-
@py / page_height
|
290
|
-
end
|
291
|
-
|
292
|
-
# {} = cell_area(x, y)
|
293
|
-
#
|
294
|
-
# the 4 values needed to locate a single cell area
|
295
|
-
def cell_area(x,y)
|
296
|
-
{
|
297
|
-
x: (cx * x ).round,
|
298
|
-
y: (cy * y ).round,
|
299
|
-
w: (cx * cell_width ).round,
|
300
|
-
h: (cy * cell_height ).round
|
301
|
-
}
|
302
|
-
end
|
303
|
-
|
304
58
|
# cell_y(q)
|
305
59
|
#
|
306
60
|
# the distance from the registration frame to the top edge
|
@@ -317,63 +71,29 @@ module Mork
|
|
317
71
|
first_x + column_width * (q / rows) + cell_spacing * c - cell_width / 2
|
318
72
|
end
|
319
73
|
|
320
|
-
|
321
|
-
|
322
|
-
# ==============
|
323
|
-
def code_cell_area(i)
|
324
|
-
{
|
325
|
-
x: (cx * code_cell_x(i)).round,
|
326
|
-
y: (cy * code_y ).round,
|
327
|
-
w: (cx * code_width ).round,
|
328
|
-
h: (cy * code_height ).round
|
329
|
-
}
|
330
|
-
end
|
331
|
-
|
332
|
-
def code_cell_x(i)
|
333
|
-
@params[:code][:left] + @params[:code][:spacing] * i
|
334
|
-
end
|
335
|
-
|
336
|
-
def code_y
|
337
|
-
reg_frame_height - code_height
|
74
|
+
def cal_cell_x
|
75
|
+
reg_frame_width - cell_spacing
|
338
76
|
end
|
339
77
|
|
340
|
-
#
|
341
|
-
# =
|
342
|
-
#
|
343
|
-
|
344
|
-
def rmx(corner, i)
|
345
|
-
case corner
|
346
|
-
when :tl; reg_off
|
347
|
-
when :tr; page_width - reg_search - reg_off - reg_step * i
|
348
|
-
when :br; page_width - reg_search - reg_off - reg_step * i
|
349
|
-
when :bl; reg_off
|
350
|
-
end
|
351
|
-
end
|
78
|
+
# ===========
|
79
|
+
# = barcode =
|
80
|
+
# ===========
|
352
81
|
|
353
|
-
def
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
82
|
+
# def barcode_bit_area(i)
|
83
|
+
# raise "Invalid barcode bit" if i >= barcode_bits
|
84
|
+
# cal_cell_x i+1
|
85
|
+
# end
|
86
|
+
|
87
|
+
def barcode_bit_x(i)
|
88
|
+
@params[:barcode][:left] + @params[:barcode][:spacing] * i
|
360
89
|
end
|
361
90
|
|
362
|
-
def reg_step
|
363
|
-
reg_radius
|
364
|
-
end
|
365
|
-
|
366
91
|
# ===============================
|
367
92
|
# = Simple parameter extraction =
|
368
93
|
# ===============================
|
369
|
-
def
|
370
|
-
def
|
371
|
-
def
|
372
|
-
def ctrl_cell_y() @params[:control][:top].to_f end
|
373
|
-
def code_height() @params[:code][:height].to_f end
|
374
|
-
def code_width() @params[:code][:width].to_f end
|
375
|
-
def page_width() @params[:page_size][:width].to_f end
|
376
|
-
def page_height() @params[:page_size][:height].to_f end
|
94
|
+
def barcode_y() reg_frame_height - barcode_height end
|
95
|
+
def barcode_height() @params[:barcode][:height].to_f end
|
96
|
+
def barcode_width() @params[:barcode][:width].to_f end
|
377
97
|
def cell_width() @params[:items][:cell_width].to_f end
|
378
98
|
def cell_height() @params[:items][:cell_height].to_f end
|
379
99
|
def cell_spacing() @params[:items][:x_spacing].to_f end
|
@@ -384,11 +104,13 @@ module Mork
|
|
384
104
|
def first_y() @params[:items][:first_y].to_f end
|
385
105
|
def rows() @params[:items][:rows] end
|
386
106
|
def columns() @params[:items][:columns] end
|
387
|
-
def reg_margin() @params[:regmarks][:margin].to_f end
|
388
107
|
def reg_search() @params[:regmarks][:search].to_f end
|
389
|
-
def
|
108
|
+
def reg_off() @params[:regmarks][:offset].to_f end
|
390
109
|
def reg_frame_width() page_width - reg_margin * 2 end
|
391
110
|
def reg_frame_height() page_height - reg_margin * 2 end
|
392
|
-
def
|
111
|
+
def page_width() @params[:page_size][:width].to_f end
|
112
|
+
def page_height() @params[:page_size][:height].to_f end
|
113
|
+
def reg_margin() @params[:regmarks][:margin].to_f end
|
114
|
+
def reg_radius() @params[:regmarks][:radius].to_f end
|
393
115
|
end
|
394
|
-
end
|
116
|
+
end
|