SVG印刷対応とメディア情報APIを追加
SVG入力を現在テープ幅に自動フィットして印刷できるようにし、アプリ側が余白計算できるようにテープ幅・DPI・最小送り量を取得するAPIを追加する。 Made-with: Cursor
This commit is contained in:
@@ -31,9 +31,10 @@ export LIBPTOUCH_LIB=/usr/local/lib/libptouch.so
|
||||
|
||||
(`cmake --install` で共有ライブラリをインストールした場合は、通常は `libptouch` 名でローダが解決します。)
|
||||
|
||||
## コマンド `ptouch-print-png`(PNG のみ)
|
||||
## コマンド `ptouch-print-png`(PNG/SVG)
|
||||
|
||||
C の `ptouch-print` と同様の流れですが、**PNG 入力のみ**(`-w`/`-H` や 1bit ラスターは扱いません)。`gem install` 後は PATH に `ptouch-print-png` が入ります。
|
||||
C の `ptouch-print` と同様の流れで、**PNG/SVG 入力**(`-w`/`-H` や 1bit ラスターは扱いません)を扱います。`gem install` 後は PATH に `ptouch-print-png` が入ります。
|
||||
SVG は現在装着テープの印字可能幅に合わせて自動拡大・縮小します(USB 接続が必要)。
|
||||
|
||||
オプションは C 側に合わせ、**`-p` / `--pid`** で USB 製品 ID(16 進可)を指定できます。省略時は PT-P900W(`Libptouch::USB_PID_PTP900W` = `0x2085`)。例: PT-P750W `0x2062`、PT-P710BT `0x20af`(`libptouch.h` / `Libptouch` 定数と同じ)。
|
||||
|
||||
@@ -42,6 +43,7 @@ C の `ptouch-print` と同様の流れですが、**PNG 入力のみ**(`-w`/`
|
||||
```bash
|
||||
bundle exec ruby -I lib exe/ptouch-print-png --help
|
||||
bundle exec ruby -I lib exe/ptouch-print-png -n -f ../samples/your.png
|
||||
bundle exec ruby -I lib exe/ptouch-print-png -n -f ../samples/your.svg
|
||||
bundle exec ruby -I lib exe/ptouch-print-png --status -p 0x2062
|
||||
bundle exec ruby -I lib exe/ptouch-print-png -f ../samples/your.png -p 0x20af
|
||||
```
|
||||
@@ -76,12 +78,23 @@ ctx.print_raster(data, width_dots: w, height_dots: h, margin_mm: 0)
|
||||
ctx.dispose
|
||||
```
|
||||
|
||||
SVG から現在テープ幅に合わせてラスターへ:
|
||||
|
||||
```ruby
|
||||
ctx = Libptouch::Context.new
|
||||
ctx.open_usb
|
||||
data, w, h = ctx.svg_file_to_raster_fit_current_tape("/path/to/label.svg")
|
||||
ctx.print_raster(data, width_dots: w, height_dots: h, margin_mm: 0)
|
||||
ctx.dispose
|
||||
```
|
||||
|
||||
**解像度:** P750W / P710BT はラスター幅方向が **180 dpi**、P900W 系は **360 dpi**(C の `libptouch_print` / 各機種ラスター PDF に合わせて画像を用意してください)。
|
||||
|
||||
## API の範囲
|
||||
|
||||
- 実行ファイル `ptouch-print-png` … PNG のみ(`-f`, `-t`, `-p`, `-n`, `-S`, `-V`, `-h`)。ステータスは JSON(`status_bytes` を `parse_status` したもの、`raw_bytes` 除く)
|
||||
- 実行ファイル `ptouch-print-png` … PNG/SVG(`-f`, `-t`, `-p`, `-n`, `-S`, `-V`, `-h`)。ステータスは JSON(`status_bytes` を `parse_status` したもの、`raw_bytes` 除く)
|
||||
- `Libptouch::Context` … `open_usb` / `open_usb_vid_pid` / `close` / `dispose`
|
||||
- `check_raster` / `print_raster` / `png_file_to_raster` / `status_bytes` / `status_hash`
|
||||
- `check_raster` / `print_raster` / `png_file_to_raster` / `svg_file_to_raster_fit_current_tape` / `status_bytes` / `status_hash` / `current_media_info`
|
||||
- `current_media_info` には `print_dpi` / `feed_dpi` / `tape_width_mm` / `min_feed_mm` などを含む
|
||||
- `Libptouch.parse_status(raw)` … 32 バイトを Hash に展開(機種・テープ幅・**テープ種類**・色・**状態(status_kind)**・エラービット・`raw_hex` など)
|
||||
- C の `libptouch_status_fprint`(`FILE *`)は FFI からはバインドしていません。テキスト出力の代わりに `parse_status` / `status_hash` を使ってください。
|
||||
|
||||
@@ -28,10 +28,28 @@ module Libptouch
|
||||
:_pad, [:uint8, 3]
|
||||
end
|
||||
|
||||
class MediaInfo < FFI::Struct
|
||||
layout :media_width_code, :uint8,
|
||||
:media_kind_code, :uint8,
|
||||
:_pad0, [:uint8, 6],
|
||||
:print_dpi, :double,
|
||||
:feed_dpi, :double,
|
||||
:tape_width_mm, :double,
|
||||
:printable_dots, :uint16,
|
||||
:left_margin_dots, :uint16,
|
||||
:right_margin_dots, :uint16,
|
||||
:min_feed_dots, :uint16,
|
||||
:min_feed_mm, :double
|
||||
end
|
||||
|
||||
class PngOptions < FFI::Struct
|
||||
layout :threshold, :uint8
|
||||
end
|
||||
|
||||
class SvgOptions < FFI::Struct
|
||||
layout :threshold, :uint8
|
||||
end
|
||||
|
||||
attach_function :libptouch_create, [], :pointer
|
||||
attach_function :libptouch_destroy, [:pointer], :void
|
||||
attach_function :libptouch_strerror, [:pointer], :string
|
||||
@@ -40,9 +58,12 @@ module Libptouch
|
||||
attach_function :libptouch_open_usb_vid_pid, %i[pointer uint16 uint16], :int
|
||||
attach_function :libptouch_close, [:pointer], :void
|
||||
attach_function :libptouch_check_raster, %i[pointer pointer size_t pointer], :int
|
||||
attach_function :libptouch_get_current_media_info, %i[pointer pointer], :int
|
||||
attach_function :libptouch_print_raster, %i[pointer pointer size_t pointer], :int
|
||||
attach_function :libptouch_png_file_to_raster,
|
||||
%i[pointer string pointer pointer pointer pointer], :int
|
||||
attach_function :libptouch_svg_file_to_raster_fit_current_tape,
|
||||
%i[pointer string pointer pointer pointer pointer], :int
|
||||
attach_function :libptouch_free_raster, [:pointer], :void
|
||||
attach_function :libptouch_get_status, %i[pointer pointer], :int
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ require "libptouch"
|
||||
|
||||
module Libptouch
|
||||
module Cli
|
||||
# PNG のみを扱う ptouch-print 相当の CLI(1bit ラスター経路なし)。
|
||||
# PNG/SVG を扱う ptouch-print 相当の CLI(1bit ラスター経路なし)。
|
||||
module PngPrint
|
||||
module_function
|
||||
|
||||
@@ -26,9 +26,10 @@ module Libptouch
|
||||
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)
|
||||
kind = image_kind(path)
|
||||
return usage_error("not a PNG/SVG file: #{path}") if kind.nil?
|
||||
|
||||
run_print(path, opts)
|
||||
run_print(path, opts, kind)
|
||||
end
|
||||
|
||||
def png_file?(path)
|
||||
@@ -43,6 +44,17 @@ module Libptouch
|
||||
false
|
||||
end
|
||||
|
||||
def svg_file?(path)
|
||||
path.downcase.end_with?(".svg")
|
||||
end
|
||||
|
||||
def image_kind(path)
|
||||
return :png if png_file?(path)
|
||||
return :svg if svg_file?(path)
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def parse(argv)
|
||||
opts_hash = default_cli_opts
|
||||
build_cli_parser(opts_hash).parse!(argv)
|
||||
@@ -101,9 +113,9 @@ module Libptouch
|
||||
OptionParser.new do |p|
|
||||
p.banner = usage_banner
|
||||
p.separator ""
|
||||
p.on("-f", "--file PATH", "入力 PNG ファイル") { |v| opts_hash[:file] = v }
|
||||
p.on("-f", "--file PATH", "入力 PNG/SVG ファイル") { |v| opts_hash[:file] = v }
|
||||
p.on("-t", "--threshold N", Integer,
|
||||
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD})") do |v|
|
||||
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD}、PNG/SVG)") do |v|
|
||||
opts_hash[:threshold] = v
|
||||
end
|
||||
p.on("-p", "--pid PID", pid_option_description) do |v|
|
||||
@@ -122,7 +134,8 @@ module Libptouch
|
||||
<<~BANNER
|
||||
Usage: ptouch-print-png [options]
|
||||
|
||||
PNG のみ対応。幅・高さは画像から取得します(-w/-H はありません)。
|
||||
PNG/SVG 対応。幅・高さは画像から取得します(-w/-H はありません)。
|
||||
SVG は現在テープ幅に合わせて自動拡大・縮小します(USB 接続必須)。
|
||||
--status のときは -f は不要です。
|
||||
BANNER
|
||||
end
|
||||
@@ -149,9 +162,9 @@ module Libptouch
|
||||
opts_hash = default_cli_opts
|
||||
p = OptionParser.new do |parser|
|
||||
parser.banner = "ptouch-print-png [options]"
|
||||
parser.on("-f", "--file PATH", "入力 PNG ファイル") { |v| opts_hash[:file] = v }
|
||||
parser.on("-f", "--file PATH", "入力 PNG/SVG ファイル") { |v| opts_hash[:file] = v }
|
||||
parser.on("-t", "--threshold N", Integer,
|
||||
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD})") do |v|
|
||||
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD}、PNG/SVG)") do |v|
|
||||
opts_hash[:threshold] = v
|
||||
end
|
||||
parser.on("-p", "--pid PID", pid_option_description) do |v|
|
||||
@@ -192,12 +205,19 @@ module Libptouch
|
||||
end
|
||||
end
|
||||
|
||||
def run_print(path, opts)
|
||||
def run_print(path, opts, kind)
|
||||
ctx = nil
|
||||
begin
|
||||
ctx = Libptouch::Context.new
|
||||
threshold = opts[:threshold]
|
||||
data, width, height = if threshold.nil?
|
||||
data, width, height = if kind == :svg
|
||||
open_usb_for_opts(ctx, opts)
|
||||
if threshold.nil?
|
||||
ctx.svg_file_to_raster_fit_current_tape(path)
|
||||
else
|
||||
ctx.svg_file_to_raster_fit_current_tape(path, threshold: threshold)
|
||||
end
|
||||
elsif threshold.nil?
|
||||
ctx.png_file_to_raster(path)
|
||||
else
|
||||
ctx.png_file_to_raster(path, threshold: threshold)
|
||||
@@ -206,11 +226,11 @@ module Libptouch
|
||||
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"
|
||||
puts "dry-run OK: #{data.bytesize} bytes, src #{width}x#{height} dots (print lengthxwidth #{width}x#{height})"
|
||||
return 0
|
||||
end
|
||||
|
||||
open_usb_for_opts(ctx, opts)
|
||||
open_usb_for_opts(ctx, opts) if kind == :png
|
||||
ctx.print_raster(data, width_dots: width, height_dots: height)
|
||||
0
|
||||
rescue Libptouch::Error => e
|
||||
|
||||
@@ -93,6 +93,28 @@ module Libptouch
|
||||
[bytes, out_params[:width_dots], out_params[:height_dots]]
|
||||
end
|
||||
|
||||
def svg_file_to_raster_fit_current_tape(path, threshold: nil)
|
||||
opt_ptr = nil
|
||||
unless threshold.nil?
|
||||
o = Binding::SvgOptions.new
|
||||
o[:threshold] = threshold
|
||||
opt_ptr = o.pointer
|
||||
end
|
||||
out_pp = FFI::MemoryPointer.new(:pointer)
|
||||
out_len = FFI::MemoryPointer.new(:size_t)
|
||||
out_params = Binding::RasterParams.new
|
||||
raise_on_error(Binding.libptouch_svg_file_to_raster_fit_current_tape(
|
||||
@native, path, opt_ptr, out_pp, out_len, out_params.pointer
|
||||
))
|
||||
raw = out_pp.read_pointer
|
||||
raise Libptouch::Error.new(OK, "null raster from SVG") if raw.null?
|
||||
|
||||
len = out_len.get(:size_t, 0)
|
||||
bytes = raw.read_bytes(len)
|
||||
Binding.libptouch_free_raster(raw)
|
||||
[bytes, out_params[:width_dots], out_params[:height_dots]]
|
||||
end
|
||||
|
||||
def status_bytes
|
||||
buf = FFI::MemoryPointer.new(:uint8, STATUS_LENGTH)
|
||||
raise_on_error(Binding.libptouch_get_status(@native, buf))
|
||||
@@ -102,5 +124,22 @@ module Libptouch
|
||||
def status_hash
|
||||
Libptouch.parse_status(status_bytes)
|
||||
end
|
||||
|
||||
def current_media_info
|
||||
info = Binding::MediaInfo.new
|
||||
raise_on_error(Binding.libptouch_get_current_media_info(@native, info.pointer))
|
||||
{
|
||||
media_width_code: info[:media_width_code],
|
||||
media_kind_code: info[:media_kind_code],
|
||||
print_dpi: info[:print_dpi],
|
||||
feed_dpi: info[:feed_dpi],
|
||||
tape_width_mm: info[:tape_width_mm],
|
||||
printable_dots: info[:printable_dots],
|
||||
left_margin_dots: info[:left_margin_dots],
|
||||
right_margin_dots: info[:right_margin_dots],
|
||||
min_feed_dots: info[:min_feed_dots],
|
||||
min_feed_mm: info[:min_feed_mm]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user