For reasons I do not profess to understand, GIMP 3.0 does not work with plugins written for GIMP 2.0, including the XSane plugin that handles scanning. This seems like an obvious oversight, but after three months it also seems to be one of those things that’s like that and that’s the way it is.
Protracted searching turned up gimp-xsanecli, a GIMP 3.0 plugin invoking XSane through its command-line interface to scan an image into a temporary file, then stuff the file into GIMP. Unfortunately, it didn’t work over the network with the Epson ET-3830 printer / scanner in the basement.
It turns out gimp-xsanecli tells XSane to output the filename it’s using, then expects to find the identifying XSANE_IMAGE_FILENAME string followed by the filename on the first line of whatever it gets back:
if result != 'XSANE_IMAGE_FILENAME: ' + png_out:
Gimp.message('Unexpected XSane result: ' + result)
return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.EXECUTION_ERROR)])
The font ligature that may or may not mash != into ≠ is not under my control.
Protracted poking showed the scanner fires a glob of HTML through proc/stdout into gimp-xsanecli before XSane produces its output, but after the scan completes:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN "
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
… snippage …
</head>
<body><noscript>Enable your browser's JavaScript setting.</noscript></body></HTML>XSANE_IMAGE_FILENAME: /tmp/out.png
Complicating the process:
- The HTML glob only appears on the first scan, after which XSane produces exactly what
gimp-xsanecliexpects - There is no newline separating the glob from the expected output on the last line
So …
Insert a while loop into the main loop to strip off the HTML glob line by line by line:
while True:
# 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
# discard HTML header added by scanner to first scan
while True :
result = proc.stdout.readline().strip()
if r'</body>' in result :
result = result.partition(r'</HTML>')[-1]
# Gimp.message('Found end of HTML: ' + result)
break
elif 'XSANE_IMAGE_FILENAME:' in result :
# Gimp.message('Found filename: ' + result)
break
else :
# Gimp.message('Discarding: ' + result)
continue
if result == '':
# XSane was closed
break
if result != 'XSANE_IMAGE_FILENAME: ' + png_out:
Gimp.message('Unexpected XSane result: ' + 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)
if not SCAN_MULTIPLE:
proc.terminate()
break
os.rmdir(tempdir)
return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.SUCCESS), GObject.Value(Gimp.Image.__gtype__, image)])
While it’s tempting to absorb the whole thing in one gulp with proc.stdout.read().strip(), that doesn’t work because nothing arrives until the XSane subprocess terminates, which is not what you want.
A scan to show It Just Works™ :

I expect it doesn’t work under a variety of common conditions, but … so far so good.




































