ruby binding 追加

- FFI gem (libptouch)、exe ptouch-print-png(PNG のみ)
- ステータス 32 バイトを Hash に展開(parse_status / status_hash)
- CMake: libptouch 共有ライブラリ(ptouch_shared)
- RuboCop、gemspec(homepage / source_code_uri)

Made-with: Cursor
This commit is contained in:
knb
2026-04-12 16:06:10 +09:00
parent d649b1b014
commit c5c7c2ba52
16 changed files with 889 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
# frozen_string_literal: true
require "json"
require "optparse"
require "libptouch"
module Libptouch
module Cli
# PNG のみを扱う ptouch-print 相当の CLI1bit ラスター経路なし)。
module PngPrint
module_function
def run(argv)
opts = parse(argv)
return 2 if opts.nil?
return run_version if opts[:version]
return run_help if opts[:help]
if opts[:status]
warn_unused_file_options(opts)
return run_status
end
return usage_error("-f is required (or use --status)") if opts[:file].to_s.empty?
path = opts[:file]
return usage_error("not a PNG file: #{path}") unless png_file?(path)
run_print(path, opts)
end
def png_file?(path)
return true if path.downcase.end_with?(".png")
File.open(path, "rb") do |f|
sig = f.read(8)
sig == "\x89PNG\r\n\x1a\n".b
end
rescue Errno::ENOENT, Errno::EACCES => e
warn "open #{path}: #{e.message}"
false
end
def parse(argv)
o = {
file: nil,
threshold: nil,
dry_run: false,
status: false,
version: false,
help: false
}
parser = OptionParser.new do |p|
p.banner = usage_banner
p.separator ""
p.on("-f", "--file PATH", "入力 PNG ファイル") { |v| o[:file] = v }
p.on("-t", "--threshold N", Integer,
"二値化しきい値 0255既定 #{Libptouch::PNG_DEFAULT_THRESHOLD}") do |v|
o[:threshold] = v
end
p.on("-n", "--dry-run", "読み込みと検証のみUSB なし)") { o[:dry_run] = true }
p.on("-S", "--status", "USB プリンタのステータスを JSON で表示して終了") { o[:status] = true }
p.on("-V", "--version", "バージョンを表示して終了") { o[:version] = true }
p.on("-h", "--help", "このヘルプ") { o[:help] = true }
end
parser.parse!(argv)
unless o[:threshold].nil? || (0..255).cover?(o[:threshold])
warn "-t must be 0..255"
return nil
end
o
end
def usage_banner
<<~BANNER
Usage: ptouch-print-png [options]
PNG -w/-H
--status -f
BANNER
end
def warn_unused_file_options(opts)
return unless opts[:file] || opts[:dry_run] || !opts[:threshold].nil?
warn "warning: options other than --status are ignored"
end
def run_version
puts "ptouch-print-png #{Libptouch::VERSION}"
0
end
def run_help
puts usage_banner
puts ""
puts parser_help_text
0
end
def parser_help_text
o = {
file: nil,
threshold: nil,
dry_run: false,
status: false,
version: false,
help: false
}
p = OptionParser.new do |parser|
parser.banner = "ptouch-print-png [options]"
parser.on("-f", "--file PATH", "入力 PNG ファイル") { |v| o[:file] = v }
parser.on("-t", "--threshold N", Integer,
"二値化しきい値 0255既定 #{Libptouch::PNG_DEFAULT_THRESHOLD}") do |v|
o[:threshold] = v
end
parser.on("-n", "--dry-run", "読み込みと検証のみUSB なし)") { o[:dry_run] = true }
parser.on("-S", "--status", "USB プリンタのステータスを JSON で表示して終了") { o[:status] = true }
parser.on("-V", "--version", "バージョンを表示して終了") { o[:version] = true }
parser.on("-h", "--help", "このヘルプ") { o[:help] = true }
end
p.help
end
def run_status
ctx = nil
begin
ctx = Libptouch::Context.new
ctx.open_usb
h = Libptouch.parse_status(ctx.status_bytes)
h.delete(:raw_bytes)
puts JSON.pretty_generate(h)
0
rescue Libptouch::Error => e
warn "get_status: #{e.message}"
1
ensure
ctx&.dispose
end
end
def run_print(path, opts)
ctx = nil
begin
ctx = Libptouch::Context.new
threshold = opts[:threshold]
data, width, height = if threshold.nil?
ctx.png_file_to_raster(path)
else
ctx.png_file_to_raster(path, threshold: threshold)
end
ctx.check_raster(data, width_dots: width, height_dots: height)
if opts[:dry_run]
puts "dry-run OK: #{data.bytesize} bytes, #{width}x#{height} dots"
return 0
end
ctx.open_usb
ctx.print_raster(data, width_dots: width, height_dots: height)
0
rescue Libptouch::Error => e
warn e.message
1
ensure
ctx&.dispose
end
end
def usage_error(msg)
warn msg
warn "(try ptouch-print-png --help)"
2
end
end
end
end