{"id":13816090,"url":"https://github.com/sjamesr/jfreesane","last_synced_at":"2025-05-15T12:31:16.097Z","repository":{"id":12956582,"uuid":"15634866","full_name":"sjamesr/jfreesane","owner":"sjamesr","description":"Java API to talk to the SANE scanning daemon","archived":false,"fork":false,"pushed_at":"2023-12-14T21:43:00.000Z","size":988,"stargazers_count":60,"open_issues_count":8,"forks_count":24,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-11-19T12:49:48.407Z","etag":null,"topics":["java","sane","sane-daemon","scanner"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sjamesr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2014-01-04T15:56:49.000Z","updated_at":"2024-08-06T14:33:04.000Z","dependencies_parsed_at":"2024-04-11T21:49:08.187Z","dependency_job_id":"dc5ddbc4-883d-45ba-b5e3-c5c80cbc7be4","html_url":"https://github.com/sjamesr/jfreesane","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjamesr%2Fjfreesane","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjamesr%2Fjfreesane/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjamesr%2Fjfreesane/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sjamesr%2Fjfreesane/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sjamesr","download_url":"https://codeload.github.com/sjamesr/jfreesane/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254341040,"owners_count":22054971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","sane","sane-daemon","scanner"],"created_at":"2024-08-04T05:00:33.870Z","updated_at":"2025-05-15T12:31:15.213Z","avatar_url":"https://github.com/sjamesr.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"JFreeSane: A SANE client for Java\n=================================\n[![Build Status](https://travis-ci.org/sjamesr/jfreesane.svg?branch=master)](https://travis-ci.org/sjamesr/jfreesane)\n[![Coverage Status](https://coveralls.io/repos/github/sjamesr/jfreesane/badge.svg?branch=master)](https://coveralls.io/github/sjamesr/jfreesane?branch=master)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.googlecode.jfreesane/jfreesane/badge.svg?style=plastic)](https://maven-badges.herokuapp.com/maven-central/com.googlecode.jfreesane/jfreesane)\n[![Gitter](https://badges.gitter.im/sjamesr/jfreesane.svg)](https://gitter.im/sjamesr/jfreesane)\n\nJFreeSane is a pure-Java implementation of a SANE client. See [the SANE project\npage](http://www.sane-project.org/) for more information about SANE itself.\n\n- [Introduction](#introduction)\n- [Getting JFreeSane](#getting-jfreesane)\n- [Limitations](#limitations)\n- [Please contribute](#please-contribute)\n- [Usage](#usage)\n    - [Connecting to SANE](#connecting-to-sane)\n    - [Obtaining a device handle](#obtaining-a-device-handle)\n    - [Listing known devices](#listing-known-devices)\n    - [Opening the device](#opening-the-device)\n    - [Acquiring an image](#acquiring-an-image)\n    - [Device options](#device-options)\n        - [Setting options](#setting-options)\n        - [Reading options](#reading-options)\n        - [Option getters and setters](#option-getters-and-setters)\n    - [Reading from an automatic document feeder](#reading-from-an-automatic-document-feeder)\n    - [Authentication](#authentication)\n    - [Listening to events](#listening-to-events)\n\n# Introduction\n\nThe purpose of this library is to provide a client to the Scanner Access Now Easy (SANE) daemon.\nThis allows Java programmers to obtain images from SANE image sources over a network.\n\nFor example, you can do the following:\n\n```java\nSaneSession session = SaneSession.withRemoteSane(\n    InetAddress.getByName(\"my-sane-server.intranet\"));\nList\u003cSaneDevice\u003e devices = session.listDevices();\nSaneDevice device = ...;  // determine which device you want to use\ndevice.open();\n\nBufferedImage image = device.acquireImage();  // scan an image\n```\n\n# Getting JFreeSane\n\nThe easiest way to get this software is using [Maven](http://maven.apache.org/). Put the following in your `pom.xml`:\n\n```xml\n\u003cproject\u003e\n  ...\n  \u003cdependencies\u003e\n     ...\n     \u003cdependency\u003e\n       \u003cgroupId\u003ecom.googlecode.jfreesane\u003c/groupId\u003e\n       \u003cartifactId\u003ejfreesane\u003c/artifactId\u003e\n       \u003cversion\u003e1.0\u003c/version\u003e\n     \u003c/dependency\u003e\n   \u003c/dependencies\u003e\n\u003c/project\u003e\n```\n\nOtherwise, you can [download the jar file](https://github.com/sjamesr/jfreesane/releases/tag/jfreesane-1.0)\nand put it in your project's CLASSPATH.\n\nOnce JFreeSane is available on your classpath, please read on for a tutorial on how to use it.\n\nAlso consider joining the\n[jfreesane-discuss](http://groups.google.com/group/jfreesane-discuss) mailing list.\nIt is a low-volume list where people can discuss JFreeSane, release announcements are\nmade and issues are reported.\n\n# Limitations\n\n* JFreeSane must be used with a running SANE daemon. It will not run SANE for you.\nIt cannot talk to your scanners without a SANE daemon.\n\n# Please contribute\n\nIf you've been looking for a Java SANE client and you're familiar with SANE, please\nconsider contributing documentation or patches.\n\nIf you want to contribute back to JFreeSane, please consider [forking](https://help.github.com/articles/fork-a-repo)\nthe project. Once you have some code you'd like to contribute,\n[submit a pull request](https://help.github.com/articles/using-pull-requests). We really appreciate contributions\nand we'll get it checked in as fast as possible.\n\nYou can also get in touch via [jfreesane-discuss](http://groups.google.com/group/jfreesane-discuss) or \n[Gitter](https://gitter.im/sjamesr/jfreesane).\n\n# Usage\n\nHere are some ways you can use JFreeSane.\n\n## Connecting to SANE\n\nJFreeSane is strictly a client of the [SANE](http://www.sane-project.org/) network daemon.\nYou must have a SANE daemon running first before you can do anything with JFreeSane.\nDiscussing how to set up a SANE daemon is beyond the scope of this document, please refer to the [SANE home page](http://www.sane-project.org/) for guidance.\n\nOnce you have the daemon running on some host, for example `saneserver.mydomain.com`, you\ncan start a SANE session with the following:\n\n```java\nimport au.com.southsky.jfreesane.SaneSession;\n\nInetAddress address = InetAddress.getByName(\"saneserver.mydomain.com\");\nSaneSession session = SaneSession.withRemoteSane(address);\n```\n\nNow you need to obtain a device handle.\n\n## Obtaining a device handle\n\nIf you already know the name of the SANE device, you can open it directly by name. For\nexample, SANE servers usually have (but do not advertise) a device whose name is \"test\".\nThis is a \"pseudo\" device, in that it does not represent any physical hardware. It is\nuseful for exercising JFreeSane.\n\n```java\n// Open the test device by name\nSaneSession session = ...;\nSaneDevice device = session.getDevice(\"test\");\n```\n\n## Listing known devices\n\nIf you do not know the device name, you can list devices known to the SANE server using the following:\n\n```java\nSaneSession session = ...;\nList\u003cSaneDevice\u003e devices = session.listDevices();\n```\n\n## Opening the device\n\nMost likely, you now want to interact with the scanning device. Before you do anything, you need to \"`open`\" the device.\n\n```java\nSaneDevice device = ...;\ndevice.open();\n```\n\nThe device is now open. You can now get and set options and acquire images.\n\n## Acquiring an image\n\nNow you're ready to acquire an image from the scanner. Call `acquireImage`:\n\n```java\nSaneDevice device = ...;\ndevice.open();\nBufferedImage image = device.acquireImage();\n```\n\nIf the default options are not sufficient, see the \"Device options\" section below.\n\n## Device options\n\nEach device has a set of parameters that control aspects of that device's operation.\nThere are a handful of SANE \n[built-in options](http://www.sane-project.org/html/doc014.html):\n\n  * scan resolution\n  * preview mode\n  * scan area\n\nDifferent scanners have different capabilities (for example, duplex scan, color, various\ndocument sources). Each scanner type will expose a set of options in addition to the ones\ndescribed above.\n\nIn order to see what options are supported by a device:\n\n```java\nSaneDevice device = ...;\ndevice.open();\nList\u003cSaneOption\u003e options = device.listOptions();\n```\n\nEach option has\n\n  * a name, used as an identifier by JFreeSane (e.g. mode)\n  * a title, in English, suitable for display to the user (e.g. Scan Mode)\n  * description, a brief description of the purpose of the option\n\n### Setting options\n\nYou can set the value of an option, so long as \n`SaneOption.isActive()` and `SaneOption.isWriteable` are both true. For example, the\n\"source\" option's value can be set by the following:\n\n```java\nSaneOption option = device.getOption(\"source\");\noption.setStringValue(\"Auto Document Feeder\");\n```\n\nThe string \"Auto Document Feeder\" may not be correct for your particular device.\nIn fact, valid option values differ from device to device. In this case, you may\nneed to ask SANE what values are valid for a given option.\n\n```java\nSaneOption option = device.getOption(\"source\");\nList\u003cString\u003e validValues = option.getStringConstraints();\n```\n\n`validValues` now contains a list of strings, each of which is a valid value for this\noption.\n\nThere are some options whose valid values are in some range. For example, the \"pixma\"\nbackend has an option called \"tl-x\" (the x-ordinate of the top left of the scan area).\nIts valid values are in the range 0 to 216.069 millimeters.\nTo determine this programmatically:\n\n```java\nSaneOption option = device.getOption(\"tl-x\");\nassert option.isConstrained() \n    \u0026\u0026 option.getConstraintType() == OptionValueConstraintType.RANGE_CONSTRAINT;\nRangeConstraint constraint = option.getRangeConstraint();\n\n// this option is in mm, which is a fractional value. SANE uses fixed-point arithmetic,\n// so JFreeSane calls this a value of type \"FIXED\"\nassert option.getType() == OptionValueType.FIXED;\nassert option.getUnits() == SaneOption.OptionUnits.UNITS_MM;\n\ndouble min = constraint.getMinimumFixed();\ndouble max = constraint.getMaximumFixed();\n```\n\nTo set the value of the \"tl-x\" option, you can do the following:\n\n```java\nSaneOption option = device.getOption(\"tl-x\");\ndouble actualValue = option.setFixedValue(97.5);\n```\n\nJFreeSane will return the value actually set by the backend. For example, the valid\nvalues for \"tl-x\" are 0 to 216.069 (see the preceding section to see how this can be\ndetermined programmatically). If you set the value to something out of range, SANE will\nclamp the value to something in the valid range.\n\n```java\nSaneOption option = device.getOption(\"tl-x\");\ndouble actualValue = option.setFixedValue(-4);\n// actualValue will be set to 0, the minimum value for this option\n```\n\n### Reading options\n\nThe methods for reading option values depend on the option's type. Options are readable\nonly if `isActive()` and `isReadable()` are true for the option. See the table in the\nfollowing section for the list of option accessors.\n\n### Option getters and setters\n\nThe following table lists the `SaneOption` methods for reading and writing options of a given type:\n\n| *`SaneOption.getType()`* | *Getter* | *Setter* |\n|--------------------------|----------|----------|\n| `BOOLEAN` | `getBooleanValue` | `setBooleanValue` |\n| `INT` | `getIntegerValue` | `setIntegerValue` |\n| `FIXED` | `getFixedValue` | `setFixedValue` |\n| `STRING` | `getStringValue` | `setStringValue` |\n| `BUTTON` | None | `setButtonValue` |\n| `GROUP` | None | None |\n\nAdditionally, `INT`- and `FIXED`-type options may actually be an array of `INT` or\n`FIXED`. You can always use `SaneOption.getValueCount` to know for sure. \nIf the result is more than 1, you have an array.\n\n  * `getIntegerArrayValue` reads an INT array, `setIntegerValue(List\u003cInteger\u003e)` writes one\n  * `getFixedArrayValue` reads a FIXED array, `setFixedValue(List\u003cDouble\u003e)` writes one\n\n## Reading from an automatic document feeder\n\nYou may have a scanner with an Automatic Document Feeder (ADF). In this case, you may\nwant to acquire all the images until the ADF is out of paper. Use the following technique:\n\n```java\nSaneDevice device = ...;\n\n// this value is device-dependent. See the section on \"Setting Options\" to find out\n// how to enumerate the valid values\ndevice.getOption(\"source\").setStringValue(\"Automatic Document Feeder\");\n\nwhile (true) {\n  try {\n    BufferedImage image = device.acquireImage();\n    process(image);\n  } catch (SaneException e) {\n    if (e.getStatus() == SaneStatus.STATUS_NO_DOCS) {\n      // this is the out of paper condition that we expect\n      break;\n    } else {\n      // some other exception that was not expected\n      throw e;\n    }\n  }\n}\n```\n\n## Authentication\n\nThanks to generous contributions from Paul and Matthias, JFreeSane now supports connecting to authenticated resources.\n\nBy default, JFreeSane will use SANE-style authentication as documented in\n[the `scanimage(1)` man page](http://www.sane-project.org/man/scanimage.1.html). If you want to implement\nan alternative method of supplying usernames and passwords, see the javadoc for `SaneSession.setPasswordProvider`.\n\n## Listening to events\n\nAs of JFreeSane 0.93, you can now receive feedback when various scan events occur. For example, you can use\na `ScanListener` to provide scan progress information to the user. In the following example, a Swing progress bar\nis updated as the scan proceeds.\n\n```java\nSaneDevice device = ...;\nfinal JProgressBar progressBar = new JProgressBar();\nprogressBar.setStringPainted(true);\nprogressbar.setString(\"Starting scan...\");\n\nScanListener progressBarUpdater = new ScanListenerAdapter() {\n  @Override public void recordRead(\n      SaneDevice device,\n      final int totalBytesRead,\n      final int imageSize) {\n    final double fraction = 1.0 * totalBytesRead / imageSize;\n    SwingUtilities.invokeLater(new Runnable() {\n      @Override public void run() {\n        progressBar.setValue((int) (fraction * 100));\n        progressBar.setString(\n            String.format(\"Read %d of %d bytes (%.2f%%)\",\n              totalBytesRead, imageSize, fraction));\n      }\n    });\n  }\n\n  @Override public void scanningFinished(SaneDevice device) {\n    SwingUtilities.invokeLater(new Runnable() {\n      @Override public void run() {\n        progressBar.setValue(100);\n        progressBar.setString(\"Scanning completed!\");\n      }\n    });\n  }\n};\n\n// JFreeSane can generate recordRead events at a high rate. We don't really\n// need more than a few of these per second, otherwise we spend too much\n// time updating the UI and not enough time scanning. Use the\n// RateLimitingScanListeners class to get a wrapper around our existing\n// listener that delivers messages at an acceptable rate (10 per second max).\nScanListener rateLimitedListener = RateLimitedScanListeners.noMoreFrequentlyThan(\n    progressBarUpdater, 100, TimeUnit.MILLISECONDS);\n  \nBufferedImage image = device.acquireImage(rateLimitedListener);\n```\n\nIn some cases, JFreeSane cannot know the eventual size of the image. In this case, the `imageSize`\nparameter of `recordRead` will be set to `-1`. Examples of this situation are:\n\n* when using a handheld scanner\n* when using a scanning driver that supports page height detection (that is, scanning stops only\nwhen the scanner detects the end of the page)\n\nAlso, if you are using an old three-pass scanner (where one pass is made for each of three color\nbands), you will want to listen to the `frameAcquisitionStarted` message. JFreeSane will try to\nguess how many frames will eventually be sent and which frame it is currently acquiring.\n\nSee the javadoc for `ScanListener` for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsjamesr%2Fjfreesane","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsjamesr%2Fjfreesane","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsjamesr%2Fjfreesane/lists"}