I scan pretty much everything — bills, old photos, mortgage documents, tax stuff, etc. It can be tedious, but it makes finding things much easier when you need to remember whether you filed in 83(b) election in 2006 or what exactly the previous owners disclosed about the condition of the roof.

Having a good scanner with a document feeder helps a lot, but the standard scanning UI in Mac OS X isn’t exactly efficient for quickly scanning a whole bunch of stuff into labeled directories. What I really wanted was a way that I could just type out something quickly on the command line to control the scanner, but I couldn’t find anything out there. So I wrote scanline, which I’m now making available as an MIT-licensed open source project.

Using scanline

The basic way to use scanline is to just invoke it on the command line with no arguments (or hit up-arrow if you just scanned something previously):

airica:~ klep$ scanline

This will scan the current document(s) in the document feeder of the default scanner into ~/Documents/Archive/[year]/scan_[hms].pdf

But there’s a whole lot more you can do. scanline uses directories as labels, so if the document you’re scanning is your auto registration, you can specify that scanline should put it in the “car” directory:

airica:~ klep$ scanline car

This puts the document into ~/Documents/Archive/car/[year]/scan_[hms].pdf

If you also want your registration to show up in your directory for taxes (so you remember to deduct the VLF), you can specify multiple labels:

airica:~ klep$ scanline car taxes

This creates an alias of the scanned document in ~/Documents/Archive/taxes/[year]/scan_[hms].pdf in addition to the original in the car directory.

If it’s a double-sided document (and your scanner supports it), use -duplex to scan both sides:

airica:~ klep$ scanline -duplex car taxes

Want to scan from the flatbed scanner instead of the document feeder?

airica:~ klep$ scanline -flatbed

Want to scan into a different root directory instead of ~/Documents/Archive?

airica:~ klep$ scanline -dir ~/Desktop

Here’s a good one — got lots of items to scan on the flatbed but want them all put together into a single PDF?

airica:~ klep$ scanline -batch -flatbed

Want to name the document something that isn’t generic?

airica:~ klep$ scanline -flatbed -name registration car

The above command will scan the item on the flatbed into ~/Documents/Archive/car/[year]/registration.pdf

If you have multiple scanners connected to your computer, you can select them by name:

airica:~ klep$ scanline -scanner "Epson 565655"

If you’re not sure what scanners are available, list them:

airica:~ klep$ scanline -list

Obtaining scanline

If you’d like to build scanline from source, enhance it, or tweak it for your needs, get it from:

https://github.com/klep/scanline

If you just want the binary (it’s a debug build but plenty fast and stable for real use), you can download it here.

 

How It Works

scanline was created using the ImageCaptureCore framework. It uses Lumberjack for logging and XCTest/OCMock for unit tests. It’s my first Mac app in quite some time, so go easy on me if you look at the source.

 

 

 

 


				

