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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/bin/imagewriter +244 -0
  3. metadata +46 -0
@@ -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
@@ -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: []