diff --git a/javase/src/main/java/com/google/zxing/client/j2se/CommandLineRunner.java b/javase/src/main/java/com/google/zxing/client/j2se/CommandLineRunner.java index 75e555df4..d2fa2f021 100644 --- a/javase/src/main/java/com/google/zxing/client/j2se/CommandLineRunner.java +++ b/javase/src/main/java/com/google/zxing/client/j2se/CommandLineRunner.java @@ -20,6 +20,7 @@ import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import java.io.IOException; +import java.net.URI; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -27,7 +28,6 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; -import java.util.Locale; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -43,6 +43,7 @@ import java.util.regex.Pattern; * directories, summary statistics are also displayed. * * @author dswitkin@google.com (Daniel Switkin) + * @author Sean Owen */ public final class CommandLineRunner { @@ -58,7 +59,7 @@ public final class CommandLineRunner { } Config config = new Config(); - Queue inputs = new ConcurrentLinkedQueue<>(); + Queue inputs = new ConcurrentLinkedQueue<>(); for (String arg : args) { String[] argValue = arg.split("="); @@ -104,13 +105,25 @@ public final class CommandLineRunner { printUsage(); return; } - addArgumentToInputs(Paths.get(arg), config, inputs); + URI argURI = URI.create(arg); + if (argURI.getScheme() == null) { + argURI = new URI("file", argURI.getSchemeSpecificPart(), argURI.getFragment()); + } + addArgumentToInputs(argURI, config, inputs); break; } } + + int numInputs = inputs.size(); + if (numInputs == 0) { + System.err.println("No inputs specified"); + printUsage(); + return; + } + config.setHints(buildHints(config)); - int numThreads = Math.min(inputs.size(), Runtime.getRuntime().availableProcessors()); + int numThreads = Math.min(numInputs, Runtime.getRuntime().availableProcessors()); int successful = 0; if (numThreads > 1) { ExecutorService executor = Executors.newFixedThreadPool(numThreads); @@ -126,44 +139,38 @@ public final class CommandLineRunner { successful += new DecodeWorker(config, inputs).call(); } - int total = inputs.size(); - if (total > 1) { - System.out.println("\nDecoded " + successful + " files out of " + total + - " successfully (" + (successful * 100 / total) + "%)\n"); + if (numInputs > 1) { + System.out.println("\nDecoded " + successful + " files out of " + numInputs + + " successfully (" + (successful * 100 / numInputs) + "%)\n"); } } - // Build all the inputs up front into a single flat list, so the threads can atomically pull - // paths/URLs off the queue. - private static void addArgumentToInputs(Path inputFile, Config config, Queue inputs) throws IOException { - if (Files.isDirectory(inputFile)) { - try (DirectoryStream paths = Files.newDirectoryStream(inputFile)) { - for (Path singleFile : paths) { - String filename = singleFile.getFileName().toString().toLowerCase(Locale.ENGLISH); + /** + * Build all the inputs up front into a single flat list, so the threads can atomically pull + * paths/URLs off the queue. + */ + private static void addArgumentToInputs(URI input, Config config, Queue inputs) throws IOException { + // Special case: a local directory + if ("file".equals(input.getScheme()) && Files.isDirectory(Paths.get(input))) { + try (DirectoryStream childPaths = Files.newDirectoryStream(Paths.get(input))) { + for (Path childPath : childPaths) { + Path realChildPath = childPath.toRealPath(); // Skip hidden files and directories (e.g. svn stuff). - if (filename.startsWith(".")) { - continue; - } - // Recur on nested directories if requested, otherwise skip them. - if (Files.isDirectory(singleFile)) { - if (config.isRecursive()) { - addArgumentToInputs(singleFile, config, inputs); + if (!realChildPath.getFileName().toString().startsWith(".")) { + // Recur on nested directories if requested, otherwise skip them. + if (config.isRecursive() && Files.isDirectory(realChildPath)) { + addArgumentToInputs(realChildPath.toUri(), config, inputs); + } else { + inputs.add(realChildPath.toUri()); } - continue; } - // Skip text files and the results of dumping the black point. - if (filename.endsWith(".txt") || filename.contains(".mono.png")) { - continue; - } - inputs.add(singleFile); } } } else { - inputs.add(inputFile); + inputs.add(input); } } - // Manually turn on all formats, even those not yet considered production quality. private static Map buildHints(Config config) { Collection possibleFormats = new ArrayList<>(); String[] possibleFormatsNames = config.getPossibleFormats(); diff --git a/javase/src/main/java/com/google/zxing/client/j2se/DecodeWorker.java b/javase/src/main/java/com/google/zxing/client/j2se/DecodeWorker.java index 5275c8b83..5146da612 100644 --- a/javase/src/main/java/com/google/zxing/client/j2se/DecodeWorker.java +++ b/javase/src/main/java/com/google/zxing/client/j2se/DecodeWorker.java @@ -42,7 +42,6 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Map; import java.util.Queue; import java.util.concurrent.Callable; @@ -59,9 +58,9 @@ final class DecodeWorker implements Callable { private static final int WHITE = 0xFFFFFFFF; private final Config config; - private final Queue inputs; + private final Queue inputs; - DecodeWorker(Config config, Queue inputs) { + DecodeWorker(Config config, Queue inputs) { this.config = config; this.inputs = inputs; } @@ -69,139 +68,101 @@ final class DecodeWorker implements Callable { @Override public Integer call() throws IOException { int successful = 0; - Path input; - while ((input = inputs.poll()) != null) { - if (Files.exists(input)) { - if (config.isMulti()) { - Result[] results = decodeMulti(input.toUri(), config.getHints()); - if (results != null) { - successful++; - if (config.isDumpResults()) { - dumpResultMulti(input, results); - } - } - } else { - Result result = decode(input.toUri(), config.getHints()); - if (result != null) { - successful++; - if (config.isDumpResults()) { - dumpResult(input, result); - } - } - } - } else { - if (decode(input.toUri(), config.getHints()) != null) { - successful++; + for (URI input; (input = inputs.poll()) != null;) { + Result[] results = decode(input, config.getHints()); + if (results != null) { + successful++; + if (config.isDumpResults()) { + dumpResult(input, results); } } } return successful; } - private static void dumpResult(Path input, Result result) throws IOException { - String name = input.getFileName().toString(); - int pos = name.lastIndexOf('.'); - if (pos > 0) { - name = name.substring(0, pos) + ".txt"; + private static Path buildOutputPath(URI input, String suffix) throws IOException { + Path outDir; + String inputFileName; + if ("file".equals(input.getScheme())) { + Path inputPath = Paths.get(input); + outDir = inputPath.getParent(); + inputFileName = inputPath.getFileName().toString(); + } else { + outDir = Paths.get(".").toRealPath(); + String[] pathElements = input.getPath().split("/"); + inputFileName = pathElements[pathElements.length - 1]; } - Path dumpFile = input.getParent().resolve(name); - Files.write(dumpFile, Collections.singleton(result.getText()), StandardCharsets.UTF_8); + + // Replace/add extension + int pos = inputFileName.lastIndexOf('.'); + if (pos > 0) { + inputFileName = inputFileName.substring(0, pos) + suffix; + } else { + inputFileName += suffix; + } + + return outDir.resolve(inputFileName); } - private static void dumpResultMulti(Path input, Result[] results) throws IOException { - String name = input.getFileName().toString(); - int pos = name.lastIndexOf('.'); - if (pos > 0) { - name = name.substring(0, pos) + ".txt"; - } - Path dumpFile = input.getParent().resolve(name); + private static void dumpResult(URI input, Result... results) throws IOException { Collection resultTexts = new ArrayList<>(); for (Result result : results) { resultTexts.add(result.getText()); } - Files.write(dumpFile, resultTexts, StandardCharsets.UTF_8); + Files.write(buildOutputPath(input, ".txt"), resultTexts, StandardCharsets.UTF_8); } - private Result decode(URI uri, Map hints) throws IOException { + private Result[] decode(URI uri, Map hints) throws IOException { BufferedImage image = ImageReader.readImage(uri); - try { - LuminanceSource source; - if (config.getCrop() == null) { - source = new BufferedImageLuminanceSource(image); - } else { - int[] crop = config.getCrop(); - source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]); - } - BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); - if (config.isDumpBlackPoint()) { - dumpBlackPoint(uri, image, bitmap); - } - Result result = new MultiFormatReader().decode(bitmap, hints); - if (config.isBrief()) { - System.out.println(uri + ": Success"); - } else { - ParsedResult parsedResult = ResultParser.parseResult(result); - System.out.println(uri + " (format: " + result.getBarcodeFormat() + ", type: " + - parsedResult.getType() + "):\nRaw result:\n" + result.getText() + "\nParsed result:\n" + - parsedResult.getDisplayResult()); + LuminanceSource source; + if (config.getCrop() == null) { + source = new BufferedImageLuminanceSource(image); + } else { + int[] crop = config.getCrop(); + source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]); + } + + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + if (config.isDumpBlackPoint()) { + dumpBlackPoint(uri, image, bitmap); + } + + MultiFormatReader multiFormatReader = new MultiFormatReader(); + Result[] results; + try { + if (config.isMulti()) { + MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(multiFormatReader); + results = reader.decodeMultiple(bitmap, hints); + } else { + results = new Result[]{multiFormatReader.decode(bitmap, hints)}; + } + } catch (NotFoundException ignored) { + System.out.println(uri + ": No barcode found"); + return null; + } + + if (config.isBrief()) { + System.out.println(uri + ": Success"); + } else { + for (Result result : results) { + ParsedResult parsedResult = ResultParser.parseResult(result); + System.out.println(uri + + " (format: " + result.getBarcodeFormat() + + ", type: " + parsedResult.getType() + "):\n" + + "Raw result:\n" + + result.getText() + "\n" + + "Parsed result:\n" + + parsedResult.getDisplayResult()); System.out.println("Found " + result.getResultPoints().length + " result points."); for (int i = 0; i < result.getResultPoints().length; i++) { ResultPoint rp = result.getResultPoints()[i]; - if (rp != null) { - System.out.println(" Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')'); - } + System.out.println(" Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')'); } } - - return result; - } catch (NotFoundException ignored) { - System.out.println(uri + ": No barcode found"); - return null; } - } - private Result[] decodeMulti(URI uri, Map hints) throws IOException { - BufferedImage image = ImageReader.readImage(uri); - try { - LuminanceSource source; - if (config.getCrop() == null) { - source = new BufferedImageLuminanceSource(image); - } else { - int[] crop = config.getCrop(); - source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]); - } - BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); - if (config.isDumpBlackPoint()) { - dumpBlackPoint(uri, image, bitmap); - } - - MultiFormatReader multiFormatReader = new MultiFormatReader(); - MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(multiFormatReader); - Result[] results = reader.decodeMultiple(bitmap, hints); - - if (config.isBrief()) { - System.out.println(uri + ": Success"); - } else { - for (Result result : results) { - ParsedResult parsedResult = ResultParser.parseResult(result); - System.out.println(uri + " (format: " - + result.getBarcodeFormat() + ", type: " - + parsedResult.getType() + "):\nRaw result:\n" - + result.getText() + "\nParsed result:\n" - + parsedResult.getDisplayResult()); - System.out.println("Found " + result.getResultPoints().length + " result points."); - for (int i = 0; i < result.getResultPoints().length; i++) { - ResultPoint rp = result.getResultPoints()[i]; - System.out.println(" Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')'); - } - } - } - return results; - } catch (NotFoundException ignored) { - System.out.println(uri + ": No barcode found"); - return null; - } + return results; } /** @@ -209,11 +170,7 @@ final class DecodeWorker implements Callable { * to right: the original image, the row sampling monochrome version, and the 2D sampling * monochrome version. */ - private static void dumpBlackPoint(URI uri, BufferedImage image, BinaryBitmap bitmap) { - if (uri.getPath().contains(".mono.png")) { - return; - } - + private static void dumpBlackPoint(URI uri, BufferedImage image, BinaryBitmap bitmap) throws IOException { int width = bitmap.getWidth(); int height = bitmap.getHeight(); int stride = width * 3; @@ -263,30 +220,17 @@ final class DecodeWorker implements Callable { private static void writeResultImage(int stride, int height, int[] pixels, - URI uri, - String suffix) { + URI input, + String suffix) throws IOException { BufferedImage result = new BufferedImage(stride, height, BufferedImage.TYPE_INT_ARGB); result.setRGB(0, 0, stride, height, pixels, 0, stride); - - // Use the current working directory for URLs - String resultName = uri.getPath(); - if ("http".equals(uri.getScheme())) { - int pos = resultName.lastIndexOf('/'); - if (pos > 0) { - resultName = '.' + resultName.substring(pos); - } - } - int pos = resultName.lastIndexOf('.'); - if (pos > 0) { - resultName = resultName.substring(0, pos); - } - resultName += suffix; + Path imagePath = buildOutputPath(input, suffix); try { - if (!ImageIO.write(result, "png", Paths.get(resultName).toFile())) { - System.err.println("Could not encode an image to " + resultName); + if (!ImageIO.write(result, "png", imagePath.toFile())) { + System.err.println("Could not encode an image to " + imagePath); } } catch (IOException ignored) { - System.err.println("Could not write to " + resultName); + System.err.println("Could not write to " + imagePath); } } diff --git a/javase/src/main/java/com/google/zxing/client/j2se/ImageReader.java b/javase/src/main/java/com/google/zxing/client/j2se/ImageReader.java index 3ee4dbf75..45312baa1 100644 --- a/javase/src/main/java/com/google/zxing/client/j2se/ImageReader.java +++ b/javase/src/main/java/com/google/zxing/client/j2se/ImageReader.java @@ -53,8 +53,8 @@ public final class ImageReader { } public static BufferedImage readDataURIImage(URI uri) throws IOException { - String uriString = uri.toString(); - if (!uriString.startsWith("data:image/")) { + String uriString = uri.getSchemeSpecificPart(); + if (!uriString.startsWith("image/")) { throw new IOException("Unsupported data URI MIME type"); } int base64Start = uriString.indexOf(BASE64TOKEN);