Merge lp:~holger+lp/simple-scan/simple-scan-cli into lp:~simple-scan-team/simple-scan/trunk

Proposed by Holger Hans Peter Freyther
Status: Rejected
Rejected by: Michael Nagel
Proposed branch: lp:~holger+lp/simple-scan/simple-scan-cli
Merge into: lp:~simple-scan-team/simple-scan/trunk
Diff against target: 558 lines (+476/-8)
5 files modified
.bzrignore (+7/-0)
configure.ac (+1/-0)
src/Makefile.am (+35/-7)
src/simple-scan-cli.vala (+432/-0)
src/ui.vala (+1/-1)
To merge this branch: bzr merge lp:~holger+lp/simple-scan/simple-scan-cli
Reviewer Review Type Date Requested Status
Michael Nagel Disapprove
Review via email: mp+91558@code.launchpad.net

Description of the change

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

To post a comment you must log in.
Revision history for this message
Michael Nagel (nailor) wrote :

Hi Holger,

ultimately Robert will have to approve ;)

I have never worked with such merge requests.

Here are a few thoughts of mine though:
- thanks for contributing!
- why didn't you write a three line shell wrapper around scanimage that scans to a temporary file and then calls some combination of convert/pdftk? i am positively interested in this!
- what's the benefit from merging this into trunk? it's not exactly in line with the mission statement for simple scan. (there is no mission statement -- but if there was one it would be about making scanning simple for casual users). on the other hand, it might increase usage of the simple-scan, leading to fixes in the code for the benefit of all users. also it might improve maintainability by allowing to easily reproduce crashes...
- allowing some configuration via command line arguments would definitly make it more useful for other users...
- there seem to be some unrelated changes mixed into the commits, like changing from grayscale to lineart. lineart is theoretically correct, but caused problems for many users, so grayscale it is...

Thanks again,
it would be great if you could comment on some of these
and I am waiting for a comment from Robert.

Also, I am not really sure if this is the best place to have such discussion, but let's just try.
If it does not work out, we might just open a ticket/bug report, IMHO those can be abused for general discussion quite well...

Best Regards

Revision history for this message
Holger Hans Peter Freyther (holger+lp) wrote :

shell_wrapper: I'm happy with the PDF generated by simple-scan, after playing with the code (never did any Vala before), I could see myself writing a ncurses UI based on the Book/Page classes, or at least a simple batch mode.

configuration: Sure, I just wondered how to proceed. E.g. if you manage to provide the lib with stable API/ABI or if you want to include a cli utility.

unrelated_change: Yes, I don't know how to do git rebase -i with bzr. :)

talk: I'm happy to take the discussion somewhere else.

Revision history for this message
Michael Nagel (nailor) wrote :
review: Needs Information
Revision history for this message
Michael Nagel (nailor) wrote :
review: Disapprove

Unmerged revisions

563. By ich <ich@sanmingze>

cli: First standalone program that can scan stuff

562. By ich <ich@sanmingze>

cli: Remove some methods we don't need.

561. By ich <ich@sanmingze>

cli: Introduce a CLI application (or what will become one)

560. By ich <ich@sanmingze>

