eee1bd432b560883d06af215baf2d3874c067b68
[zxing.git] / core / src / com / google / zxing / pdf417 / PDF417Reader.java
1 /*\r
2  * Copyright 2009 ZXing authors\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 package com.google.zxing.pdf417;\r
18 \r
19 import com.google.zxing.BarcodeFormat;\r
20 import com.google.zxing.BinaryBitmap;\r
21 import com.google.zxing.DecodeHintType;\r
22 import com.google.zxing.Reader;\r
23 import com.google.zxing.ReaderException;\r
24 import com.google.zxing.Result;\r
25 import com.google.zxing.ResultPoint;\r
26 import com.google.zxing.common.BitMatrix;\r
27 import com.google.zxing.common.DecoderResult;\r
28 import com.google.zxing.common.DetectorResult;\r
29 import com.google.zxing.pdf417.decoder.Decoder;\r
30 import com.google.zxing.pdf417.detector.Detector;\r
31 \r
32 import java.util.Hashtable;\r
33 \r
34 /**\r
35  * This implementation can detect and decode PDF417 codes in an image.\r
36  *\r
37  * @author SITA Lab (kevin.osullivan@sita.aero)\r
38  */\r
39 public final class PDF417Reader implements Reader {\r
40 \r
41   private static final ResultPoint[] NO_POINTS = new ResultPoint[0];\r
42 \r
43   private final Decoder decoder = new Decoder();\r
44 \r
45   /**\r
46    * Locates and decodes a PDF417 code in an image.\r
47    *\r
48    * @return a String representing the content encoded by the PDF417 code\r
49    * @throws ReaderException if a PDF417 code cannot be found, or cannot be decoded\r
50    */\r
51   public Result decode(BinaryBitmap image) throws ReaderException {\r
52     return decode(image, null);\r
53   }\r
54 \r
55   public Result decode(BinaryBitmap image, Hashtable hints)\r
56       throws ReaderException {\r
57     DecoderResult decoderResult;\r
58     ResultPoint[] points;\r
59     if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {\r
60       BitMatrix bits = extractPureBits(image);\r
61       decoderResult = decoder.decode(bits);\r
62       points = NO_POINTS;\r
63     } else {\r
64       DetectorResult detectorResult = new Detector(image).detect();\r
65       decoderResult = decoder.decode(detectorResult.getBits());\r
66       points = detectorResult.getPoints();\r
67     }\r
68     return new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,\r
69         BarcodeFormat.PDF417);\r
70   }\r
71 \r
72   /**\r
73    * This method detects a barcode in a "pure" image -- that is, pure monochrome image\r
74    * which contains only an unrotated, unskewed, image of a barcode, with some white border\r
75    * around it. This is a specialized method that works exceptionally fast in this special\r
76    * case.\r
77    */\r
78   private static BitMatrix extractPureBits(BinaryBitmap image) throws ReaderException {\r
79     // Now need to determine module size in pixels\r
80 \r
81     int height = image.getHeight();\r
82     int width = image.getWidth();\r
83     int minDimension = Math.min(height, width);\r
84 \r
85     // First, skip white border by tracking diagonally from the top left down and to the right:\r
86     int borderWidth = 0;\r
87     while (borderWidth < minDimension && !image.isBlack(borderWidth, borderWidth)) {\r
88       borderWidth++;\r
89     }\r
90     if (borderWidth == minDimension) {\r
91       throw ReaderException.getInstance();\r
92     }\r
93 \r
94     // And then keep tracking across the top-left black module to determine module size\r
95     int moduleEnd = borderWidth;\r
96     while (moduleEnd < minDimension && image.isBlack(moduleEnd, moduleEnd)) {\r
97       moduleEnd++;\r
98     }\r
99     if (moduleEnd == minDimension) {\r
100       throw ReaderException.getInstance();\r
101     }\r
102 \r
103     int moduleSize = moduleEnd - borderWidth;\r
104 \r
105     // And now find where the rightmost black module on the first row ends\r
106     int rowEndOfSymbol = width - 1;\r
107     while (rowEndOfSymbol >= 0 && !image.isBlack(rowEndOfSymbol, borderWidth)) {\r
108       rowEndOfSymbol--;\r
109     }\r
110     if (rowEndOfSymbol < 0) {\r
111       throw ReaderException.getInstance();\r
112     }\r
113     rowEndOfSymbol++;\r
114 \r
115     // Make sure width of barcode is a multiple of module size\r
116     if ((rowEndOfSymbol - borderWidth) % moduleSize != 0) {\r
117       throw ReaderException.getInstance();\r
118     }\r
119     int dimension = (rowEndOfSymbol - borderWidth) / moduleSize;\r
120 \r
121     // Push in the "border" by half the module width so that we start\r
122     // sampling in the middle of the module. Just in case the image is a\r
123     // little off, this will help recover.\r
124     borderWidth += moduleSize >> 1;\r
125 \r
126     int sampleDimension = borderWidth + (dimension - 1) * moduleSize;\r
127     if (sampleDimension >= width || sampleDimension >= height) {\r
128       throw ReaderException.getInstance();\r
129     }\r
130 \r
131     // Now just read off the bits\r
132     BitMatrix bits = new BitMatrix(dimension);\r
133     for (int i = 0; i < dimension; i++) {\r
134       int iOffset = borderWidth + i * moduleSize;\r
135       for (int j = 0; j < dimension; j++) {\r
136         if (image.isBlack(borderWidth + j * moduleSize, iOffset)) {\r
137           bits.set(i, j);\r
138         }\r
139       }\r
140     }\r
141     return bits;\r
142   }\r
143 }\r
144 \r