66 thoughts on “scanline – Command-line Document Scanning for Mac OS X

  1. Dainius

    Amazing app! Just what I was looking for. However, is there any way to control the resolution of the scan? I.e. “scan max dpi” or somesuch? Keep up the great work!

    -D

  2. klep

    Thanks, Dainius. That’s on my short list of things to add. The only tricky part is that supported resolutions vary based on the device. So I could add something like -resolution X, but there’s no guarantee that your device supports X. Not a big problem, but just makes it a little more complicated.

  3. klep

    Just added a -minResolution option (also -res or -resolution will work). You can re-download the new binary or build from github. Enjoy!

  4. Markus

    This tool is great. It’s exactly the same resolution as with the GUI. I hope you keep adding new functions like color modes and support for TIFF files in the future.

  5. Derick

    Hi,

    Sorry for the obvious question, but how do run this? command not found if I try your command in terminal and dropping your binary download onto Terminal brings up ‘Permission denied’.
    But it sounds like exactly what I’ve been looking for

  6. klep

    Depends where you downloaded it to. It’s probably:

    $ ~/Downloads/scanline

  7. Derick

    Thanks for the reply –

    at first I had permission issues. took care of that, then I had ‘scanner not found’ message (due to scanner not supported in Mavericks) – again taken cared of: just finally got Mavericks to recognize the scanner again (and not just through VueScan). So I no longer get the ‘scanner not found’ message – though now when I try to run scanline it just hangs with no message but also no scanning either. Do you happen to know what I might be doing wrong?

  8. Hein Bollo

    Command line parameters for black/white scan would be perfect.

  9. klep

    Thanks for the suggestion! I just updated it with a -mono option to scan in monochrome. You can also use -bw.

  10. klep

    Not sure — try “scanline -verbose” and let me know if it outputs anything…

  11. Hein Bollo

    Thanks for adding the mono option! I just found one bug.If I scan documents with A4 size the scanned document is missing content at the bottom.
    I think scanline uses the letter format?
    A4 should have:
    210 mm × 297 mm

    While Letter has:
    216mm × 279mm

    So another option for the document scan size would be perfect!

  12. Hein Bollo

    Fixed my problem by adding the following to AppController.m after line 543(dfu.duplexScanningEnabled = [configuration isDuplex];):

    dfu.documentType=ICScannerDocumentTypeA4;

    But I don’t have enough Obj-C knowledge to make this configurable by a command line option.

  13. Chris

    Hi klep. I am having a similar issue to Derick in that scanline just hangs until I control-c it. When I run “scanline -verbose” I get the following output:

    2014-10-18 08:00:49:271 scanline[443:513] setting functional unit
    2014-10-18 08:00:49:271 scanline[443:513] current functional unit: 0
    2014-10-18 08:00:49:271 scanline[443:513] doc feeder is 3
    2014-10-18 08:00:49:271 scanline[443:513] flatbed is 0
    2014-10-18 08:00:49:271 scanline[443:513] selected functionalUnitType: 0
    2014-10-18 08:00:49:271 scanline[443:513] error: Error Domain=com.apple.ImageCaptureCore Code=-9922 “The operation couldn’t be completed. (com.apple.ImageCaptureCore error -9922.)”

    This output repeated endlessly until I hit control-c. Your scanline tool sounds exactly like what I have been searching for and I’d love to use it. Any chance you can look at this error and fix? Thanks!

  14. Chris

    I should have added that I am using OS X Yosemite 10.10. Cheers.

  15. Chris

    Apologies – it works fine! I just needed to run it with the -flatbed argument. Great tool, thanks!

  16. michael

    Hi, Great idea with your tool. I’m trying to get it to work with my Brother MFC-8710DW. This is what I get so far. The tool detects my scanner when using the using the -list option. Nothing happens when I try to scan. Regardless if regular or with the flatbed option. When I try verbose it keeps looping an generating the same error that Chris reported.

    Mine is the following error:

    014-12-11 14:25:45:858 scanline[5798:507] setting functional unit
    2014-12-11 14:25:45:858 scanline[5798:507] current functional unit: 0
    2014-12-11 14:25:45:858 scanline[5798:507] doc feeder is 3
    2014-12-11 14:25:45:858 scanline[5798:507] flatbed is 0
    2014-12-11 14:25:45:858 scanline[5798:507] selected functionalUnitType: 0

    2014-12-11 14:25:45:858 scanline[5798:507] error: Error Domain=com.apple.ImageCaptureCore Code=-9922 “The operation couldn’t be completed. (com.apple.ImageCaptureCore error -9922.)”

    Any idea? I would appreciate if you could point me to the right information to get your tool working. Thanks.

    Michael

  17. klep

    Hi Michael — That error tends to show up (repeatedly) when the scanner is warming up. But that shouldn’t take more than a few seconds. Are you able to scan using Preview.app?

  18. Tom

    This is not working for me. I try using the command and I get No such file or directory.

    I know where it is even when I specify the path directly it does not work.

  19. klep

    You may need to set it to be executable:

    % chmod a+x ./scanline
    % ./scanline

  20. Tom

    Thanks so much. That got it working. If I could set DPI and feed a specified file name I can drop this right into daily use. Is there a way to do those things?

  21. Tom

    Caught the comment on resolution. What about filename? I suppose there is a way to get the file name in a round about way and then rename it.

  22. Andreas K. Auen

    Hey, I love this! It works perfectly on my computer with my scanner. However at my parents house, with Mavericks and a Canon MF8580CDW set up over network, I only get this output in verbose:
    2015-03-03 20:08:52:775 scanline[33379:507] Looking for available scanners…
    2015-03-03 20:08:52:811 scanline[33379:507] Found scanner: Canon MF8500C Series
    2015-03-03 20:08:52:811 scanline[33379:507] All devices have been added.

    The scanner works good with Preview.

    Any ideas are very welcome!

    Andreas

  23. klep

    Hmm… Not sure. I didn’t have any problems on Mavericks. Have you tried restarting the scanner and the Mac?

  24. klep

    You can set the filename with -name. For example:

    ./scanline -name phone_bill -dir ~/Desktop

  25. Tom

    Thank you. The naming works.

    I am having trouble with OSX accessing the file right after it is created. I can navigate to the file in the Finder and see it but terminal cannot see it and the app I need to import the PDF into cannot see it either. If I view the file in Finder and click the file name and then the open folder space the terminal and the app can see it after that.

    Is there something I need to do to solidify the new file in the index?

  26. Tom

    Page size was mentioned before, but I did not see a response. Can I set the page dimensions?

  27. Tom

    I also noticed when using the actual scan utility the B&W setting gives a grayscale PDF about 30% larger in file size and also a bit clearer for the given DPI. The info on both shows the same encoding.

  28. Trevor W

    Hello, When I run this program I am getting the follwoing Output:

    2015-03-20 12:33:31:730 scanline[556:513] Starting scan…
    2015-03-20 12:33:32:255 scanline[556:513] No document was scanned.
    2015-03-20 12:33:32.256 scanline[556:79750] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[NSFileManager copyItemAtURL:toURL:error:]: source URL is nil’
    *** First throw call stack:
    (
    0 CoreFoundation 0x00007fff945f566c __exceptionPreprocess + 172
    1 libobjc.A.dylib 0x00007fff89bd276e objc_exception_throw + 43
    2 CoreFoundation 0x00007fff945f551d +[NSException raise:format:] + 205
    3 Foundation 0x00007fff8d2672cf -[NSFileManager copyItemAtURL:toURL:error:] + 113
    4 scanline 0x000000010002065f -[AppController outputAndTagFile:] + 3311
    5 scanline 0x000000010001f8b3 -[AppController scannerDevice:didCompleteScanWithError:] + 1331
    6 ImageCaptureCore 0x00007fff8a6fffd5 -[ICScannerDevice handleCommandCompletion:] + 5612
    7 ImageCaptureCore 0x00007fff8a6cd2cb -[ICCommandCenter handleCompletionEvent:replyEvent:] + 341
    8 ImageCaptureCore 0x00007fff8a6cde8b -[ICCommandCenter handleMachMessage:] + 342
    9 Foundation 0x00007fff8d1d5ce1 __NSFireMachPort + 94
    10 CoreFoundation 0x00007fff94513a2d __CFMachPortPerform + 285
    11 CoreFoundation 0x00007fff945138f9 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
    12 CoreFoundation 0x00007fff9451386b __CFRunLoopDoSource1 + 475
    13 CoreFoundation 0x00007fff945053e7 __CFRunLoopRun + 2375
    14 CoreFoundation 0x00007fff94504858 CFRunLoopRunSpecific + 296
    15 CoreFoundation 0x00007fff945baef1 CFRunLoopRun + 97
    16 scanline 0x0000000100015735 main + 165
    17 scanline 0x0000000100001844 start + 52
    )
    libc++abi.dylib: terminating with uncaught exception of type NSException
    Abort trap: 6

    Any Help? Am I missing a library or something?

  29. klep

    Does it show any scanners if you do “scanline -list”?

  30. Tom

    @Trevor
    Are you properly specifying the flatbed or doc feeder? I get something similar when there is no document in the feeder when it is run.

  31. Tom

    This has been a great utility. Will you ever add support for paper size? Do you take donations?

  32. Jason WU

    I am using Mac OS X 10.10 with the scanner EPSON Perfection V37/V370

    the command line is like

    scanline -flatbed -scanner “EPSON Perfection V37/V370” -verbose

    The scanner did the scan action. But I got the same error as @Trevor. And finally get nothing. Any idea about it?
    thanks

  33. klep

    @Jason – Does anything seem to happen on the scanner side? What if you specify -flatbed?

    @Tom – Thanks for your comments! Sorry I didn’t see them earlier. No donations necessary. I’ll add paper size support to the feature list. Be sure to watch the project on GitHub:

    https://github.com/klep/scanline

  34. Art Westin

    Seems scanline can only be run as admin. Is this true and intended?

  35. klep

    Should be able to run as any user. Might need to “chmod a+x scanline” first

  36. Art Westin

    Thanks for your quick reply.

    This is the error (repeated 5 times) when I run as regular user:

    $ ./scanline -list
    2015-08-04 06:35:08:921 scanline[86042:513] Available scanners:
    2015-08-04 06:35:08.934 scanline[86042:4877560] ERROR: [ICCommandCenter launchAgent] – Failed to launch agent[1], status: -10810, url: ‘file:///System/Library/Image%20Capture/Support/Image%20Capture%20Extension.app/’
    2015-08-04 06:35:08.934 scanline[86042:4877560]
    ***** Failed to launch Image Capture Extension.

    2015-08-04 06:35:28:956 scanline[86042:513] No scanners found.
    $

    scanline sits on the Desktop (not in /Applications) for the admin and regular user, and has the same chmods. It doesn’t matter who owns it, or if it’s setuid.

    -r-xr-xr-x 1 user1 staff 329020 Aug 4 06:34 scanline
    -r-xr-xr-x 1 admin wheel 329020 Jul 2 06:00 scanline

    What do you think?

  37. Conor

    Thank you for this! It makes scanning wicked easy! Is there any documentation that lists its full range of options other than this blog?

    I also wonder whether there is a way to scan as text, as opposed to an image? What kind of additional code would that require?

  38. klep

    Thanks for the kind words, Conor. I’m working on an update that will include help, and I’m also considering using the GitHub page as the central repository for downloading and using the app.

    In terms of text, looks like there are some open source OCR libraries out there that I could use. Would be handy to maybe store the text alongside the images, so you could easily find the document you wanted then look at the original…

  39. klep

    Unfortunately, that’s a fairly generic error, so I’m not sure what’s going on. I assume a restart didn’t help? Based on your earlier question, perhaps this app (/System/Library/Image Capture/Support/Image Capture Extension.app) somehow has permissions that don’t allow normal users to start scanning?

  40. Art Westin

    Well I got it to work but I’m not exactly sure how. I did add the _lpadmin group to my user, but I don’t think that was it. When it failed, I trying running the UI app Image Capture and Quit gracefully. That often reset it so scanline could access the device again.

    If I get any better insight, I’ll let you know.

    Thanks for your active support.

  41. Art Westin

    More data:

    Something changed in El Capitan. Now in order to run scanline, you have to be logged into the user’s GUI. This causes HPScanner to run in the background. Without that, scanline cannot find the scanner and fails.

    So the questions are:

    – Why did this change?

    – Can HPScanner be run manually to support the command line invocation of scanline? With what arguments?

  42. victor magiros

    is there a way to choose a scanner by ip address or device address instead of name because my scanners are only giving out the default names for some reason and when i -list i get a list of all the names of the printers but when i try to scan from one it cannot find the scanner then when i -list again the name has changed to the default

    here is the terminal exert

    VictorIT:~ victor$ /Users/victor/Desktop/scanline -list
    2015-11-18 11:36:31:312 scanline[11805:50f] Available scanners:
    2015-11-18 11:36:31:633 scanline[11805:50f] * HP425dn-Loren
    VictorIT:~ victor$ /Users/victor/Desktop/scanline -scanner “HP425dn-Loren”
    2015-11-18 11:36:42:166 scanline[11818:50f] Starting scan…
    2015-11-18 11:36:42:166 scanline[11818:50f] Unable to find scanner named “HP425dn-Loren”
    VictorIT:~ victor$ /Users/victor/Desktop/scanline -list
    2015-11-18 11:36:44:716 scanline[11834:50f] Available scanners:
    2015-11-18 11:36:44:778 scanline[11834:50f] * HP LaserJet 400 MFP M425dn
    VictorIT:~ victor$

  43. Brian White

    I am getting this too.
    I have a “Samsung CLX-3170 Series”

    Brians-Air:tsu bkw$ ./scanline -list
    2016-01-06 02:35:56:245 scanline[17622:50f] Available scanners:
    2016-01-06 02:35:56:290 scanline[17622:50f] * Samsung CLX-3170 Series

    So I try:
    Brians-Air:tsu bkw$ ./scanline -scanner “Samsung CLX-3170 Series” -verbose -letter -mono -resolution 200 -dir . aljex_tsu_0000

    And I get:
    2016-01-06 02:35:08:453 scanline[17603:50f] setting functional unit
    2016-01-06 02:35:08:453 scanline[17603:50f] current functional unit: 0
    2016-01-06 02:35:08:453 scanline[17603:50f] doc feeder is 3
    2016-01-06 02:35:08:453 scanline[17603:50f] flatbed is 0
    2016-01-06 02:35:08:453 scanline[17603:50f] selected functionalUnitType: 0

    2016-01-06 02:35:08:454 scanline[17603:50f] error: Error Domain=com.apple.ImageCaptureCore Code=-9922 “(null)”

    2016-01-06 02:35:08:454 scanline[17603:50f] setting functional unit
    2016-01-06 02:35:08:454 scanline[17603:50f] current functional unit: 0
    2016-01-06 02:35:08:454 scanline[17603:50f] doc feeder is 3
    2016-01-06 02:35:08:454 scanline[17603:50f] flatbed is 0
    2016-01-06 02:35:08:454 scanline[17603:50f] selected functionalUnitType: 0

    2016-01-06 02:35:08:454 scanline[17603:50f] error: Error Domain=com.apple.ImageCaptureCore Code=-9922 “(null)”

    2016-01-06 02:35:08:454 scanline[17603:50f] setting functional unit
    2016-01-06 02:35:08:454 scanline[17603:50f] current functional unit: 0
    2016-01-06 02:35:08:454 scanline[17603:50f] doc feeder is 3
    2016-01-06 02:35:08:455 scanline[17603:50f] flatbed is 0
    2016-01-06 02:35:08:455 scanline[17603:50f] selected functionalUnitType: 0

    2016-01-06 02:35:08:455 scanline[17603:50f] error: Error Domain=com.apple.ImageCaptureCore Code=-9922 “(null)”

    ^C

    The scanner has a sheet feeder and there is paper in the sheet feeder, and the scanner works from System Preferences…>Scanner & Printers>Scan>Open Scanner and then select all the same options. Document feeder, 200dpi, letter, mono (called “Text”).

    Without even physically touching the scanner or the sheets that were in the feeder, just ^c scanline and try the system prefs util, and it scanned both sheets.

    I’m on El Capitan

  44. Brian White

    Never mind, it’s working fine. 🙂
    I think I just didn’t wait long enough for a one-time initial warm-up or something. But today the same scanner, connected the same way (lan, not usb), with the same scanline commandline options, and even the same papers in the feeder, is working fine.

  45. Michel

    This is exactly what we needed, but we do have one question, of course .. 🙂

    We will call an AppleScript from within FileMaker. It will start scanning multiple pages into one pdf. Therefor we will use -batch. In the Terminal this works as we press enter for each new page. But we can’t do that in our flow. Isn’t there a possibility to keep this going until the last page, without the need for interaction with the user?

  46. klep

    Hi. The -batch option is only relevant if you’re scanning on the flatbed. If you have a stack of documents in the feeder, omit -batch and it will scan them all into a single PDF. If you don’t have a document feeder, and aren’t able to press RETURN on the command line, I could potentially have a -delay option that waits for you to change each page. Seems a little risky though — I wouldn’t want your scanner to fire up when you’re looking right at it.

  47. dainius

    Almost two years later, I’m still a proud scanline user 🙂 Another feature suggestion: manual duplex scanning (from tray).

    Essentially, this would merge two batch scans: the first scan, odd numbers, would have the second scan, even numbers, placed appropriately between pages.

    This would save all of us doing tray-scans the trouble of exporting to individual images, doing a batch rename, and then recombining the scans.

    Thanks!

    -Dainius

  48. Gunnar

    Error Domain=com.apple.ImageCaptureCore Code=-9922 “The operation couldn’t be completed. (com.apple.ImageCaptureCore error -9922.)”
    This error is also solved by adding -flatbed (Scanner is an combined printer/scanner by brother). The message is in verbose mode looping until the scanner gets ready. Without -flatbed the Message ist looping infenite. (And therefore scanline uses up to 100% CPU until closing the terminal window.)

    -> May this could be improved by an resonable timeout and automate quit of scanline?

    Thanks for providing your app anyway!
    -Gunnar

  49. klep

    Thanks for the note. Not sure when I’ll have time, but will try to fix it. I believe there is a timeout in there, but verbose mode might be triggering the CPU usage before it gets a chance to stop.

  50. Gunnar

    Hi Klep,

    is it possible (for you) to add a functionality which adds the scan as a new last page to an existing .pdf file.
    Like:
    scanline -dir ~/Desktop -name “ExistingScan” -add

    Special would be:
    -addLast
    -addFirst

    This would be great, cause the -batch function is not working when the script is called by another script as there interaction of the user is needed.
    Best, Gunnar

Leave A Comment

Recommended Posts