imagewriter 0.1.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.
- 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: []
|