← Back to team overview

simple-scan-team team mailing list archive

[Merge] lp:~holger+lp/simple-scan/simple-scan-cli into lp:simple-scan

 

Holger Hans Peter Freyther has proposed merging lp:~holger+lp/simple-scan/simple-scan-cli into lp:simple-scan.

Requested reviews:
  Simple Scan Development Team (simple-scan-team)

For more details, see:
https://code.launchpad.net/~holger+lp/simple-scan/simple-scan-cli/+merge/91558

I was not able to find a tool that can scan to a PDF from cli (scanimage does not support it). I hacked this together, it hardcodes a lot (A4, DPI, scan mode), I messed up the author and (bzr rewrite has no interactive mode that would allow me to fix it).

Right now the question is if you would do:
1.) Build a libsimplescan and install it (.vapi file so I could build my cli with that)
2.) Include a cli directly
-- 
https://code.launchpad.net/~holger+lp/simple-scan/simple-scan-cli/+merge/91558
Your team Simple Scan Development Team is requested to review the proposed merge of lp:~holger+lp/simple-scan/simple-scan-cli into lp:simple-scan.
=== modified file '.bzrignore'
--- .bzrignore	2011-06-12 10:13:06 +0000
+++ .bzrignore	2012-02-04 17:34:18 +0000
@@ -32,3 +32,10 @@
 src/scanner.c
 src/simple-scan.c
 src/ui.c
+src/simple-scan-cli.c
+src/libsimplescan_a_vala.stamp
+src/simple-scan-cli
+src/simple-scan.h
+src/simple-scan.vapi
+src/simple_scan_cli_vala.stamp
+

=== modified file 'configure.ac'
--- configure.ac	2011-12-08 04:37:24 +0000
+++ configure.ac	2012-02-04 17:34:18 +0000
@@ -11,6 +11,7 @@
 AM_PROG_VALAC([0.13.0])
 AM_PROG_CC_C_O
 AC_HEADER_STDC
+AC_PROG_RANLIB
 
 GLIB_GSETTINGS
 

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2011-07-10 06:59:21 +0000
+++ src/Makefile.am	2012-02-04 17:34:18 +0000
@@ -1,6 +1,7 @@
-bin_PROGRAMS = simple-scan
+bin_PROGRAMS = simple-scan simple-scan-cli
+noinst_LIBRARIES = libsimplescan.a
 
-simple_scan_SOURCES = \
+libsimplescan_a_SOURCES = \
 	config.vapi \
 	book.vala \
 	book-view.vala \
@@ -9,21 +10,38 @@
 	page.vala \
 	page-view.vala \
 	sane.vapi \
+	scanner.vala
+
+libsimplescan_a_VALAFLAGS = --library simple-scan -H simple-scan.h
+
+BUILT_SOURCES = simple-scan.vapi
+
+simple-scan.vapi: libsimplescan.a
+
+simple_scan_SOURCES = \
+	config.vapi \
+	sane.vapi \
+	simple-scan.vapi \
 	simple-scan.vala \
-	scanner.vala \
 	ui.vala
 
-simple_scan_VALAFLAGS = \
+simple_scan_cli_SOURCES = \
+	config.vapi \
+	sane.vapi \
+	simple-scan.vapi \
+	simple-scan-cli.vala
+
+VALAFLAGS = \
 	--pkg=zlib \
 	--pkg=gudev-1.0 \
 	--pkg=gio-2.0 \
 	--pkg=gtk+-3.0
 
 if HAVE_COLORD
-simple_scan_VALAFLAGS += -D HAVE_COLORD
+VALAFLAGS += -D HAVE_COLORD
 endif
 
-simple_scan_CFLAGS = \
+AM_CFLAGS = \
 	$(SIMPLE_SCAN_CFLAGS) \
 	$(COLORD_CFLAGS) \
 	$(WARN_CFLAGS) \
@@ -38,13 +56,23 @@
 simple_scan_LDADD = \
 	$(SIMPLE_SCAN_LIBS) \
 	$(COLORD_LIBS) \
+	libsimplescan.a \
+	-lsane \
+	-ljpeg \
+	-lm
+
+simple_scan_cli_LDADD = \
+	$(SIMPLE_SCAN_LIBS) \
+	$(COLORD_LIBS) \
+	libsimplescan.a \
 	-lsane \
 	-ljpeg \
 	-lm
 
 CLEANFILES = \
 	$(patsubst %.vala,%.c,$(filter %.vala, $(SOURCES))) \