scan: Use LineArt for my Canon LIDE110

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-06-12 10:13:06 +0000
3+++ .bzrignore 2012-02-04 17:34:18 +0000
4@@ -32,3 +32,10 @@
5 src/scanner.c
6 src/simple-scan.c
7 src/ui.c
8+src/simple-scan-cli.c
9+src/libsimplescan_a_vala.stamp
10+src/simple-scan-cli
11+src/simple-scan.h
12+src/simple-scan.vapi
13+src/simple_scan_cli_vala.stamp
14+
15
16=== modified file 'configure.ac'
17--- configure.ac 2011-12-08 04:37:24 +0000
18+++ configure.ac 2012-02-04 17:34:18 +0000
19@@ -11,6 +11,7 @@
20 AM_PROG_VALAC([0.13.0])
21 AM_PROG_CC_C_O
22 AC_HEADER_STDC
23+AC_PROG_RANLIB
24
25 GLIB_GSETTINGS
26
27
28=== modified file 'src/Makefile.am'
29--- src/Makefile.am 2011-07-10 06:59:21 +0000
30+++ src/Makefile.am 2012-02-04 17:34:18 +0000
31@@ -1,6 +1,7 @@
32-bin_PROGRAMS = simple-scan
33+bin_PROGRAMS = simple-scan simple-scan-cli
34+noinst_LIBRARIES = libsimplescan.a
35
36-simple_scan_SOURCES = \
37+libsimplescan_a_SOURCES = \
38 config.vapi \
39 book.vala \
40 book-view.vala \
41@@ -9,21 +10,38 @@
42 page.vala \
43 page-view.vala \
44 sane.vapi \
45+ scanner.vala
46+
47+libsimplescan_a_VALAFLAGS = --library simple-scan -H simple-scan.h
48+
49+BUILT_SOURCES = simple-scan.vapi
50+
51+simple-scan.vapi: libsimplescan.a
52+
53+simple_scan_SOURCES = \
54+ config.vapi \
55+ sane.vapi \
56+ simple-scan.vapi \
57 simple-scan.vala \
58- scanner.vala \
59 ui.vala
60
61-simple_scan_VALAFLAGS = \
62+simple_scan_cli_SOURCES = \
63+ config.vapi \
64+ sane.vapi \
65+ simple-scan.vapi \
66+ simple-scan-cli.vala
67+
68+VALAFLAGS = \
69 --pkg=zlib \
70 --pkg=gudev-1.0 \
71 --pkg=gio-2.0 \
72 --pkg=gtk+-3.0
73
74 if HAVE_COLORD
75-simple_scan_VALAFLAGS += -D HAVE_COLORD
76+VALAFLAGS += -D HAVE_COLORD
77 endif
78
79-simple_scan_CFLAGS = \
80+AM_CFLAGS = \
81 $(SIMPLE_SCAN_CFLAGS) \
82 $(COLORD_CFLAGS) \
83 $(WARN_CFLAGS) \
84@@ -38,13 +56,23 @@
85 simple_scan_LDADD = \
86 $(SIMPLE_SCAN_LIBS) \
87 $(COLORD_LIBS) \
88+ libsimplescan.a \
89+ -lsane \
90+ -ljpeg \
91+ -lm
92+
93+simple_scan_cli_LDADD = \
94+ $(SIMPLE_SCAN_LIBS) \
95+ $(COLORD_LIBS) \
96+ libsimplescan.a \
97 -lsane \
98 -ljpeg \
99 -lm
100
101 CLEANFILES = \
102 $(patsubst %.vala,%.c,$(filter %.vala, $(SOURCES))) \
103- *_vala.stamp
104+ *_vala.stamp \
105+ simple-scan.vapi
106
107 DISTCLEANFILES = \
108 Makefile.in
109
110=== added file 'src/simple-scan-cli.vala'
111--- src/simple-scan-cli.vala 1970-01-01 00:00:00 +0000
112+++ src/simple-scan-cli.vala 2012-02-04 17:34:18 +0000
113@@ -0,0 +1,432 @@
114+/*
115+ * Copyright (C) 2009-2011 Canonical Ltd.
116+ * Author: Robert Ancell <robert.ancell@canonical.com>
117+ *
118+ * This program is free software: you can redistribute it and/or modify it under
119+ * the terms of the GNU General Public License as published by the Free Software
120+ * Foundation, either version 3 of the License, or (at your option) any later
121+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
122+ * license.
123+ */
124+
125+public class Application
126+{
127+ static bool show_version;
128+ static bool debug_enabled;
129+ static string? output_name;
130+ public static const OptionEntry[] options =
131+ {
132+ { "version", 'v', 0, OptionArg.NONE, ref show_version,
133+ /* Help string for command line --version flag */
134+ N_("Show release version"), null},
135+ { "debug", 'd', 0, OptionArg.NONE, ref debug_enabled,
136+ /* Help string for command line --debug flag */
137+ N_("Print debugging messages"), null},
138+ { "output", 'o', 0, OptionArg.STRING, ref output_name,
139+ /* Help string for --output */
140+ N_("Store the PDF to this file"), null},
141+ { null }
142+ };
143+ private static Timer log_timer;
144+ private static FileStream? log_file;
145+
146+ private ScanDevice? default_device = null;
147+ private bool have_devices = false;
148+ private GUdev.Client udev_client;
149+ private Scanner scanner;
150+ private Book book;
151+ private GLib.MainLoop loop = new MainLoop ();
152+
153+ private void initialize_book ()
154+ {
155+ book = new Book ();
156+ book.append_page (1260, 1771,
157+ 150,
158+ ScanDirection.BOTTOM_TO_TOP);
159+ }
160+
161+ public Application (ScanDevice? device = null)
162+ {
163+ default_device = device;
164+
165+ initialize_book ();
166+
167+ scanner = Scanner.get_instance ();
168+ scanner.update_devices.connect (update_scan_devices_cb);
169+ scanner.request_authorization.connect (authorize_cb);
170+ scanner.expect_page.connect (scanner_new_page_cb);
171+ scanner.got_page_info.connect (scanner_page_info_cb);
172+ scanner.got_line.connect (scanner_line_cb);
173+ scanner.page_done.connect (scanner_page_done_cb);
174+ scanner.document_done.connect (scanner_document_done_cb);
175+ scanner.scan_failed.connect (scanner_failed_cb);
176+ scanner.scanning_changed.connect (scanner_scanning_changed_cb);
177+
178+ string[]? subsystems = { "usb", null };
179+ udev_client = new GUdev.Client (subsystems);
180+ udev_client.uevent.connect (on_uevent);
181+
182+ if (default_device != null)
183+ {
184+ List<ScanDevice> device_list = null;
185+
186+ device_list.append (default_device);
187+ }
188+ }
189+
190+ public void start ()
191+ {
192+ stdout.printf(_("Starting simple-scan cli.\n"));
193+ scanner.start ();
194+ }
195+
196+ public void run ()
197+ {
198+ loop.run ();
199+ }
200+
201+ private void update_scan_devices_cb (Scanner scanner, List<ScanDevice> devices)
202+ {
203+ var devices_copy = devices.copy ();
204+
205+ /* If the default device is not detected add it to the list */
206+ if (default_device != null)
207+ {
208+ var default_in_list = false;
209+ foreach (var device in devices_copy)
210+ {
211+ if (device.name == default_device.name)
212+ {
213+ default_in_list = true;
214+ break;
215+ }
216+ }
217+
218+ if (!default_in_list)
219+ devices_copy.prepend (default_device);
220+ }
221+
222+ have_devices = devices_copy.length () > 0;
223+ //ui.set_scan_devices (devices_copy);
224+
225+ stdout.printf("Found scan devices. Starting\n");
226+ scan ();
227+ }
228+
229+ private void authorize_cb (Scanner scanner, string resource)
230+ {
231+ stderr.printf ("Authorization not implemented.\n");
232+ //string username, password;
233+ //scanner.authorize (username, password);
234+ }
235+
236+ private Page append_page ()
237+ {
238+ /* Use current page if not used */
239+ var page = book.get_page (-1);
240+ if (page != null && !page.has_data ())
241+ {
242+ page.start ();
243+ return page;
244+ }
245+
246+ /* Copy info from previous page */
247+ var scan_direction = ScanDirection.TOP_TO_BOTTOM;
248+ bool do_crop = false;
249+ string named_crop = null;
250+ var width = 100, height = 100, dpi = 100, cx = 0, cy = 0, cw = 0, ch = 0;
251+ if (page != null)
252+ {
253+ scan_direction = page.get_scan_direction ();
254+ width = page.get_width ();
255+ height = page.get_height ();
256+ dpi = page.get_dpi ();
257+
258+ do_crop = page.has_crop ();
259+ if (do_crop)
260+ {
261+ named_crop = page.get_named_crop ();
262+ page.get_crop (out cx, out cy, out cw, out ch);
263+ }
264+ }
265+
266+ page = book.append_page (width, height, dpi, scan_direction);
267+ if (do_crop)
268+ {
269+ if (named_crop != null)
270+ {
271+ page.set_named_crop (named_crop);
272+ }
273+ else
274+ page.set_custom_crop (cw, ch);
275+ page.move_crop (cx, cy);
276+ }
277+ page.start ();
278+
279+ return page;
280+ }
281+
282+ public void scan ()
283+ {
284+ stdout.printf("Going to scan a page.\n");
285+ if (!scanner.is_scanning ())
286+ append_page();
287+
288+ var options = new ScanOptions ();
289+ options.type = ScanType.SINGLE;
290+ options.scan_mode = ScanMode.LINEART;
291+ options.dpi = 150;
292+ options.depth = 2;
293+ options.paper_width = 0;
294+ options.paper_height = 0;
295+ scanner.scan(null, options);
296+ }
297+
298+ private void scanner_new_page_cb (Scanner scanner)
299+ {
300+ append_page ();
301+ }
302+
303+ private string? get_profile_for_device (string device_name)
304+ {
305+#if HAVE_COLORD
306+ var device_id = "sane:%s".printf (device_name);
307+ debug ("Getting color profile for device %s", device_name);
308+
309+ var client = new Colord.Client ();
310+ try
311+ {
312+ client.connect_sync ();
313+ }
314+ catch (Error e)
315+ {
316+ debug ("Failed to connect to colord: %s", e.message);
317+ return null;
318+ }
319+
320+ Colord.Device device;
321+ try
322+ {
323+ device = client.find_device_by_property_sync (Colord.DEVICE_PROPERTY_SERIAL, device_id);
324+ }
325+ catch (Error e)
326+ {
327+ debug ("Unable to find colord device %s: %s", device_name, e.message);
328+ return null;
329+ }
330+
331+ try
332+ {
333+ device.connect_sync ();
334+ }
335+ catch (Error e)
336+ {
337+ debug ("Failed to get properties from the device %s: %s", device_name, e.message);
338+ return null;
339+ }
340+
341+ var profile = device.get_default_profile ();
342+ if (profile == null)
343+ {
344+ debug ("No default color profile for device: %s", device_name);
345+ return null;
346+ }
347+
348+ try
349+ {
350+ profile.connect_sync ();
351+ }
352+ catch (Error e)
353+ {
354+ debug ("Failed to get properties from the profile %s: %s", device_name, e.message);
355+ return null;
356+ }
357+
358+ if (profile.filename == null)
359+ {
360+ debug ("No icc color profile for the device %s", device_name);
361+ return null;
362+ }
363+
364+ debug ("Using color profile %s for device %s", profile.filename, device_name);
365+ return profile.filename;
366+#else
367+ return null;
368+#endif
369+ }
370+
371+ private void scanner_page_info_cb (Scanner scanner, ScanPageInfo info)
372+ {
373+ debug ("Page is %d pixels wide, %d pixels high, %d bits per pixel",
374+ info.width, info.height, info.depth);
375+
376+ /* Add a new page */
377+ var page = append_page ();
378+ page.set_page_info (info);
379+
380+ /* Get ICC color profile */
381+ /* FIXME: The ICC profile could change */
382+ /* FIXME: Don't do a D-bus call for each page, cache color profiles */
383+ page.set_color_profile (get_profile_for_device (info.device));
384+ }
385+
386+ private void scanner_line_cb (Scanner scanner, ScanLine line)
387+ {
388+ var page = book.get_page ((int) book.get_n_pages () - 1);
389+ page.parse_scan_line (line);
390+ }
391+
392+ private void scanner_page_done_cb (Scanner scanner)
393+ {
394+ var page = book.get_page ((int) book.get_n_pages () - 1);
395+ page.finish ();
396+
397+ stdout.printf("Finished a page.\n");
398+ book.save("pdf", File.new_for_path(output_name));
399+ quit();
400+ }
401+
402+ private void remove_empty_page ()
403+ {
404+ var page = book.get_page ((int) book.get_n_pages () - 1);
405+
406+ /* Remove a failed page */
407+ if (page.has_data ())
408+ page.finish ();
409+ else
410+ book.delete_page (page);
411+ }
412+
413+ private void scanner_document_done_cb (Scanner scanner)
414+ {
415+ remove_empty_page ();
416+ }
417+
418+ private void scanner_failed_cb (Scanner scanner, int error_code, string error_string)
419+ {
420+ remove_empty_page ();
421+ if (error_code != Sane.Status.CANCELLED)
422+ {
423+ stderr.printf("%s: %s\n", _("Failed to scan"), error_string);
424+ }
425+ }
426+
427+ private void scanner_scanning_changed_cb (Scanner scanner)
428+ {
429+ }
430+
431+ private void quit()
432+ {
433+ book = null;
434+ udev_client = null;
435+ scanner.free ();
436+
437+ stdout.printf("Quit...\n");
438+ loop.quit ();
439+ }
440+
441+ private static void log_cb (string? log_domain, LogLevelFlags log_level, string message)
442+ {
443+ /* Log everything to a file */
444+ if (log_file != null)
445+ {
446+ string prefix;
447+
448+ switch (log_level & LogLevelFlags.LEVEL_MASK)
449+ {
450+ case LogLevelFlags.LEVEL_ERROR:
451+ prefix = "ERROR:";
452+ break;
453+ case LogLevelFlags.LEVEL_CRITICAL:
454+ prefix = "CRITICAL:";
455+ break;
456+ case LogLevelFlags.LEVEL_WARNING:
457+ prefix = "WARNING:";
458+ break;
459+ case LogLevelFlags.LEVEL_MESSAGE:
460+ prefix = "MESSAGE:";
461+ break;
462+ case LogLevelFlags.LEVEL_INFO:
463+ prefix = "INFO:";
464+ break;
465+ case LogLevelFlags.LEVEL_DEBUG:
466+ prefix = "DEBUG:";
467+ break;
468+ default:
469+ prefix = "LOG:";
470+ break;
471+ }
472+
473+ log_file.printf ("[%+.2fs] %s %s\n", log_timer.elapsed (), prefix, message);
474+ }
475+
476+ /* Only show debug if requested */
477+ if ((log_level & LogLevelFlags.LEVEL_DEBUG) != 0)
478+ {
479+ if (debug_enabled)
480+ Log.default_handler (log_domain, log_level, message);
481+ }
482+ else
483+ Log.default_handler (log_domain, log_level, message);
484+ }
485+
486+ private void on_uevent (GUdev.Client client, string action, GUdev.Device device)
487+ {
488+ scanner.redetect ();
489+ }
490+
491+ public static int main (string[] args)
492+ {
493+ Intl.setlocale (LocaleCategory.ALL, "");
494+ Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALE_DIR);
495+ Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
496+ Intl.textdomain (Config.GETTEXT_PACKAGE);
497+
498+ var c = new OptionContext (/* Arguments and description for --help text */
499+ _("[DEVICE...] - Scanning utility"));
500+ c.add_main_entries (options, Config.GETTEXT_PACKAGE);
501+ try
502+ {
503+ c.parse (ref args);
504+ }
505+ catch (Error e)
506+ {
507+ stderr.printf ("%s\n", e.message);
508+ stderr.printf (/* Text printed out when an unknown command-line argument provided */
509+ _("Run '%s --help' to see a full list of available command line options."), args[0]);
510+ stderr.printf ("\n");
511+ return Posix.EXIT_FAILURE;
512+ }
513+ if (show_version)
514+ {
515+ /* Note, not translated so can be easily parsed */
516+ stderr.printf ("simple-scan-cli %s\n", Config.VERSION);
517+ return Posix.EXIT_SUCCESS;
518+ }
519+
520+ ScanDevice? device = null;
521+ if (args.length > 1)
522+ {
523+ device = new ScanDevice ();
524+ device.name = args[1];
525+ device.label = args[1];
526+ }
527+
528+
529+ /* Log to a file */
530+ log_timer = new Timer ();
531+ var path = Path.build_filename (Environment.get_user_cache_dir (), "simple-scan", null);
532+ DirUtils.create_with_parents (path, 0700);
533+ path = Path.build_filename (Environment.get_user_cache_dir (), "simple-scan", "simple-scan-cli.log", null);
534+ log_file = FileStream.open (path, "w");
535+ Log.set_default_handler (log_cb);
536+
537+ debug ("Starting Simple Scan CLI %s, PID=%i", Config.VERSION, Posix.getpid ());
538+
539+ Application app = new Application (device);
540+ app.start ();
541+ app.run ();
542+
543+ return Posix.EXIT_SUCCESS;
544+ }
545+}
546
547=== modified file 'src/ui.vala'
548--- src/ui.vala 2011-12-08 04:37:24 +0000
549+++ src/ui.vala 2012-02-04 17:34:18 +0000
550@@ -652,7 +652,7 @@
551 var options = new ScanOptions ();
552 if (document_hint == "text")
553 {
554- options.scan_mode = ScanMode.GRAY;
555+ options.scan_mode = ScanMode.LINEART;
556 options.dpi = get_text_dpi ();
557 options.depth = 2;
558 }

Subscribers

People subscribed via source and target branches