imagewriter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/imagewriter +244 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f9cc11fca615fa5553ed388686b6d88150bb7385
|
4
|
+
data.tar.gz: ea380b30ae49e7238c6e20bc1dcd880d938858b9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59a6e33c951000f9be772c61b06f00c6c5da4dbba5af5ad36e0c0033f15031d757b530748b1146c296b0f443801c64dda94bf0688ab3d6758d0bfa85459e9039
|
7
|
+
data.tar.gz: e1622a9dbb2032360d73b63b79df3d5b1b901ab0b1df512d2e25ab02217902d360405910af5cb4caf1012c8707092db1c70f9689fd5496e87d1ef3ee38b0dc06
|
data/bin/imagewriter
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# imagewriter
|
4
|
+
#
|
5
|
+
# writes a black and white PNG image onto a piece of paper,
|
6
|
+
# using an Apple Imagewriter or ImageWriter II printer
|
7
|
+
#
|
8
|
+
# run `./imagewriter -h` to see options.
|
9
|
+
#
|
10
|
+
# , 2020 pete gamache pete@gamache.org
|
11
|
+
# (k) all rights reversed
|
12
|
+
|
13
|
+
require 'chunky_png'
|
14
|
+
require 'optparse'
|
15
|
+
|
16
|
+
class ImageWriter
|
17
|
+
def initialize(argv)
|
18
|
+
parse_argv!(argv.clone)
|
19
|
+
|
20
|
+
@img = ChunkyPNG::Image.from_file(@filename)
|
21
|
+
STDERR.puts "#{@filename}: height #{@img.height}, width #{@img.width}"
|
22
|
+
|
23
|
+
printable_width = @hdpi * 8
|
24
|
+
if @img.width > printable_width
|
25
|
+
STDERR.puts "Warning: image exceeds printable width of #{printable_width} pixels; printout will be clipped"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def print!
|
30
|
+
init_printer!
|
31
|
+
|
32
|
+
if @quality == "regular"
|
33
|
+
do_print!(16, 0, :b1_v144_regular, :b2_v144_regular, 1, 15)
|
34
|
+
elsif @quality == "enhanced"
|
35
|
+
do_print!(12, -4, :b1_v144_enhanced, :b2_v144_enhanced, 1, 11)
|
36
|
+
elsif @quality == "best"
|
37
|
+
do_print!(4, 0, :b1_v144_best, :b2_v144_best, 1, 3)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@private
|
42
|
+
|
43
|
+
## Send Esc + this letter to change horizontal DPI
|
44
|
+
HORIZ_DPI_CODE = {
|
45
|
+
72 => "n",
|
46
|
+
80 => "N",
|
47
|
+
96 => "E",
|
48
|
+
107 => "e",
|
49
|
+
120 => "q",
|
50
|
+
136 => "Q",
|
51
|
+
144 => "p",
|
52
|
+
160 => "P"
|
53
|
+
}
|
54
|
+
|
55
|
+
## Send \x1F then this letter to send N line feeds
|
56
|
+
LINE_FEEDS = {
|
57
|
+
1 => "1",
|
58
|
+
2 => "2",
|
59
|
+
3 => "3",
|
60
|
+
4 => "4",
|
61
|
+
5 => "5",
|
62
|
+
6 => "6",
|
63
|
+
7 => "7",
|
64
|
+
8 => "8",
|
65
|
+
9 => "9",
|
66
|
+
10 => ":",
|
67
|
+
11 => ";",
|
68
|
+
12 => "<",
|
69
|
+
13 => "=",
|
70
|
+
14 => ">",
|
71
|
+
15 => "?"
|
72
|
+
}
|
73
|
+
|
74
|
+
QUALITY = ["regular", "enhanced", "best"]
|
75
|
+
|
76
|
+
def parse_argv!(argv)
|
77
|
+
options = {
|
78
|
+
hdpi: 144,
|
79
|
+
quality: "regular",
|
80
|
+
sleep: 0.75
|
81
|
+
}
|
82
|
+
|
83
|
+
help = nil
|
84
|
+
OptionParser.new do |parser|
|
85
|
+
parser.banner = <<-EOT
|
86
|
+
Usage: imagewriter [options] filename.png"
|
87
|
+
Vertical resolution is 144 dpi; horizontal resolution is adjustable.
|
88
|
+
Max printable width is 8 inches, or 8 * horizontal DPI pixels.
|
89
|
+
Options:
|
90
|
+
EOT
|
91
|
+
|
92
|
+
dpis = HORIZ_DPI_CODE.keys.join(", ")
|
93
|
+
parser.on('-H', '--horizontal DPI', Integer, "Horizontal DPI (one of: #{dpis}; default #{options[:hdpi]})") do |n|
|
94
|
+
if HORIZ_DPI_CODE[n]
|
95
|
+
options[:hdpi] = n
|
96
|
+
else
|
97
|
+
STDERR.puts "Bad horizontal DPI setting #{n} (must be one of: #{dpis})\n"
|
98
|
+
STDERR.puts parser.to_s
|
99
|
+
exit 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
parser.on("-q", "--quality QUALITY", "Print quality (one of: #{QUALITY.join(", ")}; default #{options[:quality]})") do |n|
|
104
|
+
if QUALITY.include?(n)
|
105
|
+
options[:quality] = n
|
106
|
+
else
|
107
|
+
STDERR.puts "Bad quality setting #{n} (must be one of: #{QUALITY.join(", ")})\n"
|
108
|
+
STDERR.puts parser
|
109
|
+
exit 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
parser.on("-s", "--sleep", Float, "Sleep this many seconds between passes (default #{options[:sleep]})") do |n|
|
114
|
+
options[:sleep] = n
|
115
|
+
end
|
116
|
+
|
117
|
+
parser.on("-h", "--help", "Print this help message to STDERR") do
|
118
|
+
STDERR.puts parser
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
|
122
|
+
help = parser.to_s
|
123
|
+
end.parse!(argv)
|
124
|
+
|
125
|
+
@hdpi = options[:hdpi]
|
126
|
+
@quality = options[:quality]
|
127
|
+
@sleep = options[:sleep]
|
128
|
+
|
129
|
+
@filename = argv.shift
|
130
|
+
if !@filename
|
131
|
+
STDERR.puts "Missing filename\n"
|
132
|
+
STDERR.puts help
|
133
|
+
exit 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def do_print!(lines_per_double_pass, y0, b1_fun, b2_fun, lf1, lf2)
|
138
|
+
double_passes = (@img.height / lines_per_double_pass).ceil + 1
|
139
|
+
y = y0
|
140
|
+
width = [@hdpi * 8, @img.width].min
|
141
|
+
|
142
|
+
0.upto(double_passes) do
|
143
|
+
bytes1 = 0.upto(width).map{|x| self.send(b1_fun, x, y)}
|
144
|
+
bytes2 = 0.upto(width).map{|x| self.send(b2_fun, x, y)}
|
145
|
+
|
146
|
+
printf "\eG%.4d", width
|
147
|
+
bytes1.each{|b| print b.chr}
|
148
|
+
printf "\r"
|
149
|
+
|
150
|
+
printf "\x1F%s", LINE_FEEDS[lf1]
|
151
|
+
|
152
|
+
printf "\eG%.4d", width
|
153
|
+
bytes2.each{|b| print b.chr}
|
154
|
+
printf "\r"
|
155
|
+
|
156
|
+
printf "\x1F%s", LINE_FEEDS[lf2]
|
157
|
+
|
158
|
+
sleep @sleep
|
159
|
+
y += lines_per_double_pass
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
### Regular quality: 16 lines per double-pass
|
164
|
+
def b1_v144_regular(x, y)
|
165
|
+
0 +
|
166
|
+
0b00000001 * get(x, y) +
|
167
|
+
0b00000010 * get(x, y+2) +
|
168
|
+
0b00000100 * get(x, y+4) +
|
169
|
+
0b00001000 * get(x, y+6) +
|
170
|
+
0b00010000 * get(x, y+8) +
|
171
|
+
0b00100000 * get(x, y+10) +
|
172
|
+
0b01000000 * get(x, y+12) +
|
173
|
+
0b10000000 * get(x, y+14)
|
174
|
+
end
|
175
|
+
|
176
|
+
def b2_v144_regular(x, y)
|
177
|
+
0 +
|
178
|
+
0b00000001 * get(x, y+1) +
|
179
|
+
0b00000010 * get(x, y+3) +
|
180
|
+
0b00000100 * get(x, y+5) +
|
181
|
+
0b00001000 * get(x, y+7) +
|
182
|
+
0b00010000 * get(x, y+9) +
|
183
|
+
0b00100000 * get(x, y+11) +
|
184
|
+
0b01000000 * get(x, y+13) +
|
185
|
+
0b10000000 * get(x, y+15)
|
186
|
+
end
|
187
|
+
|
188
|
+
### Enhanced quality: 12 lines per double-pass, 4 dots dovetailed
|
189
|
+
def b1_v144_enhanced(x, y)
|
190
|
+
0 +
|
191
|
+
0b00000001 * get(x, y) +
|
192
|
+
0b00000010 * get(x, y+2) +
|
193
|
+
0b00000100 * get(x, y+4) +
|
194
|
+
0b00001000 * get(x, y+6) +
|
195
|
+
0b00010000 * get(x, y+8) +
|
196
|
+
0b00100000 * get(x, y+10)
|
197
|
+
#0b01000000 * get(x, y+12) +
|
198
|
+
#0b10000000 * get(x, y+14)
|
199
|
+
end
|
200
|
+
|
201
|
+
def b2_v144_enhanced(x, y)
|
202
|
+
0 +
|
203
|
+
#0b00000001 * get(x, y+1) +
|
204
|
+
#0b00000010 * get(x, y+3) +
|
205
|
+
0b00000100 * get(x, y+5) +
|
206
|
+
0b00001000 * get(x, y+7) +
|
207
|
+
0b00010000 * get(x, y+9) +
|
208
|
+
0b00100000 * get(x, y+11) +
|
209
|
+
0b01000000 * get(x, y+13) +
|
210
|
+
0b10000000 * get(x, y+15)
|
211
|
+
end
|
212
|
+
|
213
|
+
### Best quality: 4 lines per double-pass
|
214
|
+
def b1_v144_best(x, y)
|
215
|
+
0 +
|
216
|
+
0b00000001 * get(x, y) +
|
217
|
+
0b00000010 * get(x, y+2)
|
218
|
+
end
|
219
|
+
|
220
|
+
def b2_v144_best(x, y)
|
221
|
+
0 +
|
222
|
+
0b00000001 * get(x, y+1) +
|
223
|
+
0b00000010 * get(x, y+3)
|
224
|
+
end
|
225
|
+
|
226
|
+
## Returns 0 for black pixels that exist, 1 otherwise
|
227
|
+
def get(x, y)
|
228
|
+
x = @img[x, y] & 0xffffff00
|
229
|
+
return 0 if x > 0
|
230
|
+
1
|
231
|
+
rescue
|
232
|
+
1
|
233
|
+
end
|
234
|
+
|
235
|
+
def init_printer!
|
236
|
+
printf "\ef" # forward line feeds
|
237
|
+
printf "\e\x6C1" # do not insert carriage return before LF and FF
|
238
|
+
printf "\eZ\x80\x00" # no line feed added after CR
|
239
|
+
printf "\eT01" # set line feed to 1/144 inch
|
240
|
+
printf "\e%s", HORIZ_DPI_CODE[@hdpi]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
ImageWriter.new(ARGV).print!
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: imagewriter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- pete gamache
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-29 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: pete@gamache.org
|
15
|
+
executables:
|
16
|
+
- imagewriter
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/imagewriter
|
21
|
+
homepage: https://github.com/gamache/imagewriter
|
22
|
+
licenses:
|
23
|
+
- CC-PDDC
|
24
|
+
metadata: {}
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
requirements: []
|
40
|
+
rubyforge_project:
|
41
|
+
rubygems_version: 2.5.2.3
|
42
|
+
signing_key:
|
43
|
+
specification_version: 4
|
44
|
+
summary: writes PNG images onto paper using an Apple Imagewriter or ImageWriter II
|
45
|
+
printer
|
46
|
+
test_files: []
|