commit a2681d2a2240a89ed60f01ebe3724e4f31bb1314 Author: RunasSudo Date: Wed Nov 13 22:48:09 2024 +1100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..0844be2 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# gimp-xsanecli + +GIMP 3.0 plug-in for scanning via XSane + +Uses XSane via CLI rather than requiring XSane to be compiled with GIMP support (which is currently unavailable for GIMP 3.0) + +## Usage + +gimp-xsanecli has only been tested on Linux. It requires [xsane](http://www.xsane.org/) to be installed, and the *xsane* command to be available on PATH. + +Install *xsanecli.py* to *$HOME/.config/GIMP/3.0/plug-ins/xsanecli/xsanecli.py* and ensure that *xsanecli.py* is executable. + +Click *File* → *Create* → *XSane via CLI…*. + +By default, XSane's device chooser dialog is displayed. To hard-code a specific device, set *DEVICE_NAME* in *xsanecli.py* (e.g. `DEVICE_NAME = 'escl:http://10.0.0.1:80'`). + +## Licence + +Copyright © 2024 Lee Yingtong Li (RunasSudo) + +Licensed under the GNU General Public License version 3 or later. diff --git a/xsanecli.py b/xsanecli.py new file mode 100755 index 0000000..3587800 --- /dev/null +++ b/xsanecli.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +# GIMP 3.0 plug-in for scanning via XSane +# Copyright (C) 2024 Lee Yingtong Li (RunasSudo) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import gi +gi.require_version('Gimp', '3.0') +from gi.repository import Gimp +from gi.repository import GObject +from gi.repository import Gio + +import os +import subprocess +import sys +import tempfile + +DEVICE_NAME = None # e.g. DEVICE_NAME = 'escl:http://10.0.0.1:80' + +def xsanecli_run(procedure, config, run_data, *args): + # Get temporary directory + tempdir = tempfile.mkdtemp('gimp-plugin-xsanecli') + png_out = os.path.join(tempdir, 'out.png') + + # Open XSane + args = ['xsane', '--save', '--no-mode-selection', '--force-filename', png_out, '--print-filenames'] + ([DEVICE_NAME] if DEVICE_NAME else []) + proc = subprocess.Popen(args, stdout=subprocess.PIPE, encoding='utf-8') + + # Wait until XSane prints the name of the scanned file, indicating scanning is finished + # This blocks Python but that is ok because GIMP UI is not affected + result = proc.stdout.readline().strip() + + proc.terminate() + + if result == '': + # XSane was closed + return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.CANCEL)]) + + if result != 'XSANE_IMAGE_FILENAME: ' + png_out: + Gimp.message('Unexpected XSane result') + return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.EXECUTION_ERROR)]) + + # Open image + image = Gimp.file_load(Gimp.RunMode.NONINTERACTIVE, Gio.File.new_for_path(png_out)) + Gimp.Display.new(image) + + # Remove temporary files + os.unlink(png_out) + os.rmdir(tempdir) + + return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.SUCCESS), GObject.Value(Gimp.Image.__gtype__, image)]) + +class XSaneCLI(Gimp.PlugIn): + ## GimpPlugIn virtual methods ## + def do_set_i18n(self, procname): + return False + + def do_query_procedures(self): + return ['plug-in-xsanecli'] + + def do_create_procedure(self, name): + if name == 'plug-in-xsanecli': + procedure = Gimp.Procedure.new(self, name, Gimp.PDBProcType.PLUGIN, xsanecli_run, None, None) + procedure.set_menu_label('_XSane via CLI...') + procedure.add_menu_path('/File/Create') + procedure.add_enum_argument('run-mode', 'Run mode', 'The run mode', Gimp.RunMode.__gtype__, Gimp.RunMode.NONINTERACTIVE, GObject.ParamFlags.READWRITE) + procedure.add_image_return_value('image', 'Image', 'Output image', False, GObject.ParamFlags.READWRITE) + else: + raise Exception('Unknown procedure') + + procedure.set_attribution( + 'Lee Yingtong Li (RunasSudo)', # Author + 'Lee Yingtong Li (RunasSudo)', # Copyright + 'Lee Yingtong Li (RunasSudo)' # Year + ) + return procedure + +Gimp.main(XSaneCLI.__gtype__, sys.argv)