-	*_vala.stamp
+	*_vala.stamp \
+	simple-scan.vapi
 
 DISTCLEANFILES = \
 	Makefile.in

=== added file 'src/simple-scan-cli.vala'
--- src/simple-scan-cli.vala	1970-01-01 00:00:00 +0000
+++ src/simple-scan-cli.vala	2012-02-04 17:34:18 +0000
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2009-2011 Canonical Ltd.
+ * Author: Robert Ancell <robert.ancell@xxxxxxxxxxxxx>
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class Application
+{
+    static bool show_version;
+    static bool debug_enabled;
+    static string? output_name;
+    public static const OptionEntry[] options =
+    {
+        { "version", 'v', 0, OptionArg.NONE, ref show_version,
+          /* Help string for command line --version flag */
+          N_("Show release version"), null},
+        { "debug", 'd', 0, OptionArg.NONE, ref debug_enabled,
+          /* Help string for command line --debug flag */
+          N_("Print debugging messages"), null},
+        { "output", 'o', 0, OptionArg.STRING, ref output_name,
+          /* Help string for --output */
+          N_("Store the PDF to this file"), null},
+        { null }
+    };
+    private static Timer log_timer;
+    private static FileStream? log_file;
+
+    private ScanDevice? default_device = null;
+    private bool have_devices = false;
+    private GUdev.Client udev_client;
+    private Scanner scanner;
+    private Book book;
+    private GLib.MainLoop loop = new MainLoop ();
+
+    private void initialize_book ()
+    {
+        book = new Book ();
+        book.append_page (1260, 1771,
+                          150,
+                          ScanDirection.BOTTOM_TO_TOP);
+    }
+
+    public Application (ScanDevice? device = null)
+    {
+        default_device = device;
+
+        initialize_book ();
+
+        scanner = Scanner.get_instance ();
+        scanner.update_devices.connect (update_scan_devices_cb);
+        scanner.request_authorization.connect (authorize_cb);
+        scanner.expect_page.connect (scanner_new_page_cb);
+        scanner.got_page_info.connect (scanner_page_info_cb);
+        scanner.got_line.connect (scanner_line_cb);
+        scanner.page_done.connect (scanner_page_done_cb);
+        scanner.document_done.connect (scanner_document_done_cb);
+        scanner.scan_failed.connect (scanner_failed_cb);
+        scanner.scanning_changed.connect (scanner_scanning_changed_cb);
+
+        string[]? subsystems = { "usb", null };
+        udev_client = new GUdev.Client (subsystems);
+        udev_client.uevent.connect (on_uevent);
+
+        if (default_device != null)
+        {
+            List<ScanDevice> device_list = null;
+
+            device_list.append (default_device);
+        }
+    }
+    
+    public void start ()
+    {
+        stdout.printf(_("Starting simple-scan cli.\n"));
+        scanner.start ();
+    }
+
+    public void run ()
+    {
+        loop.run ();
+    }
+
+    private void update_scan_devices_cb (Scanner scanner, List<ScanDevice> devices)
+    {
+        var devices_copy = devices.copy ();
+
+        /* If the default device is not detected add it to the list */
+        if (default_device != null)
+        {
+            var default_in_list = false;
+            foreach (var device in devices_copy)
+            {
+                if (device.name == default_device.name)
+                {
+                    default_in_list = true;
+                    break;
+                }
+            }
+
+            if (!default_in_list)
+                devices_copy.prepend (default_device);
+        }
+
+        have_devices = devices_copy.length () > 0;
+        //ui.set_scan_devices (devices_copy);
+
+        stdout.printf("Found scan devices. Starting\n");
+        scan ();
+    }
+
+    private void authorize_cb (Scanner scanner, string resource)
+    {
+        stderr.printf ("Authorization not implemented.\n");
+        //string username, password;
+        //scanner.authorize (username, password);
+    }
+
+    private Page append_page ()
+    {
+        /* Use current page if not used */
+        var page = book.get_page (-1);
+        if (page != null && !page.has_data ())
+        {
+            page.start ();
+            return page;
+        }
+
+        /* Copy info from previous page */
+        var scan_direction = ScanDirection.TOP_TO_BOTTOM;
+        bool do_crop = false;
+        string named_crop = null;
+        var width = 100, height = 100, dpi = 100, cx = 0, cy = 0, cw = 0, ch = 0;
+        if (page != null)
+        {
+            scan_direction = page.get_scan_direction ();
+            width = page.get_width ();
+            height = page.get_height ();
+            dpi = page.get_dpi ();
+
+            do_crop = page.has_crop ();
+            if (do_crop)
+            {
+                named_crop = page.get_named_crop ();
+                page.get_crop (out cx, out cy, out cw, out ch);
+            }
+        }
+
+        page = book.append_page (width, height, dpi, scan_direction);
+        if (do_crop)
+        {
+            if (named_crop != null)
+            {
+                page.set_named_crop (named_crop);
+            }
+            else
+                page.set_custom_crop (cw, ch);
+            page.move_crop (cx, cy);
+        }
+        page.start ();
+
+        return page;
+    }
+
+    public void scan ()
+    {
+        stdout.printf("Going to scan a page.\n");
+        if (!scanner.is_scanning ())
+            append_page();
+
+        var options = new ScanOptions ();
+        options.type = ScanType.SINGLE;
+        options.scan_mode = ScanMode.LINEART;
+        options.dpi = 150;
+        options.depth = 2;
+        options.paper_width = 0;
+        options.paper_height = 0;
+        scanner.scan(null, options);
+    }
+
+    private void scanner_new_page_cb (Scanner scanner)
+    {
+        append_page ();
+    }
+
+    private string? get_profile_for_device (string device_name)
+    {
+#if HAVE_COLORD
+        var device_id = "sane:%s".printf (device_name);
+        debug ("Getting color profile for device %s", device_name);
+
+        var client = new Colord.Client ();
+        try
+        {
+            client.connect_sync ();
+        }
+        catch (Error e)
+        {
+            debug ("Failed to connect to colord: %s", e.message);
+            return null;
+        }
+
+        Colord.Device device;
+        try
+        {
+            device = client.find_device_by_property_sync (Colord.DEVICE_PROPERTY_SERIAL, device_id);
+        }
+        catch (Error e)
+        {
+            debug ("Unable to find colord device %s: %s", device_name, e.message);
+            return null;
+        }
+
+        try
+        {
+            device.connect_sync ();
+        }
+        catch (Error e)
+        {
+            debug ("Failed to get properties from the device %s: %s", device_name, e.message);
+            return null;
+        }
+
+        var profile = device.get_default_profile ();
+        if (profile == null)
+        {
+            debug ("No default color profile for device: %s", device_name);
+            return null;
+        }
+
+        try
+        {
+            profile.connect_sync ();
+        }
+        catch (Error e)
+        {
+            debug ("Failed to get properties from the profile %s: %s", device_name, e.message);
+            return null;
+        }
+
+        if (profile.filename == null)
+        {
+            debug ("No icc color profile for the device %s", device_name);
+            return null;
+        }
+
+        debug ("Using color profile %s for device %s", profile.filename, device_name);
+        return profile.filename;
+#else
+        return null;
+#endif        
+    }
+
+    private void scanner_page_info_cb (Scanner scanner, ScanPageInfo info)
+    {
+        debug ("Page is %d pixels wide, %d pixels high, %d bits per pixel",
+               info.width, info.height, info.depth);
+
+        /* Add a new page */
+        var page = append_page ();
+        page.set_page_info (info);
+
+        /* Get ICC color profile */
+        /* FIXME: The ICC profile could change */
+        /* FIXME: Don't do a D-bus call for each page, cache color profiles */
+        page.set_color_profile (get_profile_for_device (info.device));
+    }
+
+    private void scanner_line_cb (Scanner scanner, ScanLine line)
+    {
+        var page = book.get_page ((int) book.get_n_pages () - 1);
+        page.parse_scan_line (line);
+    }
+
+    private void scanner_page_done_cb (Scanner scanner)
+    {
+        var page = book.get_page ((int) book.get_n_pages () - 1);
+        page.finish ();
+
+        stdout.printf("Finished a page.\n");
+        book.save("pdf", File.new_for_path(output_name));
+        quit();
+    }
+
+    private void remove_empty_page ()
+    {
+        var page = book.get_page ((int) book.get_n_pages () - 1);
+
+        /* Remove a failed page */
+        if (page.has_data ())
+            page.finish ();
+        else
+            book.delete_page (page);
+    }
+
+    private void scanner_document_done_cb (Scanner scanner)
+    {
+        remove_empty_page ();
+    }
+
+    private void scanner_failed_cb (Scanner scanner, int error_code, string error_string)
+    {
+        remove_empty_page ();
+        if (error_code != Sane.Status.CANCELLED)
+        {
+            stderr.printf("%s: %s\n", _("Failed to scan"), error_string);
+        }
+    }
+
+    private void scanner_scanning_changed_cb (Scanner scanner)
+    {
+    }
+
+    private void quit()
+    {
+        book = null;
+        udev_client = null;
+        scanner.free ();
+        
+        stdout.printf("Quit...\n");
+        loop.quit ();
+    }
+
+    private static void log_cb (string? log_domain, LogLevelFlags log_level, string message)
+    {
+        /* Log everything to a file */
+        if (log_file != null) 
+        {
+            string prefix;
+
+            switch (log_level & LogLevelFlags.LEVEL_MASK)
+            {
+            case LogLevelFlags.LEVEL_ERROR:
+                prefix = "ERROR:";
+                break;
+            case LogLevelFlags.LEVEL_CRITICAL:
+                prefix = "CRITICAL:";
+                break;
+            case LogLevelFlags.LEVEL_WARNING:
+                prefix = "WARNING:";
+                break;
+            case LogLevelFlags.LEVEL_MESSAGE:
+                prefix = "MESSAGE:";
+                break;
+            case LogLevelFlags.LEVEL_INFO:
+                prefix = "INFO:";
+                break;
+            case LogLevelFlags.LEVEL_DEBUG:
+                prefix = "DEBUG:";
+                break;
+            default:
+                prefix = "LOG:";
+                break;
+            }
+
+            log_file.printf ("[%+.2fs] %s %s\n", log_timer.elapsed (), prefix, message);
+        }
+
+        /* Only show debug if requested */
+        if ((log_level & LogLevelFlags.LEVEL_DEBUG) != 0)
+        {
+            if (debug_enabled)
+                Log.default_handler (log_domain, log_level, message);
+        }
+        else
+            Log.default_handler (log_domain, log_level, message);
+    }
+
+    private void on_uevent (GUdev.Client client, string action, GUdev.Device device)
+    {
+        scanner.redetect ();
+    }
+    
+    public static int main (string[] args)
+    {
+        Intl.setlocale (LocaleCategory.ALL, "");
+        Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALE_DIR);
+        Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
+        Intl.textdomain (Config.GETTEXT_PACKAGE);
+
+        var c = new OptionContext (/* Arguments and description for --help text */
+                                   _("[DEVICE...] - Scanning utility"));
+        c.add_main_entries (options, Config.GETTEXT_PACKAGE);
+        try
+        {
+            c.parse (ref args);
+        }
+        catch (Error e)
+        {
+            stderr.printf ("%s\n", e.message);
+            stderr.printf (/* Text printed out when an unknown command-line argument provided */
+                           _("Run '%s --help' to see a full list of available command line options."), args[0]);
+            stderr.printf ("\n");
+            return Posix.EXIT_FAILURE;
+        }
+        if (show_version)
+        {
+            /* Note, not translated so can be easily parsed */
+            stderr.printf ("simple-scan-cli %s\n", Config.VERSION);
+            return Posix.EXIT_SUCCESS;
+        }
+
+        ScanDevice? device = null;
+        if (args.length > 1)
+        {
+            device = new ScanDevice ();
+            device.name = args[1];
+            device.label = args[1];
+        }
+
+
+        /* Log to a file */
+        log_timer = new Timer ();
+        var path = Path.build_filename (Environment.get_user_cache_dir (), "simple-scan", null);
+        DirUtils.create_with_parents (path, 0700);
+        path = Path.build_filename (Environment.get_user_cache_dir (), "simple-scan", "simple-scan-cli.log", null);
+        log_file = FileStream.open (path, "w");
+        Log.set_default_handler (log_cb);
+
+        debug ("Starting Simple Scan CLI %s, PID=%i", Config.VERSION, Posix.getpid ());
+
+        Application app = new Application (device);
+        app.start ();
+        app.run ();
+
+        return Posix.EXIT_SUCCESS;
+    }
+}

=== modified file 'src/ui.vala'
--- src/ui.vala	2011-12-08 04:37:24 +0000
+++ src/ui.vala	2012-02-04 17:34:18 +0000
@@ -652,7 +652,7 @@
         var options = new ScanOptions ();
         if (document_hint == "text")
         {
-            options.scan_mode = ScanMode.GRAY;
+            options.scan_mode = ScanMode.LINEART;
             options.dpi = get_text_dpi ();
             options.depth = 2;
         }


Follow ups