SANE on FreeBSD - connecting two scanners, control VIA HTML
Post ReplySANE on FreeBSD - connecting two scanners, control VIA HTMLPosted: Friday, July 3, 2020 [12:35:32] - 1
FreeBSD on Intel NUC is easy! Check out OS installation page: www.codemacs.com/other/an.. Next, we need to control two CanoScan LiDE 300 scanners to do an open book style scans. After OS installation ports collection is installed with: Then install portsmaster to make life easier: Next we need to install Perl (yes, it is our weapon of choice); Now we need to install Apache Web server (NGINX would work as well): For light image manipulation ImageMagick (convert) is installed: since we do not use X11 Windows we don't need anything related to it. and now SANE backends: Next we need to make sure we can use scanners from Apache. For this we need proper user/group/permissions. This is a good source: www.freebsd.org/doc/handb.. This example creates a group called usb: Then, make the /dev/ugen0.2 symlink and the /dev/usb/0.2.0 device node accessible to the usb group with write permissions of 0660 or 0664 by adding the following lines to /etc/devfs.rules: Note: It happens the device node changes with the addition or removal of devices, so one may want to give access to all USB devices using this ruleset instead: Refer to devfs.rules(5) for more information about this file. Next, enable the ruleset in /etc/rc.conf: And, restart the devfs(8) system: Finally, add the users to usb in order to allow access to the scanner: instead of user "webserver" use the one set in Apache config file. Next - web interface access to scanners. |
SANE on FreeBSD Web interfacePosted: Friday, July 3, 2020 [12:50:20] - 2
When SANE permissions are properly set and scanners working from Terminal a simple Web interface can be set to scan. This is a web page: <!DOCTYPE html> <html dir="ltr" lang="en-US"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Scannin</title> <link rel="profile" href="https://gmpg.org/xfn/11"> <meta name="theme-color" content="#ffffff"> <link rel="icon" href="/favicon.ico" type="image/x-icon" /> <link rel="stylesheet" type="text/css" href="/css/all.css" media="screen" /> <script> function formsubmit(frm) { var col = document.getElementById(frm).elements["color"].value; var scan = document.getElementById(frm).elements["scanner"].value; var img = 'image' + scan; var scanimgs = 'Scanning..<br /><img src="/scanning.gif">'; document.getElementById(img).style.display="inline-block"; document.getElementById(img).innerHTML = scanimgs; var url = '/scan.cgi?c=' + col + '&s=' + scan; request = new XMLHttpRequest(); try { request.onreadystatechange = function() { if (request.readyState == 4) { var val = request.responseText; document.getElementById(img).innerHTML = val; document.getElementById(img).style.display="inline-block"; } } request.open("GET", url, true); request.send(); } catch (e) { alert("Unable to connect to server - Scanner" + scan); } } function formsubmit2(frm) { var cols = document.getElementById(frm).elements["color"].value; var scans = document.getElementById(frm).elements["scanner"].value; var imgs = 'image' + scans; var scanimg = 'Scanning..<br /><img src="/scanning.gif">'; document.getElementById(imgs).style.display="inline-block"; document.getElementById(imgs).innerHTML = scanimg var urls = '/scan.cgi?c=' + cols + '&s=' + scans; xhr = new XMLHttpRequest(); try { xhr.onreadystatechange = function() { if (xhr.readyState == 4) { var vals = xhr.responseText; document.getElementById(imgs).innerHTML = vals; document.getElementById(imgs).style.display="inline-block"; } } xhr.open("GET", urls, true); xhr.send(); } catch (e) { alert("Unable to connect to server - Scanner" + scans); } } </script> </head> <body> <div id="content"> <h1>Scanning images</h1> <div id="headimages"> <div id="image1" style="display:none;"></div> <div id="image2" style="display:none;"></div> </div> <div id="forms"> <div id="forms1"> <h4>Left Page</h4> <form id="scan1"> <input type="radio" name="color" id="color11" value="gray" checked> <label for="color11">Gray</label> <input type="radio" name="color" id="color21" value="color"> <label for="color21">Color</label> <input type="radio" name="color" id="color31" value="noscan"> <label for="color31">No Scan</label> <input type="hidden" name="scanner" value="1"> </form> </div><!-- form 1--> <div id="forms2"> <h4>Right Page</h4> <form id="scan2"> <input type="radio" name="color" id="color12" value="gray" checked> <label for="color12">Gray</label> <input type="radio" name="color" id="color22" value="color"> <label for="color22">Color</label> <input type="radio" name="color" id="color32" value="noscan"> <label for="color32">No Scan</label> <input type="hidden" name="scanner" value="2"> </form> </div><!-- form 2--> </div><!-- forms--> <div id="fsubm"> <a href="#" class="myButton" onclick="formsubmit('scan1');formsubmit2('scan2');return false;">Scan</a> </div> </div><!-- content--> </body> </html> It uses two Ajax requests to send scan requests. Why two - we're not that proficient in JS and went with simple solution. It works. Here is the Perl script that actually handles scanning: use strict; our %in=(); eval {require '/home/scan/cgi-bin/cgi-lib.pl'}; unless($@) {&ReadParse;} # Define hashes to properly show results and access scanners my %pgs = (1,'Left Page',2,'Right Page'); my %scanners = ( ## Defines scanners 1,'pixma:04A91913_XXXXX1', 2,'pixma:04A91913_XXXXX2'); my %colorsby = ('gray','Gray', 'color','Color'); unless($colorsby{$in{c}}) {print "Content-type: text/html\n\n$pgs{$in{s}}<br />\nNot Scanned<br /><img src=\"scanner.open.png\">"; my $imn = time; `scanimage --mode $colorsby{$in{c}} --device-name=$scanners{$in{s}} --resolution 300 --format=tiff > /tmp/scans/$imn.$in{s}.tif`; if(-f "/tmp/scans/$imn.$in{s}.tif") {`chmod 777 /tmp/scans/$imn.$in{s}.tif`;} my $printImage=''; if(-f "/tmp/scans/$imn.$in{s}.tif") { # Make small image to show what was scanned on a screen `convert -density 72 /tmp/scans/$imn.$in{s}.tif -resize x400 /home/scan/http/images/$imn.$in{s}.jpg`; $printImage = "<img src=\"/images/$imn.$in{s}.jpg\" alt=\"$pgs{$in{s}}\">\n"; } ## END FILE PRESENT print "Content-type: text/html\n\n$pgs{$in{s}}<br />\n$printImage"; as simple as that |
SANE on FreeBSD - Web interface CSS filePosted: Friday, July 3, 2020 [13:01:40] - 3
This is CSS file for the web interface: margin: 0; padding: 0; background-color: #fff; color: #090909; font: 1em Exo, sans-serif, Corbel, Calibri, Helvetica, Arial } #content { margin: 0; padding: 0 2em; text-align: center; } a { color: #ef5a00; text-decoration: none } a:hover { color: #ff6900; background-color: #d0d0d0 } a:visited { color: #d34c00 } h1 { font-size: 2em; color: #6792c5; } #headimages img { float: left; display: inline-block; margin-right: 2em; margin-top: 1em; } #forms { width: 80%; margin: 2em auto; clear: both; display: block; overflow: auto; } #forms input[type=radio]{ opacity: 0; position: fixed; width: 0; text-align: center; } #forms label { display: block; background-color: #f1f0f2; padding: 10px 20px; font-family: sans-serif, Arial; font-size: 16px; border: 2px solid #f1f0f2; border-radius: 4px; width: 80px; text-align: center; margin-bottom: 5px; } #forms input[type="radio"]:checked +label { background: rgba(226,226,226,1); background: -moz-linear-gradient(top, rgba(226,226,226,1) 0%, rgba(219,219,219,1) 50%, rgba(209,209,209,1) 51%, rgba(254,254,254,1) 100%); background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(226,226,226,1)), color-stop(50%, rgba(219,219,219,1)), color-stop(51%, rgba(209,209,209,1)), color-stop(100%, rgba(254,254,254,1))); background: -webkit-linear-gradient(top, rgba(226,226,226,1) 0%, rgba(219,219,219,1) 50%, rgba(209,209,209,1) 51%, rgba(254,254,254,1) 100%); background: -o-linear-gradient(top, rgba(226,226,226,1) 0%, rgba(219,219,219,1) 50%, rgba(209,209,209,1) 51%, rgba(254,254,254,1) 100%); background: -ms-linear-gradient(top, rgba(226,226,226,1) 0%, rgba(219,219,219,1) 50%, rgba(209,209,209,1) 51%, rgba(254,254,254,1) 100%); background: linear-gradient(to bottom, rgba(226,226,226,1) 0%, rgba(219,219,219,1) 50%, rgba(209,209,209,1) 51%, rgba(254,254,254,1) 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e2e2e2', endColorstr='#fefefe', GradientType=0 ); border-color: #828784; } input#color11:checked +label, input#color12:checked +label { background-color: #fbf9ff!important; border-color: #444!important; } input#color21:checked +label, input#color22:checked +label { background: rgba(239,197,202,1)!important; background: -moz-linear-gradient(top, rgba(239,197,202,1) 0%, rgba(210,75,90,1) 50%, rgba(186,39,55,1) 51%, rgba(241,142,153,1) 100%)!important; background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(239,197,202,1)), color-stop(50%, rgba(210,75,90,1)), color-stop(51%, rgba(186,39,55,1)), color-stop(100%, rgba(241,142,153,1)))!important; background: -webkit-linear-gradient(top, rgba(239,197,202,1) 0%, rgba(210,75,90,1) 50%, rgba(186,39,55,1) 51%, rgba(241,142,153,1) 100%)!important; background: -o-linear-gradient(top, rgba(239,197,202,1) 0%, rgba(210,75,90,1) 50%, rgba(186,39,55,1) 51%, rgba(241,142,153,1) 100%)!important; background: -ms-linear-gradient(top, rgba(239,197,202,1) 0%, rgba(210,75,90,1) 50%, rgba(186,39,55,1) 51%, rgba(241,142,153,1) 100%)!important; background: linear-gradient(to bottom, rgba(239,197,202,1) 0%, rgba(210,75,90,1) 50%, rgba(186,39,55,1) 51%, rgba(241,142,153,1) 100%)!important; filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#efc5ca', endColorstr='#f18e99', GradientType=0 )!important; border-color: #444!important; color: #fff!important; } input#color31:checked +label, input#color32:checked +label { background: rgba(255,255,255,1)!important; background: -moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%)!important; background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(255,255,255,1)), color-stop(47%, rgba(246,246,246,1)), color-stop(100%, rgba(237,237,237,1)))!important; background: -webkit-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%)!important; background: -o-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%)!important; background: -ms-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%)!important; background: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%)!important; filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed', GradientType=0 )!important; border-color: #444!important; color: #c1c1c2!important; } #forms input[type="radio"]:focus + label { border: 2px solid #444; } #forms h4 { font-weight: normal; font-style: normal; font-size: 1.3em; margin-top: 0; margin-bottom: 15px; } #forms1 { float: left; display: inline-block; width: 40%; margin-right: 2em; border: 1px solid #c2c2c2; } #forms2 { float: left; display: inline-block; width: 40%; border: 1px solid #c2c2c2; } #forms1,#forms2 { padding: 20px; } #fsubm { margin-top: 2em; text-align: center; display: block; margin-bottom: 4em; } ,cl { min-height: 1em; clear: both; } .myButton { box-shadow: 0px 0px 0px 2px #9fb4f2; background: linear-gradient(to bottom, #7892c2 5%, #476e9e 100%); background-color: #7892c2; border-radius: 10px; border: 1px solid #4e6096; display: inline-block; cursor: pointer; color: #fff; font-family: Arial; font-size: 28px; font-weight: bold; padding: 12px 37px; text-decoration: none; text-shadow: 0px 1px 0px #283966; } .myButton:hover { background: linear-gradient(to bottom, #476e9e 5%, #7892c2 100%); background-color: #476e9e; color: #faf9fb; } .myButton:active { position: relative; top: 1px; } just to make it easier |
RE: SANE on FreeBSD - connecting two scanners, control VIA HTMLPosted: Thursday, May 11, 2023 [23:58:42] - 4
This FreeBSD setup works great with Canon imageFORMULA DR-9080C scanner: www.codemacs.com/other/an.. It was very simple to add new scanner to the settings. |