二维码编解码程序
This commit is contained in:
		
						commit
						20dc2d6bec
					
				
							
								
								
									
										6
									
								
								.classpath
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.classpath
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <classpath> | ||||||
|  | 	<classpathentry kind="src" path="src"/> | ||||||
|  | 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> | ||||||
|  | 	<classpathentry kind="output" path="bin"/> | ||||||
|  | </classpath> | ||||||
							
								
								
									
										17
									
								
								.project
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.project
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <projectDescription> | ||||||
|  | 	<name>zxing</name> | ||||||
|  | 	<comment></comment> | ||||||
|  | 	<projects> | ||||||
|  | 	</projects> | ||||||
|  | 	<buildSpec> | ||||||
|  | 		<buildCommand> | ||||||
|  | 			<name>org.eclipse.jdt.core.javabuilder</name> | ||||||
|  | 			<arguments> | ||||||
|  | 			</arguments> | ||||||
|  | 		</buildCommand> | ||||||
|  | 	</buildSpec> | ||||||
|  | 	<natures> | ||||||
|  | 		<nature>org.eclipse.jdt.core.javanature</nature> | ||||||
|  | 	</natures> | ||||||
|  | </projectDescription> | ||||||
							
								
								
									
										53
									
								
								build.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								build.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | 
 | ||||||
|  | <!-- | ||||||
|  |  Copyright 2007 ZXing authors | ||||||
|  | 
 | ||||||
|  |  Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  you may not use this file except in compliance with the License. | ||||||
|  |  You may obtain a copy of the License at | ||||||
|  | 
 | ||||||
|  |       http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | 
 | ||||||
|  |  Unless required by applicable law or agreed to in writing, software | ||||||
|  |  distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  See the License for the specific language governing permissions and | ||||||
|  |  limitations under the License. | ||||||
|  | --> | ||||||
|  | 
 | ||||||
|  | <project name="javase" default="build"> | ||||||
|  | 
 | ||||||
|  |   <target name="init"> | ||||||
|  |     <tstamp/> | ||||||
|  |     <fail message="Please build 'core' first"> | ||||||
|  |       <condition> | ||||||
|  |         <not> | ||||||
|  |           <available file="../core/core.jar" type="file"/> | ||||||
|  |         </not> | ||||||
|  |       </condition> | ||||||
|  |     </fail> | ||||||
|  |   </target> | ||||||
|  | 
 | ||||||
|  |   <target name="build" depends="init"> | ||||||
|  |     <mkdir dir="build"/> | ||||||
|  |     <javac srcdir="src" | ||||||
|  |            destdir="build" | ||||||
|  |            source="1.5" | ||||||
|  |            target="1.5" | ||||||
|  |            optimize="true" | ||||||
|  |            debug="true" | ||||||
|  |            deprecation="true"> | ||||||
|  |       <classpath> | ||||||
|  |         <pathelement location="../core/core.jar"/> | ||||||
|  |       </classpath> | ||||||
|  |     </javac> | ||||||
|  |     <jar jarfile="javase.jar" basedir="build"/> | ||||||
|  |   </target> | ||||||
|  | 
 | ||||||
|  |   <target name="clean"> | ||||||
|  |     <delete dir="build"/> | ||||||
|  |     <delete file="javase.jar"/> | ||||||
|  |   </target> | ||||||
|  | 
 | ||||||
|  | </project> | ||||||
							
								
								
									
										118
									
								
								pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- | ||||||
|  |  Copyright (C) 2010 ZXing authors | ||||||
|  | 
 | ||||||
|  |  Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  you may not use this file except in compliance with the License. | ||||||
|  |  You may obtain a copy of the License at | ||||||
|  | 
 | ||||||
|  |       http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | 
 | ||||||
|  |  Unless required by applicable law or agreed to in writing, software | ||||||
|  |  distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  See the License for the specific language governing permissions and | ||||||
|  |  limitations under the License. | ||||||
|  |  --> | ||||||
|  | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||||||
|  |   <modelVersion>4.0.0</modelVersion> | ||||||
|  |   <groupId>com.google.zxing</groupId> | ||||||
|  |   <artifactId>javase</artifactId> | ||||||
|  |   <packaging>jar</packaging> | ||||||
|  |   <name>ZXing Java SE extensions</name> | ||||||
|  |   <version>1.6-SNAPSHOT</version> | ||||||
|  |   <description>Java SE-specific extensions to core ZXing library</description> | ||||||
|  |   <url>http://code.google.com/p/zxing</url> | ||||||
|  |   <inceptionYear>2007</inceptionYear> | ||||||
|  |   <issueManagement> | ||||||
|  |     <system>Google Code</system> | ||||||
|  |     <url>http://code.google.com/p/zxing/issues/list</url> | ||||||
|  |   </issueManagement> | ||||||
|  |   <licenses> | ||||||
|  |     <license> | ||||||
|  |       <name>The Apache Software License, Version 2.0</name> | ||||||
|  |       <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | ||||||
|  |       <distribution>repo</distribution> | ||||||
|  |     </license> | ||||||
|  |   </licenses> | ||||||
|  |   <developers> | ||||||
|  |     <developer> | ||||||
|  |       <name>Daniel Switkin</name> | ||||||
|  |       <email>dswitkin@gmail.com</email> | ||||||
|  |     </developer> | ||||||
|  |     <developer> | ||||||
|  |       <name>Sean Owen</name> | ||||||
|  |       <email>srowen@gmail.com</email> | ||||||
|  |     </developer> | ||||||
|  |   </developers> | ||||||
|  |   <scm> | ||||||
|  |     <connection>scm:svn:http://zxing.googlecode.com/svn/trunk</connection> | ||||||
|  |     <developerConnection>scm:svn:https://zxing.googlecode.com/svn/trunk</developerConnection> | ||||||
|  |     <url>http://zxing.googlecode.com/svn/trunk</url> | ||||||
|  |   </scm> | ||||||
|  |   <dependencies> | ||||||
|  |     <dependency> | ||||||
|  |       <groupId>com.google.zxing</groupId> | ||||||
|  |       <artifactId>core</artifactId> | ||||||
|  |       <version>1.6-SNAPSHOT</version> | ||||||
|  |     </dependency> | ||||||
|  |   </dependencies> | ||||||
|  |   <build> | ||||||
|  |     <sourceDirectory>src</sourceDirectory> | ||||||
|  |     <outputDirectory>build</outputDirectory> | ||||||
|  |     <plugins> | ||||||
|  |       <plugin> | ||||||
|  |         <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |         <artifactId>maven-compiler-plugin</artifactId> | ||||||
|  |         <configuration> | ||||||
|  |           <source>6</source> | ||||||
|  |           <target>6</target> | ||||||
|  |         </configuration> | ||||||
|  |       </plugin> | ||||||
|  |       <plugin> | ||||||
|  |         <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |         <artifactId>maven-release-plugin</artifactId> | ||||||
|  |       </plugin> | ||||||
|  |     </plugins> | ||||||
|  |   </build> | ||||||
|  |   <distributionManagement> | ||||||
|  |     <repository> | ||||||
|  |       <id>sonatype-nexus-staging</id> | ||||||
|  |       <name>Sonatype Nexus Staging</name> | ||||||
|  |       <url>http://oss.sonatype.org/content/groups/google-with-staging/</url> | ||||||
|  |     </repository> | ||||||
|  |     <snapshotRepository> | ||||||
|  |       <id>sonatype-nexus-snapshots</id> | ||||||
|  |       <name>Sonatype Nexus Snapshots</name> | ||||||
|  |       <url>http://oss.sonatype.org/content/repositories/google-snapshots/</url> | ||||||
|  |     </snapshotRepository> | ||||||
|  |   </distributionManagement> | ||||||
|  |   <profiles> | ||||||
|  |     <profile> | ||||||
|  |       <id>release-sign-artifacts</id> | ||||||
|  |       <activation> | ||||||
|  |         <property> | ||||||
|  |           <name>performRelease</name> | ||||||
|  |           <value>true</value> | ||||||
|  |         </property> | ||||||
|  |       </activation> | ||||||
|  |       <build> | ||||||
|  |         <plugins> | ||||||
|  |           <plugin> | ||||||
|  |             <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |             <artifactId>maven-gpg-plugin</artifactId> | ||||||
|  |             <executions> | ||||||
|  |               <execution> | ||||||
|  |                 <id>sign-artifacts</id> | ||||||
|  |                 <phase>verify</phase> | ||||||
|  |                 <goals> | ||||||
|  |                   <goal>sign</goal> | ||||||
|  |                 </goals> | ||||||
|  |               </execution> | ||||||
|  |             </executions> | ||||||
|  |           </plugin> | ||||||
|  |         </plugins> | ||||||
|  |       </build> | ||||||
|  |     </profile> | ||||||
|  |   </profiles> | ||||||
|  | </project> | ||||||
							
								
								
									
										103
									
								
								src/com/google/zxing/BarcodeFormat.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/com/google/zxing/BarcodeFormat.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Enumerates barcode formats known to this package. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class BarcodeFormat { | ||||||
|  | 
 | ||||||
|  |   // No, we can't use an enum here. J2ME doesn't support it. | ||||||
|  | 
 | ||||||
|  |   private static final Hashtable VALUES = new Hashtable(); | ||||||
|  | 
 | ||||||
|  |   /** QR Code 2D barcode format. */ | ||||||
|  |   public static final BarcodeFormat QR_CODE = new BarcodeFormat("QR_CODE"); | ||||||
|  | 
 | ||||||
|  |   /** Data Matrix 2D barcode format. */ | ||||||
|  |   public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat("DATA_MATRIX"); | ||||||
|  | 
 | ||||||
|  |   /** UPC-E 1D format. */ | ||||||
|  |   public static final BarcodeFormat UPC_E = new BarcodeFormat("UPC_E"); | ||||||
|  | 
 | ||||||
|  |   /** UPC-A 1D format. */ | ||||||
|  |   public static final BarcodeFormat UPC_A = new BarcodeFormat("UPC_A"); | ||||||
|  | 
 | ||||||
|  |   /** EAN-8 1D format. */ | ||||||
|  |   public static final BarcodeFormat EAN_8 = new BarcodeFormat("EAN_8"); | ||||||
|  | 
 | ||||||
|  |   /** EAN-13 1D format. */ | ||||||
|  |   public static final BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13"); | ||||||
|  | 
 | ||||||
|  |   /** UPC/EAN extension format. Not a stand-alone format. */ | ||||||
|  |   public static final BarcodeFormat UPC_EAN_EXTENSION = new BarcodeFormat("UPC_EAN_EXTENSION"); | ||||||
|  | 
 | ||||||
|  |   /** Code 128 1D format. */ | ||||||
|  |   public static final BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128"); | ||||||
|  | 
 | ||||||
|  |   /** Code 39 1D format. */ | ||||||
|  |   public static final BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39"); | ||||||
|  | 
 | ||||||
|  |   /** Code 93 1D format. */ | ||||||
|  |   public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93"); | ||||||
|  | 
 | ||||||
|  |   /** CODABAR 1D format. */ | ||||||
|  |   public static final BarcodeFormat CODABAR = new BarcodeFormat("CODABAR"); | ||||||
|  | 
 | ||||||
|  |   /** ITF (Interleaved Two of Five) 1D format. */ | ||||||
|  |   public static final BarcodeFormat ITF = new BarcodeFormat("ITF"); | ||||||
|  | 
 | ||||||
|  |   /** RSS 14 */ | ||||||
|  |   public static final BarcodeFormat RSS14 = new BarcodeFormat("RSS14"); | ||||||
|  | 
 | ||||||
|  |   /** PDF417 format. */ | ||||||
|  |   public static final BarcodeFormat PDF417 = new BarcodeFormat("PDF417"); | ||||||
|  | 
 | ||||||
|  |   /** RSS EXPANDED */ | ||||||
|  |   public static final BarcodeFormat RSS_EXPANDED = new BarcodeFormat("RSS_EXPANDED"); | ||||||
|  | 
 | ||||||
|  |   private final String name; | ||||||
|  | 
 | ||||||
|  |   private BarcodeFormat(String name) { | ||||||
|  |     this.name = name; | ||||||
|  |     VALUES.put(name, this); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getName() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static BarcodeFormat valueOf(String name) { | ||||||
|  |     if (name == null || name.length() == 0) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     BarcodeFormat format = (BarcodeFormat) VALUES.get(name); | ||||||
|  |     if (format == null) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     return format; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								src/com/google/zxing/Binarizer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/com/google/zxing/Binarizer.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.common.BitArray; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This class hierarchy provides a set of methods to convert luminance data to 1 bit data. | ||||||
|  |  * It allows the algorithm to vary polymorphically, for example allowing a very expensive | ||||||
|  |  * thresholding technique for servers and a fast one for mobile. It also permits the implementation | ||||||
|  |  * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public abstract class Binarizer { | ||||||
|  | 
 | ||||||
|  |   private final LuminanceSource source; | ||||||
|  | 
 | ||||||
|  |   protected Binarizer(LuminanceSource source) { | ||||||
|  |     if (source == null) { | ||||||
|  |       throw new IllegalArgumentException("Source must be non-null."); | ||||||
|  |     } | ||||||
|  |     this.source = source; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public LuminanceSource getLuminanceSource() { | ||||||
|  |     return source; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return | ||||||
|  |    * cached data. Callers should assume this method is expensive and call it as seldom as possible. | ||||||
|  |    * This method is intended for decoding 1D barcodes and may choose to apply sharpening. | ||||||
|  |    * For callers which only examine one row of pixels at a time, the same BitArray should be reused | ||||||
|  |    * and passed in with each call for performance. However it is legal to keep more than one row | ||||||
|  |    * at a time if needed. | ||||||
|  |    * | ||||||
|  |    * @param y The row to fetch, 0 <= y < bitmap height. | ||||||
|  |    * @param row An optional preallocated array. If null or too small, it will be ignored. | ||||||
|  |    *            If used, the Binarizer will call BitArray.clear(). Always use the returned object. | ||||||
|  |    * @return The array of bits for this row (true means black). | ||||||
|  |    */ | ||||||
|  |   public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive | ||||||
|  |    * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or | ||||||
|  |    * may not apply sharpening. Therefore, a row from this matrix may not be identical to one | ||||||
|  |    * fetched using getBlackRow(), so don't mix and match between them. | ||||||
|  |    * | ||||||
|  |    * @return The 2D array of bits for the image (true means black). | ||||||
|  |    */ | ||||||
|  |   public abstract BitMatrix getBlackMatrix() throws NotFoundException; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Creates a new object with the same type as this Binarizer implementation, but with pristine | ||||||
|  |    * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache | ||||||
|  |    * of 1 bit data. See Effective Java for why we can't use Java's clone() method. | ||||||
|  |    * | ||||||
|  |    * @param source The LuminanceSource this Binarizer will operate on. | ||||||
|  |    * @return A new concrete Binarizer implementation object. | ||||||
|  |    */ | ||||||
|  |   public abstract Binarizer createBinarizer(LuminanceSource source); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										128
									
								
								src/com/google/zxing/BinaryBitmap.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/com/google/zxing/BinaryBitmap.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.common.BitArray; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects | ||||||
|  |  * accept a BinaryBitmap and attempt to decode it. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class BinaryBitmap { | ||||||
|  | 
 | ||||||
|  |   private final Binarizer binarizer; | ||||||
|  |   private BitMatrix matrix; | ||||||
|  | 
 | ||||||
|  |   public BinaryBitmap(Binarizer binarizer) { | ||||||
|  |     if (binarizer == null) { | ||||||
|  |       throw new IllegalArgumentException("Binarizer must be non-null."); | ||||||
|  |     } | ||||||
|  |     this.binarizer = binarizer; | ||||||
|  |     matrix = null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return The width of the bitmap. | ||||||
|  |    */ | ||||||
|  |   public int getWidth() { | ||||||
|  |     return binarizer.getLuminanceSource().getWidth(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return The height of the bitmap. | ||||||
|  |    */ | ||||||
|  |   public int getHeight() { | ||||||
|  |     return binarizer.getLuminanceSource().getHeight(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return | ||||||
|  |    * cached data. Callers should assume this method is expensive and call it as seldom as possible. | ||||||
|  |    * This method is intended for decoding 1D barcodes and may choose to apply sharpening. | ||||||
|  |    * | ||||||
|  |    * @param y The row to fetch, 0 <= y < bitmap height. | ||||||
|  |    * @param row An optional preallocated array. If null or too small, it will be ignored. | ||||||
|  |    *            If used, the Binarizer will call BitArray.clear(). Always use the returned object. | ||||||
|  |    * @return The array of bits for this row (true means black). | ||||||
|  |    */ | ||||||
|  |   public BitArray getBlackRow(int y, BitArray row) throws NotFoundException { | ||||||
|  |     return binarizer.getBlackRow(y, row); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive | ||||||
|  |    * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or | ||||||
|  |    * may not apply sharpening. Therefore, a row from this matrix may not be identical to one | ||||||
|  |    * fetched using getBlackRow(), so don't mix and match between them. | ||||||
|  |    * | ||||||
|  |    * @return The 2D array of bits for the image (true means black). | ||||||
|  |    */ | ||||||
|  |   public BitMatrix getBlackMatrix() throws NotFoundException { | ||||||
|  |     // The matrix is created on demand the first time it is requested, then cached. There are two | ||||||
|  |     // reasons for this: | ||||||
|  |     // 1. This work will never be done if the caller only installs 1D Reader objects, or if a | ||||||
|  |     //    1D Reader finds a barcode before the 2D Readers run. | ||||||
|  |     // 2. This work will only be done once even if the caller installs multiple 2D Readers. | ||||||
|  |     if (matrix == null) { | ||||||
|  |       matrix = binarizer.getBlackMatrix(); | ||||||
|  |     } | ||||||
|  |     return matrix; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return Whether this bitmap can be cropped. | ||||||
|  |    */ | ||||||
|  |   public boolean isCropSupported() { | ||||||
|  |     return binarizer.getLuminanceSource().isCropSupported(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns a new object with cropped image data. Implementations may keep a reference to the | ||||||
|  |    * original data rather than a copy. Only callable if isCropSupported() is true. | ||||||
|  |    * | ||||||
|  |    * @param left The left coordinate, 0 <= left < getWidth(). | ||||||
|  |    * @param top The top coordinate, 0 <= top <= getHeight(). | ||||||
|  |    * @param width The width of the rectangle to crop. | ||||||
|  |    * @param height The height of the rectangle to crop. | ||||||
|  |    * @return A cropped version of this object. | ||||||
|  |    */ | ||||||
|  |   public BinaryBitmap crop(int left, int top, int width, int height) { | ||||||
|  |     LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height); | ||||||
|  |     return new BinaryBitmap(binarizer.createBinarizer(newSource)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return Whether this bitmap supports counter-clockwise rotation. | ||||||
|  |    */ | ||||||
|  |   public boolean isRotateSupported() { | ||||||
|  |     return binarizer.getLuminanceSource().isRotateSupported(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns a new object with rotated image data. Only callable if isRotateSupported() is true. | ||||||
|  |    * | ||||||
|  |    * @return A rotated version of this object. | ||||||
|  |    */ | ||||||
|  |   public BinaryBitmap rotateCounterClockwise() { | ||||||
|  |     LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise(); | ||||||
|  |     return new BinaryBitmap(binarizer.createBinarizer(newSource)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								src/com/google/zxing/ChecksumException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/com/google/zxing/ChecksumException.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Thrown when a barcode was successfully detected and decoded, but | ||||||
|  |  * was not returned because its checksum feature failed. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class ChecksumException extends ReaderException { | ||||||
|  | 
 | ||||||
|  |   private static final ChecksumException instance = new ChecksumException(); | ||||||
|  | 
 | ||||||
|  |   private ChecksumException() { | ||||||
|  |     // do nothing | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static ChecksumException getChecksumInstance() { | ||||||
|  |     return instance; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								src/com/google/zxing/DecodeHintType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/com/google/zxing/DecodeHintType.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Encapsulates a type of hint that a caller may pass to a barcode reader to help it | ||||||
|  |  * more quickly or accurately decode it. It is up to implementations to decide what, | ||||||
|  |  * if anything, to do with the information that is supplied. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  * @see Reader#decode(BinaryBitmap,java.util.Hashtable) | ||||||
|  |  */ | ||||||
|  | public final class DecodeHintType { | ||||||
|  | 
 | ||||||
|  |   // No, we can't use an enum here. J2ME doesn't support it. | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Unspecified, application-specific hint. Maps to an unspecified {@link Object}. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType OTHER = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to; | ||||||
|  |    * use {@link Boolean#TRUE}. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType PURE_BARCODE = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Image is known to be of one of a few possible formats. | ||||||
|  |    * Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Spend more time to try to find a barcode; optimize for accuracy, not speed. | ||||||
|  |    * Doesn't matter what it maps to; use {@link Boolean#TRUE}. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType TRY_HARDER = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Specifies what character encoding to use when decoding, where applicable (type String) | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType CHARACTER_SET = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Allowed lengths of encoded data -- reject anything else. Maps to an int[]. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType ALLOWED_LENGTHS = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Assume Code 39 codes employ a check digit. Maps to {@link Boolean}. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * The caller needs to be notified via callback when a possible {@link ResultPoint} | ||||||
|  |    * is found. Maps to a {@link ResultPointCallback}. | ||||||
|  |    */ | ||||||
|  |   public static final DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType(); | ||||||
|  | 
 | ||||||
|  |   private DecodeHintType() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/com/google/zxing/EncodeHintType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/com/google/zxing/EncodeHintType.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * These are a set of hints that you may pass to Writers to specify their behavior. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class EncodeHintType { | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Specifies what degree of error correction to use, for example in QR Codes (type Integer). | ||||||
|  |    */ | ||||||
|  |   public static final EncodeHintType ERROR_CORRECTION = new EncodeHintType(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Specifies what character encoding to use where applicable (type String) | ||||||
|  |    */ | ||||||
|  |   public static final EncodeHintType CHARACTER_SET = new EncodeHintType(); | ||||||
|  | 
 | ||||||
|  |   private EncodeHintType() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								src/com/google/zxing/FormatException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/com/google/zxing/FormatException.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Thrown when a barcode was successfully detected, but some aspect of | ||||||
|  |  * the content did not conform to the barcode's format rules. This could have | ||||||
|  |  * been due to a mis-detection. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class FormatException extends ReaderException { | ||||||
|  | 
 | ||||||
|  |   private static final FormatException instance = new FormatException(); | ||||||
|  | 
 | ||||||
|  |   private FormatException() { | ||||||
|  |     // do nothing | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static FormatException getFormatInstance() { | ||||||
|  |     return instance; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								src/com/google/zxing/LuminanceSource.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/com/google/zxing/LuminanceSource.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * The purpose of this class hierarchy is to abstract different bitmap implementations across | ||||||
|  |  * platforms into a standard interface for requesting greyscale luminance values. The interface | ||||||
|  |  * only provides immutable methods; therefore crop and rotation create copies. This is to ensure | ||||||
|  |  * that one Reader does not modify the original luminance source and leave it in an unknown state | ||||||
|  |  * for other Readers in the chain. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public abstract class LuminanceSource { | ||||||
|  | 
 | ||||||
|  |   private final int width; | ||||||
|  |   private final int height; | ||||||
|  | 
 | ||||||
|  |   protected LuminanceSource(int width, int height) { | ||||||
|  |     this.width = width; | ||||||
|  |     this.height = height; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Fetches one row of luminance data from the underlying platform's bitmap. Values range from | ||||||
|  |    * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have | ||||||
|  |    * to bitwise and with 0xff for each value. It is preferable for implementations of this method | ||||||
|  |    * to only fetch this row rather than the whole image, since no 2D Readers may be installed and | ||||||
|  |    * getMatrix() may never be called. | ||||||
|  |    * | ||||||
|  |    * @param y The row to fetch, 0 <= y < getHeight(). | ||||||
|  |    * @param row An optional preallocated array. If null or too small, it will be ignored. | ||||||
|  |    *            Always use the returned object, and ignore the .length of the array. | ||||||
|  |    * @return An array containing the luminance data. | ||||||
|  |    */ | ||||||
|  |   public abstract byte[] getRow(int y, byte[] row); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Fetches luminance data for the underlying bitmap. Values should be fetched using: | ||||||
|  |    * int luminance = array[y * width + x] & 0xff; | ||||||
|  |    * | ||||||
|  |    * @return A row-major 2D array of luminance values. Do not use result.length as it may be | ||||||
|  |    *         larger than width * height bytes on some platforms. Do not modify the contents | ||||||
|  |    *         of the result. | ||||||
|  |    */ | ||||||
|  |   public abstract byte[] getMatrix(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return The width of the bitmap. | ||||||
|  |    */ | ||||||
|  |   public final int getWidth() { | ||||||
|  |     return width; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return The height of the bitmap. | ||||||
|  |    */ | ||||||
|  |   public final int getHeight() { | ||||||
|  |     return height; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return Whether this subclass supports cropping. | ||||||
|  |    */ | ||||||
|  |   public boolean isCropSupported() { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns a new object with cropped image data. Implementations may keep a reference to the | ||||||
|  |    * original data rather than a copy. Only callable if isCropSupported() is true. | ||||||
|  |    * | ||||||
|  |    * @param left The left coordinate, 0 <= left < getWidth(). | ||||||
|  |    * @param top The top coordinate, 0 <= top <= getHeight(). | ||||||
|  |    * @param width The width of the rectangle to crop. | ||||||
|  |    * @param height The height of the rectangle to crop. | ||||||
|  |    * @return A cropped version of this object. | ||||||
|  |    */ | ||||||
|  |   public LuminanceSource crop(int left, int top, int width, int height) { | ||||||
|  |     throw new RuntimeException("This luminance source does not support cropping."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return Whether this subclass supports counter-clockwise rotation. | ||||||
|  |    */ | ||||||
|  |   public boolean isRotateSupported() { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns a new object with rotated image data. Only callable if isRotateSupported() is true. | ||||||
|  |    * | ||||||
|  |    * @return A rotated version of this object. | ||||||
|  |    */ | ||||||
|  |   public LuminanceSource rotateCounterClockwise() { | ||||||
|  |     throw new RuntimeException("This luminance source does not support rotation."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										166
									
								
								src/com/google/zxing/MultiFormatReader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/com/google/zxing/MultiFormatReader.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.datamatrix.DataMatrixReader; | ||||||
|  | import com.google.zxing.oned.MultiFormatOneDReader; | ||||||
|  | import com.google.zxing.pdf417.PDF417Reader; | ||||||
|  | import com.google.zxing.qrcode.QRCodeReader; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * MultiFormatReader is a convenience class and the main entry point into the library for most uses. | ||||||
|  |  * By default it attempts to decode all barcode formats that the library supports. Optionally, you | ||||||
|  |  * can provide a hints object to request different behavior, for example only decoding QR codes. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class MultiFormatReader implements Reader { | ||||||
|  | 
 | ||||||
|  |   private Hashtable hints; | ||||||
|  |   private Vector readers; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it | ||||||
|  |    * passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly. | ||||||
|  |    * Use setHints() followed by decodeWithState() for continuous scan applications. | ||||||
|  |    * | ||||||
|  |    * @param image The pixel data to decode | ||||||
|  |    * @return The contents of the image | ||||||
|  |    * @throws NotFoundException Any errors which occurred | ||||||
|  |    */ | ||||||
|  |   public Result decode(BinaryBitmap image) throws NotFoundException { | ||||||
|  |     setHints(null); | ||||||
|  |     return decodeInternal(image); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Decode an image using the hints provided. Does not honor existing state. | ||||||
|  |    * | ||||||
|  |    * @param image The pixel data to decode | ||||||
|  |    * @param hints The hints to use, clearing the previous state. | ||||||
|  |    * @return The contents of the image | ||||||
|  |    * @throws NotFoundException Any errors which occurred | ||||||
|  |    */ | ||||||
|  |   public Result decode(BinaryBitmap image, Hashtable hints) throws NotFoundException { | ||||||
|  |     setHints(hints); | ||||||
|  |     return decodeInternal(image); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Decode an image using the state set up by calling setHints() previously. Continuous scan | ||||||
|  |    * clients will get a <b>large</b> speed increase by using this instead of decode(). | ||||||
|  |    * | ||||||
|  |    * @param image The pixel data to decode | ||||||
|  |    * @return The contents of the image | ||||||
|  |    * @throws NotFoundException Any errors which occurred | ||||||
|  |    */ | ||||||
|  |   public Result decodeWithState(BinaryBitmap image) throws NotFoundException { | ||||||
|  |     // Make sure to set up the default state so we don't crash | ||||||
|  |     if (readers == null) { | ||||||
|  |       setHints(null); | ||||||
|  |     } | ||||||
|  |     return decodeInternal(image); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls | ||||||
|  |    * to decodeWithState(image) can reuse the same set of readers without reallocating memory. This | ||||||
|  |    * is important for performance in continuous scan clients. | ||||||
|  |    * | ||||||
|  |    * @param hints The set of hints to use for subsequent calls to decode(image) | ||||||
|  |    */ | ||||||
|  |   public void setHints(Hashtable hints) { | ||||||
|  |     this.hints = hints; | ||||||
|  | 
 | ||||||
|  |     boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); | ||||||
|  |     Vector formats = hints == null ? null : (Vector) hints.get(DecodeHintType.POSSIBLE_FORMATS); | ||||||
|  |     readers = new Vector(); | ||||||
|  |     if (formats != null) { | ||||||
|  |       boolean addOneDReader = | ||||||
|  |           formats.contains(BarcodeFormat.UPC_A) || | ||||||
|  |               formats.contains(BarcodeFormat.UPC_E) || | ||||||
|  |               formats.contains(BarcodeFormat.EAN_13) || | ||||||
|  |               formats.contains(BarcodeFormat.EAN_8) || | ||||||
|  |               //formats.contains(BarcodeFormat.CODABAR) || | ||||||
|  |               formats.contains(BarcodeFormat.CODE_39) || | ||||||
|  |               formats.contains(BarcodeFormat.CODE_93) || | ||||||
|  |               formats.contains(BarcodeFormat.CODE_128) || | ||||||
|  |               formats.contains(BarcodeFormat.ITF) || | ||||||
|  |               formats.contains(BarcodeFormat.RSS14) || | ||||||
|  |               formats.contains(BarcodeFormat.RSS_EXPANDED); | ||||||
|  |       // Put 1D readers upfront in "normal" mode | ||||||
|  |       if (addOneDReader && !tryHarder) { | ||||||
|  |         readers.addElement(new MultiFormatOneDReader(hints)); | ||||||
|  |       } | ||||||
|  |       if (formats.contains(BarcodeFormat.QR_CODE)) { | ||||||
|  |         readers.addElement(new QRCodeReader()); | ||||||
|  |       } | ||||||
|  |       if (formats.contains(BarcodeFormat.DATA_MATRIX)) { | ||||||
|  |         readers.addElement(new DataMatrixReader()); | ||||||
|  |       } | ||||||
|  |       if (formats.contains(BarcodeFormat.PDF417)) { | ||||||
|  |          readers.addElement(new PDF417Reader()); | ||||||
|  |        } | ||||||
|  |       // At end in "try harder" mode | ||||||
|  |       if (addOneDReader && tryHarder) { | ||||||
|  |         readers.addElement(new MultiFormatOneDReader(hints)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (readers.isEmpty()) { | ||||||
|  |       if (!tryHarder) { | ||||||
|  |         readers.addElement(new MultiFormatOneDReader(hints)); | ||||||
|  |       } | ||||||
|  |       readers.addElement(new QRCodeReader()); | ||||||
|  | 
 | ||||||
|  |       readers.addElement(new DataMatrixReader()); | ||||||
|  | 
 | ||||||
|  |       // TODO: Enable once PDF417 has passed QA | ||||||
|  |       //readers.addElement(new PDF417Reader()); | ||||||
|  | 
 | ||||||
|  |       if (tryHarder) { | ||||||
|  |         readers.addElement(new MultiFormatOneDReader(hints)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void reset() { | ||||||
|  |     int size = readers.size(); | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       Reader reader = (Reader) readers.elementAt(i); | ||||||
|  |       reader.reset(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private Result decodeInternal(BinaryBitmap image) throws NotFoundException { | ||||||
|  |     int size = readers.size(); | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       Reader reader = (Reader) readers.elementAt(i); | ||||||
|  |       try { | ||||||
|  |         return reader.decode(image, hints); | ||||||
|  |       } catch (ReaderException re) { | ||||||
|  |         // continue | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     throw NotFoundException.getNotFoundInstance(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								src/com/google/zxing/MultiFormatWriter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/com/google/zxing/MultiFormatWriter.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.oned.Code128Writer; | ||||||
|  | import com.google.zxing.oned.Code39Writer; | ||||||
|  | import com.google.zxing.oned.EAN13Writer; | ||||||
|  | import com.google.zxing.oned.EAN8Writer; | ||||||
|  | import com.google.zxing.oned.ITFWriter; | ||||||
|  | import com.google.zxing.qrcode.QRCodeWriter; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This is a factory class which finds the appropriate Writer subclass for the BarcodeFormat | ||||||
|  |  * requested and encodes the barcode with the supplied contents. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class MultiFormatWriter implements Writer { | ||||||
|  | 
 | ||||||
|  |   public BitMatrix encode(String contents, BarcodeFormat format, int width, | ||||||
|  |       int height) throws WriterException { | ||||||
|  | 
 | ||||||
|  |     return encode(contents, format, width, height, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, | ||||||
|  |       Hashtable hints) throws WriterException { | ||||||
|  | 
 | ||||||
|  |     Writer writer; | ||||||
|  |     if (format == BarcodeFormat.EAN_8) { | ||||||
|  |       writer = new EAN8Writer(); | ||||||
|  |     } else if (format == BarcodeFormat.EAN_13) { | ||||||
|  |       writer = new EAN13Writer(); | ||||||
|  |     } else if (format == BarcodeFormat.QR_CODE) { | ||||||
|  |       writer = new QRCodeWriter(); | ||||||
|  |     } else if (format == BarcodeFormat.CODE_39) { | ||||||
|  |       writer = new Code39Writer(); | ||||||
|  |     } else if (format == BarcodeFormat.CODE_128) { | ||||||
|  |       writer = new Code128Writer(); | ||||||
|  |     } else if (format == BarcodeFormat.ITF) { | ||||||
|  |       writer = new ITFWriter(); | ||||||
|  |     } else { | ||||||
|  |       throw new IllegalArgumentException("No encoder available for format " + format); | ||||||
|  |     } | ||||||
|  |     return writer.encode(contents, format, width, height, hints); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								src/com/google/zxing/NotFoundException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/com/google/zxing/NotFoundException.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Thrown when a barcode was not found in the image. It might have been | ||||||
|  |  * partially detected but could not be confirmed. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class NotFoundException extends ReaderException { | ||||||
|  | 
 | ||||||
|  |   private static final NotFoundException instance = new NotFoundException(); | ||||||
|  | 
 | ||||||
|  |   private NotFoundException() { | ||||||
|  |     // do nothing | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static NotFoundException getNotFoundInstance() { | ||||||
|  |     return instance; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								src/com/google/zxing/Reader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/com/google/zxing/Reader.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implementations of this interface can decode an image of a barcode in some format into | ||||||
|  |  * the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can | ||||||
|  |  * decode a QR code. The decoder may optionally receive hints from the caller which may help | ||||||
|  |  * it decode more quickly or accurately. | ||||||
|  |  * | ||||||
|  |  * See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode | ||||||
|  |  * format is present within the image as well, and then decodes it accordingly. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public interface Reader { | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Locates and decodes a barcode in some format within an image. | ||||||
|  |    * | ||||||
|  |    * @param image image of barcode to decode | ||||||
|  |    * @return String which the barcode encodes | ||||||
|  |    * @throws NotFoundException if the barcode cannot be located or decoded for any reason | ||||||
|  |    */ | ||||||
|  |   Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Locates and decodes a barcode in some format within an image. This method also accepts | ||||||
|  |    * hints, each possibly associated to some data, which may help the implementation decode. | ||||||
|  |    * | ||||||
|  |    * @param image image of barcode to decode | ||||||
|  |    * @param hints passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType} | ||||||
|  |    * to arbitrary data. The | ||||||
|  |    * meaning of the data depends upon the hint type. The implementation may or may not do | ||||||
|  |    * anything with these hints. | ||||||
|  |    * @return String which the barcode encodes | ||||||
|  |    * @throws NotFoundException if the barcode cannot be located or decoded for any reason | ||||||
|  |    */ | ||||||
|  |   Result decode(BinaryBitmap image, Hashtable hints) throws NotFoundException, ChecksumException, FormatException; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Resets any internal state the implementation has after a decode, to prepare it | ||||||
|  |    * for reuse. | ||||||
|  |    */ | ||||||
|  |   void reset(); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										98
									
								
								src/com/google/zxing/ReaderException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/com/google/zxing/ReaderException.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * The general exception class throw when something goes wrong during decoding of a barcode. | ||||||
|  |  * This includes, but is not limited to, failing checksums / error correction algorithms, being | ||||||
|  |  * unable to locate finder timing patterns, and so on. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public abstract class ReaderException extends Exception { | ||||||
|  | 
 | ||||||
|  |   // TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before | ||||||
|  |   // rejecting it. This involves a lot of overhead and memory allocation, and affects both performance | ||||||
|  |   // and latency on continuous scan clients. In the future, we should change all the decoders not to | ||||||
|  |   // throw exceptions for routine events, like not finding a barcode on a given row. Instead, we | ||||||
|  |   // should return error codes back to the callers, and simply delete this class. In the mean time, I | ||||||
|  |   // have altered this class to be as lightweight as possible, by ignoring the exception string, and | ||||||
|  |   // by disabling the generation of stack traces, which is especially time consuming. These are just | ||||||
|  |   // temporary measures, pending the big cleanup. | ||||||
|  | 
 | ||||||
|  |   //private static final ReaderException instance = new ReaderException(); | ||||||
|  | 
 | ||||||
|  |   // EXCEPTION TRACKING SUPPORT | ||||||
|  |   // Identifies who is throwing exceptions and how often. To use: | ||||||
|  |   // | ||||||
|  |   // 1. Uncomment these lines and the code below which uses them. | ||||||
|  |   // 2. Uncomment the two corresponding lines in j2se/CommandLineRunner.decode() | ||||||
|  |   // 3. Change core to build as Java 1.5 temporarily | ||||||
|  | //  private static int exceptionCount = 0; | ||||||
|  | //  private static Map<String,Integer> throwers = new HashMap<String,Integer>(32); | ||||||
|  | 
 | ||||||
|  |   ReaderException() { | ||||||
|  |     // do nothing | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   //public static ReaderException getInstance() { | ||||||
|  | //    Exception e = new Exception(); | ||||||
|  | //    // Take the stack frame before this one. | ||||||
|  | //    StackTraceElement stack = e.getStackTrace()[1]; | ||||||
|  | //    String key = stack.getClassName() + "." + stack.getMethodName() + "(), line " + | ||||||
|  | //        stack.getLineNumber(); | ||||||
|  | //    if (throwers.containsKey(key)) { | ||||||
|  | //      Integer value = throwers.get(key); | ||||||
|  | //      value++; | ||||||
|  | //      throwers.put(key, value); | ||||||
|  | //    } else { | ||||||
|  | //      throwers.put(key, 1); | ||||||
|  | //    } | ||||||
|  | //    exceptionCount++; | ||||||
|  | 
 | ||||||
|  |     //return instance; | ||||||
|  |   //} | ||||||
|  | 
 | ||||||
|  | //  public static int getExceptionCountAndReset() { | ||||||
|  | //    int temp = exceptionCount; | ||||||
|  | //    exceptionCount = 0; | ||||||
|  | //    return temp; | ||||||
|  | //  } | ||||||
|  | // | ||||||
|  | //  public static String getThrowersAndReset() { | ||||||
|  | //    StringBuilder builder = new StringBuilder(1024); | ||||||
|  | //    Object[] keys = throwers.keySet().toArray(); | ||||||
|  | //    for (int x = 0; x < keys.length; x++) { | ||||||
|  | //      String key = (String) keys[x]; | ||||||
|  | //      Integer value = throwers.get(key); | ||||||
|  | //      builder.append(key); | ||||||
|  | //      builder.append(": "); | ||||||
|  | //      builder.append(value); | ||||||
|  | //      builder.append("\n"); | ||||||
|  | //    } | ||||||
|  | //    throwers.clear(); | ||||||
|  | //    return builder.toString(); | ||||||
|  | //  } | ||||||
|  | 
 | ||||||
|  |   // Prevent stack traces from being taken | ||||||
|  |   // srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden? | ||||||
|  |   // This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow. | ||||||
|  |   public final Throwable fillInStackTrace() { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										143
									
								
								src/com/google/zxing/Result.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/com/google/zxing/Result.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import java.util.Enumeration; | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Encapsulates the result of decoding a barcode within an image.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class Result { | ||||||
|  | 
 | ||||||
|  |   private final String text; | ||||||
|  |   private final byte[] rawBytes; | ||||||
|  |   private ResultPoint[] resultPoints; | ||||||
|  |   private final BarcodeFormat format; | ||||||
|  |   private Hashtable resultMetadata; | ||||||
|  |   private final long timestamp; | ||||||
|  | 
 | ||||||
|  |   public Result(String text, | ||||||
|  |                 byte[] rawBytes, | ||||||
|  |                 ResultPoint[] resultPoints, | ||||||
|  |                 BarcodeFormat format) { | ||||||
|  |     this(text, rawBytes, resultPoints, format, System.currentTimeMillis()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Result(String text, | ||||||
|  |                 byte[] rawBytes, | ||||||
|  |                 ResultPoint[] resultPoints, | ||||||
|  |                 BarcodeFormat format, | ||||||
|  |                 long timestamp) { | ||||||
|  |     if (text == null && rawBytes == null) { | ||||||
|  |       throw new IllegalArgumentException("Text and bytes are null"); | ||||||
|  |     } | ||||||
|  |     this.text = text; | ||||||
|  |     this.rawBytes = rawBytes; | ||||||
|  |     this.resultPoints = resultPoints; | ||||||
|  |     this.format = format; | ||||||
|  |     this.resultMetadata = null; | ||||||
|  |     this.timestamp = timestamp; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return raw text encoded by the barcode, if applicable, otherwise <code>null</code> | ||||||
|  |    */ | ||||||
|  |   public String getText() { | ||||||
|  |     return text; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return raw bytes encoded by the barcode, if applicable, otherwise <code>null</code> | ||||||
|  |    */ | ||||||
|  |   public byte[] getRawBytes() { | ||||||
|  |     return rawBytes; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return points related to the barcode in the image. These are typically points | ||||||
|  |    *         identifying finder patterns or the corners of the barcode. The exact meaning is | ||||||
|  |    *         specific to the type of barcode that was decoded. | ||||||
|  |    */ | ||||||
|  |   public ResultPoint[] getResultPoints() { | ||||||
|  |     return resultPoints; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return {@link BarcodeFormat} representing the format of the barcode that was decoded | ||||||
|  |    */ | ||||||
|  |   public BarcodeFormat getBarcodeFormat() { | ||||||
|  |     return format; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return {@link Hashtable} mapping {@link ResultMetadataType} keys to values. May be | ||||||
|  |    *   <code>null</code>. This contains optional metadata about what was detected about the barcode, | ||||||
|  |    *   like orientation. | ||||||
|  |    */ | ||||||
|  |   public Hashtable getResultMetadata() { | ||||||
|  |     return resultMetadata; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void putMetadata(ResultMetadataType type, Object value) { | ||||||
|  |     if (resultMetadata == null) { | ||||||
|  |       resultMetadata = new Hashtable(3); | ||||||
|  |     } | ||||||
|  |     resultMetadata.put(type, value); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void putAllMetadata(Hashtable metadata) { | ||||||
|  |     if (metadata != null) { | ||||||
|  |       if (resultMetadata == null) { | ||||||
|  |         resultMetadata = metadata; | ||||||
|  |       } else { | ||||||
|  |         Enumeration e = metadata.keys(); | ||||||
|  |         while (e.hasMoreElements()) { | ||||||
|  |           ResultMetadataType key = (ResultMetadataType) e.nextElement(); | ||||||
|  |           Object value = metadata.get(key); | ||||||
|  |           resultMetadata.put(key, value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void addResultPoints(ResultPoint[] newPoints) { | ||||||
|  |     if (resultPoints == null) { | ||||||
|  |       resultPoints = newPoints; | ||||||
|  |     } else if (newPoints != null && newPoints.length > 0) { | ||||||
|  |       ResultPoint[] allPoints = new ResultPoint[resultPoints.length + newPoints.length]; | ||||||
|  |       System.arraycopy(resultPoints, 0, allPoints, 0, resultPoints.length); | ||||||
|  |       System.arraycopy(newPoints, 0, allPoints, resultPoints.length, newPoints.length); | ||||||
|  |       resultPoints = allPoints; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public long getTimestamp() { | ||||||
|  |     return timestamp; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     if (text == null) { | ||||||
|  |       return "[" + rawBytes.length + " bytes]"; | ||||||
|  |     } else { | ||||||
|  |       return text; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										109
									
								
								src/com/google/zxing/ResultMetadataType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/com/google/zxing/ResultMetadataType.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,109 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Represents some type of metadata about the result of the decoding that the decoder | ||||||
|  |  * wishes to communicate back to the caller. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class ResultMetadataType { | ||||||
|  | 
 | ||||||
|  |   // No, we can't use an enum here. J2ME doesn't support it. | ||||||
|  | 
 | ||||||
|  |   private static final Hashtable VALUES = new Hashtable(); | ||||||
|  | 
 | ||||||
|  |   // No, we can't use an enum here. J2ME doesn't support it. | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType OTHER = new ResultMetadataType("OTHER"); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Denotes the likely approximate orientation of the barcode in the image. This value | ||||||
|  |    * is given as degrees rotated clockwise from the normal, upright orientation. | ||||||
|  |    * For example a 1D barcode which was found by reading top-to-bottom would be | ||||||
|  |    * said to have orientation "90". This key maps to an {@link Integer} whose | ||||||
|  |    * value is in the range [0,360). | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType ORIENTATION = new ResultMetadataType("ORIENTATION"); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode' | ||||||
|  |    * which is sometimes used to encode binary data. While {@link Result} makes available | ||||||
|  |    * the complete raw bytes in the barcode for these formats, it does not offer the bytes | ||||||
|  |    * from the byte segments alone.</p> | ||||||
|  |    * | ||||||
|  |    * <p>This maps to a {@link java.util.Vector} of byte arrays corresponding to the | ||||||
|  |    * raw bytes in the byte segments in the barcode, in order.</p> | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType("BYTE_SEGMENTS"); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Error correction level used, if applicable. The value type depends on the | ||||||
|  |    * format, but is typically a String. | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType("ERROR_CORRECTION_LEVEL"); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * For some periodicals, indicates the issue number as an {@link Integer}. | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType ISSUE_NUMBER = new ResultMetadataType("ISSUE_NUMBER"); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * For some products, indicates the suggested retail price in the barcode as a | ||||||
|  |    * formatted {@link String}. | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType SUGGESTED_PRICE = new ResultMetadataType("SUGGESTED_PRICE"); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * For some products, the possible country of manufacture as a {@link String} denoting the | ||||||
|  |    * ISO country code. Some map to multiple possible countries, like "US/CA". | ||||||
|  |    */ | ||||||
|  |   public static final ResultMetadataType POSSIBLE_COUNTRY = new ResultMetadataType("POSSIBLE_COUNTRY"); | ||||||
|  | 
 | ||||||
|  |   private final String name; | ||||||
|  | 
 | ||||||
|  |   private ResultMetadataType(String name) { | ||||||
|  |     this.name = name; | ||||||
|  |     VALUES.put(name, this); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getName() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static ResultMetadataType valueOf(String name) { | ||||||
|  |     if (name == null || name.length() == 0) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     ResultMetadataType format = (ResultMetadataType) VALUES.get(name); | ||||||
|  |     if (format == null) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     return format; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								src/com/google/zxing/ResultPoint.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/com/google/zxing/ResultPoint.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Encapsulates a point of interest in an image containing a barcode. Typically, this | ||||||
|  |  * would be the location of a finder pattern or the corner of the barcode, for example.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public class ResultPoint { | ||||||
|  | 
 | ||||||
|  |   private final float x; | ||||||
|  |   private final float y; | ||||||
|  | 
 | ||||||
|  |   public ResultPoint(float x, float y) { | ||||||
|  |     this.x = x; | ||||||
|  |     this.y = y; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public final float getX() { | ||||||
|  |     return x; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public final float getY() { | ||||||
|  |     return y; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public boolean equals(Object other) { | ||||||
|  |     if (other instanceof ResultPoint) { | ||||||
|  |       ResultPoint otherPoint = (ResultPoint) other; | ||||||
|  |       return x == otherPoint.x && y == otherPoint.y; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int hashCode() { | ||||||
|  |     return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     StringBuffer result = new StringBuffer(25); | ||||||
|  |     result.append('('); | ||||||
|  |     result.append(x); | ||||||
|  |     result.append(','); | ||||||
|  |     result.append(y); | ||||||
|  |     result.append(')'); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and | ||||||
|  |    * BC < AC and the angle between BC and BA is less than 180 degrees. | ||||||
|  |    */ | ||||||
|  |   public static void orderBestPatterns(ResultPoint[] patterns) { | ||||||
|  | 
 | ||||||
|  |     // Find distances between pattern centers | ||||||
|  |     float zeroOneDistance = distance(patterns[0], patterns[1]); | ||||||
|  |     float oneTwoDistance = distance(patterns[1], patterns[2]); | ||||||
|  |     float zeroTwoDistance = distance(patterns[0], patterns[2]); | ||||||
|  | 
 | ||||||
|  |     ResultPoint pointA, pointB, pointC; | ||||||
|  |     // Assume one closest to other two is B; A and C will just be guesses at first | ||||||
|  |     if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) { | ||||||
|  |       pointB = patterns[0]; | ||||||
|  |       pointA = patterns[1]; | ||||||
|  |       pointC = patterns[2]; | ||||||
|  |     } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) { | ||||||
|  |       pointB = patterns[1]; | ||||||
|  |       pointA = patterns[0]; | ||||||
|  |       pointC = patterns[2]; | ||||||
|  |     } else { | ||||||
|  |       pointB = patterns[2]; | ||||||
|  |       pointA = patterns[0]; | ||||||
|  |       pointC = patterns[1]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Use cross product to figure out whether A and C are correct or flipped. | ||||||
|  |     // This asks whether BC x BA has a positive z component, which is the arrangement | ||||||
|  |     // we want for A, B, C. If it's negative, then we've got it flipped around and | ||||||
|  |     // should swap A and C. | ||||||
|  |     if (crossProductZ(pointA, pointB, pointC) < 0.0f) { | ||||||
|  |       ResultPoint temp = pointA; | ||||||
|  |       pointA = pointC; | ||||||
|  |       pointC = temp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     patterns[0] = pointA; | ||||||
|  |     patterns[1] = pointB; | ||||||
|  |     patterns[2] = pointC; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return distance between two points | ||||||
|  |    */ | ||||||
|  |   public static float distance(ResultPoint pattern1, ResultPoint pattern2) { | ||||||
|  |     float xDiff = pattern1.getX() - pattern2.getX(); | ||||||
|  |     float yDiff = pattern1.getY() - pattern2.getY(); | ||||||
|  |     return (float) Math.sqrt((double) (xDiff * xDiff + yDiff * yDiff)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns the z component of the cross product between vectors BC and BA. | ||||||
|  |    */ | ||||||
|  |   private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) { | ||||||
|  |     float bX = pointB.x; | ||||||
|  |     float bY = pointB.y; | ||||||
|  |     return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								src/com/google/zxing/ResultPointCallback.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/com/google/zxing/ResultPointCallback.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Callback which is invoked when a possible result point (significant | ||||||
|  |  * point in the barcode image such as a corner) is found. | ||||||
|  |  * | ||||||
|  |  * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK | ||||||
|  |  */ | ||||||
|  | public interface ResultPointCallback { | ||||||
|  | 
 | ||||||
|  |   void foundPossibleResultPoint(ResultPoint point); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										219
									
								
								src/com/google/zxing/StringsResourceTranslator.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								src/com/google/zxing/StringsResourceTranslator.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,219 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import java.io.BufferedReader; | ||||||
|  | import java.io.Closeable; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.FileFilter; | ||||||
|  | import java.io.FileInputStream; | ||||||
|  | import java.io.FileOutputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStreamReader; | ||||||
|  | import java.io.OutputStreamWriter; | ||||||
|  | import java.io.Reader; | ||||||
|  | import java.io.Writer; | ||||||
|  | import java.net.URL; | ||||||
|  | import java.net.URLEncoder; | ||||||
|  | import java.nio.charset.Charset; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.SortedMap; | ||||||
|  | import java.util.TreeMap; | ||||||
|  | import java.util.regex.Matcher; | ||||||
|  | import java.util.regex.Pattern; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>A utility which auto-translates English strings in Android string resources using | ||||||
|  |  * Google Translate.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Pass the Android client res/ directory as first argument, and optionally message keys | ||||||
|  |  * who should be forced to retranslate. | ||||||
|  |  * Usage: <code>StringsResourceTranslator android/res/ [key_1 ...]</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class StringsResourceTranslator { | ||||||
|  | 
 | ||||||
|  |   private static final Charset UTF8 = Charset.forName("UTF-8"); | ||||||
|  |   private static final Pattern ENTRY_PATTERN = Pattern.compile("<string name=\"([^\"]+)\">([^<]+)</string>"); | ||||||
|  |   private static final Pattern STRINGS_FILE_NAME_PATTERN = Pattern.compile("values-(.+)"); | ||||||
|  |   private static final Pattern TRANSLATE_RESPONSE_PATTERN = Pattern.compile( | ||||||
|  |       "\\{\"translatedText\":\"([^\"]+)\"\\}"); | ||||||
|  | 
 | ||||||
|  |   private static final String APACHE_2_LICENSE = | ||||||
|  |       "<!--\n" + | ||||||
|  |       " Copyright (C) 2010 ZXing authors\n" + | ||||||
|  |       '\n' + | ||||||
|  |       " Licensed under the Apache License, Version 2.0 (the \"License\");\n" + | ||||||
|  |       " you may not use this file except in compliance with the License.\n" + | ||||||
|  |       " You may obtain a copy of the License at\n" + | ||||||
|  |       '\n' + | ||||||
|  |       "      http://www.apache.org/licenses/LICENSE-2.0\n" + | ||||||
|  |       '\n' + | ||||||
|  |       " Unless required by applicable law or agreed to in writing, software\n" + | ||||||
|  |       " distributed under the License is distributed on an \"AS IS\" BASIS,\n" + | ||||||
|  |       " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + | ||||||
|  |       " See the License for the specific language governing permissions and\n" + | ||||||
|  |       " limitations under the License.\n" + | ||||||
|  |       " -->\n"; | ||||||
|  | 
 | ||||||
|  |   private static final Map<String,String> LANGUAGE_CODE_MASSAGINGS = new HashMap<String,String>(4); | ||||||
|  |   static { | ||||||
|  |     LANGUAGE_CODE_MASSAGINGS.put("ja-rJP", "ja"); | ||||||
|  |     LANGUAGE_CODE_MASSAGINGS.put("zh-rCN", "zh-cn"); | ||||||
|  |     LANGUAGE_CODE_MASSAGINGS.put("zh-rTW", "zh-tw"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private StringsResourceTranslator() {} | ||||||
|  | 
 | ||||||
|  |   public static void main(String[] args) throws IOException { | ||||||
|  | 
 | ||||||
|  |     File resDir = new File(args[0]); | ||||||
|  |     File valueDir = new File(resDir, "values"); | ||||||
|  |     File stringsFile = new File(valueDir, "strings.xml"); | ||||||
|  |     Collection<String> forceRetranslation = Arrays.asList(args).subList(1, args.length); | ||||||
|  | 
 | ||||||
|  |     File[] translatedValuesDirs = resDir.listFiles(new FileFilter() { | ||||||
|  |       public boolean accept(File file) { | ||||||
|  |         return file.isDirectory() && file.getName().startsWith("values-"); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     for (File translatedValuesDir : translatedValuesDirs) { | ||||||
|  |       File translatedStringsFile = new File(translatedValuesDir, "strings.xml"); | ||||||
|  |       translate(stringsFile, translatedStringsFile, forceRetranslation); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void translate(File englishFile, File translatedFile, Collection<String> forceRetranslation) | ||||||
|  |       throws IOException { | ||||||
|  | 
 | ||||||
|  |     SortedMap<String,String> english = readLines(englishFile); | ||||||
|  |     SortedMap<String,String> translated = readLines(translatedFile); | ||||||
|  |     String parentName = translatedFile.getParentFile().getName(); | ||||||
|  | 
 | ||||||
|  |     Matcher stringsFileNameMatcher = STRINGS_FILE_NAME_PATTERN.matcher(parentName); | ||||||
|  |     stringsFileNameMatcher.find(); | ||||||
|  |     String language = stringsFileNameMatcher.group(1); | ||||||
|  |     String massagedLanguage = LANGUAGE_CODE_MASSAGINGS.get(language); | ||||||
|  |     if (massagedLanguage != null) { | ||||||
|  |       language = massagedLanguage; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     System.out.println("Translating " + language); | ||||||
|  | 
 | ||||||
|  |     File resultTempFile = File.createTempFile(parentName, ".xml"); | ||||||
|  | 
 | ||||||
|  |     boolean anyChange = false; | ||||||
|  |     Writer out = null; | ||||||
|  |     try { | ||||||
|  |       out = new OutputStreamWriter(new FileOutputStream(resultTempFile), UTF8); | ||||||
|  |       out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||||||
|  |       out.write(APACHE_2_LICENSE); | ||||||
|  |       out.write("<resources>\n"); | ||||||
|  | 
 | ||||||
|  |       for (Map.Entry<String,String> englishEntry : english.entrySet()) { | ||||||
|  |         String key = englishEntry.getKey(); | ||||||
|  |         out.write("  <string name=\""); | ||||||
|  |         out.write(key); | ||||||
|  |         out.write("\">"); | ||||||
|  | 
 | ||||||
|  |         String translatedString = translated.get(key); | ||||||
|  |         if (translatedString == null || forceRetranslation.contains(key)) { | ||||||
|  |           anyChange = true; | ||||||
|  |           translatedString = translateString(englishEntry.getValue(), language); | ||||||
|  |         } | ||||||
|  |         out.write(translatedString); | ||||||
|  | 
 | ||||||
|  |         out.write("</string>\n"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       out.write("</resources>\n"); | ||||||
|  |       out.flush(); | ||||||
|  | 
 | ||||||
|  |     } finally { | ||||||
|  |       quietClose(out); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (anyChange) { | ||||||
|  |       System.out.println("  Writing translations"); | ||||||
|  |       translatedFile.delete(); | ||||||
|  |       resultTempFile.renameTo(translatedFile); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String translateString(String english, String language) throws IOException { | ||||||
|  |     System.out.println("  Need translation for " + english); | ||||||
|  |     URL translateURL = new URL( | ||||||
|  |         "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=" + | ||||||
|  |         URLEncoder.encode(english, "UTF-8") + | ||||||
|  |         "&langpair=en%7C" + language); | ||||||
|  |     StringBuilder translateResult = new StringBuilder(); | ||||||
|  |     Reader in = null; | ||||||
|  |     try { | ||||||
|  |       in = new InputStreamReader(translateURL.openStream(), UTF8); | ||||||
|  |       char[] buffer = new char[1024]; | ||||||
|  |       int charsRead; | ||||||
|  |       while ((charsRead = in.read(buffer)) > 0) { | ||||||
|  |         translateResult.append(buffer, 0, charsRead); | ||||||
|  |       } | ||||||
|  |     } finally { | ||||||
|  |       quietClose(in); | ||||||
|  |     } | ||||||
|  |     Matcher m = TRANSLATE_RESPONSE_PATTERN.matcher(translateResult); | ||||||
|  |     if (!m.find()) { | ||||||
|  |       throw new IOException("No translate result"); | ||||||
|  |     } | ||||||
|  |     String translation = m.group(1); | ||||||
|  |     System.out.println("  Got translation " + translation); | ||||||
|  |     return translation; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static SortedMap<String,String> readLines(File file) throws IOException { | ||||||
|  |     SortedMap<String,String> entries = new TreeMap<String,String>(); | ||||||
|  |     BufferedReader reader = null; | ||||||
|  |     try { | ||||||
|  |       reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), UTF8)); | ||||||
|  |       String line; | ||||||
|  |       while ((line = reader.readLine()) != null) { | ||||||
|  |         Matcher m = ENTRY_PATTERN.matcher(line); | ||||||
|  |         if (m.find()) { | ||||||
|  |           String key = m.group(1); | ||||||
|  |           String value = m.group(2); | ||||||
|  |           entries.put(key, value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return entries; | ||||||
|  |     } finally { | ||||||
|  |       quietClose(reader); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void quietClose(Closeable closeable) { | ||||||
|  |     if (closeable != null) { | ||||||
|  |       try { | ||||||
|  |         closeable.close(); | ||||||
|  |       } catch (IOException ioe) { | ||||||
|  |         // continue | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/com/google/zxing/Writer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/com/google/zxing/Writer.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * The base class for all objects which encode/generate a barcode image. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public interface Writer { | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Encode a barcode using the default settings. | ||||||
|  |    * | ||||||
|  |    * @param contents The contents to encode in the barcode | ||||||
|  |    * @param format The barcode format to generate | ||||||
|  |    * @param width The preferred width in pixels | ||||||
|  |    * @param height The preferred height in pixels | ||||||
|  |    * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) | ||||||
|  |    */ | ||||||
|  |   BitMatrix encode(String contents, BarcodeFormat format, int width, int height) | ||||||
|  |       throws WriterException; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * | ||||||
|  |    * @param contents The contents to encode in the barcode | ||||||
|  |    * @param format The barcode format to generate | ||||||
|  |    * @param width The preferred width in pixels | ||||||
|  |    * @param height The preferred height in pixels | ||||||
|  |    * @param hints Additional parameters to supply to the encoder | ||||||
|  |    * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) | ||||||
|  |    */ | ||||||
|  |   BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) | ||||||
|  |       throws WriterException; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								src/com/google/zxing/WriterException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/com/google/zxing/WriterException.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A base class which covers the range of exceptions which may occur when encoding a barcode using | ||||||
|  |  * the Writer framework. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class WriterException extends Exception { | ||||||
|  | 
 | ||||||
|  |   public WriterException() { | ||||||
|  |     super(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public WriterException(String message) { | ||||||
|  |     super(message); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,148 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.j2se; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.LuminanceSource; | ||||||
|  | 
 | ||||||
|  | import java.awt.Graphics2D; | ||||||
|  | import java.awt.image.BufferedImage; | ||||||
|  | import java.awt.geom.AffineTransform; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This LuminanceSource implementation is meant for J2SE clients and our blackbox unit tests. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class BufferedImageLuminanceSource extends LuminanceSource { | ||||||
|  | 
 | ||||||
|  |   private final BufferedImage image; | ||||||
|  |   private final int left; | ||||||
|  |   private final int top; | ||||||
|  |   private int[] rgbData; | ||||||
|  | 
 | ||||||
|  |   public BufferedImageLuminanceSource(BufferedImage image) { | ||||||
|  |     this(image, 0, 0, image.getWidth(), image.getHeight()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, | ||||||
|  |       int height) { | ||||||
|  |     super(width, height); | ||||||
|  | 
 | ||||||
|  |     int sourceWidth = image.getWidth(); | ||||||
|  |     int sourceHeight = image.getHeight(); | ||||||
|  |     if (left + width > sourceWidth || top + height > sourceHeight) { | ||||||
|  |       throw new IllegalArgumentException("Crop rectangle does not fit within image data."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.image = image; | ||||||
|  |     this.left = left; | ||||||
|  |     this.top = top; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // These methods use an integer calculation for luminance derived from: | ||||||
|  |   // <code>Y = 0.299R + 0.587G + 0.114B</code> | ||||||
|  |   @Override | ||||||
|  |   public byte[] getRow(int y, byte[] row) { | ||||||
|  |     if (y < 0 || y >= getHeight()) { | ||||||
|  |       throw new IllegalArgumentException("Requested row is outside the image: " + y); | ||||||
|  |     } | ||||||
|  |     int width = getWidth(); | ||||||
|  |     if (row == null || row.length < width) { | ||||||
|  |       row = new byte[width]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (rgbData == null || rgbData.length < width) { | ||||||
|  |       rgbData = new int[width]; | ||||||
|  |     } | ||||||
|  |     image.getRGB(left, top + y, width, 1, rgbData, 0, width); | ||||||
|  |     for (int x = 0; x < width; x++) { | ||||||
|  |       int pixel = rgbData[x]; | ||||||
|  |       int luminance = (306 * ((pixel >> 16) & 0xFF) + | ||||||
|  |           601 * ((pixel >> 8) & 0xFF) + | ||||||
|  |           117 * (pixel & 0xFF) + | ||||||
|  |           (0x200)) >> 10; // 0x200 = 1<<9, half an lsb of the result to force rounding | ||||||
|  |       row[x] = (byte) luminance; | ||||||
|  |     } | ||||||
|  |     return row; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Override | ||||||
|  |   public byte[] getMatrix() { | ||||||
|  |     int width = getWidth(); | ||||||
|  |     int height = getHeight(); | ||||||
|  |     int area = width * height; | ||||||
|  |     byte[] matrix = new byte[area]; | ||||||
|  | 
 | ||||||
|  |     int[] rgb = new int[area]; | ||||||
|  |     image.getRGB(left, top, width, height, rgb, 0, width); | ||||||
|  |     for (int y = 0; y < height; y++) { | ||||||
|  |       int offset = y * width; | ||||||
|  |       for (int x = 0; x < width; x++) { | ||||||
|  |         int pixel = rgb[offset + x]; | ||||||
|  |         int luminance = (306 * ((pixel >> 16) & 0xFF) + | ||||||
|  |             601 * ((pixel >> 8) & 0xFF) + | ||||||
|  |             117 * (pixel & 0xFF) + | ||||||
|  |             (0x200)) >> 10; // 0x200 = 1<<9, half an lsb of the result to force rounding | ||||||
|  |         matrix[offset + x] = (byte) luminance; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return matrix; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Override | ||||||
|  |   public boolean isCropSupported() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Override | ||||||
|  |   public LuminanceSource crop(int left, int top, int width, int height) { | ||||||
|  |     return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Can't run AffineTransforms on images of unknown format. | ||||||
|  |   @Override | ||||||
|  |   public boolean isRotateSupported() { | ||||||
|  |     return image.getType() != BufferedImage.TYPE_CUSTOM; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Override | ||||||
|  |   public LuminanceSource rotateCounterClockwise() { | ||||||
|  |     if (!isRotateSupported()) { | ||||||
|  |       throw new IllegalStateException("Rotate not supported"); | ||||||
|  |     } | ||||||
|  |     int sourceWidth = image.getWidth(); | ||||||
|  |     int sourceHeight = image.getHeight(); | ||||||
|  | 
 | ||||||
|  |     // Rotate 90 degrees counterclockwise. | ||||||
|  |     AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth); | ||||||
|  | 
 | ||||||
|  |     // Note width/height are flipped since we are rotating 90 degrees. | ||||||
|  |     BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, image.getType()); | ||||||
|  | 
 | ||||||
|  |     // Draw the original image into rotated, via transformation | ||||||
|  |     Graphics2D g = rotatedImage.createGraphics(); | ||||||
|  |     g.drawImage(image, transform, null); | ||||||
|  |     g.dispose(); | ||||||
|  | 
 | ||||||
|  |     // Maintain the cropped region, but rotate it too. | ||||||
|  |     int width = getWidth(); | ||||||
|  |     return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), | ||||||
|  |         getHeight(), width); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										358
									
								
								src/com/google/zxing/client/j2se/CommandLineRunner.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								src/com/google/zxing/client/j2se/CommandLineRunner.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.j2se; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BarcodeFormat; | ||||||
|  | import com.google.zxing.BinaryBitmap; | ||||||
|  | import com.google.zxing.DecodeHintType; | ||||||
|  | import com.google.zxing.LuminanceSource; | ||||||
|  | import com.google.zxing.MultiFormatReader; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.client.result.ParsedResult; | ||||||
|  | import com.google.zxing.client.result.ResultParser; | ||||||
|  | import com.google.zxing.common.BitArray; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.common.HybridBinarizer; | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | 
 | ||||||
|  | import java.awt.image.BufferedImage; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.FileNotFoundException; | ||||||
|  | import java.io.FileOutputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.io.OutputStreamWriter; | ||||||
|  | import java.io.Writer; | ||||||
|  | import java.net.URI; | ||||||
|  | import java.net.URISyntaxException; | ||||||
|  | import java.nio.charset.Charset; | ||||||
|  | import java.util.Hashtable; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | import javax.imageio.ImageIO; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>This simple command line utility decodes files, directories of files, or URIs which are passed | ||||||
|  |  * as arguments. By default it uses the normal decoding algorithms, but you can pass --try_harder to | ||||||
|  |  * request that hint. The raw text of each barcode is printed, and when running against directories, | ||||||
|  |  * summary statistics are also displayed.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class CommandLineRunner { | ||||||
|  | 
 | ||||||
|  |   private CommandLineRunner() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static void main(String[] args) throws Exception { | ||||||
|  |     if (args == null || args.length == 0) { | ||||||
|  |       printUsage(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     boolean tryHarder = false; | ||||||
|  |     boolean pureBarcode = false; | ||||||
|  |     boolean productsOnly = false; | ||||||
|  |     boolean dumpResults = false; | ||||||
|  |     boolean dumpBlackPoint = false; | ||||||
|  |     int[] crop = null; | ||||||
|  |     for (String arg : args) { | ||||||
|  |       if ("--try_harder".equals(arg)) { | ||||||
|  |         tryHarder = true; | ||||||
|  |       } else if ("--pure_barcode".equals(arg)) { | ||||||
|  |         pureBarcode = true; | ||||||
|  |       } else if ("--products_only".equals(arg)) { | ||||||
|  |         productsOnly = true; | ||||||
|  |       } else if ("--dump_results".equals(arg)) { | ||||||
|  |         dumpResults = true; | ||||||
|  |       } else if ("--dump_black_point".equals(arg)) { | ||||||
|  |         dumpBlackPoint = true; | ||||||
|  |       } else if (arg.startsWith("--crop")) { | ||||||
|  |         crop = new int[4]; | ||||||
|  |         String[] tokens = arg.substring(7).split(","); | ||||||
|  |         for (int i = 0; i < crop.length; i++) { | ||||||
|  |           crop[i] = Integer.parseInt(tokens[i]); | ||||||
|  |         } | ||||||
|  |       } else if (arg.startsWith("-")) { | ||||||
|  |         System.err.println("Unknown command line option " + arg); | ||||||
|  |         printUsage(); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Hashtable<DecodeHintType, Object> hints = buildHints(tryHarder, pureBarcode, productsOnly); | ||||||
|  |     for (String arg : args) { | ||||||
|  |       if (!arg.startsWith("--")) { | ||||||
|  |         decodeOneArgument(arg, hints, dumpResults, dumpBlackPoint, crop); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Manually turn on all formats, even those not yet considered production quality. | ||||||
|  |   private static Hashtable<DecodeHintType, Object> buildHints(boolean tryHarder, | ||||||
|  |                                                               boolean pureBarcode, | ||||||
|  |                                                               boolean productsOnly) { | ||||||
|  |     Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3); | ||||||
|  |     Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(8); | ||||||
|  |     vector.addElement(BarcodeFormat.UPC_A); | ||||||
|  |     vector.addElement(BarcodeFormat.UPC_E); | ||||||
|  |     vector.addElement(BarcodeFormat.EAN_13); | ||||||
|  |     vector.addElement(BarcodeFormat.EAN_8); | ||||||
|  |     vector.addElement(BarcodeFormat.RSS14); | ||||||
|  |     if (!productsOnly) { | ||||||
|  |       vector.addElement(BarcodeFormat.CODE_39); | ||||||
|  |       vector.addElement(BarcodeFormat.CODE_93); | ||||||
|  |       vector.addElement(BarcodeFormat.CODE_128); | ||||||
|  |       vector.addElement(BarcodeFormat.ITF); | ||||||
|  |       vector.addElement(BarcodeFormat.QR_CODE); | ||||||
|  |       vector.addElement(BarcodeFormat.DATA_MATRIX); | ||||||
|  |       vector.addElement(BarcodeFormat.PDF417); | ||||||
|  |       //vector.addElement(BarcodeFormat.CODABAR); | ||||||
|  |     } | ||||||
|  |     hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); | ||||||
|  |     if (tryHarder) { | ||||||
|  |       hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); | ||||||
|  |     } | ||||||
|  |     if (pureBarcode) { | ||||||
|  |       hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE); | ||||||
|  |     } | ||||||
|  |     return hints; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void printUsage() { | ||||||
|  |     System.err.println("Decode barcode images using the ZXing library\n"); | ||||||
|  |     System.err.println("usage: CommandLineRunner { file | dir | url } [ options ]"); | ||||||
|  |     System.err.println("  --try_harder: Use the TRY_HARDER hint, default is normal (mobile) mode"); | ||||||
|  |     System.err.println("  --pure_barcode: Input image is a pure monochrome barcode image, not a photo"); | ||||||
|  |     System.err.println("  --products_only: Only decode the UPC and EAN families of barcodes"); | ||||||
|  |     System.err.println("  --dump_results: Write the decoded contents to input.txt"); | ||||||
|  |     System.err.println("  --dump_black_point: Compare black point algorithms as input.mono.png"); | ||||||
|  |     System.err.println("  --crop=left,top,width,height: Only examine cropped region of input image(s)"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void decodeOneArgument(String argument, | ||||||
|  |                                         Hashtable<DecodeHintType, Object> hints, | ||||||
|  |                                         boolean dumpResults, | ||||||
|  |                                         boolean dumpBlackPoint, | ||||||
|  |                                         int[] crop) throws IOException, | ||||||
|  |       URISyntaxException { | ||||||
|  | 
 | ||||||
|  |     File inputFile = new File(argument); | ||||||
|  |     if (inputFile.exists()) { | ||||||
|  |       if (inputFile.isDirectory()) { | ||||||
|  |         int successful = 0; | ||||||
|  |         int total = 0; | ||||||
|  |         for (File input : inputFile.listFiles()) { | ||||||
|  |           String filename = input.getName().toLowerCase(); | ||||||
|  |           // Skip hidden files and text files (the latter is found in the blackbox tests). | ||||||
|  |           if (filename.startsWith(".") || filename.endsWith(".txt")) { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           // Skip the results of dumping the black point. | ||||||
|  |           if (filename.contains(".mono.png")) { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           Result result = decode(input.toURI(), hints, dumpBlackPoint, crop); | ||||||
|  |           if (result != null) { | ||||||
|  |             successful++; | ||||||
|  |             if (dumpResults) { | ||||||
|  |               dumpResult(input, result); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           total++; | ||||||
|  |         } | ||||||
|  |         System.out.println("\nDecoded " + successful + " files out of " + total + | ||||||
|  |             " successfully (" + (successful * 100 / total) + "%)\n"); | ||||||
|  |       } else { | ||||||
|  |         Result result = decode(inputFile.toURI(), hints, dumpBlackPoint, crop); | ||||||
|  |         if (dumpResults) { | ||||||
|  |           dumpResult(inputFile, result); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       decode(new URI(argument), hints, dumpBlackPoint, crop); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void dumpResult(File input, Result result) throws IOException { | ||||||
|  |     String name = input.getAbsolutePath(); | ||||||
|  |     int pos = name.lastIndexOf('.'); | ||||||
|  |     if (pos > 0) { | ||||||
|  |       name = name.substring(0, pos); | ||||||
|  |     } | ||||||
|  |     File dump = new File(name + ".txt"); | ||||||
|  |     writeStringToFile(result.getText(), dump); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void writeStringToFile(String value, File file) throws IOException { | ||||||
|  |     Writer out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF8")); | ||||||
|  |     try { | ||||||
|  |       out.write(value); | ||||||
|  |     } finally { | ||||||
|  |       out.close(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static Result decode(URI uri, | ||||||
|  |                                Hashtable<DecodeHintType, Object> hints, | ||||||
|  |                                boolean dumpBlackPoint, | ||||||
|  |                                int[] crop) throws IOException { | ||||||
|  |     BufferedImage image; | ||||||
|  |     try { | ||||||
|  |       image = ImageIO.read(uri.toURL()); | ||||||
|  |     } catch (IllegalArgumentException iae) { | ||||||
|  |       throw new FileNotFoundException("Resource not found: " + uri); | ||||||
|  |     } | ||||||
|  |     if (image == null) { | ||||||
|  |       System.err.println(uri.toString() + ": Could not load image"); | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     try { | ||||||
|  |       LuminanceSource source; | ||||||
|  |       if (crop == null) { | ||||||
|  |         source = new BufferedImageLuminanceSource(image); | ||||||
|  |       } else { | ||||||
|  |         source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]); | ||||||
|  |       } | ||||||
|  |       BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); | ||||||
|  |       if (dumpBlackPoint) { | ||||||
|  |         dumpBlackPoint(uri, image, bitmap); | ||||||
|  |       } | ||||||
|  |       Result result = new MultiFormatReader().decode(bitmap, hints); | ||||||
|  |       ParsedResult parsedResult = ResultParser.parseResult(result); | ||||||
|  |       System.out.println(uri.toString() + " (format: " + result.getBarcodeFormat() + | ||||||
|  |           ", type: " + parsedResult.getType() + "):\nRaw result:\n" + result.getText() + | ||||||
|  |           "\nParsed result:\n" + parsedResult.getDisplayResult()); | ||||||
|  | 
 | ||||||
|  |       System.out.println("Also, there were " + 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 result; | ||||||
|  |     } catch (NotFoundException nfe) { | ||||||
|  |       System.out.println(uri.toString() + ": No barcode found"); | ||||||
|  |       return null; | ||||||
|  |     } finally { | ||||||
|  |       // Uncomment these lines when turning on exception tracking in ReaderException. | ||||||
|  |       //System.out.println("Threw " + ReaderException.getExceptionCountAndReset() + " exceptions"); | ||||||
|  |       //System.out.println("Throwers:\n" + ReaderException.getThrowersAndReset()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Writes out a single PNG which is three times the width of the input image, containing from left | ||||||
|  |   // to right: the original image, the row sampling monochrome version, and the 2D sampling | ||||||
|  |   // monochrome version. | ||||||
|  |   // TODO: Update to compare different Binarizer implementations. | ||||||
|  |   private static void dumpBlackPoint(URI uri, BufferedImage image, BinaryBitmap bitmap) { | ||||||
|  |     String inputName = uri.getPath(); | ||||||
|  |     if (inputName.contains(".mono.png")) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int width = bitmap.getWidth(); | ||||||
|  |     int height = bitmap.getHeight(); | ||||||
|  |     int stride = width * 3; | ||||||
|  |     int[] pixels = new int[stride * height]; | ||||||
|  | 
 | ||||||
|  |     // The original image | ||||||
|  |     int[] argb = new int[width]; | ||||||
|  |     for (int y = 0; y < height; y++) { | ||||||
|  |       image.getRGB(0, y, width, 1, argb, 0, width); | ||||||
|  |       System.arraycopy(argb, 0, pixels, y * stride, width); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Row sampling | ||||||
|  |     BitArray row = new BitArray(width); | ||||||
|  |     for (int y = 0; y < height; y++) { | ||||||
|  |       try { | ||||||
|  |         row = bitmap.getBlackRow(y, row); | ||||||
|  |       } catch (NotFoundException nfe) { | ||||||
|  |         // If fetching the row failed, draw a red line and keep going. | ||||||
|  |         int offset = y * stride + width; | ||||||
|  |         for (int x = 0; x < width; x++) { | ||||||
|  |           pixels[offset + x] = 0xffff0000; | ||||||
|  |         } | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       int offset = y * stride + width; | ||||||
|  |       for (int x = 0; x < width; x++) { | ||||||
|  |         if (row.get(x)) { | ||||||
|  |           pixels[offset + x] = 0xff000000; | ||||||
|  |         } else { | ||||||
|  |           pixels[offset + x] = 0xffffffff; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 2D sampling | ||||||
|  |     try { | ||||||
|  |       for (int y = 0; y < height; y++) { | ||||||
|  |         BitMatrix matrix = bitmap.getBlackMatrix(); | ||||||
|  |         int offset = y * stride + width * 2; | ||||||
|  |         for (int x = 0; x < width; x++) { | ||||||
|  |           if (matrix.get(x, y)) { | ||||||
|  |             pixels[offset + x] = 0xff000000; | ||||||
|  |           } else { | ||||||
|  |             pixels[offset + x] = 0xffffffff; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } catch (NotFoundException nfe) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Write the result | ||||||
|  |     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 = inputName; | ||||||
|  |     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 += ".mono.png"; | ||||||
|  |     OutputStream outStream = null; | ||||||
|  |     try { | ||||||
|  |       outStream = new FileOutputStream(resultName); | ||||||
|  |       ImageIO.write(result, "png", outStream); | ||||||
|  |     } catch (FileNotFoundException e) { | ||||||
|  |       System.err.println("Could not create " + resultName); | ||||||
|  |     } catch (IOException e) { | ||||||
|  |       System.err.println("Could not write to " + resultName); | ||||||
|  |     } finally { | ||||||
|  |       try { | ||||||
|  |         if (outStream != null) { | ||||||
|  |           outStream.close(); | ||||||
|  |         } | ||||||
|  |       } catch (IOException ioe) { | ||||||
|  |         // continue | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										185
									
								
								src/com/google/zxing/client/j2se/GUIRunner.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/com/google/zxing/client/j2se/GUIRunner.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.j2se; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BarcodeFormat; | ||||||
|  | import com.google.zxing.BinaryBitmap; | ||||||
|  | import com.google.zxing.LuminanceSource; | ||||||
|  | import com.google.zxing.MultiFormatReader; | ||||||
|  | import com.google.zxing.MultiFormatWriter; | ||||||
|  | import com.google.zxing.ReaderException; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.WriterException; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.common.HybridBinarizer; | ||||||
|  | 
 | ||||||
|  | import java.awt.Container; | ||||||
|  | import java.awt.GridLayout; | ||||||
|  | import java.awt.Image; | ||||||
|  | import java.awt.TextArea; | ||||||
|  | import java.awt.event.ActionEvent; | ||||||
|  | import java.awt.event.ActionListener; | ||||||
|  | import java.awt.image.BufferedImage; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.net.MalformedURLException; | ||||||
|  | 
 | ||||||
|  | import javax.imageio.ImageIO; | ||||||
|  | import javax.swing.Icon; | ||||||
|  | import javax.swing.ImageIcon; | ||||||
|  | import javax.swing.JButton; | ||||||
|  | import javax.swing.JFileChooser; | ||||||
|  | import javax.swing.JFrame; | ||||||
|  | import javax.swing.JLabel; | ||||||
|  | import javax.swing.JPanel; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p> | ||||||
|  |  * Simple GUI frontend to the library. Right now, only decodes a local file. | ||||||
|  |  * This definitely needs some improvement. Just throwing something down to | ||||||
|  |  * start. | ||||||
|  |  * </p> | ||||||
|  |  *  | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | //设置编码格式 Encoder.java static final String DEFAULT_BYTE_MODE_ENCODING ="UTF-8" | ||||||
|  | //设置图片大小本文件中 | ||||||
|  | public final class GUIRunner extends JFrame implements ActionListener { | ||||||
|  | 
 | ||||||
|  | 	private final JLabel imageLabel; | ||||||
|  | 	private final TextArea textArea; | ||||||
|  | 	private final JButton sel_button; | ||||||
|  | 	private final JButton generate_button; | ||||||
|  | 	private final JLabel label2; | ||||||
|  | 	private final TextArea textArea2; | ||||||
|  | 
 | ||||||
|  | 	private GUIRunner() { | ||||||
|  | 		imageLabel = new JLabel(); | ||||||
|  | 		textArea = new TextArea(); | ||||||
|  | 		textArea.setEditable(false); | ||||||
|  | //		textArea.setLineWrap(true); | ||||||
|  | //		textArea.setMaximumSize(new Dimension(400, 200)); | ||||||
|  | 		label2=new JLabel("请输入要编码信息:"); | ||||||
|  | 		textArea2 =new TextArea(); | ||||||
|  | 		// Button | ||||||
|  | 		sel_button = new JButton("选择图片"); | ||||||
|  | 		sel_button.addActionListener(this); | ||||||
|  | 		generate_button=new JButton("生成二维码"); | ||||||
|  | 		generate_button.addActionListener(this); | ||||||
|  | 		Container panel = new JPanel(new GridLayout(2,1)); | ||||||
|  | 		//panel.setLayout(new FlowLayout());//new GridLayout(2,1) | ||||||
|  | 		panel.add(imageLabel);//一行一列 | ||||||
|  | 		JPanel panel2=new JPanel(new GridLayout(4,1));//2行一列 | ||||||
|  | 		panel2.add(textArea); | ||||||
|  | 		panel2.add(label2); | ||||||
|  | 		panel2.add(textArea2); | ||||||
|  | 		JPanel panel3=new JPanel(new GridLayout(1,2)); | ||||||
|  | 		panel3.add(sel_button); | ||||||
|  | 		panel3.add(generate_button); | ||||||
|  | 		panel2.add(panel3); | ||||||
|  | 		panel.add(panel2); | ||||||
|  | 		setTitle("二维码编码解码程序_Powered by Zxing (C)xuenhua"); | ||||||
|  | 		setSize(420, 420); | ||||||
|  | 		setDefaultCloseOperation(EXIT_ON_CLOSE); | ||||||
|  | 		setContentPane(panel); | ||||||
|  | 		setLocationRelativeTo(null); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public static void main(String[] args) throws MalformedURLException { | ||||||
|  | 		GUIRunner runner = new GUIRunner(); | ||||||
|  | 		runner.setVisible(true); | ||||||
|  | 		//runner.chooseImage(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void chooseImage() throws MalformedURLException { | ||||||
|  | 		JFileChooser fileChooser = new JFileChooser(); | ||||||
|  | 		fileChooser.showOpenDialog(this); | ||||||
|  | 		File file = fileChooser.getSelectedFile();	 | ||||||
|  | 		Icon imageIcon = new ImageIcon(file.toURI().toURL()); | ||||||
|  | 		if(imageIcon.getIconWidth()>200){//转换尺寸 | ||||||
|  | 			Image image=null; | ||||||
|  | 			try { | ||||||
|  | 				image = ImageIO.read(file); | ||||||
|  | 			} catch (IOException ioe) { | ||||||
|  | 				System.out.println( ioe.toString()); | ||||||
|  | 			} | ||||||
|  | 			if (image == null) { | ||||||
|  | 				System.out.println("Could not decode image"); | ||||||
|  | 			} | ||||||
|  | 			Image scaledImage=image.getScaledInstance(200,200,Image.SCALE_DEFAULT);  | ||||||
|  | 			imageIcon=new ImageIcon(scaledImage);  | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | //		setSize(imageIcon.getIconWidth(), imageIcon.getIconHeight() + 100); | ||||||
|  | 		imageLabel.setIcon(imageIcon); | ||||||
|  | 		String decodeText = getDecodeText(file); | ||||||
|  | 		textArea.setText(decodeText); | ||||||
|  | 	} | ||||||
|  | 	private static String getDecodeText(File file) { | ||||||
|  | 		BufferedImage image; | ||||||
|  | 		try { | ||||||
|  | 			image = ImageIO.read(file); | ||||||
|  | 		} catch (IOException ioe) { | ||||||
|  | 			return ioe.toString(); | ||||||
|  | 		} | ||||||
|  | 		if (image == null) { | ||||||
|  | 			return "Could not decode image"; | ||||||
|  | 		} | ||||||
|  | 		LuminanceSource source = new BufferedImageLuminanceSource(image); | ||||||
|  | 		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); | ||||||
|  | 		Result result; | ||||||
|  | 		try { | ||||||
|  | 			result = new MultiFormatReader().decode(bitmap); | ||||||
|  | 		} catch (ReaderException re) { | ||||||
|  | 			return re.toString(); | ||||||
|  | 		} | ||||||
|  | 		return result.getText(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Override | ||||||
|  | 	public void actionPerformed(ActionEvent e) { | ||||||
|  | 		try { | ||||||
|  | 			if(e.getActionCommand()=="选择图片"){ | ||||||
|  | 				chooseImage(); | ||||||
|  | 			} | ||||||
|  | 			if(e.getActionCommand()=="生成二维码"){ | ||||||
|  | 				BitMatrix bitMatrix; | ||||||
|  | 				String str=textArea2.getText(); | ||||||
|  | 				String user_dir=System.getProperty("user.dir"); | ||||||
|  | 				String path=user_dir+"\\qr.png"; | ||||||
|  | 				try { | ||||||
|  | 					bitMatrix = new MultiFormatWriter().encode | ||||||
|  | 					(str, BarcodeFormat.QR_CODE, 200, 200);//图片大小 | ||||||
|  | 					File file = new File(path);	 | ||||||
|  | 					if(!file.exists()) { | ||||||
|  | 						file.createNewFile(); | ||||||
|  | 					} | ||||||
|  | 						MatrixToImageWriter.writeToFile(bitMatrix, "png", file); | ||||||
|  | 						textArea.setText("二维码生成成功\n"+path); | ||||||
|  | 					} catch (IOException eIO) { | ||||||
|  | 					eIO.printStackTrace(); | ||||||
|  | 					} catch (WriterException e1) { | ||||||
|  | 					e1.printStackTrace(); | ||||||
|  | 					} | ||||||
|  | 			} | ||||||
|  | 		} catch (MalformedURLException e1) { | ||||||
|  | 			// TODO Auto-generated catch block | ||||||
|  | 			e1.printStackTrace(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										182
									
								
								src/com/google/zxing/client/j2se/ImageConverter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/com/google/zxing/client/j2se/ImageConverter.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,182 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.j2se; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BinaryBitmap; | ||||||
|  | import com.google.zxing.LuminanceSource; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.ReaderException; | ||||||
|  | import com.google.zxing.common.BitArray; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.common.HybridBinarizer; | ||||||
|  | 
 | ||||||
|  | import java.awt.image.BufferedImage; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.net.URI; | ||||||
|  | 
 | ||||||
|  | import javax.imageio.ImageIO; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A utility application for evaluating the effectiveness of various thresholding algorithms. | ||||||
|  |  * Given a set of images on the command line, it converts each to a black-and-white PNG. | ||||||
|  |  * The result is placed in a file based on the input name, with either ".row.png" or ".2d.png" | ||||||
|  |  * appended. | ||||||
|  |  * | ||||||
|  |  * TODO: Needs to be updated to accept different Binarizer implementations. | ||||||
|  |  * TODO: Consider whether to keep this separate app, as CommandLineRunner has similar functionality. | ||||||
|  |  * | ||||||
|  |  * @author alasdair@google.com (Alasdair Mackintosh) | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class ImageConverter { | ||||||
|  | 
 | ||||||
|  |   private static final String FORMAT = "png"; | ||||||
|  |   private static final int WHITE = 0xFFFFFFFF; | ||||||
|  |   private static final int BLACK = 0xFF000000; | ||||||
|  |   private static final int RED = 0xFFFF0000; | ||||||
|  |   private static boolean rowSampling = false; | ||||||
|  | 
 | ||||||
|  |   private ImageConverter() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static void main(String[] args) throws Exception { | ||||||
|  |     for (String arg : args) { | ||||||
|  |       if ("-row".equals(arg)) { | ||||||
|  |         rowSampling = true; | ||||||
|  |       } else if ("-2d".equals(arg)) { | ||||||
|  |         rowSampling = false; | ||||||
|  |       } else if (arg.startsWith("-")) { | ||||||
|  |         System.err.println("Ignoring unrecognized option: " + arg); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     for (String arg : args) { | ||||||
|  |       if (arg.startsWith("-")) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       File inputFile = new File(arg); | ||||||
|  |       if (inputFile.exists()) { | ||||||
|  |         if (inputFile.isDirectory()) { | ||||||
|  |           for (File input : inputFile.listFiles()) { | ||||||
|  |             String filename = input.getName().toLowerCase(); | ||||||
|  |             // Skip hidden files and text files (the latter is found in the blackbox tests). | ||||||
|  |             if (filename.startsWith(".") || filename.endsWith(".txt")) { | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|  |             // Skip the results of dumping the black point. | ||||||
|  |             if (filename.contains(".mono.png") || filename.contains(".row.png") || | ||||||
|  |                 filename.contains(".2d.png")) { | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|  |             convertImage(input.toURI()); | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           convertImage(inputFile.toURI()); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         convertImage(new URI(arg)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   private static void convertImage(URI uri) throws IOException { | ||||||
|  |     BufferedImage image = ImageIO.read(uri.toURL()); | ||||||
|  |     LuminanceSource source = new BufferedImageLuminanceSource(image); | ||||||
|  |     BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); | ||||||
|  |     int width = bitmap.getWidth(); | ||||||
|  |     int height = bitmap.getHeight(); | ||||||
|  | 
 | ||||||
|  |     BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | ||||||
|  |     BitArray array = new BitArray(width); | ||||||
|  | 
 | ||||||
|  |     if (rowSampling) { | ||||||
|  |       for (int y = 0; y < height; y++) { | ||||||
|  |           try { | ||||||
|  |             array = bitmap.getBlackRow(y, array); | ||||||
|  |           } catch (NotFoundException nfe) { | ||||||
|  |             // Draw rows with insufficient dynamic range in red | ||||||
|  |             for (int x = 0; x < width; x++) { | ||||||
|  |               result.setRGB(x, y, RED); | ||||||
|  |             } | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |         for (int x = 0; x < width; x++) { | ||||||
|  |           result.setRGB(x, y, array.get(x) ? BLACK : WHITE); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       try { | ||||||
|  |         BitMatrix matrix = bitmap.getBlackMatrix(); | ||||||
|  |         for (int y = 0; y < height; y++) { | ||||||
|  |           for (int x = 0; x < width; x++) { | ||||||
|  |             result.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } catch (NotFoundException nfe) { | ||||||
|  | 
 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     File output = getOutput(uri); | ||||||
|  |     System.out.printf("Writing output to %s\n", output); | ||||||
|  |     ImageIO.write(result, FORMAT, output); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static File getFileOfUri(URI uri) { | ||||||
|  |     String name = uri.getPath(); | ||||||
|  |     int slashPos = name.lastIndexOf((int) '/'); | ||||||
|  |     String parent; | ||||||
|  |     String basename; | ||||||
|  |     if (slashPos != -1 && slashPos != name.length() - 1) { | ||||||
|  |       parent = name.substring(0, slashPos); | ||||||
|  |       basename = name.substring(slashPos + 1); | ||||||
|  |     } else { | ||||||
|  |       parent = "."; | ||||||
|  |       basename = name; | ||||||
|  |     } | ||||||
|  |     File parentFile = new File(parent); | ||||||
|  |     if (!parentFile.exists()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     File baseFile = new File(parent, basename); | ||||||
|  |     if (!baseFile.exists()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return baseFile; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static File getOutput(URI uri) { | ||||||
|  |     File result = getFileOfUri(uri); | ||||||
|  |     if (result == null) { | ||||||
|  |       result = new File("ConvertedImage." + FORMAT); | ||||||
|  |     } else { | ||||||
|  |       String name = result.getPath(); | ||||||
|  |       int dotpos = name.lastIndexOf((int) '.'); | ||||||
|  |       if (dotpos != -1) { | ||||||
|  |         name = name.substring(0, dotpos); | ||||||
|  |       } | ||||||
|  |       String suffix = rowSampling ? "row" : "2d"; | ||||||
|  |       result = new File(name + '.' + suffix + '.' + FORMAT); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								src/com/google/zxing/client/j2se/MatrixToImageWriter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/com/google/zxing/client/j2se/MatrixToImageWriter.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.j2se; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | import javax.imageio.ImageIO; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.awt.image.BufferedImage; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Writes a {@link BitMatrix} to {@link BufferedImage}, | ||||||
|  |  * file or stream. Provided here instead of core since it depends on | ||||||
|  |  * Java SE libraries. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class MatrixToImageWriter { | ||||||
|  | 
 | ||||||
|  |   private static final int BLACK = 0xFF000000; | ||||||
|  |   private static final int WHITE = 0xFFFFFFFF; | ||||||
|  | 
 | ||||||
|  |   private MatrixToImageWriter() {} | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Renders a {@link BitMatrix} as an image, where "false" bits are rendered | ||||||
|  |    * as white, and "true" bits are rendered as black. | ||||||
|  |    */ | ||||||
|  |   public static BufferedImage toBufferedImage(BitMatrix matrix) { | ||||||
|  |     int width = matrix.getWidth(); | ||||||
|  |     int height = matrix.getHeight(); | ||||||
|  |     BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | ||||||
|  |     for (int x = 0; x < width; x++) { | ||||||
|  |       for (int y = 0; y < height; y++) { | ||||||
|  |         image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return image; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Writes a {@link BitMatrix} to a file. | ||||||
|  |    * | ||||||
|  |    * @see #toBufferedImage(BitMatrix) | ||||||
|  |    */ | ||||||
|  |   public static void writeToFile(BitMatrix matrix, String format, File file) | ||||||
|  |           throws IOException { | ||||||
|  |     BufferedImage image = toBufferedImage(matrix); | ||||||
|  |     ImageIO.write(image, format, file); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Writes a {@link BitMatrix} to a stream. | ||||||
|  |    * | ||||||
|  |    * @see #toBufferedImage(BitMatrix) | ||||||
|  |    */ | ||||||
|  |   public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) | ||||||
|  |           throws IOException { | ||||||
|  |     BufferedImage image = toBufferedImage(matrix); | ||||||
|  |     ImageIO.write(image, format, stream); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>See | ||||||
|  |  * <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html"> | ||||||
|  |  * DoCoMo's documentation</a> about the result types represented by subclasses of this class.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less | ||||||
|  |  * on exception-based mechanisms during parsing.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | abstract class AbstractDoCoMoResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   static String[] matchDoCoMoPrefixedField(String prefix, String rawText, boolean trim) { | ||||||
|  |     return matchPrefixedField(prefix, rawText, ';', trim); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String matchSingleDoCoMoPrefixedField(String prefix, String rawText, boolean trim) { | ||||||
|  |     return matchSinglePrefixedField(prefix, rawText, ';', trim); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,73 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implements KDDI AU's address book format. See | ||||||
|  |  * <a href="http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html"> | ||||||
|  |  * http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html</a>. | ||||||
|  |  * (Thanks to Yuzo for translating!) | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class AddressBookAUResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   public static AddressBookParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     // MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF | ||||||
|  |     if (rawText == null || rawText.indexOf("MEMORY") < 0 || rawText.indexOf("\r\n") < 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively. | ||||||
|  |     // Therefore we treat them specially instead of as an array of names. | ||||||
|  |     String name = matchSinglePrefixedField("NAME1:", rawText, '\r', true); | ||||||
|  |     String pronunciation = matchSinglePrefixedField("NAME2:", rawText, '\r', true); | ||||||
|  | 
 | ||||||
|  |     String[] phoneNumbers = matchMultipleValuePrefix("TEL", 3, rawText, true); | ||||||
|  |     String[] emails = matchMultipleValuePrefix("MAIL", 3, rawText, true); | ||||||
|  |     String note = matchSinglePrefixedField("MEMORY:", rawText, '\r', false); | ||||||
|  |     String address = matchSinglePrefixedField("ADD:", rawText, '\r', true); | ||||||
|  |     String[] addresses = address == null ? null : new String[] {address}; | ||||||
|  |     return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note, | ||||||
|  |         addresses, null, null, null, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String[] matchMultipleValuePrefix(String prefix, int max, String rawText, | ||||||
|  |       boolean trim) { | ||||||
|  |     Vector values = null; | ||||||
|  |     for (int i = 1; i <= max; i++) { | ||||||
|  |       String value = matchSinglePrefixedField(prefix + i + ':', rawText, '\r', trim); | ||||||
|  |       if (value == null) { | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       if (values == null) { | ||||||
|  |         values = new Vector(max); // lazy init | ||||||
|  |       } | ||||||
|  |       values.addElement(value); | ||||||
|  |     } | ||||||
|  |     if (values == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     return toStringArray(values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,85 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implements the "MECARD" address book entry format. | ||||||
|  |  * | ||||||
|  |  * Supported keys: N, SOUND, TEL, EMAIL, NOTE, ADR, BDAY, URL, plus ORG | ||||||
|  |  * Unsupported keys: TEL-AV, NICKNAME | ||||||
|  |  * | ||||||
|  |  * Except for TEL, multiple values for keys are also not supported; | ||||||
|  |  * the first one found takes precedence. | ||||||
|  |  * | ||||||
|  |  * Our understanding of the MECARD format is based on this document: | ||||||
|  |  * | ||||||
|  |  * http://www.mobicode.org.tw/files/OMIA%20Mobile%20Bar%20Code%20Standard%20v3.2.1.doc  | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class AddressBookDoCoMoResultParser extends AbstractDoCoMoResultParser { | ||||||
|  | 
 | ||||||
|  |   public static AddressBookParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || !rawText.startsWith("MECARD:")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String[] rawName = matchDoCoMoPrefixedField("N:", rawText, true); | ||||||
|  |     if (rawName == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String name = parseName(rawName[0]); | ||||||
|  |     String pronunciation = matchSingleDoCoMoPrefixedField("SOUND:", rawText, true); | ||||||
|  |     String[] phoneNumbers = matchDoCoMoPrefixedField("TEL:", rawText, true); | ||||||
|  |     String[] emails = matchDoCoMoPrefixedField("EMAIL:", rawText, true); | ||||||
|  |     String note = matchSingleDoCoMoPrefixedField("NOTE:", rawText, false); | ||||||
|  |     String[] addresses = matchDoCoMoPrefixedField("ADR:", rawText, true); | ||||||
|  |     String birthday = matchSingleDoCoMoPrefixedField("BDAY:", rawText, true); | ||||||
|  |     if (birthday != null && !isStringOfDigits(birthday, 8)) { | ||||||
|  |       // No reason to throw out the whole card because the birthday is formatted wrong. | ||||||
|  |       birthday = null; | ||||||
|  |     } | ||||||
|  |     String url = matchSingleDoCoMoPrefixedField("URL:", rawText, true); | ||||||
|  | 
 | ||||||
|  |     // Although ORG may not be strictly legal in MECARD, it does exist in VCARD and we might as well | ||||||
|  |     // honor it when found in the wild. | ||||||
|  |     String org = matchSingleDoCoMoPrefixedField("ORG:", rawText, true); | ||||||
|  | 
 | ||||||
|  |     return new AddressBookParsedResult(maybeWrap(name), | ||||||
|  |                                        pronunciation, | ||||||
|  |                                        phoneNumbers, | ||||||
|  |                                        emails, | ||||||
|  |                                        note, | ||||||
|  |                                        addresses, | ||||||
|  |                                        org, | ||||||
|  |                                        birthday, | ||||||
|  |                                        null, | ||||||
|  |                                        url); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String parseName(String name) { | ||||||
|  |     int comma = name.indexOf((int) ','); | ||||||
|  |     if (comma >= 0) { | ||||||
|  |       // Format may be last,first; switch it around | ||||||
|  |       return name.substring(comma + 1) + ' ' + name.substring(0, comma); | ||||||
|  |     } | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										122
									
								
								src/com/google/zxing/client/result/AddressBookParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/com/google/zxing/client/result/AddressBookParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class AddressBookParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String[] names; | ||||||
|  |   private final String pronunciation; | ||||||
|  |   private final String[] phoneNumbers; | ||||||
|  |   private final String[] emails; | ||||||
|  |   private final String note; | ||||||
|  |   private final String[] addresses; | ||||||
|  |   private final String org; | ||||||
|  |   private final String birthday; | ||||||
|  |   private final String title; | ||||||
|  |   private final String url; | ||||||
|  | 
 | ||||||
|  |   public AddressBookParsedResult(String[] names, | ||||||
|  |                                  String pronunciation, | ||||||
|  |                                  String[] phoneNumbers, | ||||||
|  |                                  String[] emails, | ||||||
|  |                                  String note, | ||||||
|  |                                  String[] addresses, | ||||||
|  |                                  String org, | ||||||
|  |                                  String birthday, | ||||||
|  |                                  String title, | ||||||
|  |                                  String url) { | ||||||
|  |     super(ParsedResultType.ADDRESSBOOK); | ||||||
|  |     this.names = names; | ||||||
|  |     this.pronunciation = pronunciation; | ||||||
|  |     this.phoneNumbers = phoneNumbers; | ||||||
|  |     this.emails = emails; | ||||||
|  |     this.note = note; | ||||||
|  |     this.addresses = addresses; | ||||||
|  |     this.org = org; | ||||||
|  |     this.birthday = birthday; | ||||||
|  |     this.title = title; | ||||||
|  |     this.url = url; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String[] getNames() { | ||||||
|  |     return names; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * In Japanese, the name is written in kanji, which can have multiple readings. Therefore a hint | ||||||
|  |    * is often provided, called furigana, which spells the name phonetically. | ||||||
|  |    * | ||||||
|  |    * @return The pronunciation of the getNames() field, often in hiragana or katakana. | ||||||
|  |    */ | ||||||
|  |   public String getPronunciation() { | ||||||
|  |     return pronunciation; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String[] getPhoneNumbers() { | ||||||
|  |     return phoneNumbers; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String[] getEmails() { | ||||||
|  |     return emails; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getNote() { | ||||||
|  |     return note; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String[] getAddresses() { | ||||||
|  |     return addresses; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getTitle() { | ||||||
|  |     return title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getOrg() { | ||||||
|  |     return org; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getURL() { | ||||||
|  |     return url; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return birthday formatted as yyyyMMdd (e.g. 19780917) | ||||||
|  |    */ | ||||||
|  |   public String getBirthday() { | ||||||
|  |     return birthday; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(100); | ||||||
|  |     maybeAppend(names, result); | ||||||
|  |     maybeAppend(pronunciation, result); | ||||||
|  |     maybeAppend(title, result); | ||||||
|  |     maybeAppend(org, result); | ||||||
|  |     maybeAppend(addresses, result); | ||||||
|  |     maybeAppend(phoneNumbers, result); | ||||||
|  |     maybeAppend(emails, result); | ||||||
|  |     maybeAppend(url, result); | ||||||
|  |     maybeAppend(birthday, result); | ||||||
|  |     maybeAppend(note, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								src/com/google/zxing/client/result/BizcardResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/com/google/zxing/client/result/BizcardResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implements the "BIZCARD" address book entry format, though this has been | ||||||
|  |  * largely reverse-engineered from examples observed in the wild -- still | ||||||
|  |  * looking for a definitive reference. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class BizcardResultParser extends AbstractDoCoMoResultParser { | ||||||
|  | 
 | ||||||
|  |   // Yes, we extend AbstractDoCoMoResultParser since the format is very much | ||||||
|  |   // like the DoCoMo MECARD format, but this is not technically one of  | ||||||
|  |   // DoCoMo's proposed formats | ||||||
|  | 
 | ||||||
|  |   public static AddressBookParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || !rawText.startsWith("BIZCARD:")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String firstName = matchSingleDoCoMoPrefixedField("N:", rawText, true); | ||||||
|  |     String lastName = matchSingleDoCoMoPrefixedField("X:", rawText, true); | ||||||
|  |     String fullName = buildName(firstName, lastName); | ||||||
|  |     String title = matchSingleDoCoMoPrefixedField("T:", rawText, true); | ||||||
|  |     String org = matchSingleDoCoMoPrefixedField("C:", rawText, true); | ||||||
|  |     String[] addresses = matchDoCoMoPrefixedField("A:", rawText, true); | ||||||
|  |     String phoneNumber1 = matchSingleDoCoMoPrefixedField("B:", rawText, true); | ||||||
|  |     String phoneNumber2 = matchSingleDoCoMoPrefixedField("M:", rawText, true); | ||||||
|  |     String phoneNumber3 = matchSingleDoCoMoPrefixedField("F:", rawText, true); | ||||||
|  |     String email = matchSingleDoCoMoPrefixedField("E:", rawText, true); | ||||||
|  | 
 | ||||||
|  |     return new AddressBookParsedResult(maybeWrap(fullName), | ||||||
|  |                                        null, | ||||||
|  |                                        buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3), | ||||||
|  |                                        maybeWrap(email), | ||||||
|  |                                        null, | ||||||
|  |                                        addresses, | ||||||
|  |                                        org, | ||||||
|  |                                        null, | ||||||
|  |                                        title, | ||||||
|  |                                        null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String[] buildPhoneNumbers(String number1, String number2, String number3) { | ||||||
|  |     Vector numbers = new Vector(3); | ||||||
|  |     if (number1 != null) { | ||||||
|  |       numbers.addElement(number1); | ||||||
|  |     } | ||||||
|  |     if (number2 != null) { | ||||||
|  |       numbers.addElement(number2); | ||||||
|  |     } | ||||||
|  |     if (number3 != null) { | ||||||
|  |       numbers.addElement(number3); | ||||||
|  |     } | ||||||
|  |     int size = numbers.size(); | ||||||
|  |     if (size == 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String[] result = new String[size]; | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       result[i] = (String) numbers.elementAt(i); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String buildName(String firstName, String lastName) { | ||||||
|  |     if (firstName == null) { | ||||||
|  |       return lastName; | ||||||
|  |     } else { | ||||||
|  |       return lastName == null ? firstName : firstName + ' ' + lastName; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,46 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class BookmarkDoCoMoResultParser extends AbstractDoCoMoResultParser { | ||||||
|  | 
 | ||||||
|  |   private BookmarkDoCoMoResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static URIParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || !rawText.startsWith("MEBKM:")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String title = matchSingleDoCoMoPrefixedField("TITLE:", rawText, true); | ||||||
|  |     String[] rawUri = matchDoCoMoPrefixedField("URL:", rawText, true); | ||||||
|  |     if (rawUri == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String uri = rawUri[0]; | ||||||
|  |     if (!URIResultParser.isBasicallyValidURI(uri)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     return new URIParsedResult(uri, title); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										134
									
								
								src/com/google/zxing/client/result/CalendarParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/com/google/zxing/client/result/CalendarParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class CalendarParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String summary; | ||||||
|  |   private final String start; | ||||||
|  |   private final String end; | ||||||
|  |   private final String location; | ||||||
|  |   private final String attendee; | ||||||
|  |   private final String description; | ||||||
|  | 
 | ||||||
|  |   public CalendarParsedResult(String summary, | ||||||
|  |                               String start, | ||||||
|  |                               String end, | ||||||
|  |                               String location, | ||||||
|  |                               String attendee, | ||||||
|  |                               String description) { | ||||||
|  |     super(ParsedResultType.CALENDAR); | ||||||
|  |     // Start is required, end is not | ||||||
|  |     if (start == null) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     validateDate(start); | ||||||
|  |     if (end == null) { | ||||||
|  |       end = start; | ||||||
|  |     } else { | ||||||
|  |       validateDate(end); | ||||||
|  |     } | ||||||
|  |     this.summary = summary; | ||||||
|  |     this.start = start; | ||||||
|  |     this.end = end; | ||||||
|  |     this.location = location; | ||||||
|  |     this.attendee = attendee; | ||||||
|  |     this.description = description; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getSummary() { | ||||||
|  |     return summary; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>We would return the start and end date as a {@link java.util.Date} except that this code | ||||||
|  |    * needs to work under JavaME / MIDP and there is no date parsing library available there, such | ||||||
|  |    * as <code>java.text.SimpleDateFormat</code>.</p> See validateDate() for the return format. | ||||||
|  |    * | ||||||
|  |    * @return start time formatted as a RFC 2445 DATE or DATE-TIME.</p> | ||||||
|  |    */ | ||||||
|  |   public String getStart() { | ||||||
|  |     return start; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @see #getStart(). May return null if the event has no duration. | ||||||
|  |    */ | ||||||
|  |   public String getEnd() { | ||||||
|  |     return end; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getLocation() { | ||||||
|  |     return location; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getAttendee() { | ||||||
|  |     return attendee; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDescription() { | ||||||
|  |     return description; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(100); | ||||||
|  |     maybeAppend(summary, result); | ||||||
|  |     maybeAppend(start, result); | ||||||
|  |     maybeAppend(end, result); | ||||||
|  |     maybeAppend(location, result); | ||||||
|  |     maybeAppend(attendee, result); | ||||||
|  |     maybeAppend(description, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) or DATE-TIME | ||||||
|  |    * (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC). | ||||||
|  |    * | ||||||
|  |    * @param date The string to validate | ||||||
|  |    */ | ||||||
|  |   private static void validateDate(String date) { | ||||||
|  |     if (date != null) { | ||||||
|  |       int length = date.length(); | ||||||
|  |       if (length != 8 && length != 15 && length != 16) { | ||||||
|  |         throw new IllegalArgumentException(); | ||||||
|  |       } | ||||||
|  |       for (int i = 0; i < 8; i++) { | ||||||
|  |         if (!Character.isDigit(date.charAt(i))) { | ||||||
|  |           throw new IllegalArgumentException(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (length > 8) { | ||||||
|  |         if (date.charAt(8) != 'T') { | ||||||
|  |           throw new IllegalArgumentException(); | ||||||
|  |         } | ||||||
|  |         for (int i = 9; i < 15; i++) { | ||||||
|  |           if (!Character.isDigit(date.charAt(i))) { | ||||||
|  |             throw new IllegalArgumentException(); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         if (length == 16 && date.charAt(15) != 'Z') { | ||||||
|  |           throw new IllegalArgumentException(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,61 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class EmailAddressParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String emailAddress; | ||||||
|  |   private final String subject; | ||||||
|  |   private final String body; | ||||||
|  |   private final String mailtoURI; | ||||||
|  | 
 | ||||||
|  |   EmailAddressParsedResult(String emailAddress, String subject, String body, String mailtoURI) { | ||||||
|  |     super(ParsedResultType.EMAIL_ADDRESS); | ||||||
|  |     this.emailAddress = emailAddress; | ||||||
|  |     this.subject = subject; | ||||||
|  |     this.body = body; | ||||||
|  |     this.mailtoURI = mailtoURI; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getEmailAddress() { | ||||||
|  |     return emailAddress; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getSubject() { | ||||||
|  |     return subject; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getBody() { | ||||||
|  |     return body; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getMailtoURI() { | ||||||
|  |     return mailtoURI; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(30); | ||||||
|  |     maybeAppend(emailAddress, result); | ||||||
|  |     maybeAppend(subject, result); | ||||||
|  |     maybeAppend(body, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,64 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Represents a result that encodes an e-mail address, either as a plain address | ||||||
|  |  * like "joe@example.org" or a mailto: URL like "mailto:joe@example.org". | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class EmailAddressResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   public static EmailAddressParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String emailAddress; | ||||||
|  |     if (rawText.startsWith("mailto:") || rawText.startsWith("MAILTO:")) { | ||||||
|  |       // If it starts with mailto:, assume it is definitely trying to be an email address | ||||||
|  |       emailAddress = rawText.substring(7); | ||||||
|  |       int queryStart = emailAddress.indexOf('?'); | ||||||
|  |       if (queryStart >= 0) { | ||||||
|  |         emailAddress = emailAddress.substring(0, queryStart); | ||||||
|  |       } | ||||||
|  |       Hashtable nameValues = parseNameValuePairs(rawText); | ||||||
|  |       String subject = null; | ||||||
|  |       String body = null; | ||||||
|  |       if (nameValues != null) { | ||||||
|  |         if (emailAddress.length() == 0) { | ||||||
|  |           emailAddress = (String) nameValues.get("to"); | ||||||
|  |         } | ||||||
|  |         subject = (String) nameValues.get("subject"); | ||||||
|  |         body = (String) nameValues.get("body"); | ||||||
|  |       } | ||||||
|  |       return new EmailAddressParsedResult(emailAddress, subject, body, rawText); | ||||||
|  |     } else { | ||||||
|  |       if (!EmailDoCoMoResultParser.isBasicallyValidEmailAddress(rawText)) { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |       emailAddress = rawText; | ||||||
|  |       return new EmailAddressParsedResult(emailAddress, null, null, "mailto:" + emailAddress); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,87 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implements the "MATMSG" email message entry format. | ||||||
|  |  * | ||||||
|  |  * Supported keys: TO, SUB, BODY | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class EmailDoCoMoResultParser extends AbstractDoCoMoResultParser { | ||||||
|  | 
 | ||||||
|  |   private static final char[] ATEXT_SYMBOLS = | ||||||
|  |       {'@','.','!','#','$','%','&','\'','*','+','-','/','=','?','^','_','`','{','|','}','~'}; | ||||||
|  | 
 | ||||||
|  |   public static EmailAddressParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || !rawText.startsWith("MATMSG:")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String[] rawTo = matchDoCoMoPrefixedField("TO:", rawText, true); | ||||||
|  |     if (rawTo == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String to = rawTo[0]; | ||||||
|  |     if (!isBasicallyValidEmailAddress(to)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String subject = matchSingleDoCoMoPrefixedField("SUB:", rawText, false); | ||||||
|  |     String body = matchSingleDoCoMoPrefixedField("BODY:", rawText, false); | ||||||
|  |     return new EmailAddressParsedResult(to, subject, body, "mailto:" + to); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * This implements only the most basic checking for an email address's validity -- that it contains | ||||||
|  |    * an '@' contains no characters disallowed by RFC 2822. This is an overly lenient definition of | ||||||
|  |    * validity. We want to generally be lenient here since this class is only intended to encapsulate what's | ||||||
|  |    * in a barcode, not "judge" it. | ||||||
|  |    */ | ||||||
|  |   static boolean isBasicallyValidEmailAddress(String email) { | ||||||
|  |     if (email == null) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     boolean atFound = false; | ||||||
|  |     for (int i = 0; i < email.length(); i++) { | ||||||
|  |       char c = email.charAt(i); | ||||||
|  |       if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && | ||||||
|  |           !isAtextSymbol(c)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |       if (c == '@') { | ||||||
|  |         if (atFound) { | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  |         atFound = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return atFound; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static boolean isAtextSymbol(char c) { | ||||||
|  |     for (int i = 0; i < ATEXT_SYMBOLS.length; i++) { | ||||||
|  |       if (c == ATEXT_SYMBOLS[i]) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,195 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * These authors would like to acknowledge the Spanish Ministry of Industry, | ||||||
|  |  * Tourism and Trade, for the support in the project TSI020301-2008-2 | ||||||
|  |  * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled | ||||||
|  |  * Mobile Dynamic Environments", led by Treelogic | ||||||
|  |  * ( http://www.treelogic.com/ ): | ||||||
|  |  * | ||||||
|  |  *   http://www.piramidepse.com/ | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Antonio Manuel Benjumea Conde, Servinform, S.A. | ||||||
|  |  * @author Agustín Delgado, Servinform, S.A. | ||||||
|  |  */ | ||||||
|  | public class ExpandedProductParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   public static final String KILOGRAM = "KG"; | ||||||
|  |   public static final String POUND = "LB"; | ||||||
|  | 
 | ||||||
|  |   private final String productID; | ||||||
|  |   private final String sscc; | ||||||
|  |   private final String lotNumber; | ||||||
|  |   private final String productionDate; | ||||||
|  |   private final String packagingDate; | ||||||
|  |   private final String bestBeforeDate; | ||||||
|  |   private final String expirationDate; | ||||||
|  |   private final String weight; | ||||||
|  |   private final String weightType; | ||||||
|  |   private final String weightIncrement; | ||||||
|  |   private final String price; | ||||||
|  |   private final String priceIncrement; | ||||||
|  |   private final String priceCurrency; | ||||||
|  |   // For AIS that not exist in this object | ||||||
|  |   private final Hashtable uncommonAIs; | ||||||
|  | 
 | ||||||
|  |   ExpandedProductParsedResult() { | ||||||
|  |     super(ParsedResultType.PRODUCT); | ||||||
|  |     this.productID = ""; | ||||||
|  |     this.sscc = ""; | ||||||
|  |     this.lotNumber = ""; | ||||||
|  |     this.productionDate = ""; | ||||||
|  |     this.packagingDate = ""; | ||||||
|  |     this.bestBeforeDate = ""; | ||||||
|  |     this.expirationDate = ""; | ||||||
|  |     this.weight = ""; | ||||||
|  |     this.weightType = ""; | ||||||
|  |     this.weightIncrement = ""; | ||||||
|  |     this.price = ""; | ||||||
|  |     this.priceIncrement = ""; | ||||||
|  |     this.priceCurrency = ""; | ||||||
|  |     this.uncommonAIs = new Hashtable(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public ExpandedProductParsedResult(String productID, String sscc, | ||||||
|  |       String lotNumber, String productionDate, String packagingDate, | ||||||
|  |       String bestBeforeDate, String expirationDate, String weight, | ||||||
|  |       String weightType, String weightIncrement, String price, | ||||||
|  |       String priceIncrement, String priceCurrency, Hashtable uncommonAIs) { | ||||||
|  |     super(ParsedResultType.PRODUCT); | ||||||
|  |     this.productID = productID; | ||||||
|  |     this.sscc = sscc; | ||||||
|  |     this.lotNumber = lotNumber; | ||||||
|  |     this.productionDate = productionDate; | ||||||
|  |     this.packagingDate = packagingDate; | ||||||
|  |     this.bestBeforeDate = bestBeforeDate; | ||||||
|  |     this.expirationDate = expirationDate; | ||||||
|  |     this.weight = weight; | ||||||
|  |     this.weightType = weightType; | ||||||
|  |     this.weightIncrement = weightIncrement; | ||||||
|  |     this.price = price; | ||||||
|  |     this.priceIncrement = priceIncrement; | ||||||
|  |     this.priceCurrency = priceCurrency; | ||||||
|  |     this.uncommonAIs = uncommonAIs; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public boolean equals(Object o){ | ||||||
|  |     if (!(o instanceof ExpandedProductParsedResult)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ExpandedProductParsedResult other = (ExpandedProductParsedResult)o; | ||||||
|  | 
 | ||||||
|  |     return this.productID.equals(       other.productID) | ||||||
|  |       && this.sscc.equals(            other.sscc) | ||||||
|  |       && this.lotNumber.equals(       other.lotNumber) | ||||||
|  |       && this.productionDate.equals(  other.productionDate) | ||||||
|  |       && this.bestBeforeDate.equals(  other.bestBeforeDate) | ||||||
|  |       && this.expirationDate.equals(  other.expirationDate) | ||||||
|  |       && this.weight.equals(          other.weight) | ||||||
|  |       && this.weightType.equals(      other.weightType) | ||||||
|  |       && this.weightIncrement.equals( other.weightIncrement) | ||||||
|  |       && this.price.equals(           other.price) | ||||||
|  |       && this.priceIncrement.equals(  other.priceIncrement) | ||||||
|  |       && this.priceCurrency.equals(   other.priceCurrency) | ||||||
|  |       && this.uncommonAIs.equals(     other.uncommonAIs); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int hashCode(){ | ||||||
|  |     int hash1 = this.productID.hashCode(); | ||||||
|  |     hash1 = 31 * hash1 + this.sscc.hashCode(); | ||||||
|  |     hash1 = 31 * hash1 + this.lotNumber.hashCode(); | ||||||
|  |     hash1 = 31 * hash1 + this.productionDate.hashCode(); | ||||||
|  |     hash1 = 31 * hash1 + this.bestBeforeDate.hashCode(); | ||||||
|  |     hash1 = 31 * hash1 + this.expirationDate.hashCode(); | ||||||
|  |     hash1 = 31 * hash1 + this.weight.hashCode(); | ||||||
|  | 
 | ||||||
|  |     int hash2 = this.weightType.hashCode(); | ||||||
|  |     hash2 = 31 * hash2 + this.weightIncrement.hashCode(); | ||||||
|  |     hash2 = 31 * hash2 + this.price.hashCode(); | ||||||
|  |     hash2 = 31 * hash2 + this.priceIncrement.hashCode(); | ||||||
|  |     hash2 = 31 * hash2 + this.priceCurrency.hashCode(); | ||||||
|  |     hash2 = 31 * hash2 + this.uncommonAIs.hashCode(); | ||||||
|  |     return hash1 ^ hash2; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getProductID() { | ||||||
|  |     return productID; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getSscc() { | ||||||
|  |     return sscc; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getLotNumber() { | ||||||
|  |     return lotNumber; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getProductionDate() { | ||||||
|  |     return productionDate; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getPackagingDate() { | ||||||
|  |     return packagingDate; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getBestBeforeDate() { | ||||||
|  |     return bestBeforeDate; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getExpirationDate() { | ||||||
|  |     return expirationDate; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getWeight() { | ||||||
|  |     return weight; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getWeightType() { | ||||||
|  |     return weightType; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getWeightIncrement() { | ||||||
|  |     return weightIncrement; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getPrice() { | ||||||
|  |     return price; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getPriceIncrement() { | ||||||
|  |     return priceIncrement; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getPriceCurrency() { | ||||||
|  |     return priceCurrency; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Hashtable getUncommonAIs() { | ||||||
|  |     return uncommonAIs; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     return productID; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,199 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * These authors would like to acknowledge the Spanish Ministry of Industry, | ||||||
|  |  * Tourism and Trade, for the support in the project TSI020301-2008-2 | ||||||
|  |  * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled | ||||||
|  |  * Mobile Dynamic Environments", led by Treelogic | ||||||
|  |  * ( http://www.treelogic.com/ ): | ||||||
|  |  * | ||||||
|  |  *   http://www.piramidepse.com/ | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BarcodeFormat; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses strings of digits that represent a RSS Extended code. | ||||||
|  |  *  | ||||||
|  |  * @author Antonio Manuel Benjumea Conde, Servinform, S.A. | ||||||
|  |  * @author Agustín Delgado, Servinform, S.A. | ||||||
|  |  */ | ||||||
|  | final class ExpandedProductResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private ExpandedProductResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Treat all RSS EXPANDED, in the sense that they are all | ||||||
|  |   // product barcodes with complementary data. | ||||||
|  |   public static ExpandedProductParsedResult parse(Result result) { | ||||||
|  |     BarcodeFormat format = result.getBarcodeFormat(); | ||||||
|  |     if (!(BarcodeFormat.RSS_EXPANDED.equals(format))) { | ||||||
|  |       // ExtendedProductParsedResult NOT created. Not a RSS Expanded barcode | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     // Really neither of these should happen: | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       // ExtendedProductParsedResult NOT created. Input text is NULL | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     String productID = "-"; | ||||||
|  |     String sscc = "-"; | ||||||
|  |     String lotNumber = "-"; | ||||||
|  |     String productionDate = "-"; | ||||||
|  |     String packagingDate = "-"; | ||||||
|  |     String bestBeforeDate = "-"; | ||||||
|  |     String expirationDate = "-"; | ||||||
|  |     String weight = "-"; | ||||||
|  |     String weightType = "-"; | ||||||
|  |     String weightIncrement = "-"; | ||||||
|  |     String price = "-"; | ||||||
|  |     String priceIncrement = "-"; | ||||||
|  |     String priceCurrency = "-"; | ||||||
|  |     Hashtable uncommonAIs = new Hashtable(); | ||||||
|  | 
 | ||||||
|  |     int i = 0; | ||||||
|  | 
 | ||||||
|  |     while (i < rawText.length()) { | ||||||
|  |       String ai = findAIvalue(i, rawText); | ||||||
|  |       if ("ERROR".equals(ai)) { | ||||||
|  |         // Error. Code doesn't match with RSS expanded pattern | ||||||
|  |         // ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |       i += ai.length() + 2; | ||||||
|  |       String value = findValue(i, rawText); | ||||||
|  |       i += value.length(); | ||||||
|  | 
 | ||||||
|  |       if ("00".equals(ai)) { | ||||||
|  |         sscc = value; | ||||||
|  |       } else if ("01".equals(ai)) { | ||||||
|  |         productID = value; | ||||||
|  |       } else if ("10".equals(ai)) { | ||||||
|  |         lotNumber = value; | ||||||
|  |       } else if ("11".equals(ai)) { | ||||||
|  |         productionDate = value; | ||||||
|  |       } else if ("13".equals(ai)) { | ||||||
|  |         packagingDate = value; | ||||||
|  |       } else if ("15".equals(ai)) { | ||||||
|  |         bestBeforeDate = value; | ||||||
|  |       } else if ("17".equals(ai)) { | ||||||
|  |         expirationDate = value; | ||||||
|  |       } else if ("3100".equals(ai) || "3101".equals(ai) | ||||||
|  |           || "3102".equals(ai) || "3103".equals(ai) | ||||||
|  |           || "3104".equals(ai) || "3105".equals(ai) | ||||||
|  |           || "3106".equals(ai) || "3107".equals(ai) | ||||||
|  |           || "3108".equals(ai) || "3109".equals(ai)) { | ||||||
|  |         weight = value; | ||||||
|  |         weightType = ExpandedProductParsedResult.KILOGRAM; | ||||||
|  |         weightIncrement = ai.substring(3); | ||||||
|  |       } else if ("3200".equals(ai) || "3201".equals(ai) | ||||||
|  |           || "3202".equals(ai) || "3203".equals(ai) | ||||||
|  |           || "3204".equals(ai) || "3205".equals(ai) | ||||||
|  |           || "3206".equals(ai) || "3207".equals(ai) | ||||||
|  |           || "3208".equals(ai) || "3209".equals(ai)) { | ||||||
|  |         weight = value; | ||||||
|  |         weightType = ExpandedProductParsedResult.POUND; | ||||||
|  |         weightIncrement = ai.substring(3); | ||||||
|  |       } else if ("3920".equals(ai) || "3921".equals(ai) | ||||||
|  |           || "3922".equals(ai) || "3923".equals(ai)) { | ||||||
|  |         price = value; | ||||||
|  |         priceIncrement = ai.substring(3); | ||||||
|  |       } else if ("3930".equals(ai) || "3931".equals(ai) | ||||||
|  |           || "3932".equals(ai) || "3933".equals(ai)) { | ||||||
|  |         if (value.length() < 4) { | ||||||
|  |           // The value must have more of 3 symbols (3 for currency and | ||||||
|  |           // 1 at least for the price) | ||||||
|  |           // ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern | ||||||
|  |           return null; | ||||||
|  |         } | ||||||
|  |         price = value.substring(3); | ||||||
|  |         priceCurrency = value.substring(0, 3); | ||||||
|  |         priceIncrement = ai.substring(3); | ||||||
|  |       } else { | ||||||
|  |         // No match with common AIs | ||||||
|  |         uncommonAIs.put(ai, value); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new ExpandedProductParsedResult(productID, sscc, lotNumber, | ||||||
|  |         productionDate, packagingDate, bestBeforeDate, expirationDate, | ||||||
|  |         weight, weightType, weightIncrement, price, priceIncrement, | ||||||
|  |         priceCurrency, uncommonAIs); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String findAIvalue(int i, String rawText) { | ||||||
|  |     StringBuffer buf = new StringBuffer(); | ||||||
|  |     char c = rawText.charAt(i); | ||||||
|  |     // First character must be a open parenthesis.If not, ERROR | ||||||
|  |     if (c != '(') { | ||||||
|  |       return "ERROR"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     String rawTextAux = rawText.substring(i + 1); | ||||||
|  | 
 | ||||||
|  |     for (int index = 0; index < rawTextAux.length(); index++) { | ||||||
|  |       char currentChar = rawTextAux.charAt(index); | ||||||
|  |       switch (currentChar){ | ||||||
|  |         case '0': | ||||||
|  |         case '1': | ||||||
|  |         case '2': | ||||||
|  |         case '3': | ||||||
|  |         case '4': | ||||||
|  |         case '5': | ||||||
|  |         case '6': | ||||||
|  |         case '7': | ||||||
|  |         case '8': | ||||||
|  |         case '9': | ||||||
|  |           buf.append(currentChar); | ||||||
|  |           break; | ||||||
|  |         case ')': | ||||||
|  |           return buf.toString(); | ||||||
|  |         default: | ||||||
|  |           return "ERROR"; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return buf.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String findValue(int i, String rawText) { | ||||||
|  |     StringBuffer buf = new StringBuffer(); | ||||||
|  |     String rawTextAux = rawText.substring(i); | ||||||
|  | 
 | ||||||
|  |     for (int index = 0; index < rawTextAux.length(); index++) { | ||||||
|  |       char c = rawTextAux.charAt(index); | ||||||
|  |       if (c == '(') { | ||||||
|  |         // We look for a new AI. If it doesn't exist (ERROR), we coninue | ||||||
|  |         // with the iteration | ||||||
|  |         if ("ERROR".equals(findAIvalue(index, rawTextAux))) { | ||||||
|  |           buf.append('('); | ||||||
|  |         } else { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         buf.append(c); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return buf.toString(); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										132
									
								
								src/com/google/zxing/client/result/GeoParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/com/google/zxing/client/result/GeoParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class GeoParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final double latitude; | ||||||
|  |   private final double longitude; | ||||||
|  |   private final double altitude; | ||||||
|  |   private final String query; | ||||||
|  | 
 | ||||||
|  |   GeoParsedResult(double latitude, double longitude, double altitude, String query) { | ||||||
|  |     super(ParsedResultType.GEO); | ||||||
|  |     this.latitude = latitude; | ||||||
|  |     this.longitude = longitude; | ||||||
|  |     this.altitude = altitude; | ||||||
|  |     this.query = query; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getGeoURI() { | ||||||
|  |     StringBuffer result = new StringBuffer(); | ||||||
|  |     result.append("geo:"); | ||||||
|  |     result.append(latitude); | ||||||
|  |     result.append(','); | ||||||
|  |     result.append(longitude); | ||||||
|  |     if (altitude > 0) { | ||||||
|  |       result.append(','); | ||||||
|  |       result.append(altitude); | ||||||
|  |     } | ||||||
|  |     if (query != null) { | ||||||
|  |       result.append('?'); | ||||||
|  |       result.append(query); | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return latitude in degrees | ||||||
|  |    */ | ||||||
|  |   public double getLatitude() { | ||||||
|  |     return latitude; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return longitude in degrees | ||||||
|  |    */ | ||||||
|  |   public double getLongitude() { | ||||||
|  |     return longitude; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return altitude in meters. If not specified, in the geo URI, returns 0.0 | ||||||
|  |    */ | ||||||
|  |   public double getAltitude() { | ||||||
|  |     return altitude; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return query string associated with geo URI or null if none exists | ||||||
|  |    */ | ||||||
|  |   public String getQuery() { | ||||||
|  |     return query; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(20); | ||||||
|  |     result.append(latitude); | ||||||
|  |     result.append(", "); | ||||||
|  |     result.append(longitude); | ||||||
|  |     if (altitude > 0.0) { | ||||||
|  |       result.append(", "); | ||||||
|  |       result.append(altitude); | ||||||
|  |       result.append('m'); | ||||||
|  |     } | ||||||
|  |     if (query != null) { | ||||||
|  |       result.append(" ("); | ||||||
|  |       result.append(query); | ||||||
|  |       result.append(')'); | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return a URI link to Google Maps which display the point on the Earth described | ||||||
|  |    *  by this instance, and sets the zoom level in a way that roughly reflects the | ||||||
|  |    *  altitude, if specified | ||||||
|  |    */ | ||||||
|  |   /* | ||||||
|  |   public String getGoogleMapsURI() { | ||||||
|  |     StringBuffer result = new StringBuffer(50); | ||||||
|  |     result.append("http://maps.google.com/?ll="); | ||||||
|  |     result.append(latitude); | ||||||
|  |     result.append(','); | ||||||
|  |     result.append(longitude); | ||||||
|  |     if (altitude > 0.0f) { | ||||||
|  |       // Map altitude to zoom level, cleverly. Roughly, zoom level 19 is like a | ||||||
|  |       // view from 1000ft, 18 is like 2000ft, 17 like 4000ft, and so on. | ||||||
|  |       double altitudeInFeet = altitude * 3.28; | ||||||
|  |       int altitudeInKFeet = (int) (altitudeInFeet / 1000.0); | ||||||
|  |       // No Math.log() available here, so compute log base 2 the old fashioned way | ||||||
|  |       // Here logBaseTwo will take on a value between 0 and 18 actually | ||||||
|  |       int logBaseTwo = 0; | ||||||
|  |       while (altitudeInKFeet > 1 && logBaseTwo < 18) { | ||||||
|  |         altitudeInKFeet >>= 1; | ||||||
|  |         logBaseTwo++; | ||||||
|  |       } | ||||||
|  |       int zoom = 19 - logBaseTwo; | ||||||
|  |       result.append("&z="); | ||||||
|  |       result.append(zoom); | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  |    */ | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								src/com/google/zxing/client/result/GeoResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/com/google/zxing/client/result/GeoResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses a "geo:" URI result, which specifies a location on the surface of | ||||||
|  |  * the Earth as well as an optional altitude above the surface. See | ||||||
|  |  * <a href="http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00"> | ||||||
|  |  * http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00</a>. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class GeoResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private GeoResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static GeoParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || (!rawText.startsWith("geo:") && !rawText.startsWith("GEO:"))) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     // Drop geo, query portion | ||||||
|  |     int queryStart = rawText.indexOf('?', 4); | ||||||
|  |     String query; | ||||||
|  |     String geoURIWithoutQuery; | ||||||
|  |     if (queryStart < 0) { | ||||||
|  |       query = null; | ||||||
|  |       geoURIWithoutQuery = rawText.substring(4); | ||||||
|  |     } else { | ||||||
|  |       query = rawText.substring(queryStart + 1); | ||||||
|  |       geoURIWithoutQuery = rawText.substring(4, queryStart); | ||||||
|  |     } | ||||||
|  |     int latitudeEnd = geoURIWithoutQuery.indexOf(','); | ||||||
|  |     if (latitudeEnd < 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int longitudeEnd = geoURIWithoutQuery.indexOf(',', latitudeEnd + 1);     | ||||||
|  |     double latitude, longitude, altitude; | ||||||
|  |     try { | ||||||
|  |       latitude = Double.parseDouble(geoURIWithoutQuery.substring(0, latitudeEnd)); | ||||||
|  |       if (latitude > 90.0 || latitude < -90.0) { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |       if (longitudeEnd < 0) { | ||||||
|  |         longitude = Double.parseDouble(geoURIWithoutQuery.substring(latitudeEnd + 1)); | ||||||
|  |         altitude = 0.0; | ||||||
|  |       } else { | ||||||
|  |         longitude = Double.parseDouble(geoURIWithoutQuery.substring(latitudeEnd + 1, longitudeEnd)); | ||||||
|  |         altitude = Double.parseDouble(geoURIWithoutQuery.substring(longitudeEnd + 1)); | ||||||
|  |       } | ||||||
|  |       if (longitude > 180.0 || longitude < -180.0 || altitude < 0) { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |     } catch (NumberFormatException nfe) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     return new GeoParsedResult(latitude, longitude, altitude, query); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/com/google/zxing/client/result/ISBNParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/com/google/zxing/client/result/ISBNParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author jbreiden@google.com (Jeff Breidenbach) | ||||||
|  |  */ | ||||||
|  | public final class ISBNParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String isbn; | ||||||
|  | 
 | ||||||
|  |   ISBNParsedResult(String isbn) { | ||||||
|  |     super(ParsedResultType.ISBN); | ||||||
|  |     this.isbn = isbn; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getISBN() { | ||||||
|  |     return isbn; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     return isbn; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/com/google/zxing/client/result/ISBNResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/com/google/zxing/client/result/ISBNResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BarcodeFormat; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses strings of digits that represent a ISBN. | ||||||
|  |  *  | ||||||
|  |  * @author jbreiden@google.com (Jeff Breidenbach) | ||||||
|  |  */ | ||||||
|  | public class ISBNResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private ISBNResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // ISBN-13 For Dummies  | ||||||
|  |   // http://www.bisg.org/isbn-13/for.dummies.html | ||||||
|  |   public static ISBNParsedResult parse(Result result) { | ||||||
|  |     BarcodeFormat format = result.getBarcodeFormat(); | ||||||
|  |     if (!BarcodeFormat.EAN_13.equals(format)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int length = rawText.length(); | ||||||
|  |     if (length != 13) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     if (!rawText.startsWith("978") && !rawText.startsWith("979")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     | ||||||
|  |     return new ISBNParsedResult(rawText); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								src/com/google/zxing/client/result/ParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/com/google/zxing/client/result/ParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Abstract class representing the result of decoding a barcode, as more than | ||||||
|  |  * a String -- as some type of structured data. This might be a subclass which represents | ||||||
|  |  * a URL, or an e-mail address. {@link ResultParser#parseResult(Result)} will turn a raw | ||||||
|  |  * decoded string into the most appropriate type of structured representation.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less | ||||||
|  |  * on exception-based mechanisms during parsing.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public abstract class ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final ParsedResultType type; | ||||||
|  | 
 | ||||||
|  |   protected ParsedResult(ParsedResultType type) { | ||||||
|  |     this.type = type; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public ParsedResultType getType() { | ||||||
|  |     return type; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public abstract String getDisplayResult(); | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     return getDisplayResult(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static void maybeAppend(String value, StringBuffer result) { | ||||||
|  |     if (value != null && value.length() > 0) { | ||||||
|  |       // Don't add a newline before the first value | ||||||
|  |       if (result.length() > 0) { | ||||||
|  |         result.append('\n'); | ||||||
|  |       } | ||||||
|  |       result.append(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static void maybeAppend(String[] value, StringBuffer result) { | ||||||
|  |     if (value != null) { | ||||||
|  |       for (int i = 0; i < value.length; i++) { | ||||||
|  |         if (value[i] != null && value[i].length() > 0) { | ||||||
|  |           if (result.length() > 0) { | ||||||
|  |             result.append('\n'); | ||||||
|  |           } | ||||||
|  |           result.append(value[i]); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								src/com/google/zxing/client/result/ParsedResultType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/com/google/zxing/client/result/ParsedResultType.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Represents the type of data encoded by a barcode -- from plain text, to a | ||||||
|  |  * URI, to an e-mail address, etc. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class ParsedResultType { | ||||||
|  | 
 | ||||||
|  |   public static final ParsedResultType ADDRESSBOOK = new ParsedResultType("ADDRESSBOOK"); | ||||||
|  |   public static final ParsedResultType EMAIL_ADDRESS = new ParsedResultType("EMAIL_ADDRESS"); | ||||||
|  |   public static final ParsedResultType PRODUCT = new ParsedResultType("PRODUCT"); | ||||||
|  |   public static final ParsedResultType URI = new ParsedResultType("URI"); | ||||||
|  |   public static final ParsedResultType TEXT = new ParsedResultType("TEXT"); | ||||||
|  |   public static final ParsedResultType ANDROID_INTENT = new ParsedResultType("ANDROID_INTENT"); | ||||||
|  |   public static final ParsedResultType GEO = new ParsedResultType("GEO"); | ||||||
|  |   public static final ParsedResultType TEL = new ParsedResultType("TEL"); | ||||||
|  |   public static final ParsedResultType SMS = new ParsedResultType("SMS"); | ||||||
|  |   public static final ParsedResultType CALENDAR = new ParsedResultType("CALENDAR"); | ||||||
|  |   public static final ParsedResultType WIFI = new ParsedResultType("WIFI"); | ||||||
|  |   // "optional" types | ||||||
|  |   public static final ParsedResultType NDEF_SMART_POSTER = new ParsedResultType("NDEF_SMART_POSTER"); | ||||||
|  |   public static final ParsedResultType MOBILETAG_RICH_WEB = new ParsedResultType("MOBILETAG_RICH_WEB"); | ||||||
|  |   public static final ParsedResultType ISBN = new ParsedResultType("ISBN"); | ||||||
|  | 
 | ||||||
|  |   private final String name; | ||||||
|  | 
 | ||||||
|  |   private ParsedResultType(String name) { | ||||||
|  |     this.name = name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								src/com/google/zxing/client/result/ProductParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/com/google/zxing/client/result/ProductParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class ProductParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String productID; | ||||||
|  |   private final String normalizedProductID; | ||||||
|  | 
 | ||||||
|  |   ProductParsedResult(String productID) { | ||||||
|  |     this(productID, productID); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ProductParsedResult(String productID, String normalizedProductID) { | ||||||
|  |     super(ParsedResultType.PRODUCT); | ||||||
|  |     this.productID = productID; | ||||||
|  |     this.normalizedProductID = normalizedProductID; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getProductID() { | ||||||
|  |     return productID; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getNormalizedProductID() { | ||||||
|  |     return normalizedProductID; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     return productID; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								src/com/google/zxing/client/result/ProductResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/com/google/zxing/client/result/ProductResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BarcodeFormat; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.oned.UPCEReader; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses strings of digits that represent a UPC code. | ||||||
|  |  *  | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | final class ProductResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private ProductResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes. | ||||||
|  |   public static ProductParsedResult parse(Result result) { | ||||||
|  |     BarcodeFormat format = result.getBarcodeFormat(); | ||||||
|  |     if (!(BarcodeFormat.UPC_A.equals(format) || BarcodeFormat.UPC_E.equals(format) || | ||||||
|  |           BarcodeFormat.EAN_8.equals(format) || BarcodeFormat.EAN_13.equals(format))) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     // Really neither of these should happen: | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int length = rawText.length(); | ||||||
|  |     for (int x = 0; x < length; x++) { | ||||||
|  |       char c = rawText.charAt(x); | ||||||
|  |       if (c < '0' || c > '9') { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     // Not actually checking the checksum again here     | ||||||
|  | 
 | ||||||
|  |     String normalizedProductID; | ||||||
|  |     // Expand UPC-E for purposes of searching | ||||||
|  |     if (BarcodeFormat.UPC_E.equals(format)) { | ||||||
|  |       normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText); | ||||||
|  |     } else { | ||||||
|  |       normalizedProductID = rawText; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new ProductParsedResult(rawText, normalizedProductID); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										319
									
								
								src/com/google/zxing/client/result/ResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								src/com/google/zxing/client/result/ResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,319 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Abstract class representing the result of decoding a barcode, as more than | ||||||
|  |  * a String -- as some type of structured data. This might be a subclass which represents | ||||||
|  |  * a URL, or an e-mail address. {@link #parseResult(com.google.zxing.Result)} will turn a raw | ||||||
|  |  * decoded string into the most appropriate type of structured representation.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less | ||||||
|  |  * on exception-based mechanisms during parsing.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public abstract class ResultParser { | ||||||
|  | 
 | ||||||
|  |   public static ParsedResult parseResult(Result theResult) { | ||||||
|  |     // This is a bit messy, but given limited options in MIDP / CLDC, this may well be the simplest | ||||||
|  |     // way to go about this. For example, we have no reflection available, really. | ||||||
|  |     // Order is important here. | ||||||
|  |     ParsedResult result; | ||||||
|  |     if ((result = BookmarkDoCoMoResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = AddressBookDoCoMoResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = EmailDoCoMoResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = AddressBookAUResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = VCardResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = BizcardResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = VEventResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = EmailAddressResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = TelResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = SMSMMSResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = SMSTOMMSTOResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = GeoResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = WifiResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = URLTOResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = URIResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = ISBNResultParser.parse(theResult)) != null) { | ||||||
|  |       // We depend on ISBN parsing coming before UPC, as it is a subset. | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = ProductResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } else if ((result = ExpandedProductResultParser.parse(theResult)) != null) { | ||||||
|  |       return result; | ||||||
|  |     } | ||||||
|  |     return new TextParsedResult(theResult.getText(), null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected static void maybeAppend(String value, StringBuffer result) { | ||||||
|  |     if (value != null) { | ||||||
|  |       result.append('\n'); | ||||||
|  |       result.append(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected static void maybeAppend(String[] value, StringBuffer result) { | ||||||
|  |     if (value != null) { | ||||||
|  |       for (int i = 0; i < value.length; i++) { | ||||||
|  |         result.append('\n'); | ||||||
|  |         result.append(value[i]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected static String[] maybeWrap(String value) { | ||||||
|  |     return value == null ? null : new String[] { value }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected static String unescapeBackslash(String escaped) { | ||||||
|  |     if (escaped != null) { | ||||||
|  |       int backslash = escaped.indexOf((int) '\\'); | ||||||
|  |       if (backslash >= 0) { | ||||||
|  |         int max = escaped.length(); | ||||||
|  |         StringBuffer unescaped = new StringBuffer(max - 1); | ||||||
|  |         unescaped.append(escaped.toCharArray(), 0, backslash); | ||||||
|  |         boolean nextIsEscaped = false; | ||||||
|  |         for (int i = backslash; i < max; i++) { | ||||||
|  |           char c = escaped.charAt(i); | ||||||
|  |           if (nextIsEscaped || c != '\\') { | ||||||
|  |             unescaped.append(c); | ||||||
|  |             nextIsEscaped = false; | ||||||
|  |           } else { | ||||||
|  |             nextIsEscaped = true; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         return unescaped.toString(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return escaped; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String urlDecode(String escaped) { | ||||||
|  | 
 | ||||||
|  |     // No we can't use java.net.URLDecoder here. JavaME doesn't have it. | ||||||
|  |     if (escaped == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     char[] escapedArray = escaped.toCharArray(); | ||||||
|  | 
 | ||||||
|  |     int first = findFirstEscape(escapedArray); | ||||||
|  |     if (first < 0) { | ||||||
|  |       return escaped; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int max = escapedArray.length; | ||||||
|  |     // final length is at most 2 less than original due to at least 1 unescaping | ||||||
|  |     StringBuffer unescaped = new StringBuffer(max - 2); | ||||||
|  |     // Can append everything up to first escape character | ||||||
|  |     unescaped.append(escapedArray, 0, first); | ||||||
|  | 
 | ||||||
|  |     for (int i = first; i < max; i++) { | ||||||
|  |       char c = escapedArray[i]; | ||||||
|  |       if (c == '+') { | ||||||
|  |         // + is translated directly into a space | ||||||
|  |         unescaped.append(' '); | ||||||
|  |       } else if (c == '%') { | ||||||
|  |         // Are there even two more chars? if not we will just copy the escaped sequence and be done | ||||||
|  |         if (i >= max - 2) { | ||||||
|  |           unescaped.append('%'); // append that % and move on | ||||||
|  |         } else { | ||||||
|  |           int firstDigitValue = parseHexDigit(escapedArray[++i]); | ||||||
|  |           int secondDigitValue = parseHexDigit(escapedArray[++i]); | ||||||
|  |           if (firstDigitValue < 0 || secondDigitValue < 0) { | ||||||
|  |             // bad digit, just move on | ||||||
|  |             unescaped.append('%'); | ||||||
|  |             unescaped.append(escapedArray[i-1]); | ||||||
|  |             unescaped.append(escapedArray[i]); | ||||||
|  |           } | ||||||
|  |           unescaped.append((char) ((firstDigitValue << 4) + secondDigitValue)); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         unescaped.append(c); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return unescaped.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static int findFirstEscape(char[] escapedArray) { | ||||||
|  |     int max = escapedArray.length; | ||||||
|  |     for (int i = 0; i < max; i++) { | ||||||
|  |       char c = escapedArray[i]; | ||||||
|  |       if (c == '+' || c == '%') { | ||||||
|  |         return i; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static int parseHexDigit(char c) { | ||||||
|  |     if (c >= 'a') { | ||||||
|  |       if (c <= 'f') { | ||||||
|  |         return 10 + (c - 'a'); | ||||||
|  |       } | ||||||
|  |     } else if (c >= 'A') { | ||||||
|  |       if (c <= 'F') { | ||||||
|  |         return 10 + (c - 'A'); | ||||||
|  |       } | ||||||
|  |     } else if (c >= '0') { | ||||||
|  |       if (c <= '9') { | ||||||
|  |         return c - '0'; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected static boolean isStringOfDigits(String value, int length) { | ||||||
|  |     if (value == null) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     int stringLength = value.length(); | ||||||
|  |     if (length != stringLength) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     for (int i = 0; i < length; i++) { | ||||||
|  |       char c = value.charAt(i); | ||||||
|  |       if (c < '0' || c > '9') { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   protected static boolean isSubstringOfDigits(String value, int offset, int length) { | ||||||
|  |     if (value == null) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     int stringLength = value.length(); | ||||||
|  |     int max = offset + length; | ||||||
|  |     if (stringLength < max) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     for (int i = offset; i < max; i++) { | ||||||
|  |       char c = value.charAt(i); | ||||||
|  |       if (c < '0' || c > '9') { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Hashtable parseNameValuePairs(String uri) { | ||||||
|  |     int paramStart = uri.indexOf('?'); | ||||||
|  |     if (paramStart < 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     Hashtable result = new Hashtable(3); | ||||||
|  |     paramStart++; | ||||||
|  |     int paramEnd; | ||||||
|  |     while ((paramEnd = uri.indexOf('&', paramStart)) >= 0) { | ||||||
|  |       appendKeyValue(uri, paramStart, paramEnd, result); | ||||||
|  |       paramStart = paramEnd + 1; | ||||||
|  |     } | ||||||
|  |     appendKeyValue(uri, paramStart, uri.length(), result); | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void appendKeyValue(String uri, int paramStart, int paramEnd, Hashtable result) { | ||||||
|  |     int separator = uri.indexOf('=', paramStart); | ||||||
|  |     if (separator >= 0) { | ||||||
|  |       // key = value | ||||||
|  |       String key = uri.substring(paramStart, separator); | ||||||
|  |       String value = uri.substring(separator + 1, paramEnd); | ||||||
|  |       value = urlDecode(value); | ||||||
|  |       result.put(key, value); | ||||||
|  |     } | ||||||
|  |     // Can't put key, null into a hashtable | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String[] matchPrefixedField(String prefix, String rawText, char endChar, boolean trim) { | ||||||
|  |     Vector matches = null; | ||||||
|  |     int i = 0; | ||||||
|  |     int max = rawText.length(); | ||||||
|  |     while (i < max) { | ||||||
|  |       i = rawText.indexOf(prefix, i); | ||||||
|  |       if (i < 0) { | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       i += prefix.length(); // Skip past this prefix we found to start | ||||||
|  |       int start = i; // Found the start of a match here | ||||||
|  |       boolean done = false; | ||||||
|  |       while (!done) { | ||||||
|  |         i = rawText.indexOf((int) endChar, i); | ||||||
|  |         if (i < 0) { | ||||||
|  |           // No terminating end character? uh, done. Set i such that loop terminates and break | ||||||
|  |           i = rawText.length(); | ||||||
|  |           done = true; | ||||||
|  |         } else if (rawText.charAt(i - 1) == '\\') { | ||||||
|  |           // semicolon was escaped so continue | ||||||
|  |           i++; | ||||||
|  |         } else { | ||||||
|  |           // found a match | ||||||
|  |           if (matches == null) { | ||||||
|  |             matches = new Vector(3); // lazy init | ||||||
|  |           } | ||||||
|  |           String element = unescapeBackslash(rawText.substring(start, i)); | ||||||
|  |           if (trim) { | ||||||
|  |             element = element.trim(); | ||||||
|  |           } | ||||||
|  |           matches.addElement(element); | ||||||
|  |           i++; | ||||||
|  |           done = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (matches == null || matches.isEmpty()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     return toStringArray(matches); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String matchSinglePrefixedField(String prefix, String rawText, char endChar, boolean trim) { | ||||||
|  |     String[] matches = matchPrefixedField(prefix, rawText, endChar, trim); | ||||||
|  |     return matches == null ? null : matches[0]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String[] toStringArray(Vector strings) { | ||||||
|  |     int size = strings.size(); | ||||||
|  |     String[] result = new String[size]; | ||||||
|  |     for (int j = 0; j < size; j++) { | ||||||
|  |       result[j] = (String) strings.elementAt(j); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										107
									
								
								src/com/google/zxing/client/result/SMSMMSResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/com/google/zxing/client/result/SMSMMSResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Parses an "sms:" URI result, which specifies a number to SMS. | ||||||
|  |  * See <a href="http://tools.ietf.org/html/rfc5724"> RFC 5724</a> on this.</p> | ||||||
|  |  * | ||||||
|  |  * <p>This class supports "via" syntax for numbers, which is not part of the spec. | ||||||
|  |  * For example "+12125551212;via=+12124440101" may appear as a number. | ||||||
|  |  * It also supports a "subject" query parameter, which is not mentioned in the spec. | ||||||
|  |  * These are included since they were mentioned in earlier IETF drafts and might be | ||||||
|  |  * used.</p> | ||||||
|  |  * | ||||||
|  |  * <p>This actually also parses URIs starting with "mms:" and treats them all the same way, | ||||||
|  |  * and effectively converts them to an "sms:" URI for purposes of forwarding to the platform.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class SMSMMSResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private SMSMMSResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static SMSParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     if (!(rawText.startsWith("sms:") || rawText.startsWith("SMS:") || | ||||||
|  |           rawText.startsWith("mms:") || rawText.startsWith("MMS:"))) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Check up front if this is a URI syntax string with query arguments | ||||||
|  |     Hashtable nameValuePairs = parseNameValuePairs(rawText); | ||||||
|  |     String subject = null; | ||||||
|  |     String body = null; | ||||||
|  |     boolean querySyntax = false; | ||||||
|  |     if (nameValuePairs != null && !nameValuePairs.isEmpty()) { | ||||||
|  |       subject = (String) nameValuePairs.get("subject"); | ||||||
|  |       body = (String) nameValuePairs.get("body"); | ||||||
|  |       querySyntax = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Drop sms, query portion | ||||||
|  |     int queryStart = rawText.indexOf('?', 4); | ||||||
|  |     String smsURIWithoutQuery; | ||||||
|  |     // If it's not query syntax, the question mark is part of the subject or message | ||||||
|  |     if (queryStart < 0 || !querySyntax) { | ||||||
|  |       smsURIWithoutQuery = rawText.substring(4); | ||||||
|  |     } else { | ||||||
|  |       smsURIWithoutQuery = rawText.substring(4, queryStart); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int lastComma = -1; | ||||||
|  |     int comma; | ||||||
|  |     Vector numbers = new Vector(1); | ||||||
|  |     Vector vias = new Vector(1); | ||||||
|  |     while ((comma = smsURIWithoutQuery.indexOf(',', lastComma + 1)) > lastComma) { | ||||||
|  |       String numberPart = smsURIWithoutQuery.substring(lastComma + 1, comma); | ||||||
|  |       addNumberVia(numbers, vias, numberPart); | ||||||
|  |       lastComma = comma; | ||||||
|  |     } | ||||||
|  |     addNumberVia(numbers, vias, smsURIWithoutQuery.substring(lastComma + 1));     | ||||||
|  | 
 | ||||||
|  |     return new SMSParsedResult(toStringArray(numbers), toStringArray(vias), subject, body); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void addNumberVia(Vector numbers, Vector vias, String numberPart) { | ||||||
|  |     int numberEnd = numberPart.indexOf(';'); | ||||||
|  |     if (numberEnd < 0) { | ||||||
|  |       numbers.addElement(numberPart); | ||||||
|  |       vias.addElement(null); | ||||||
|  |     } else { | ||||||
|  |       numbers.addElement(numberPart.substring(0, numberEnd)); | ||||||
|  |       String maybeVia = numberPart.substring(numberEnd + 1); | ||||||
|  |       String via; | ||||||
|  |       if (maybeVia.startsWith("via=")) { | ||||||
|  |         via = maybeVia.substring(4); | ||||||
|  |       } else { | ||||||
|  |         via = null; | ||||||
|  |       } | ||||||
|  |       vias.addElement(via); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								src/com/google/zxing/client/result/SMSParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/com/google/zxing/client/result/SMSParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class SMSParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String[] numbers; | ||||||
|  |   private final String[] vias; | ||||||
|  |   private final String subject; | ||||||
|  |   private final String body; | ||||||
|  | 
 | ||||||
|  |   public SMSParsedResult(String number, String via, String subject, String body) { | ||||||
|  |     super(ParsedResultType.SMS); | ||||||
|  |     this.numbers = new String[] {number}; | ||||||
|  |     this.vias = new String[] {via}; | ||||||
|  |     this.subject = subject; | ||||||
|  |     this.body = body; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public SMSParsedResult(String[] numbers, String[] vias, String subject, String body) { | ||||||
|  |     super(ParsedResultType.SMS); | ||||||
|  |     this.numbers = numbers; | ||||||
|  |     this.vias = vias; | ||||||
|  |     this.subject = subject; | ||||||
|  |     this.body = body; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getSMSURI() { | ||||||
|  |     StringBuffer result = new StringBuffer(); | ||||||
|  |     result.append("sms:"); | ||||||
|  |     boolean first = true; | ||||||
|  |     for (int i = 0; i < numbers.length; i++) { | ||||||
|  |       if (first) { | ||||||
|  |         first = false; | ||||||
|  |       } else { | ||||||
|  |         result.append(','); | ||||||
|  |       } | ||||||
|  |       result.append(numbers[i]); | ||||||
|  |       if (vias[i] != null) { | ||||||
|  |         result.append(";via="); | ||||||
|  |         result.append(vias[i]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     boolean hasBody = body != null; | ||||||
|  |     boolean hasSubject = subject != null; | ||||||
|  |     if (hasBody || hasSubject) { | ||||||
|  |       result.append('?'); | ||||||
|  |       if (hasBody) { | ||||||
|  |         result.append("body="); | ||||||
|  |         result.append(body); | ||||||
|  |       } | ||||||
|  |       if (hasSubject) { | ||||||
|  |         if (hasBody) { | ||||||
|  |           result.append('&'); | ||||||
|  |         } | ||||||
|  |         result.append("subject="); | ||||||
|  |         result.append(subject); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String[] getNumbers() { | ||||||
|  |     return numbers; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String[] getVias() { | ||||||
|  |     return vias; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getSubject() { | ||||||
|  |     return subject; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getBody() { | ||||||
|  |     return body; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(100); | ||||||
|  |     maybeAppend(numbers, result); | ||||||
|  |     maybeAppend(subject, result); | ||||||
|  |     maybeAppend(body, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,57 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Parses an "smsto:" URI result, whose format is not standardized but appears to be like: | ||||||
|  |  * <code>smsto:number(:body)</code>.</p> | ||||||
|  |  * | ||||||
|  |  * <p>This actually also parses URIs starting with "smsto:", "mmsto:", "SMSTO:", and | ||||||
|  |  * "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI | ||||||
|  |  * for purposes of forwarding to the platform.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class SMSTOMMSTOResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private SMSTOMMSTOResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static SMSParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     if (!(rawText.startsWith("smsto:") || rawText.startsWith("SMSTO:") || | ||||||
|  |           rawText.startsWith("mmsto:") || rawText.startsWith("MMSTO:"))) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     // Thanks to dominik.wild for suggesting this enhancement to support | ||||||
|  |     // smsto:number:body URIs | ||||||
|  |     String number = rawText.substring(6); | ||||||
|  |     String body = null; | ||||||
|  |     int bodyStart = number.indexOf(':'); | ||||||
|  |     if (bodyStart >= 0) { | ||||||
|  |       body = number.substring(bodyStart + 1); | ||||||
|  |       number = number.substring(0, bodyStart); | ||||||
|  |     } | ||||||
|  |     return new SMSParsedResult(number, null, null, body); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/com/google/zxing/client/result/TelParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/com/google/zxing/client/result/TelParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class TelParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String number; | ||||||
|  |   private final String telURI; | ||||||
|  |   private final String title; | ||||||
|  | 
 | ||||||
|  |   public TelParsedResult(String number, String telURI, String title) { | ||||||
|  |     super(ParsedResultType.TEL); | ||||||
|  |     this.number = number; | ||||||
|  |     this.telURI = telURI; | ||||||
|  |     this.title = title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getNumber() { | ||||||
|  |     return number; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getTelURI() { | ||||||
|  |     return telURI; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getTitle() { | ||||||
|  |     return title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(20); | ||||||
|  |     maybeAppend(number, result); | ||||||
|  |     maybeAppend(title, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								src/com/google/zxing/client/result/TelResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/com/google/zxing/client/result/TelResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses a "tel:" URI result, which specifies a phone number. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class TelResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private TelResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static TelParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || (!rawText.startsWith("tel:") && !rawText.startsWith("TEL:"))) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     // Normalize "TEL:" to "tel:" | ||||||
|  |     String telURI = rawText.startsWith("TEL:") ? "tel:" + rawText.substring(4) : rawText; | ||||||
|  |     // Drop tel, query portion | ||||||
|  |     int queryStart = rawText.indexOf('?', 4); | ||||||
|  |     String number = queryStart < 0 ? rawText.substring(4) : rawText.substring(4, queryStart); | ||||||
|  |     return new TelParsedResult(number, telURI, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								src/com/google/zxing/client/result/TextParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/com/google/zxing/client/result/TextParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A simple result type encapsulating a string that has no further | ||||||
|  |  * interpretation. | ||||||
|  |  *  | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class TextParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String text; | ||||||
|  |   private final String language; | ||||||
|  | 
 | ||||||
|  |   public TextParsedResult(String text, String language) { | ||||||
|  |     super(ParsedResultType.TEXT); | ||||||
|  |     this.text = text; | ||||||
|  |     this.language = language; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getText() { | ||||||
|  |     return text; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getLanguage() { | ||||||
|  |     return language; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     return text; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								src/com/google/zxing/client/result/URIParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/com/google/zxing/client/result/URIParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class URIParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   private final String uri; | ||||||
|  |   private final String title; | ||||||
|  | 
 | ||||||
|  |   public URIParsedResult(String uri, String title) { | ||||||
|  |     super(ParsedResultType.URI); | ||||||
|  |     this.uri = massageURI(uri); | ||||||
|  |     this.title = title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getURI() { | ||||||
|  |     return uri; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getTitle() { | ||||||
|  |     return title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return true if the URI contains suspicious patterns that may suggest it intends to | ||||||
|  |    *  mislead the user about its true nature. At the moment this looks for the presence | ||||||
|  |    *  of user/password syntax in the host/authority portion of a URI which may be used | ||||||
|  |    *  in attempts to make the URI's host appear to be other than it is. Example: | ||||||
|  |    *  http://yourbank.com@phisher.com  This URI connects to phisher.com but may appear | ||||||
|  |    *  to connect to yourbank.com at first glance. | ||||||
|  |    */ | ||||||
|  |   public boolean isPossiblyMaliciousURI() { | ||||||
|  |     return containsUser(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private boolean containsUser() { | ||||||
|  |     // This method is likely not 100% RFC compliant yet | ||||||
|  |     int hostStart = uri.indexOf(':'); // we should always have scheme at this point | ||||||
|  |     hostStart++; | ||||||
|  |     // Skip slashes preceding host | ||||||
|  |     int uriLength = uri.length(); | ||||||
|  |     while (hostStart < uriLength && uri.charAt(hostStart) == '/') { | ||||||
|  |       hostStart++; | ||||||
|  |     } | ||||||
|  |     int hostEnd = uri.indexOf('/', hostStart); | ||||||
|  |     if (hostEnd < 0) { | ||||||
|  |       hostEnd = uriLength; | ||||||
|  |     } | ||||||
|  |     int at = uri.indexOf('@', hostStart); | ||||||
|  |     return at >= hostStart && at < hostEnd; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(30); | ||||||
|  |     maybeAppend(title, result); | ||||||
|  |     maybeAppend(uri, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Transforms a string that represents a URI into something more proper, by adding or canonicalizing | ||||||
|  |    * the protocol. | ||||||
|  |    */ | ||||||
|  |   private static String massageURI(String uri) { | ||||||
|  |     int protocolEnd = uri.indexOf(':'); | ||||||
|  |     if (protocolEnd < 0) { | ||||||
|  |       // No protocol, assume http | ||||||
|  |       uri = "http://" + uri; | ||||||
|  |     } else if (isColonFollowedByPortNumber(uri, protocolEnd)) { | ||||||
|  |       // Found a colon, but it looks like it is after the host, so the protocol is still missing | ||||||
|  |       uri = "http://" + uri; | ||||||
|  |     } else { | ||||||
|  |       // Lowercase protocol to avoid problems | ||||||
|  |       uri = uri.substring(0, protocolEnd).toLowerCase() + uri.substring(protocolEnd); | ||||||
|  |     } | ||||||
|  |     return uri; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static boolean isColonFollowedByPortNumber(String uri, int protocolEnd) { | ||||||
|  |     int nextSlash = uri.indexOf('/', protocolEnd + 1); | ||||||
|  |     if (nextSlash < 0) { | ||||||
|  |       nextSlash = uri.length(); | ||||||
|  |     } | ||||||
|  |     if (nextSlash <= protocolEnd + 1) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     for (int x = protocolEnd + 1; x < nextSlash; x++) { | ||||||
|  |       if (uri.charAt(x) < '0' || uri.charAt(x) > '9') { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										87
									
								
								src/com/google/zxing/client/result/URIResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/com/google/zxing/client/result/URIResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Tries to parse results that are a URI of some kind. | ||||||
|  |  *  | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class URIResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private URIResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static URIParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     // We specifically handle the odd "URL" scheme here for simplicity | ||||||
|  |     if (rawText != null && rawText.startsWith("URL:")) { | ||||||
|  |       rawText = rawText.substring(4); | ||||||
|  |     } | ||||||
|  |     if (!isBasicallyValidURI(rawText)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     return new URIParsedResult(rawText, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Determines whether a string is not obviously not a URI. This implements crude checks; this class does not | ||||||
|  |    * intend to strictly check URIs as its only function is to represent what is in a barcode, but, it does | ||||||
|  |    * need to know when a string is obviously not a URI. | ||||||
|  |    */ | ||||||
|  |   static boolean isBasicallyValidURI(String uri) { | ||||||
|  |     if (uri == null || uri.indexOf(' ') >= 0 || uri.indexOf('\n') >= 0) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     // Look for period in a domain but followed by at least a two-char TLD | ||||||
|  |     // Forget strings that don't have a valid-looking protocol | ||||||
|  |     int period = uri.indexOf('.'); | ||||||
|  |     if (period >= uri.length() - 2) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     int colon = uri.indexOf(':'); | ||||||
|  |     if (period < 0 && colon < 0) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     if (colon >= 0) { | ||||||
|  |       if (period < 0 || period > colon) { | ||||||
|  |         // colon ends the protocol | ||||||
|  |         for (int i = 0; i < colon; i++) { | ||||||
|  |           char c = uri.charAt(i); | ||||||
|  |           if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         // colon starts the port; crudely look for at least two numbers | ||||||
|  |         if (colon >= uri.length() - 2) { | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  |         for (int i = colon + 1; i < colon + 3; i++) { | ||||||
|  |           char c = uri.charAt(i); | ||||||
|  |           if (c < '0' || c > '9') { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								src/com/google/zxing/client/result/URLTOResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/com/google/zxing/client/result/URLTOResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses the "URLTO" result format, which is of the form "URLTO:[title]:[url]". | ||||||
|  |  * This seems to be used sometimes, but I am not able to find documentation | ||||||
|  |  * on its origin or official format? | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class URLTOResultParser { | ||||||
|  | 
 | ||||||
|  |   private URLTOResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static URIParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || (!rawText.startsWith("urlto:") && !rawText.startsWith("URLTO:"))) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int titleEnd = rawText.indexOf(':', 6); | ||||||
|  |     if (titleEnd < 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String title = titleEnd <= 6 ? null : rawText.substring(6, titleEnd); | ||||||
|  |     String uri = rawText.substring(titleEnd + 1); | ||||||
|  |     return new URIParsedResult(uri, title); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										346
									
								
								src/com/google/zxing/client/result/VCardResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								src/com/google/zxing/client/result/VCardResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,346 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.io.ByteArrayOutputStream; | ||||||
|  | import java.io.UnsupportedEncodingException; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses contact information formatted according to the VCard (2.1) format. This is not a complete | ||||||
|  |  * implementation but should parse information as commonly encoded in 2D barcodes. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class VCardResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private VCardResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static AddressBookParsedResult parse(Result result) { | ||||||
|  |     // Although we should insist on the raw text ending with "END:VCARD", there's no reason | ||||||
|  |     // to throw out everything else we parsed just because this was omitted. In fact, Eclair | ||||||
|  |     // is doing just that, and we can't parse its contacts without this leniency. | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null || !rawText.startsWith("BEGIN:VCARD")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String[] names = matchVCardPrefixedField("FN", rawText, true); | ||||||
|  |     if (names == null) { | ||||||
|  |       // If no display names found, look for regular name fields and format them | ||||||
|  |       names = matchVCardPrefixedField("N", rawText, true); | ||||||
|  |       formatNames(names); | ||||||
|  |     } | ||||||
|  |     String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText, true); | ||||||
|  |     String[] emails = matchVCardPrefixedField("EMAIL", rawText, true); | ||||||
|  |     String note = matchSingleVCardPrefixedField("NOTE", rawText, false); | ||||||
|  |     String[] addresses = matchVCardPrefixedField("ADR", rawText, true); | ||||||
|  |     if (addresses != null) { | ||||||
|  |       for (int i = 0; i < addresses.length; i++) { | ||||||
|  |         addresses[i] = formatAddress(addresses[i]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     String org = matchSingleVCardPrefixedField("ORG", rawText, true); | ||||||
|  |     String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true); | ||||||
|  |     if (!isLikeVCardDate(birthday)) { | ||||||
|  |       birthday = null; | ||||||
|  |     } | ||||||
|  |     String title = matchSingleVCardPrefixedField("TITLE", rawText, true); | ||||||
|  |     String url = matchSingleVCardPrefixedField("URL", rawText, true); | ||||||
|  |     return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, addresses, org, | ||||||
|  |         birthday, title, url); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String[] matchVCardPrefixedField(String prefix, String rawText, boolean trim) { | ||||||
|  |     Vector matches = null; | ||||||
|  |     int i = 0; | ||||||
|  |     int max = rawText.length(); | ||||||
|  | 
 | ||||||
|  |     while (i < max) { | ||||||
|  | 
 | ||||||
|  |       i = rawText.indexOf(prefix, i); | ||||||
|  |       if (i < 0) { | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (i > 0 && rawText.charAt(i - 1) != '\n') { | ||||||
|  |         // then this didn't start a new token, we matched in the middle of something | ||||||
|  |         i++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       i += prefix.length(); // Skip past this prefix we found to start | ||||||
|  |       if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       int metadataStart = i; | ||||||
|  |       while (rawText.charAt(i) != ':') { // Skip until a colon | ||||||
|  |         i++; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       boolean quotedPrintable = false; | ||||||
|  |       String quotedPrintableCharset = null; | ||||||
|  |       if (i > metadataStart) { | ||||||
|  |         // There was something after the tag, before colon | ||||||
|  |         int j = metadataStart+1; | ||||||
|  |         while (j <= i) { | ||||||
|  |           if (rawText.charAt(j) == ';' || rawText.charAt(j) == ':') { | ||||||
|  |             String metadata = rawText.substring(metadataStart+1, j); | ||||||
|  |             int equals = metadata.indexOf('='); | ||||||
|  |             if (equals >= 0) { | ||||||
|  |               String key = metadata.substring(0, equals); | ||||||
|  |               String value = metadata.substring(equals+1); | ||||||
|  |               if (key.equalsIgnoreCase("ENCODING")) { | ||||||
|  |                 if (value.equalsIgnoreCase("QUOTED-PRINTABLE")) { | ||||||
|  |                   quotedPrintable = true; | ||||||
|  |                 } | ||||||
|  |               } else if (key.equalsIgnoreCase("CHARSET")) { | ||||||
|  |                 quotedPrintableCharset = value; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             metadataStart = j; | ||||||
|  |           } | ||||||
|  |           j++; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       i++; // skip colon | ||||||
|  | 
 | ||||||
|  |       int matchStart = i; // Found the start of a match here | ||||||
|  | 
 | ||||||
|  |       while ((i = rawText.indexOf((int) '\n', i)) >= 0) { // Really, end in \r\n | ||||||
|  |         if (i < rawText.length() - 1 &&           // But if followed by tab or space, | ||||||
|  |             (rawText.charAt(i+1) == ' ' ||        // this is only a continuation | ||||||
|  |              rawText.charAt(i+1) == '\t')) { | ||||||
|  |           i += 2; // Skip \n and continutation whitespace | ||||||
|  |         } else if (quotedPrintable &&             // If preceded by = in quoted printable | ||||||
|  |                    (rawText.charAt(i-1) == '=' || // this is a continuation | ||||||
|  |                     rawText.charAt(i-2) == '=')) { | ||||||
|  |           i++; // Skip \n | ||||||
|  |         } else { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (i < 0) { | ||||||
|  |         // No terminating end character? uh, done. Set i such that loop terminates and break | ||||||
|  |         i = max; | ||||||
|  |       } else if (i > matchStart) { | ||||||
|  |         // found a match | ||||||
|  |         if (matches == null) { | ||||||
|  |           matches = new Vector(1); // lazy init | ||||||
|  |         } | ||||||
|  |         if (rawText.charAt(i-1) == '\r') { | ||||||
|  |           i--; // Back up over \r, which really should be there | ||||||
|  |         } | ||||||
|  |         String element = rawText.substring(matchStart, i); | ||||||
|  |         if (trim) { | ||||||
|  |           element = element.trim(); | ||||||
|  |         } | ||||||
|  |         if (quotedPrintable) { | ||||||
|  |           element = decodeQuotedPrintable(element, quotedPrintableCharset); | ||||||
|  |         } else { | ||||||
|  |           element = stripContinuationCRLF(element); | ||||||
|  |         } | ||||||
|  |         matches.addElement(element); | ||||||
|  |         i++; | ||||||
|  |       } else { | ||||||
|  |         i++; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (matches == null || matches.isEmpty()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     return toStringArray(matches); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String stripContinuationCRLF(String value) { | ||||||
|  |     int length = value.length(); | ||||||
|  |     StringBuffer result = new StringBuffer(length); | ||||||
|  |     boolean lastWasLF = false; | ||||||
|  |     for (int i = 0; i < length; i++) { | ||||||
|  |       if (lastWasLF) { | ||||||
|  |         lastWasLF = false; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       char c = value.charAt(i); | ||||||
|  |       lastWasLF = false; | ||||||
|  |       switch (c) { | ||||||
|  |         case '\n': | ||||||
|  |           lastWasLF = true; | ||||||
|  |           break; | ||||||
|  |         case '\r': | ||||||
|  |           break; | ||||||
|  |         default: | ||||||
|  |           result.append(c); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String decodeQuotedPrintable(String value, String charset) { | ||||||
|  |     int length = value.length(); | ||||||
|  |     StringBuffer result = new StringBuffer(length); | ||||||
|  |     ByteArrayOutputStream fragmentBuffer = new ByteArrayOutputStream(); | ||||||
|  |     for (int i = 0; i < length; i++) { | ||||||
|  |       char c = value.charAt(i); | ||||||
|  |       switch (c) { | ||||||
|  |         case '\r': | ||||||
|  |         case '\n': | ||||||
|  |           break; | ||||||
|  |         case '=': | ||||||
|  |           if (i < length - 2) { | ||||||
|  |             char nextChar = value.charAt(i+1); | ||||||
|  |             if (nextChar == '\r' || nextChar == '\n') { | ||||||
|  |               // Ignore, it's just a continuation symbol | ||||||
|  |             } else { | ||||||
|  |               char nextNextChar = value.charAt(i+2); | ||||||
|  |               try { | ||||||
|  |                 int encodedByte = 16 * toHexValue(nextChar) + toHexValue(nextNextChar); | ||||||
|  |                 fragmentBuffer.write(encodedByte); | ||||||
|  |               } catch (IllegalArgumentException iae) { | ||||||
|  |                 // continue, assume it was incorrectly encoded | ||||||
|  |               } | ||||||
|  |               i += 2; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           break; | ||||||
|  |         default: | ||||||
|  |           maybeAppendFragment(fragmentBuffer, charset, result); | ||||||
|  |           result.append(c); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     maybeAppendFragment(fragmentBuffer, charset, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static int toHexValue(char c) { | ||||||
|  |     if (c >= '0' && c <= '9') { | ||||||
|  |       return c - '0'; | ||||||
|  |     } else if (c >= 'A' && c <= 'F') { | ||||||
|  |       return c - 'A' + 10; | ||||||
|  |     } else if (c >= 'a' && c <= 'f') { | ||||||
|  |       return c - 'a' + 10; | ||||||
|  |     } | ||||||
|  |     throw new IllegalArgumentException(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void maybeAppendFragment(ByteArrayOutputStream fragmentBuffer, | ||||||
|  |                                           String charset, | ||||||
|  |                                           StringBuffer result) { | ||||||
|  |     if (fragmentBuffer.size() > 0) { | ||||||
|  |       byte[] fragmentBytes = fragmentBuffer.toByteArray(); | ||||||
|  |       String fragment; | ||||||
|  |       if (charset == null) { | ||||||
|  |         fragment = new String(fragmentBytes); | ||||||
|  |       } else { | ||||||
|  |         try { | ||||||
|  |           fragment = new String(fragmentBytes, charset); | ||||||
|  |         } catch (UnsupportedEncodingException e) { | ||||||
|  |           // Yikes, well try anyway: | ||||||
|  |           fragment = new String(fragmentBytes); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       fragmentBuffer.reset(); | ||||||
|  |       result.append(fragment); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String matchSingleVCardPrefixedField(String prefix, String rawText, boolean trim) { | ||||||
|  |     String[] values = matchVCardPrefixedField(prefix, rawText, trim); | ||||||
|  |     return values == null ? null : values[0]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static boolean isLikeVCardDate(String value) { | ||||||
|  |     if (value == null) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     // Not really sure this is true but matches practice | ||||||
|  |     // Mach YYYYMMDD | ||||||
|  |     if (isStringOfDigits(value, 8)) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     // or YYYY-MM-DD | ||||||
|  |     return | ||||||
|  |         value.length() == 10 && | ||||||
|  |         value.charAt(4) == '-' && | ||||||
|  |         value.charAt(7) == '-' && | ||||||
|  |         isSubstringOfDigits(value, 0, 4) && | ||||||
|  |         isSubstringOfDigits(value, 5, 2) && | ||||||
|  |         isSubstringOfDigits(value, 8, 2); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static String formatAddress(String address) { | ||||||
|  |     if (address == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int length = address.length(); | ||||||
|  |     StringBuffer newAddress = new StringBuffer(length); | ||||||
|  |     for (int j = 0; j < length; j++) { | ||||||
|  |       char c = address.charAt(j); | ||||||
|  |       if (c == ';') { | ||||||
|  |         newAddress.append(' '); | ||||||
|  |       } else { | ||||||
|  |         newAddress.append(c); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return newAddress.toString().trim(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like | ||||||
|  |    * "Reverend John Q. Public III". | ||||||
|  |    * | ||||||
|  |    * @param names name values to format, in place | ||||||
|  |    */ | ||||||
|  |   private static void formatNames(String[] names) { | ||||||
|  |     if (names != null) { | ||||||
|  |       for (int i = 0; i < names.length; i++) { | ||||||
|  |         String name = names[i]; | ||||||
|  |         String[] components = new String[5]; | ||||||
|  |         int start = 0; | ||||||
|  |         int end; | ||||||
|  |         int componentIndex = 0; | ||||||
|  |         while ((end = name.indexOf(';', start)) > 0) { | ||||||
|  |           components[componentIndex] = name.substring(start, end); | ||||||
|  |           componentIndex++; | ||||||
|  |           start = end + 1; | ||||||
|  |         } | ||||||
|  |         components[componentIndex] = name.substring(start); | ||||||
|  |         StringBuffer newName = new StringBuffer(100); | ||||||
|  |         maybeAppendComponent(components, 3, newName); | ||||||
|  |         maybeAppendComponent(components, 1, newName); | ||||||
|  |         maybeAppendComponent(components, 2, newName); | ||||||
|  |         maybeAppendComponent(components, 0, newName); | ||||||
|  |         maybeAppendComponent(components, 4, newName); | ||||||
|  |         names[i] = newName.toString().trim(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void maybeAppendComponent(String[] components, int i, StringBuffer newName) { | ||||||
|  |     if (components[i] != null) { | ||||||
|  |       newName.append(' '); | ||||||
|  |       newName.append(components[i]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/com/google/zxing/client/result/VEventResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/com/google/zxing/client/result/VEventResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Partially implements the iCalendar format's "VEVENT" format for specifying a | ||||||
|  |  * calendar event. See RFC 2445. This supports SUMMARY, LOCATION, DTSTART and DTEND fields. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class VEventResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private VEventResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static CalendarParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  |     if (rawText == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int vEventStart = rawText.indexOf("BEGIN:VEVENT"); | ||||||
|  |     if (vEventStart < 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     String summary = VCardResultParser.matchSingleVCardPrefixedField("SUMMARY", rawText, true); | ||||||
|  |     String start = VCardResultParser.matchSingleVCardPrefixedField("DTSTART", rawText, true); | ||||||
|  |     String end = VCardResultParser.matchSingleVCardPrefixedField("DTEND", rawText, true); | ||||||
|  |     String location = VCardResultParser.matchSingleVCardPrefixedField("LOCATION", rawText, true); | ||||||
|  |     String description = VCardResultParser.matchSingleVCardPrefixedField("DESCRIPTION", rawText, true); | ||||||
|  |     try { | ||||||
|  |       return new CalendarParsedResult(summary, start, end, location, null, description); | ||||||
|  |     } catch (IllegalArgumentException iae) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								src/com/google/zxing/client/result/WifiParsedResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/com/google/zxing/client/result/WifiParsedResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Vikram Aggarwal | ||||||
|  |  */ | ||||||
|  | public final class WifiParsedResult extends ParsedResult { | ||||||
|  |   private final String ssid; | ||||||
|  |   private final String networkEncryption; | ||||||
|  |   private final String password; | ||||||
|  | 
 | ||||||
|  |   public WifiParsedResult(String networkEncryption, String ssid, String password) { | ||||||
|  |     super(ParsedResultType.WIFI); | ||||||
|  |     this.ssid = ssid; | ||||||
|  |     this.networkEncryption = networkEncryption; | ||||||
|  |     this.password = password; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getSsid() { | ||||||
|  |     return ssid; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getNetworkEncryption() { | ||||||
|  |     return networkEncryption; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getPassword() { | ||||||
|  |     return password; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     StringBuffer result = new StringBuffer(80); | ||||||
|  |     maybeAppend(ssid, result); | ||||||
|  |     maybeAppend(networkEncryption, result); | ||||||
|  |     maybeAppend(password, result); | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								src/com/google/zxing/client/result/WifiResultParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/com/google/zxing/client/result/WifiResultParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Parses a WIFI configuration string.  Strings will be of the form: | ||||||
|  |  * WIFI:T:WPA;S:mynetwork;P:mypass;; | ||||||
|  |  * | ||||||
|  |  * The fields can come in any order, and there should be tests to see | ||||||
|  |  * if we can parse them all correctly. | ||||||
|  |  * | ||||||
|  |  * @author Vikram Aggarwal | ||||||
|  |  */ | ||||||
|  | final class WifiResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   private WifiResultParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static WifiParsedResult parse(Result result) { | ||||||
|  |     String rawText = result.getText(); | ||||||
|  | 
 | ||||||
|  |     if (rawText == null || !rawText.startsWith("WIFI:")) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Don't remove leading or trailing whitespace | ||||||
|  |     boolean trim = false; | ||||||
|  |     String ssid = matchSinglePrefixedField("S:", rawText, ';', trim); | ||||||
|  |     String pass = matchSinglePrefixedField("P:", rawText, ';', trim); | ||||||
|  |     String type = matchSinglePrefixedField("T:", rawText, ';', trim); | ||||||
|  | 
 | ||||||
|  |     return new WifiParsedResult(type, ssid, pass); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,45 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result.optional; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.client.result.ResultParser; | ||||||
|  | 
 | ||||||
|  | import java.io.UnsupportedEncodingException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Superclass for classes encapsulating results in the NDEF format. | ||||||
|  |  * See <a href="http://www.nfc-forum.org/specs/">http://www.nfc-forum.org/specs/</a>.</p> | ||||||
|  |  * | ||||||
|  |  * <p>This code supports a limited subset of NDEF messages, ones that are plausibly | ||||||
|  |  * useful in 2D barcode formats. This generally includes 1-record messages, no chunking, | ||||||
|  |  * "short record" syntax, no ID field.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | abstract class AbstractNDEFResultParser extends ResultParser { | ||||||
|  | 
 | ||||||
|  |   static String bytesToString(byte[] bytes, int offset, int length, String encoding) { | ||||||
|  |     try { | ||||||
|  |       return new String(bytes, offset, length, encoding); | ||||||
|  |     } catch (UnsupportedEncodingException uee) { | ||||||
|  |       // This should only be used when 'encoding' is an encoding that must necessarily | ||||||
|  |       // be supported by the JVM, like UTF-8 | ||||||
|  |       throw new RuntimeException("Platform does not support required encoding: " + uee); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										87
									
								
								src/com/google/zxing/client/result/optional/NDEFRecord.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/com/google/zxing/client/result/optional/NDEFRecord.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result.optional; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Represents a record in an NDEF message. This class only supports certain types | ||||||
|  |  * of records -- namely, non-chunked records, where ID length is omitted, and only | ||||||
|  |  * "short records".</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class NDEFRecord { | ||||||
|  | 
 | ||||||
|  |   private static final int SUPPORTED_HEADER_MASK = 0x3F; // 0 0 1 1 1 111 (the bottom 6 bits matter) | ||||||
|  |   private static final int SUPPORTED_HEADER = 0x11;      // 0 0 0 1 0 001 | ||||||
|  | 
 | ||||||
|  |   public static final String TEXT_WELL_KNOWN_TYPE = "T"; | ||||||
|  |   public static final String URI_WELL_KNOWN_TYPE = "U"; | ||||||
|  |   public static final String SMART_POSTER_WELL_KNOWN_TYPE = "Sp"; | ||||||
|  |   public static final String ACTION_WELL_KNOWN_TYPE = "act"; | ||||||
|  | 
 | ||||||
|  |   private final int header; | ||||||
|  |   private final String type; | ||||||
|  |   private final byte[] payload; | ||||||
|  |   private final int totalRecordLength; | ||||||
|  | 
 | ||||||
|  |   private NDEFRecord(int header, String type, byte[] payload, int totalRecordLength) { | ||||||
|  |     this.header = header; | ||||||
|  |     this.type = type; | ||||||
|  |     this.payload = payload; | ||||||
|  |     this.totalRecordLength = totalRecordLength; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static NDEFRecord readRecord(byte[] bytes, int offset) { | ||||||
|  |     int header = bytes[offset] & 0xFF; | ||||||
|  |     // Does header match what we support in the bits we care about? | ||||||
|  |     // XOR figures out where we differ, and if any of those are in the mask, fail | ||||||
|  |     if (((header ^ SUPPORTED_HEADER) & SUPPORTED_HEADER_MASK) != 0) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int typeLength = bytes[offset + 1] & 0xFF; | ||||||
|  | 
 | ||||||
|  |     int payloadLength = bytes[offset + 2] & 0xFF; | ||||||
|  | 
 | ||||||
|  |     String type = AbstractNDEFResultParser.bytesToString(bytes, offset + 3, typeLength, "US-ASCII"); | ||||||
|  | 
 | ||||||
|  |     byte[] payload = new byte[payloadLength]; | ||||||
|  |     System.arraycopy(bytes, offset + 3 + typeLength, payload, 0, payloadLength); | ||||||
|  | 
 | ||||||
|  |     return new NDEFRecord(header, type, payload, 3 + typeLength + payloadLength); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   boolean isMessageBegin() { | ||||||
|  |     return (header & 0x80) != 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   boolean isMessageEnd() { | ||||||
|  |     return (header & 0x40) != 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String getType() { | ||||||
|  |     return type; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   byte[] getPayload() { | ||||||
|  |     return payload; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int getTotalRecordLength() { | ||||||
|  |     return totalRecordLength; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,63 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result.optional; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.client.result.ParsedResult; | ||||||
|  | import com.google.zxing.client.result.ParsedResultType; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class NDEFSmartPosterParsedResult extends ParsedResult { | ||||||
|  | 
 | ||||||
|  |   public static final int ACTION_UNSPECIFIED = -1; | ||||||
|  |   public static final int ACTION_DO = 0; | ||||||
|  |   public static final int ACTION_SAVE = 1; | ||||||
|  |   public static final int ACTION_OPEN = 2; | ||||||
|  | 
 | ||||||
|  |   private final String title; | ||||||
|  |   private final String uri; | ||||||
|  |   private final int action; | ||||||
|  | 
 | ||||||
|  |   NDEFSmartPosterParsedResult(int action, String uri, String title) { | ||||||
|  |     super(ParsedResultType.NDEF_SMART_POSTER); | ||||||
|  |     this.action = action; | ||||||
|  |     this.uri = uri; | ||||||
|  |     this.title = title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getTitle() { | ||||||
|  |     return title; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getURI() { | ||||||
|  |     return uri; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int getAction() { | ||||||
|  |     return action; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getDisplayResult() { | ||||||
|  |     if (title == null) { | ||||||
|  |       return uri; | ||||||
|  |     } else { | ||||||
|  |       return title + '\n' + uri; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,81 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result.optional; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Recognizes an NDEF message that encodes information according to the | ||||||
|  |  * "Smart Poster Record Type Definition" specification.</p> | ||||||
|  |  * | ||||||
|  |  * <p>This actually only supports some parts of the Smart Poster format: title, | ||||||
|  |  * URI, and action records. Icon records are not supported because the size | ||||||
|  |  * of these records are infeasibly large for barcodes. Size and type records | ||||||
|  |  * are not supported. Multiple titles are not supported.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class NDEFSmartPosterResultParser extends AbstractNDEFResultParser { | ||||||
|  | 
 | ||||||
|  |   public static NDEFSmartPosterParsedResult parse(Result result) { | ||||||
|  |     byte[] bytes = result.getRawBytes(); | ||||||
|  |     if (bytes == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     NDEFRecord headerRecord = NDEFRecord.readRecord(bytes, 0); | ||||||
|  |     // Yes, header record starts and ends a message | ||||||
|  |     if (headerRecord == null || !headerRecord.isMessageBegin() || !headerRecord.isMessageEnd()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     if (!headerRecord.getType().equals(NDEFRecord.SMART_POSTER_WELL_KNOWN_TYPE)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int offset = 0; | ||||||
|  |     int recordNumber = 0; | ||||||
|  |     NDEFRecord ndefRecord = null; | ||||||
|  |     byte[] payload = headerRecord.getPayload(); | ||||||
|  |     int action = NDEFSmartPosterParsedResult.ACTION_UNSPECIFIED; | ||||||
|  |     String title = null; | ||||||
|  |     String uri = null; | ||||||
|  | 
 | ||||||
|  |     while (offset < payload.length && (ndefRecord = NDEFRecord.readRecord(payload, offset)) != null) { | ||||||
|  |       if (recordNumber == 0 && !ndefRecord.isMessageBegin()) { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       String type = ndefRecord.getType(); | ||||||
|  |       if (NDEFRecord.TEXT_WELL_KNOWN_TYPE.equals(type)) { | ||||||
|  |         String[] languageText = NDEFTextResultParser.decodeTextPayload(ndefRecord.getPayload()); | ||||||
|  |         title = languageText[1]; | ||||||
|  |       } else if (NDEFRecord.URI_WELL_KNOWN_TYPE.equals(type)) { | ||||||
|  |         uri = NDEFURIResultParser.decodeURIPayload(ndefRecord.getPayload()); | ||||||
|  |       } else if (NDEFRecord.ACTION_WELL_KNOWN_TYPE.equals(type)) { | ||||||
|  |         action = ndefRecord.getPayload()[0]; | ||||||
|  |       } | ||||||
|  |       recordNumber++; | ||||||
|  |       offset += ndefRecord.getTotalRecordLength(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (recordNumber == 0 || (ndefRecord != null && !ndefRecord.isMessageEnd())) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new NDEFSmartPosterParsedResult(action, uri, title); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,57 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result.optional; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.client.result.TextParsedResult; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Recognizes an NDEF message that encodes text according to the | ||||||
|  |  * "Text Record Type Definition" specification. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class NDEFTextResultParser extends AbstractNDEFResultParser { | ||||||
|  | 
 | ||||||
|  |   public static TextParsedResult parse(Result result) { | ||||||
|  |     byte[] bytes = result.getRawBytes(); | ||||||
|  |     if (bytes == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     NDEFRecord ndefRecord = NDEFRecord.readRecord(bytes, 0); | ||||||
|  |     if (ndefRecord == null || !ndefRecord.isMessageBegin() || !ndefRecord.isMessageEnd()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     if (!ndefRecord.getType().equals(NDEFRecord.TEXT_WELL_KNOWN_TYPE)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String[] languageText = decodeTextPayload(ndefRecord.getPayload()); | ||||||
|  |     return new TextParsedResult(languageText[0], languageText[1]); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String[] decodeTextPayload(byte[] payload) { | ||||||
|  |     byte statusByte = payload[0]; | ||||||
|  |     boolean isUTF16 = (statusByte & 0x80) != 0; | ||||||
|  |     int languageLength = statusByte & 0x1F; | ||||||
|  |     // language is always ASCII-encoded: | ||||||
|  |     String language = bytesToString(payload, 1, languageLength, "US-ASCII"); | ||||||
|  |     String encoding = isUTF16 ? "UTF-16" : "UTF8"; | ||||||
|  |     String text = bytesToString(payload, 1 + languageLength, payload.length - languageLength - 1, encoding); | ||||||
|  |     return new String[] { language, text }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,95 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.client.result.optional; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.client.result.URIParsedResult; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Recognizes an NDEF message that encodes a URI according to the | ||||||
|  |  * "URI Record Type Definition" specification. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class NDEFURIResultParser extends AbstractNDEFResultParser { | ||||||
|  | 
 | ||||||
|  |   private static final String[] URI_PREFIXES = { | ||||||
|  |       null, | ||||||
|  |       "http://www.", | ||||||
|  |       "https://www.", | ||||||
|  |       "http://", | ||||||
|  |       "https://", | ||||||
|  |       "tel:", | ||||||
|  |       "mailto:", | ||||||
|  |       "ftp://anonymous:anonymous@", | ||||||
|  |       "ftp://ftp.", | ||||||
|  |       "ftps://", | ||||||
|  |       "sftp://", | ||||||
|  |       "smb://", | ||||||
|  |       "nfs://", | ||||||
|  |       "ftp://", | ||||||
|  |       "dav://", | ||||||
|  |       "news:", | ||||||
|  |       "telnet://", | ||||||
|  |       "imap:", | ||||||
|  |       "rtsp://", | ||||||
|  |       "urn:", | ||||||
|  |       "pop:", | ||||||
|  |       "sip:", | ||||||
|  |       "sips:", | ||||||
|  |       "tftp:", | ||||||
|  |       "btspp://", | ||||||
|  |       "btl2cap://", | ||||||
|  |       "btgoep://", | ||||||
|  |       "tcpobex://", | ||||||
|  |       "irdaobex://", | ||||||
|  |       "file://", | ||||||
|  |       "urn:epc:id:", | ||||||
|  |       "urn:epc:tag:", | ||||||
|  |       "urn:epc:pat:", | ||||||
|  |       "urn:epc:raw:", | ||||||
|  |       "urn:epc:", | ||||||
|  |       "urn:nfc:", | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   public static URIParsedResult parse(Result result) { | ||||||
|  |     byte[] bytes = result.getRawBytes(); | ||||||
|  |     if (bytes == null) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     NDEFRecord ndefRecord = NDEFRecord.readRecord(bytes, 0); | ||||||
|  |     if (ndefRecord == null || !ndefRecord.isMessageBegin() || !ndefRecord.isMessageEnd()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     if (!ndefRecord.getType().equals(NDEFRecord.URI_WELL_KNOWN_TYPE)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     String fullURI = decodeURIPayload(ndefRecord.getPayload()); | ||||||
|  |     return new URIParsedResult(fullURI, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String decodeURIPayload(byte[] payload) { | ||||||
|  |     int identifierCode = payload[0] & 0xFF; | ||||||
|  |     String prefix = null; | ||||||
|  |     if (identifierCode < URI_PREFIXES.length) { | ||||||
|  |       prefix = URI_PREFIXES[identifierCode]; | ||||||
|  |     } | ||||||
|  |     String restOfURI = bytesToString(payload, 1, payload.length - 1, "UTF8"); | ||||||
|  |     return prefix == null ? restOfURI : prefix + restOfURI; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										247
									
								
								src/com/google/zxing/common/BitArray.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/com/google/zxing/common/BitArray.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,247 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class BitArray { | ||||||
|  | 
 | ||||||
|  |   // TODO: I have changed these members to be public so ProGuard can inline get() and set(). Ideally | ||||||
|  |   // they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the | ||||||
|  |   // resulting binary at runtime on Android. If we find a solution to this, these should be changed | ||||||
|  |   // back to private. | ||||||
|  |   public int[] bits; | ||||||
|  |   public int size; | ||||||
|  | 
 | ||||||
|  |   public BitArray() { | ||||||
|  |     this.size = 0; | ||||||
|  |     this.bits = new int[1]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BitArray(int size) { | ||||||
|  |     this.size = size; | ||||||
|  |     this.bits = makeArray(size); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int getSize() { | ||||||
|  |     return size; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int getSizeInBytes() { | ||||||
|  |     return (size + 7) >> 3; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private void ensureCapacity(int size) { | ||||||
|  |     if (size > bits.length << 5) { | ||||||
|  |       int[] newBits = makeArray(size); | ||||||
|  |       System.arraycopy(bits, 0, newBits, 0, bits.length); | ||||||
|  |       this.bits = newBits; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param i bit to get | ||||||
|  |    * @return true iff bit i is set | ||||||
|  |    */ | ||||||
|  |   public boolean get(int i) { | ||||||
|  |     return (bits[i >> 5] & (1 << (i & 0x1F))) != 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Sets bit i. | ||||||
|  |    * | ||||||
|  |    * @param i bit to set | ||||||
|  |    */ | ||||||
|  |   public void set(int i) { | ||||||
|  |     bits[i >> 5] |= 1 << (i & 0x1F); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Flips bit i. | ||||||
|  |    * | ||||||
|  |    * @param i bit to set | ||||||
|  |    */ | ||||||
|  |   public void flip(int i) { | ||||||
|  |     bits[i >> 5] ^= 1 << (i & 0x1F); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Sets a block of 32 bits, starting at bit i. | ||||||
|  |    * | ||||||
|  |    * @param i first bit to set | ||||||
|  |    * @param newBits the new value of the next 32 bits. Note again that the least-significant bit | ||||||
|  |    * corresponds to bit i, the next-least-significant to i+1, and so on. | ||||||
|  |    */ | ||||||
|  |   public void setBulk(int i, int newBits) { | ||||||
|  |     bits[i >> 5] = newBits; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Clears all bits (sets to false). | ||||||
|  |    */ | ||||||
|  |   public void clear() { | ||||||
|  |     int max = bits.length; | ||||||
|  |     for (int i = 0; i < max; i++) { | ||||||
|  |       bits[i] = 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Efficient method to check if a range of bits is set, or not set. | ||||||
|  |    * | ||||||
|  |    * @param start start of range, inclusive. | ||||||
|  |    * @param end end of range, exclusive | ||||||
|  |    * @param value if true, checks that bits in range are set, otherwise checks that they are not set | ||||||
|  |    * @return true iff all bits are set or not set in range, according to value argument | ||||||
|  |    * @throws IllegalArgumentException if end is less than or equal to start | ||||||
|  |    */ | ||||||
|  |   public boolean isRange(int start, int end, boolean value) { | ||||||
|  |     if (end < start) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     if (end == start) { | ||||||
|  |       return true; // empty range matches | ||||||
|  |     } | ||||||
|  |     end--; // will be easier to treat this as the last actually set bit -- inclusive     | ||||||
|  |     int firstInt = start >> 5; | ||||||
|  |     int lastInt = end >> 5; | ||||||
|  |     for (int i = firstInt; i <= lastInt; i++) { | ||||||
|  |       int firstBit = i > firstInt ? 0 : start & 0x1F; | ||||||
|  |       int lastBit = i < lastInt ? 31 : end & 0x1F; | ||||||
|  |       int mask; | ||||||
|  |       if (firstBit == 0 && lastBit == 31) { | ||||||
|  |         mask = -1; | ||||||
|  |       } else { | ||||||
|  |         mask = 0; | ||||||
|  |         for (int j = firstBit; j <= lastBit; j++) { | ||||||
|  |           mask |= 1 << j; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is, | ||||||
|  |       // equals the mask, or we're looking for 0s and the masked portion is not all 0s | ||||||
|  |       if ((bits[i] & mask) != (value ? mask : 0)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void appendBit(boolean bit) { | ||||||
|  |     ensureCapacity(size + 1); | ||||||
|  |     if (bit) { | ||||||
|  |       bits[size >> 5] |= (1 << (size & 0x1F)); | ||||||
|  |     } | ||||||
|  |     size++; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Appends the least-significant bits, from value, in order from most-significant to | ||||||
|  |    * least-significant. For example, appending 6 bits from 0x000001E will append the bits | ||||||
|  |    * 0, 1, 1, 1, 1, 0 in that order. | ||||||
|  |    */ | ||||||
|  |   public void appendBits(int value, int numBits) { | ||||||
|  |     if (numBits < 0 || numBits > 32) { | ||||||
|  |       throw new IllegalArgumentException("Num bits must be between 0 and 32"); | ||||||
|  |     } | ||||||
|  |     ensureCapacity(size + numBits); | ||||||
|  |     for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) { | ||||||
|  |       appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void appendBitArray(BitArray other) { | ||||||
|  |     int otherSize = other.getSize(); | ||||||
|  |     ensureCapacity(size + otherSize); | ||||||
|  |     for (int i = 0; i < otherSize; i++) { | ||||||
|  |       appendBit(other.get(i)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void xor(BitArray other) { | ||||||
|  |     if (bits.length != other.bits.length) { | ||||||
|  |       throw new IllegalArgumentException("Sizes don't match"); | ||||||
|  |     } | ||||||
|  |     for (int i = 0; i < bits.length; i++) { | ||||||
|  |       // The last byte could be incomplete (i.e. not have 8 bits in | ||||||
|  |       // it) but there is no problem since 0 XOR 0 == 0. | ||||||
|  |       bits[i] ^= other.bits[i]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * | ||||||
|  |    * @param bitOffset first bit to start writing | ||||||
|  |    * @param array array to write into. Bytes are written most-significant byte first. This is the opposite | ||||||
|  |    *  of the internal representation, which is exposed by {@link #getBitArray()} | ||||||
|  |    * @param offset position in array to start writing | ||||||
|  |    * @param numBytes how many bytes to write | ||||||
|  |    */ | ||||||
|  |   public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) { | ||||||
|  |     for (int i = 0; i < numBytes; i++) { | ||||||
|  |       int theByte = 0; | ||||||
|  |       for (int j = 0; j < 8; j++) { | ||||||
|  |         if (get(bitOffset)) { | ||||||
|  |           theByte |= 1 << (7 - j); | ||||||
|  |         } | ||||||
|  |         bitOffset++; | ||||||
|  |       } | ||||||
|  |       array[offset + i] = (byte) theByte; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return underlying array of ints. The first element holds the first 32 bits, and the least | ||||||
|  |    *         significant bit is bit 0. | ||||||
|  |    */ | ||||||
|  |   public int[] getBitArray() { | ||||||
|  |     return bits; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Reverses all bits in the array. | ||||||
|  |    */ | ||||||
|  |   public void reverse() { | ||||||
|  |     int[] newBits = new int[bits.length]; | ||||||
|  |     int size = this.size; | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       if (get(size - i - 1)) { | ||||||
|  |         newBits[i >> 5] |= 1 << (i & 0x1F); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     bits = newBits; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static int[] makeArray(int size) { | ||||||
|  |     return new int[(size + 31) >> 5]; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   public String toString() { | ||||||
|  |     StringBuffer result = new StringBuffer(size); | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       if ((i & 0x07) == 0) { | ||||||
|  |         result.append(' '); | ||||||
|  |       } | ||||||
|  |       result.append(get(i) ? 'X' : '.'); | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										226
									
								
								src/com/google/zxing/common/BitMatrix.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								src/com/google/zxing/common/BitMatrix.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,226 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common | ||||||
|  |  * module, x is the column position, and y is the row position. The ordering is always x, y. | ||||||
|  |  * The origin is at the top-left.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins | ||||||
|  |  * with a new int. This is done intentionally so that we can copy out a row into a BitArray very | ||||||
|  |  * efficiently.</p> | ||||||
|  |  * | ||||||
|  |  * <p>The ordering of bits is row-major. Within each int, the least significant bits are used first, | ||||||
|  |  * meaning they represent lower x values. This is compatible with BitArray's implementation.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class BitMatrix { | ||||||
|  | 
 | ||||||
|  |   // TODO: Just like BitArray, these need to be public so ProGuard can inline them. | ||||||
|  |   public final int width; | ||||||
|  |   public final int height; | ||||||
|  |   public final int rowSize; | ||||||
|  |   public final int[] bits; | ||||||
|  | 
 | ||||||
|  |   // A helper to construct a square matrix. | ||||||
|  |   public BitMatrix(int dimension) { | ||||||
|  |     this(dimension, dimension); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BitMatrix(int width, int height) { | ||||||
|  |     if (width < 1 || height < 1) { | ||||||
|  |       throw new IllegalArgumentException("Both dimensions must be greater than 0"); | ||||||
|  |     } | ||||||
|  |     this.width = width; | ||||||
|  |     this.height = height; | ||||||
|  |     this.rowSize = (width + 31) >> 5; | ||||||
|  |     bits = new int[rowSize * height]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Gets the requested bit, where true means black.</p> | ||||||
|  |    * | ||||||
|  |    * @param x The horizontal component (i.e. which column) | ||||||
|  |    * @param y The vertical component (i.e. which row) | ||||||
|  |    * @return value of given bit in matrix | ||||||
|  |    */ | ||||||
|  |   public boolean get(int x, int y) { | ||||||
|  |     int offset = y * rowSize + (x >> 5); | ||||||
|  |     return ((bits[offset] >>> (x & 0x1f)) & 1) != 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Sets the given bit to true.</p> | ||||||
|  |    * | ||||||
|  |    * @param x The horizontal component (i.e. which column) | ||||||
|  |    * @param y The vertical component (i.e. which row) | ||||||
|  |    */ | ||||||
|  |   public void set(int x, int y) { | ||||||
|  |     int offset = y * rowSize + (x >> 5); | ||||||
|  |     bits[offset] |= 1 << (x & 0x1f); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Flips the given bit.</p> | ||||||
|  |    * | ||||||
|  |    * @param x The horizontal component (i.e. which column) | ||||||
|  |    * @param y The vertical component (i.e. which row) | ||||||
|  |    */ | ||||||
|  |   public void flip(int x, int y) { | ||||||
|  |     int offset = y * rowSize + (x >> 5); | ||||||
|  |     bits[offset] ^= 1 << (x & 0x1f); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Clears all bits (sets to false). | ||||||
|  |    */ | ||||||
|  |   public void clear() { | ||||||
|  |     int max = bits.length; | ||||||
|  |     for (int i = 0; i < max; i++) { | ||||||
|  |       bits[i] = 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Sets a square region of the bit matrix to true.</p> | ||||||
|  |    * | ||||||
|  |    * @param left The horizontal position to begin at (inclusive) | ||||||
|  |    * @param top The vertical position to begin at (inclusive) | ||||||
|  |    * @param width The width of the region | ||||||
|  |    * @param height The height of the region | ||||||
|  |    */ | ||||||
|  |   public void setRegion(int left, int top, int width, int height) { | ||||||
|  |     if (top < 0 || left < 0) { | ||||||
|  |       throw new IllegalArgumentException("Left and top must be nonnegative"); | ||||||
|  |     } | ||||||
|  |     if (height < 1 || width < 1) { | ||||||
|  |       throw new IllegalArgumentException("Height and width must be at least 1"); | ||||||
|  |     } | ||||||
|  |     int right = left + width; | ||||||
|  |     int bottom = top + height; | ||||||
|  |     if (bottom > this.height || right > this.width) { | ||||||
|  |       throw new IllegalArgumentException("The region must fit inside the matrix"); | ||||||
|  |     } | ||||||
|  |     for (int y = top; y < bottom; y++) { | ||||||
|  |       int offset = y * rowSize; | ||||||
|  |       for (int x = left; x < right; x++) { | ||||||
|  |         bits[offset + (x >> 5)] |= 1 << (x & 0x1f); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * A fast method to retrieve one row of data from the matrix as a BitArray. | ||||||
|  |    * | ||||||
|  |    * @param y The row to retrieve | ||||||
|  |    * @param row An optional caller-allocated BitArray, will be allocated if null or too small | ||||||
|  |    * @return The resulting BitArray - this reference should always be used even when passing | ||||||
|  |    *         your own row | ||||||
|  |    */ | ||||||
|  |   public BitArray getRow(int y, BitArray row) { | ||||||
|  |     if (row == null || row.getSize() < width) { | ||||||
|  |       row = new BitArray(width); | ||||||
|  |     } | ||||||
|  |     int offset = y * rowSize; | ||||||
|  |     for (int x = 0; x < rowSize; x++) { | ||||||
|  |       row.setBulk(x << 5, bits[offset + x]); | ||||||
|  |     } | ||||||
|  |     return row; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * This is useful in detecting a corner of a 'pure' barcode. | ||||||
|  |    *  | ||||||
|  |    * @return {x,y} coordinate of top-left-most 1 bit, or null if it is all white | ||||||
|  |    */ | ||||||
|  |   public int[] getTopLeftOnBit() { | ||||||
|  |     int bitsOffset = 0; | ||||||
|  |     while (bitsOffset < bits.length && bits[bitsOffset] == 0) { | ||||||
|  |       bitsOffset++; | ||||||
|  |     } | ||||||
|  |     if (bitsOffset == bits.length) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     int y = bitsOffset / rowSize; | ||||||
|  |     int x = (bitsOffset % rowSize) << 5; | ||||||
|  |      | ||||||
|  |     int theBits = bits[bitsOffset]; | ||||||
|  |     int bit = 0; | ||||||
|  |     while ((theBits << (31-bit)) == 0) { | ||||||
|  |       bit++; | ||||||
|  |     } | ||||||
|  |     x += bit; | ||||||
|  |     return new int[] {x, y}; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return The width of the matrix | ||||||
|  |    */ | ||||||
|  |   public int getWidth() { | ||||||
|  |     return width; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return The height of the matrix | ||||||
|  |    */ | ||||||
|  |   public int getHeight() { | ||||||
|  |     return height; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public boolean equals(Object o) { | ||||||
|  |     if (!(o instanceof BitMatrix)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     BitMatrix other = (BitMatrix) o; | ||||||
|  |     if (width != other.width || height != other.height || | ||||||
|  |         rowSize != other.rowSize || bits.length != other.bits.length) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     for (int i = 0; i < bits.length; i++) { | ||||||
|  |       if (bits[i] != other.bits[i]) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int hashCode() { | ||||||
|  |     int hash = width; | ||||||
|  |     hash = 31 * hash + width; | ||||||
|  |     hash = 31 * hash + height; | ||||||
|  |     hash = 31 * hash + rowSize; | ||||||
|  |     for (int i = 0; i < bits.length; i++) { | ||||||
|  |       hash = 31 * hash + bits[i]; | ||||||
|  |     } | ||||||
|  |     return hash; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     StringBuffer result = new StringBuffer(height * (width + 1)); | ||||||
|  |     for (int y = 0; y < height; y++) { | ||||||
|  |       for (int x = 0; x < width; x++) { | ||||||
|  |         result.append(get(x, y) ? "X " : "  "); | ||||||
|  |       } | ||||||
|  |       result.append('\n'); | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								src/com/google/zxing/common/BitSource.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/com/google/zxing/common/BitSource.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the | ||||||
|  |  * number of bits read is not often a multiple of 8.</p> | ||||||
|  |  * | ||||||
|  |  * <p>This class is thread-safe but not reentrant. Unless the caller modifies the bytes array | ||||||
|  |  * it passed in, in which case all bets are off.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class BitSource { | ||||||
|  | 
 | ||||||
|  |   private final byte[] bytes; | ||||||
|  |   private int byteOffset; | ||||||
|  |   private int bitOffset; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param bytes bytes from which this will read bits. Bits will be read from the first byte first. | ||||||
|  |    * Bits are read within a byte from most-significant to least-significant bit. | ||||||
|  |    */ | ||||||
|  |   public BitSource(byte[] bytes) { | ||||||
|  |     this.bytes = bytes; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param numBits number of bits to read | ||||||
|  |    * @return int representing the bits read. The bits will appear as the least-significant | ||||||
|  |    *         bits of the int | ||||||
|  |    * @throws IllegalArgumentException if numBits isn't in [1,32] | ||||||
|  |    */ | ||||||
|  |   public int readBits(int numBits) { | ||||||
|  |     if (numBits < 1 || numBits > 32) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int result = 0; | ||||||
|  | 
 | ||||||
|  |     // First, read remainder from current byte | ||||||
|  |     if (bitOffset > 0) { | ||||||
|  |       int bitsLeft = 8 - bitOffset; | ||||||
|  |       int toRead = numBits < bitsLeft ? numBits : bitsLeft; | ||||||
|  |       int bitsToNotRead = bitsLeft - toRead; | ||||||
|  |       int mask = (0xFF >> (8 - toRead)) << bitsToNotRead; | ||||||
|  |       result = (bytes[byteOffset] & mask) >> bitsToNotRead; | ||||||
|  |       numBits -= toRead; | ||||||
|  |       bitOffset += toRead; | ||||||
|  |       if (bitOffset == 8) { | ||||||
|  |         bitOffset = 0; | ||||||
|  |         byteOffset++; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Next read whole bytes | ||||||
|  |     if (numBits > 0) { | ||||||
|  |       while (numBits >= 8) { | ||||||
|  |         result = (result << 8) | (bytes[byteOffset] & 0xFF); | ||||||
|  |         byteOffset++; | ||||||
|  |         numBits -= 8; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Finally read a partial byte | ||||||
|  |       if (numBits > 0) { | ||||||
|  |         int bitsToNotRead = 8 - numBits; | ||||||
|  |         int mask = (0xFF >> bitsToNotRead) << bitsToNotRead; | ||||||
|  |         result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead); | ||||||
|  |         bitOffset += numBits; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return number of bits that can be read successfully | ||||||
|  |    */ | ||||||
|  |   public int available() { | ||||||
|  |     return 8 * (bytes.length - byteOffset) - bitOffset; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								src/com/google/zxing/common/CharacterSetECI.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/com/google/zxing/common/CharacterSetECI.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 | ||||||
|  |  * of ISO 18004. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class CharacterSetECI extends ECI { | ||||||
|  | 
 | ||||||
|  |   private static Hashtable VALUE_TO_ECI; | ||||||
|  |   private static Hashtable NAME_TO_ECI; | ||||||
|  | 
 | ||||||
|  |   private static void initialize() { | ||||||
|  |     VALUE_TO_ECI = new Hashtable(29); | ||||||
|  |     NAME_TO_ECI = new Hashtable(29); | ||||||
|  |     // TODO figure out if these values are even right! | ||||||
|  |     addCharacterSet(0, "Cp437"); | ||||||
|  |     addCharacterSet(1, new String[] {"ISO8859_1", "ISO-8859-1"}); | ||||||
|  |     addCharacterSet(2, "Cp437"); | ||||||
|  |     addCharacterSet(3, new String[] {"ISO8859_1", "ISO-8859-1"}); | ||||||
|  |     addCharacterSet(4, "ISO8859_2"); | ||||||
|  |     addCharacterSet(5, "ISO8859_3"); | ||||||
|  |     addCharacterSet(6, "ISO8859_4"); | ||||||
|  |     addCharacterSet(7, "ISO8859_5"); | ||||||
|  |     addCharacterSet(8, "ISO8859_6"); | ||||||
|  |     addCharacterSet(9, "ISO8859_7"); | ||||||
|  |     addCharacterSet(10, "ISO8859_8"); | ||||||
|  |     addCharacterSet(11, "ISO8859_9"); | ||||||
|  |     addCharacterSet(12, "ISO8859_10"); | ||||||
|  |     addCharacterSet(13, "ISO8859_11"); | ||||||
|  |     addCharacterSet(15, "ISO8859_13"); | ||||||
|  |     addCharacterSet(16, "ISO8859_14"); | ||||||
|  |     addCharacterSet(17, "ISO8859_15"); | ||||||
|  |     addCharacterSet(18, "ISO8859_16"); | ||||||
|  |     addCharacterSet(20, new String[] {"SJIS", "Shift_JIS"}); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private final String encodingName; | ||||||
|  | 
 | ||||||
|  |   private CharacterSetECI(int value, String encodingName) { | ||||||
|  |     super(value); | ||||||
|  |     this.encodingName = encodingName; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getEncodingName() { | ||||||
|  |     return encodingName; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void addCharacterSet(int value, String encodingName) { | ||||||
|  |     CharacterSetECI eci = new CharacterSetECI(value, encodingName); | ||||||
|  |     VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf | ||||||
|  |     NAME_TO_ECI.put(encodingName, eci); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void addCharacterSet(int value, String[] encodingNames) { | ||||||
|  |     CharacterSetECI eci = new CharacterSetECI(value, encodingNames[0]); | ||||||
|  |     VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf | ||||||
|  |     for (int i = 0; i < encodingNames.length; i++) { | ||||||
|  |       NAME_TO_ECI.put(encodingNames[i], eci); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param value character set ECI value | ||||||
|  |    * @return {@link CharacterSetECI} representing ECI of given value, or null if it is legal but | ||||||
|  |    *   unsupported | ||||||
|  |    * @throws IllegalArgumentException if ECI value is invalid | ||||||
|  |    */ | ||||||
|  |   public static CharacterSetECI getCharacterSetECIByValue(int value) { | ||||||
|  |     if (VALUE_TO_ECI == null) { | ||||||
|  |       initialize(); | ||||||
|  |     } | ||||||
|  |     if (value < 0 || value >= 900) { | ||||||
|  |       throw new IllegalArgumentException("Bad ECI value: " + value); | ||||||
|  |     } | ||||||
|  |     return (CharacterSetECI) VALUE_TO_ECI.get(new Integer(value)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param name character set ECI encoding name | ||||||
|  |    * @return {@link CharacterSetECI} representing ECI for character encoding, or null if it is legal | ||||||
|  |    *   but unsupported | ||||||
|  |    */ | ||||||
|  |   public static CharacterSetECI getCharacterSetECIByName(String name) { | ||||||
|  |     if (NAME_TO_ECI == null) { | ||||||
|  |       initialize(); | ||||||
|  |     } | ||||||
|  |     return (CharacterSetECI) NAME_TO_ECI.get(name); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								src/com/google/zxing/common/Collections.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/com/google/zxing/common/Collections.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>This is basically a substitute for <code>java.util.Collections</code>, which is not | ||||||
|  |  * present in MIDP 2.0 / CLDC 1.1.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class Collections { | ||||||
|  | 
 | ||||||
|  |   private Collections() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Sorts its argument (destructively) using insert sort; in the context of this package | ||||||
|  |    * insertion sort is simple and efficient given its relatively small inputs. | ||||||
|  |    * | ||||||
|  |    * @param vector vector to sort | ||||||
|  |    * @param comparator comparator to define sort ordering | ||||||
|  |    */ | ||||||
|  |   public static void insertionSort(Vector vector, Comparator comparator) { | ||||||
|  |     int max = vector.size(); | ||||||
|  |     for (int i = 1; i < max; i++) { | ||||||
|  |       Object value = vector.elementAt(i); | ||||||
|  |       int j = i - 1; | ||||||
|  |       Object valueB; | ||||||
|  |       while (j >= 0 && comparator.compare((valueB = vector.elementAt(j)), value) > 0) { | ||||||
|  |         vector.setElementAt(valueB, j + 1); | ||||||
|  |         j--; | ||||||
|  |       } | ||||||
|  |       vector.setElementAt(value, j + 1); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/com/google/zxing/common/Comparator.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/com/google/zxing/common/Comparator.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This is merely a clone of <code>Comparator</code> since it is not available in | ||||||
|  |  * CLDC 1.1 / MIDP 2.0. | ||||||
|  |  */ | ||||||
|  | public interface Comparator { | ||||||
|  | 
 | ||||||
|  |   int compare(Object o1, Object o2); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								src/com/google/zxing/common/DecoderResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/com/google/zxing/common/DecoderResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; | ||||||
|  | 
 | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Encapsulates the result of decoding a matrix of bits. This typically | ||||||
|  |  * applies to 2D barcode formats. For now it contains the raw bytes obtained, | ||||||
|  |  * as well as a String interpretation of those bytes, if applicable.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class DecoderResult { | ||||||
|  | 
 | ||||||
|  |   private final byte[] rawBytes; | ||||||
|  |   private final String text; | ||||||
|  |   private final Vector byteSegments; | ||||||
|  |   private final ErrorCorrectionLevel ecLevel; | ||||||
|  | 
 | ||||||
|  |   public DecoderResult(byte[] rawBytes, String text, Vector byteSegments, ErrorCorrectionLevel ecLevel) { | ||||||
|  |     if (rawBytes == null && text == null) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     this.rawBytes = rawBytes; | ||||||
|  |     this.text = text; | ||||||
|  |     this.byteSegments = byteSegments; | ||||||
|  |     this.ecLevel = ecLevel; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public byte[] getRawBytes() { | ||||||
|  |     return rawBytes; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String getText() { | ||||||
|  |     return text; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Vector getByteSegments() { | ||||||
|  |     return byteSegments; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public ErrorCorrectionLevel getECLevel() { | ||||||
|  |     return ecLevel; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										81
									
								
								src/com/google/zxing/common/DefaultGridSampler.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/com/google/zxing/common/DefaultGridSampler.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class DefaultGridSampler extends GridSampler { | ||||||
|  | 
 | ||||||
|  |   public BitMatrix sampleGrid(BitMatrix image, | ||||||
|  |                               int dimension, | ||||||
|  |                               float p1ToX, float p1ToY, | ||||||
|  |                               float p2ToX, float p2ToY, | ||||||
|  |                               float p3ToX, float p3ToY, | ||||||
|  |                               float p4ToX, float p4ToY, | ||||||
|  |                               float p1FromX, float p1FromY, | ||||||
|  |                               float p2FromX, float p2FromY, | ||||||
|  |                               float p3FromX, float p3FromY, | ||||||
|  |                               float p4FromX, float p4FromY) throws NotFoundException { | ||||||
|  | 
 | ||||||
|  |     PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral( | ||||||
|  |         p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, | ||||||
|  |         p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); | ||||||
|  | 
 | ||||||
|  |     return sampleGrid(image, dimension, transform); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BitMatrix sampleGrid(BitMatrix image, | ||||||
|  |                               int dimension, | ||||||
|  |                               PerspectiveTransform transform) throws NotFoundException { | ||||||
|  |     BitMatrix bits = new BitMatrix(dimension); | ||||||
|  |     float[] points = new float[dimension << 1]; | ||||||
|  |     for (int y = 0; y < dimension; y++) { | ||||||
|  |       int max = points.length; | ||||||
|  |       float iValue = (float) y + 0.5f; | ||||||
|  |       for (int x = 0; x < max; x += 2) { | ||||||
|  |         points[x] = (float) (x >> 1) + 0.5f; | ||||||
|  |         points[x + 1] = iValue; | ||||||
|  |       } | ||||||
|  |       transform.transformPoints(points); | ||||||
|  |       // Quick check to see if points transformed to something inside the image; | ||||||
|  |       // sufficient to check the endpoints | ||||||
|  |       checkAndNudgePoints(image, points); | ||||||
|  |       try { | ||||||
|  |         for (int x = 0; x < max; x += 2) { | ||||||
|  |           if (image.get((int) points[x], (int) points[x + 1])) { | ||||||
|  |             // Black(-ish) pixel | ||||||
|  |             bits.set(x >> 1, y); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } catch (ArrayIndexOutOfBoundsException aioobe) { | ||||||
|  |         // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting | ||||||
|  |         // transform gets "twisted" such that it maps a straight line of points to a set of points | ||||||
|  |         // whose endpoints are in bounds, but others are not. There is probably some mathematical | ||||||
|  |         // way to detect this about the transformation that I don't know yet. | ||||||
|  |         // This results in an ugly runtime exception despite our clever checks above -- can't have | ||||||
|  |         // that. We could check each point's coordinates but that feels duplicative. We settle for | ||||||
|  |         // catching and wrapping ArrayIndexOutOfBoundsException. | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return bits; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								src/com/google/zxing/common/DetectorResult.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/com/google/zxing/common/DetectorResult.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Encapsulates the result of detecting a barcode in an image. This includes the raw | ||||||
|  |  * matrix of black/white pixels corresponding to the barcode, and possibly points of interest | ||||||
|  |  * in the image, like the location of finder patterns or corners of the barcode in the image.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class DetectorResult { | ||||||
|  | 
 | ||||||
|  |   private final BitMatrix bits; | ||||||
|  |   private final ResultPoint[] points; | ||||||
|  | 
 | ||||||
|  |   public DetectorResult(BitMatrix bits, ResultPoint[] points) { | ||||||
|  |     this.bits = bits; | ||||||
|  |     this.points = points; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BitMatrix getBits() { | ||||||
|  |     return bits; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public ResultPoint[] getPoints() { | ||||||
|  |     return points; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/com/google/zxing/common/ECI.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/com/google/zxing/common/ECI.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations" | ||||||
|  |  * 5.3 of ISO 18004. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public abstract class ECI { | ||||||
|  | 
 | ||||||
|  |   private final int value; | ||||||
|  | 
 | ||||||
|  |   ECI(int value) { | ||||||
|  |     this.value = value; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int getValue() { | ||||||
|  |     return value; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param value ECI value | ||||||
|  |    * @return {@link ECI} representing ECI of given value, or null if it is legal but unsupported | ||||||
|  |    * @throws IllegalArgumentException if ECI value is invalid | ||||||
|  |    */ | ||||||
|  |   public static ECI getECIByValue(int value) { | ||||||
|  |     if (value < 0 || value > 999999) { | ||||||
|  |       throw new IllegalArgumentException("Bad ECI value: " + value); | ||||||
|  |     } | ||||||
|  |     if (value < 900) { // Character set ECIs use 000000 - 000899 | ||||||
|  |       return CharacterSetECI.getCharacterSetECIByValue(value); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										196
									
								
								src/com/google/zxing/common/GlobalHistogramBinarizer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/com/google/zxing/common/GlobalHistogramBinarizer.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Binarizer; | ||||||
|  | import com.google.zxing.LuminanceSource; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable | ||||||
|  |  * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding | ||||||
|  |  * algorithm. However, because it picks a global black point, it cannot handle difficult shadows | ||||||
|  |  * and gradients. | ||||||
|  |  * | ||||||
|  |  * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public class GlobalHistogramBinarizer extends Binarizer { | ||||||
|  | 
 | ||||||
|  |   private static final int LUMINANCE_BITS = 5; | ||||||
|  |   private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS; | ||||||
|  |   private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS; | ||||||
|  | 
 | ||||||
|  |   private byte[] luminances = null; | ||||||
|  |   private int[] buckets = null; | ||||||
|  | 
 | ||||||
|  |   public GlobalHistogramBinarizer(LuminanceSource source) { | ||||||
|  |     super(source); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Applies simple sharpening to the row data to improve performance of the 1D Readers. | ||||||
|  |   public BitArray getBlackRow(int y, BitArray row) throws NotFoundException { | ||||||
|  |     LuminanceSource source = getLuminanceSource(); | ||||||
|  |     int width = source.getWidth(); | ||||||
|  |     if (row == null || row.getSize() < width) { | ||||||
|  |       row = new BitArray(width); | ||||||
|  |     } else { | ||||||
|  |       row.clear(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     initArrays(width); | ||||||
|  |     byte[] localLuminances = source.getRow(y, luminances); | ||||||
|  |     int[] localBuckets = buckets; | ||||||
|  |     for (int x = 0; x < width; x++) { | ||||||
|  |       int pixel = localLuminances[x] & 0xff; | ||||||
|  |       localBuckets[pixel >> LUMINANCE_SHIFT]++; | ||||||
|  |     } | ||||||
|  |     int blackPoint = estimateBlackPoint(localBuckets); | ||||||
|  | 
 | ||||||
|  |     int left = localLuminances[0] & 0xff; | ||||||
|  |     int center = localLuminances[1] & 0xff; | ||||||
|  |     for (int x = 1; x < width - 1; x++) { | ||||||
|  |       int right = localLuminances[x + 1] & 0xff; | ||||||
|  |       // A simple -1 4 -1 box filter with a weight of 2. | ||||||
|  |       int luminance = ((center << 2) - left - right) >> 1; | ||||||
|  |       if (luminance < blackPoint) { | ||||||
|  |         row.set(x); | ||||||
|  |       } | ||||||
|  |       left = center; | ||||||
|  |       center = right; | ||||||
|  |     } | ||||||
|  |     return row; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Does not sharpen the data, as this call is intended to only be used by 2D Readers. | ||||||
|  |   public BitMatrix getBlackMatrix() throws NotFoundException { | ||||||
|  |     LuminanceSource source = getLuminanceSource(); | ||||||
|  |     int width = source.getWidth(); | ||||||
|  |     int height = source.getHeight(); | ||||||
|  |     BitMatrix matrix = new BitMatrix(width, height); | ||||||
|  | 
 | ||||||
|  |     // Quickly calculates the histogram by sampling four rows from the image. This proved to be | ||||||
|  |     // more robust on the blackbox tests than sampling a diagonal as we used to do. | ||||||
|  |     initArrays(width); | ||||||
|  |     int[] localBuckets = buckets; | ||||||
|  |     for (int y = 1; y < 5; y++) { | ||||||
|  |       int row = height * y / 5; | ||||||
|  |       byte[] localLuminances = source.getRow(row, luminances); | ||||||
|  |       int right = (width << 2) / 5; | ||||||
|  |       for (int x = width / 5; x < right; x++) { | ||||||
|  |         int pixel = localLuminances[x] & 0xff; | ||||||
|  |         localBuckets[pixel >> LUMINANCE_SHIFT]++; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     int blackPoint = estimateBlackPoint(localBuckets); | ||||||
|  | 
 | ||||||
|  |     // We delay reading the entire image luminance until the black point estimation succeeds. | ||||||
|  |     // Although we end up reading four rows twice, it is consistent with our motto of | ||||||
|  |     // "fail quickly" which is necessary for continuous scanning. | ||||||
|  |     byte[] localLuminances = source.getMatrix(); | ||||||
|  |     for (int y = 0; y < height; y++) { | ||||||
|  |       int offset = y * width; | ||||||
|  |       for (int x = 0; x< width; x++) { | ||||||
|  |         int pixel = localLuminances[offset + x] & 0xff; | ||||||
|  |         if (pixel < blackPoint) { | ||||||
|  |           matrix.set(x, y); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return matrix; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Binarizer createBinarizer(LuminanceSource source) { | ||||||
|  |     return new GlobalHistogramBinarizer(source); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private void initArrays(int luminanceSize) { | ||||||
|  |     if (luminances == null || luminances.length < luminanceSize) { | ||||||
|  |       luminances = new byte[luminanceSize]; | ||||||
|  |     } | ||||||
|  |     if (buckets == null) { | ||||||
|  |       buckets = new int[LUMINANCE_BUCKETS]; | ||||||
|  |     } else { | ||||||
|  |       for (int x = 0; x < LUMINANCE_BUCKETS; x++) { | ||||||
|  |         buckets[x] = 0; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static int estimateBlackPoint(int[] buckets) throws NotFoundException { | ||||||
|  |     // Find the tallest peak in the histogram. | ||||||
|  |     int numBuckets = buckets.length; | ||||||
|  |     int maxBucketCount = 0; | ||||||
|  |     int firstPeak = 0; | ||||||
|  |     int firstPeakSize = 0; | ||||||
|  |     for (int x = 0; x < numBuckets; x++) { | ||||||
|  |       if (buckets[x] > firstPeakSize) { | ||||||
|  |         firstPeak = x; | ||||||
|  |         firstPeakSize = buckets[x]; | ||||||
|  |       } | ||||||
|  |       if (buckets[x] > maxBucketCount) { | ||||||
|  |         maxBucketCount = buckets[x]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Find the second-tallest peak which is somewhat far from the tallest peak. | ||||||
|  |     int secondPeak = 0; | ||||||
|  |     int secondPeakScore = 0; | ||||||
|  |     for (int x = 0; x < numBuckets; x++) { | ||||||
|  |       int distanceToBiggest = x - firstPeak; | ||||||
|  |       // Encourage more distant second peaks by multiplying by square of distance. | ||||||
|  |       int score = buckets[x] * distanceToBiggest * distanceToBiggest; | ||||||
|  |       if (score > secondPeakScore) { | ||||||
|  |         secondPeak = x; | ||||||
|  |         secondPeakScore = score; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Make sure firstPeak corresponds to the black peak. | ||||||
|  |     if (firstPeak > secondPeak) { | ||||||
|  |       int temp = firstPeak; | ||||||
|  |       firstPeak = secondPeak; | ||||||
|  |       secondPeak = temp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // If there is too little contrast in the image to pick a meaningful black point, throw rather | ||||||
|  |     // than waste time trying to decode the image, and risk false positives. | ||||||
|  |     // TODO: It might be worth comparing the brightest and darkest pixels seen, rather than the | ||||||
|  |     // two peaks, to determine the contrast. | ||||||
|  |     if (secondPeak - firstPeak <= numBuckets >> 4) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Find a valley between them that is low and closer to the white peak. | ||||||
|  |     int bestValley = secondPeak - 1; | ||||||
|  |     int bestValleyScore = -1; | ||||||
|  |     for (int x = secondPeak - 1; x > firstPeak; x--) { | ||||||
|  |       int fromFirst = x - firstPeak; | ||||||
|  |       int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]); | ||||||
|  |       if (score > bestValleyScore) { | ||||||
|  |         bestValley = x; | ||||||
|  |         bestValleyScore = score; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return bestValley << LUMINANCE_SHIFT; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										171
									
								
								src/com/google/zxing/common/GridSampler.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/com/google/zxing/common/GridSampler.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,171 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implementations of this class can, given locations of finder patterns for a QR code in an | ||||||
|  |  * image, sample the right points in the image to reconstruct the QR code, accounting for | ||||||
|  |  * perspective distortion. It is abstracted since it is relatively expensive and should be allowed | ||||||
|  |  * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced | ||||||
|  |  * Imaging library, but which may not be available in other environments such as J2ME, and vice | ||||||
|  |  * versa. | ||||||
|  |  * | ||||||
|  |  * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)} | ||||||
|  |  * with an instance of a class which implements this interface. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public abstract class GridSampler { | ||||||
|  | 
 | ||||||
|  |   private static GridSampler gridSampler = new DefaultGridSampler(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Sets the implementation of {@link GridSampler} used by the library. One global | ||||||
|  |    * instance is stored, which may sound problematic. But, the implementation provided | ||||||
|  |    * ought to be appropriate for the entire platform, and all uses of this library | ||||||
|  |    * in the whole lifetime of the JVM. For instance, an Android activity can swap in | ||||||
|  |    * an implementation that takes advantage of native platform libraries. | ||||||
|  |    *  | ||||||
|  |    * @param newGridSampler The platform-specific object to install. | ||||||
|  |    */ | ||||||
|  |   public static void setGridSampler(GridSampler newGridSampler) { | ||||||
|  |     if (newGridSampler == null) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     gridSampler = newGridSampler; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return the current implementation of {@link GridSampler} | ||||||
|  |    */ | ||||||
|  |   public static GridSampler getInstance() { | ||||||
|  |     return gridSampler; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Samples an image for a square matrix of bits of the given dimension. This is used to extract | ||||||
|  |    * the black/white modules of a 2D barcode like a QR Code found in an image. Because this barcode | ||||||
|  |    * may be rotated or perspective-distorted, the caller supplies four points in the source image | ||||||
|  |    * that define known points in the barcode, so that the image may be sampled appropriately.</p> | ||||||
|  |    * | ||||||
|  |    * <p>The last eight "from" parameters are four X/Y coordinate pairs of locations of points in | ||||||
|  |    * the image that define some significant points in the image to be sample. For example, | ||||||
|  |    * these may be the location of finder pattern in a QR Code.</p> | ||||||
|  |    * | ||||||
|  |    * <p>The first eight "to" parameters are four X/Y coordinate pairs measured in the destination | ||||||
|  |    * {@link BitMatrix}, from the top left, where the known points in the image given by the "from" | ||||||
|  |    * parameters map to.</p> | ||||||
|  |    * | ||||||
|  |    * <p>These 16 parameters define the transformation needed to sample the image.</p> | ||||||
|  |    * | ||||||
|  |    * @param image image to sample | ||||||
|  |    * @param dimension width/height of {@link BitMatrix} to sample from image | ||||||
|  |    * @return {@link BitMatrix} representing a grid of points sampled from the image within a region | ||||||
|  |    *   defined by the "from" parameters | ||||||
|  |    * @throws NotFoundException if image can't be sampled, for example, if the transformation defined | ||||||
|  |    *   by the given points is invalid or results in sampling outside the image boundaries | ||||||
|  |    */ | ||||||
|  |   public abstract BitMatrix sampleGrid(BitMatrix image, | ||||||
|  |                                        int dimension, | ||||||
|  |                                        float p1ToX, float p1ToY, | ||||||
|  |                                        float p2ToX, float p2ToY, | ||||||
|  |                                        float p3ToX, float p3ToY, | ||||||
|  |                                        float p4ToX, float p4ToY, | ||||||
|  |                                        float p1FromX, float p1FromY, | ||||||
|  |                                        float p2FromX, float p2FromY, | ||||||
|  |                                        float p3FromX, float p3FromY, | ||||||
|  |                                        float p4FromX, float p4FromY) throws NotFoundException; | ||||||
|  | 
 | ||||||
|  |   public BitMatrix sampleGrid(BitMatrix image, | ||||||
|  |                               int dimension, | ||||||
|  |                               PerspectiveTransform transform) throws NotFoundException { | ||||||
|  |     throw new IllegalStateException(); // Can't use UnsupportedOperationException | ||||||
|  |   } | ||||||
|  |    | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Checks a set of points that have been transformed to sample points on an image against | ||||||
|  |    * the image's dimensions to see if the point are even within the image.</p> | ||||||
|  |    * | ||||||
|  |    * <p>This method will actually "nudge" the endpoints back onto the image if they are found to be | ||||||
|  |    * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder | ||||||
|  |    * patterns in an image where the QR Code runs all the way to the image border.</p> | ||||||
|  |    * | ||||||
|  |    * <p>For efficiency, the method will check points from either end of the line until one is found | ||||||
|  |    * to be within the image. Because the set of points are assumed to be linear, this is valid.</p> | ||||||
|  |    * | ||||||
|  |    * @param image image into which the points should map | ||||||
|  |    * @param points actual points in x1,y1,...,xn,yn form | ||||||
|  |    * @throws NotFoundException if an endpoint is lies outside the image boundaries | ||||||
|  |    */ | ||||||
|  |   protected static void checkAndNudgePoints(BitMatrix image, float[] points) | ||||||
|  |       throws NotFoundException { | ||||||
|  |     int width = image.getWidth(); | ||||||
|  |     int height = image.getHeight(); | ||||||
|  |     // Check and nudge points from start until we see some that are OK: | ||||||
|  |     boolean nudged = true; | ||||||
|  |     for (int offset = 0; offset < points.length && nudged; offset += 2) { | ||||||
|  |       int x = (int) points[offset]; | ||||||
|  |       int y = (int) points[offset + 1]; | ||||||
|  |       if (x < -1 || x > width || y < -1 || y > height) { | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  |       nudged = false; | ||||||
|  |       if (x == -1) { | ||||||
|  |         points[offset] = 0.0f; | ||||||
|  |         nudged = true; | ||||||
|  |       } else if (x == width) { | ||||||
|  |         points[offset] = width - 1; | ||||||
|  |         nudged = true; | ||||||
|  |       } | ||||||
|  |       if (y == -1) { | ||||||
|  |         points[offset + 1] = 0.0f; | ||||||
|  |         nudged = true; | ||||||
|  |       } else if (y == height) { | ||||||
|  |         points[offset + 1] = height - 1; | ||||||
|  |         nudged = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     // Check and nudge points from end: | ||||||
|  |     nudged = true; | ||||||
|  |     for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) { | ||||||
|  |       int x = (int) points[offset]; | ||||||
|  |       int y = (int) points[offset + 1]; | ||||||
|  |       if (x < -1 || x > width || y < -1 || y > height) { | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  |       nudged = false; | ||||||
|  |       if (x == -1) { | ||||||
|  |         points[offset] = 0.0f; | ||||||
|  |         nudged = true; | ||||||
|  |       } else if (x == width) { | ||||||
|  |         points[offset] = width - 1; | ||||||
|  |         nudged = true; | ||||||
|  |       } | ||||||
|  |       if (y == -1) { | ||||||
|  |         points[offset + 1] = 0.0f; | ||||||
|  |         nudged = true; | ||||||
|  |       } else if (y == height) { | ||||||
|  |         points[offset + 1] = height - 1; | ||||||
|  |         nudged = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										185
									
								
								src/com/google/zxing/common/HybridBinarizer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/com/google/zxing/common/HybridBinarizer.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.Binarizer; | ||||||
|  | import com.google.zxing.LuminanceSource; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This class implements a local thresholding algorithm, which while slower than the | ||||||
|  |  * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for | ||||||
|  |  * high frequency images of barcodes with black data on white backgrounds. For this application, | ||||||
|  |  * it does a much better job than a global blackpoint with severe shadows and gradients. | ||||||
|  |  * However it tends to produce artifacts on lower frequency images and is therefore not | ||||||
|  |  * a good general purpose binarizer for uses outside ZXing. | ||||||
|  |  * | ||||||
|  |  * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers, | ||||||
|  |  * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already | ||||||
|  |  * inherently local, and only fails for horizontal gradients. We can revisit that problem later, | ||||||
|  |  * but for now it was not a win to use local blocks for 1D. | ||||||
|  |  * | ||||||
|  |  * This Binarizer is the default for the unit tests and the recommended class for library users. | ||||||
|  |  * | ||||||
|  |  * @author dswitkin@google.com (Daniel Switkin) | ||||||
|  |  */ | ||||||
|  | public final class HybridBinarizer extends GlobalHistogramBinarizer { | ||||||
|  | 
 | ||||||
|  |   // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels. | ||||||
|  |   // So this is the smallest dimension in each axis we can accept. | ||||||
|  |   private static final int MINIMUM_DIMENSION = 40; | ||||||
|  | 
 | ||||||
|  |   private BitMatrix matrix = null; | ||||||
|  | 
 | ||||||
|  |   public HybridBinarizer(LuminanceSource source) { | ||||||
|  |     super(source); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public BitMatrix getBlackMatrix() throws NotFoundException { | ||||||
|  |     binarizeEntireImage(); | ||||||
|  |     return matrix; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Binarizer createBinarizer(LuminanceSource source) { | ||||||
|  |     return new HybridBinarizer(source); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Calculates the final BitMatrix once for all requests. This could be called once from the | ||||||
|  |   // constructor instead, but there are some advantages to doing it lazily, such as making | ||||||
|  |   // profiling easier, and not doing heavy lifting when callers don't expect it. | ||||||
|  |   private void binarizeEntireImage() throws NotFoundException { | ||||||
|  |     if (matrix == null) { | ||||||
|  |       LuminanceSource source = getLuminanceSource(); | ||||||
|  |       if (source.getWidth() >= MINIMUM_DIMENSION && source.getHeight() >= MINIMUM_DIMENSION) { | ||||||
|  |         byte[] luminances = source.getMatrix(); | ||||||
|  |         int width = source.getWidth(); | ||||||
|  |         int height = source.getHeight(); | ||||||
|  |         int subWidth = width >> 3; | ||||||
|  |         if ((width & 0x07) != 0) { | ||||||
|  |           subWidth++; | ||||||
|  |         } | ||||||
|  |         int subHeight = height >> 3; | ||||||
|  |         if ((height & 0x07) != 0) { | ||||||
|  |           subHeight++; | ||||||
|  |         } | ||||||
|  |         int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height); | ||||||
|  | 
 | ||||||
|  |         matrix = new BitMatrix(width, height); | ||||||
|  |         calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, matrix); | ||||||
|  |       } else { | ||||||
|  |         // If the image is too small, fall back to the global histogram approach. | ||||||
|  |         matrix = super.getBlackMatrix(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // For each 8x8 block in the image, calculate the average black point using a 5x5 grid | ||||||
|  |   // of the blocks around it. Also handles the corner cases (fractional blocks are computed based | ||||||
|  |   // on the last 8 pixels in the row/column which are also used in the previous block). | ||||||
|  |   private static void calculateThresholdForBlock(byte[] luminances, int subWidth, int subHeight, | ||||||
|  |       int width, int height, int[][] blackPoints, BitMatrix matrix) { | ||||||
|  |     for (int y = 0; y < subHeight; y++) { | ||||||
|  |       int yoffset = y << 3; | ||||||
|  |       if ((yoffset + 8) >= height) { | ||||||
|  |         yoffset = height - 8; | ||||||
|  |       } | ||||||
|  |       for (int x = 0; x < subWidth; x++) { | ||||||
|  |         int xoffset = x << 3; | ||||||
|  |         if ((xoffset + 8) >= width) { | ||||||
|  |             xoffset = width - 8; | ||||||
|  |         } | ||||||
|  |         int left = (x > 1) ? x : 2; | ||||||
|  |         left = (left < subWidth - 2) ? left : subWidth - 3; | ||||||
|  |         int top = (y > 1) ? y : 2; | ||||||
|  |         top = (top < subHeight - 2) ? top : subHeight - 3; | ||||||
|  |         int sum = 0; | ||||||
|  |         for (int z = -2; z <= 2; z++) { | ||||||
|  |           int[] blackRow = blackPoints[top + z]; | ||||||
|  |           sum += blackRow[left - 2]; | ||||||
|  |           sum += blackRow[left - 1]; | ||||||
|  |           sum += blackRow[left]; | ||||||
|  |           sum += blackRow[left + 1]; | ||||||
|  |           sum += blackRow[left + 2]; | ||||||
|  |         } | ||||||
|  |         int average = sum / 25; | ||||||
|  |         threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Applies a single threshold to an 8x8 block of pixels. | ||||||
|  |   private static void threshold8x8Block(byte[] luminances, int xoffset, int yoffset, int threshold, | ||||||
|  |       int stride, BitMatrix matrix) { | ||||||
|  |     for (int y = 0; y < 8; y++) { | ||||||
|  |       int offset = (yoffset + y) * stride + xoffset; | ||||||
|  |       for (int x = 0; x < 8; x++) { | ||||||
|  |         int pixel = luminances[offset + x] & 0xff; | ||||||
|  |         if (pixel < threshold) { | ||||||
|  |           matrix.set(xoffset + x, yoffset + y); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Calculates a single black point for each 8x8 block of pixels and saves it away. | ||||||
|  |   private static int[][] calculateBlackPoints(byte[] luminances, int subWidth, int subHeight, | ||||||
|  |       int width, int height) { | ||||||
|  |     int[][] blackPoints = new int[subHeight][subWidth]; | ||||||
|  |     for (int y = 0; y < subHeight; y++) { | ||||||
|  |       int yoffset = y << 3; | ||||||
|  |       if ((yoffset + 8) >= height) { | ||||||
|  |         yoffset = height - 8; | ||||||
|  |       } | ||||||
|  |       for (int x = 0; x < subWidth; x++) { | ||||||
|  |         int xoffset = x << 3; | ||||||
|  |         if ((xoffset + 8) >= width) { | ||||||
|  |             xoffset = width - 8; | ||||||
|  |         } | ||||||
|  |         int sum = 0; | ||||||
|  |         int min = 255; | ||||||
|  |         int max = 0; | ||||||
|  |         for (int yy = 0; yy < 8; yy++) { | ||||||
|  |           int offset = (yoffset + yy) * width + xoffset; | ||||||
|  |           for (int xx = 0; xx < 8; xx++) { | ||||||
|  |             int pixel = luminances[offset + xx] & 0xff; | ||||||
|  |             sum += pixel; | ||||||
|  |             if (pixel < min) { | ||||||
|  |               min = pixel; | ||||||
|  |             } | ||||||
|  |             if (pixel > max) { | ||||||
|  |               max = pixel; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // If the contrast is inadequate, use half the minimum, so that this block will be | ||||||
|  |         // treated as part of the white background, but won't drag down neighboring blocks | ||||||
|  |         // too much. | ||||||
|  |         int average; | ||||||
|  |         if (max - min > 24) { | ||||||
|  |           average = sum >> 6; | ||||||
|  |         } else { | ||||||
|  |           // When min == max == 0, let average be 1 so all is black | ||||||
|  |           average = max == 0 ? 1 : min >> 1; | ||||||
|  |         } | ||||||
|  |         blackPoints[y][x] = average; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return blackPoints; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										148
									
								
								src/com/google/zxing/common/PerspectiveTransform.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/com/google/zxing/common/PerspectiveTransform.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,148 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>This class implements a perspective transform in two dimensions. Given four source and four | ||||||
|  |  * destination points, it will compute the transformation implied between them. The code is based | ||||||
|  |  * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class PerspectiveTransform { | ||||||
|  | 
 | ||||||
|  |   private final float a11, a12, a13, a21, a22, a23, a31, a32, a33; | ||||||
|  | 
 | ||||||
|  |   private PerspectiveTransform(float a11, float a21, float a31, | ||||||
|  |                                float a12, float a22, float a32, | ||||||
|  |                                float a13, float a23, float a33) { | ||||||
|  |     this.a11 = a11; | ||||||
|  |     this.a12 = a12; | ||||||
|  |     this.a13 = a13; | ||||||
|  |     this.a21 = a21; | ||||||
|  |     this.a22 = a22; | ||||||
|  |     this.a23 = a23; | ||||||
|  |     this.a31 = a31; | ||||||
|  |     this.a32 = a32; | ||||||
|  |     this.a33 = a33; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0, | ||||||
|  |                                                                   float x1, float y1, | ||||||
|  |                                                                   float x2, float y2, | ||||||
|  |                                                                   float x3, float y3, | ||||||
|  |                                                                   float x0p, float y0p, | ||||||
|  |                                                                   float x1p, float y1p, | ||||||
|  |                                                                   float x2p, float y2p, | ||||||
|  |                                                                   float x3p, float y3p) { | ||||||
|  | 
 | ||||||
|  |     PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); | ||||||
|  |     PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); | ||||||
|  |     return sToQ.times(qToS); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void transformPoints(float[] points) { | ||||||
|  |     int max = points.length; | ||||||
|  |     float a11 = this.a11; | ||||||
|  |     float a12 = this.a12; | ||||||
|  |     float a13 = this.a13; | ||||||
|  |     float a21 = this.a21; | ||||||
|  |     float a22 = this.a22; | ||||||
|  |     float a23 = this.a23; | ||||||
|  |     float a31 = this.a31; | ||||||
|  |     float a32 = this.a32; | ||||||
|  |     float a33 = this.a33; | ||||||
|  |     for (int i = 0; i < max; i += 2) { | ||||||
|  |       float x = points[i]; | ||||||
|  |       float y = points[i + 1]; | ||||||
|  |       float denominator = a13 * x + a23 * y + a33; | ||||||
|  |       points[i] = (a11 * x + a21 * y + a31) / denominator; | ||||||
|  |       points[i + 1] = (a12 * x + a22 * y + a32) / denominator; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** Convenience method, not optimized for performance. */ | ||||||
|  |   public void transformPoints(float[] xValues, float[] yValues) { | ||||||
|  |     int n = xValues.length; | ||||||
|  |     for (int i = 0; i < n; i ++) { | ||||||
|  |       float x = xValues[i]; | ||||||
|  |       float y = yValues[i]; | ||||||
|  |       float denominator = a13 * x + a23 * y + a33; | ||||||
|  |       xValues[i] = (a11 * x + a21 * y + a31) / denominator; | ||||||
|  |       yValues[i] = (a12 * x + a22 * y + a32) / denominator; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static PerspectiveTransform squareToQuadrilateral(float x0, float y0, | ||||||
|  |                                                            float x1, float y1, | ||||||
|  |                                                            float x2, float y2, | ||||||
|  |                                                            float x3, float y3) { | ||||||
|  |     float dy2 = y3 - y2; | ||||||
|  |     float dy3 = y0 - y1 + y2 - y3; | ||||||
|  |     if (dy2 == 0.0f && dy3 == 0.0f) { | ||||||
|  |       return new PerspectiveTransform(x1 - x0, x2 - x1, x0, | ||||||
|  |           y1 - y0, y2 - y1, y0, | ||||||
|  |           0.0f, 0.0f, 1.0f); | ||||||
|  |     } else { | ||||||
|  |       float dx1 = x1 - x2; | ||||||
|  |       float dx2 = x3 - x2; | ||||||
|  |       float dx3 = x0 - x1 + x2 - x3; | ||||||
|  |       float dy1 = y1 - y2; | ||||||
|  |       float denominator = dx1 * dy2 - dx2 * dy1; | ||||||
|  |       float a13 = (dx3 * dy2 - dx2 * dy3) / denominator; | ||||||
|  |       float a23 = (dx1 * dy3 - dx3 * dy1) / denominator; | ||||||
|  |       return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, | ||||||
|  |           y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, | ||||||
|  |           a13, a23, 1.0f); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public static PerspectiveTransform quadrilateralToSquare(float x0, float y0, | ||||||
|  |                                                            float x1, float y1, | ||||||
|  |                                                            float x2, float y2, | ||||||
|  |                                                            float x3, float y3) { | ||||||
|  |     // Here, the adjoint serves as the inverse: | ||||||
|  |     return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   PerspectiveTransform buildAdjoint() { | ||||||
|  |     // Adjoint is the transpose of the cofactor matrix: | ||||||
|  |     return new PerspectiveTransform(a22 * a33 - a23 * a32, | ||||||
|  |         a23 * a31 - a21 * a33, | ||||||
|  |         a21 * a32 - a22 * a31, | ||||||
|  |         a13 * a32 - a12 * a33, | ||||||
|  |         a11 * a33 - a13 * a31, | ||||||
|  |         a12 * a31 - a11 * a32, | ||||||
|  |         a12 * a23 - a13 * a22, | ||||||
|  |         a13 * a21 - a11 * a23, | ||||||
|  |         a11 * a22 - a12 * a21); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   PerspectiveTransform times(PerspectiveTransform other) { | ||||||
|  |     return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13, | ||||||
|  |         a11 * other.a21 + a21 * other.a22 + a31 * other.a23, | ||||||
|  |         a11 * other.a31 + a21 * other.a32 + a31 * other.a33, | ||||||
|  |         a12 * other.a11 + a22 * other.a12 + a32 * other.a13, | ||||||
|  |         a12 * other.a21 + a22 * other.a22 + a32 * other.a23, | ||||||
|  |         a12 * other.a31 + a22 * other.a32 + a32 * other.a33, | ||||||
|  |         a13 * other.a11 + a23 * other.a12 + a33 * other.a13, | ||||||
|  |         a13 * other.a21 + a23 * other.a22 + a33 * other.a23, | ||||||
|  |         a13 * other.a31 + a23 * other.a32 + a33 * other.a33); | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										191
									
								
								src/com/google/zxing/common/StringUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/com/google/zxing/common/StringUtils.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,191 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.DecodeHintType; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Common string-related functions. | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class StringUtils { | ||||||
|  | 
 | ||||||
|  |   private static final String PLATFORM_DEFAULT_ENCODING = | ||||||
|  |       System.getProperty("file.encoding"); | ||||||
|  |   public static final String SHIFT_JIS = "SJIS"; | ||||||
|  |   private static final String EUC_JP = "EUC_JP"; | ||||||
|  |   private static final String UTF8 = "UTF8"; | ||||||
|  |   private static final String ISO88591 = "ISO8859_1"; | ||||||
|  |   private static final boolean ASSUME_SHIFT_JIS = | ||||||
|  |       SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) || | ||||||
|  |       EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING); | ||||||
|  | 
 | ||||||
|  |   private StringUtils() {} | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param bytes bytes encoding a string, whose encoding should be guessed | ||||||
|  |    * @param hints decode hints if applicable | ||||||
|  |    * @return name of guessed encoding; at the moment will only guess one of: | ||||||
|  |    *  {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform | ||||||
|  |    *  default encoding if none of these can possibly be correct | ||||||
|  |    */ | ||||||
|  |   public static String guessEncoding(byte[] bytes, Hashtable hints) { | ||||||
|  |     if (hints != null) { | ||||||
|  |       String characterSet = (String) hints.get(DecodeHintType.CHARACTER_SET); | ||||||
|  |       if (characterSet != null) { | ||||||
|  |         return characterSet; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     // Does it start with the UTF-8 byte order mark? then guess it's UTF-8 | ||||||
|  |     if (bytes.length > 3 && | ||||||
|  |         bytes[0] == (byte) 0xEF && | ||||||
|  |         bytes[1] == (byte) 0xBB && | ||||||
|  |         bytes[2] == (byte) 0xBF) { | ||||||
|  |       return UTF8; | ||||||
|  |     } | ||||||
|  |     // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, | ||||||
|  |     // which should be by far the most common encodings. ISO-8859-1 | ||||||
|  |     // should not have bytes in the 0x80 - 0x9F range, while Shift_JIS | ||||||
|  |     // uses this as a first byte of a two-byte character. If we see this | ||||||
|  |     // followed by a valid second byte in Shift_JIS, assume it is Shift_JIS. | ||||||
|  |     // If we see something else in that second byte, we'll make the risky guess | ||||||
|  |     // that it's UTF-8. | ||||||
|  |     int length = bytes.length; | ||||||
|  |     boolean canBeISO88591 = true; | ||||||
|  |     boolean canBeShiftJIS = true; | ||||||
|  |     boolean canBeUTF8 = true; | ||||||
|  |     int utf8BytesLeft = 0; | ||||||
|  |     int maybeDoubleByteCount = 0; | ||||||
|  |     int maybeSingleByteKatakanaCount = 0; | ||||||
|  |     boolean sawLatin1Supplement = false; | ||||||
|  |     boolean sawUTF8Start = false; | ||||||
|  |     boolean lastWasPossibleDoubleByteStart = false; | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; | ||||||
|  |          i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); | ||||||
|  |          i++) { | ||||||
|  | 
 | ||||||
|  |       int value = bytes[i] & 0xFF; | ||||||
|  | 
 | ||||||
|  |       // UTF-8 stuff | ||||||
|  |       if (value >= 0x80 && value <= 0xBF) { | ||||||
|  |         if (utf8BytesLeft > 0) { | ||||||
|  |           utf8BytesLeft--; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         if (utf8BytesLeft > 0) { | ||||||
|  |           canBeUTF8 = false; | ||||||
|  |         } | ||||||
|  |         if (value >= 0xC0 && value <= 0xFD) { | ||||||
|  |           sawUTF8Start = true; | ||||||
|  |           int valueCopy = value; | ||||||
|  |           while ((valueCopy & 0x40) != 0) { | ||||||
|  |             utf8BytesLeft++; | ||||||
|  |             valueCopy <<= 1; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // ISO-8859-1 stuff | ||||||
|  | 
 | ||||||
|  |       if ((value == 0xC2 || value == 0xC3) && i < length - 1) { | ||||||
|  |         // This is really a poor hack. The slightly more exotic characters people might want to put in | ||||||
|  |         // a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings | ||||||
|  |         // that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF]. | ||||||
|  |         int nextValue = bytes[i + 1] & 0xFF; | ||||||
|  |         if (nextValue <= 0xBF && | ||||||
|  |             ((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) { | ||||||
|  |           sawLatin1Supplement = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (value >= 0x7F && value <= 0x9F) { | ||||||
|  |         canBeISO88591 = false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Shift_JIS stuff | ||||||
|  | 
 | ||||||
|  |       if (value >= 0xA1 && value <= 0xDF) { | ||||||
|  |         // count the number of characters that might be a Shift_JIS single-byte Katakana character | ||||||
|  |         if (!lastWasPossibleDoubleByteStart) { | ||||||
|  |           maybeSingleByteKatakanaCount++; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (!lastWasPossibleDoubleByteStart && | ||||||
|  |           ((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) { | ||||||
|  |         canBeShiftJIS = false; | ||||||
|  |       } | ||||||
|  |       if (((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF))) { | ||||||
|  |         // These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid | ||||||
|  |         // second byte. | ||||||
|  |         if (lastWasPossibleDoubleByteStart) { | ||||||
|  |           // If we just checked this and the last byte for being a valid double-byte | ||||||
|  |           // char, don't check starting on this byte. If this and the last byte | ||||||
|  |           // formed a valid pair, then this shouldn't be checked to see if it starts | ||||||
|  |           // a double byte pair of course. | ||||||
|  |           lastWasPossibleDoubleByteStart = false; | ||||||
|  |         } else { | ||||||
|  |           // ... otherwise do check to see if this plus the next byte form a valid | ||||||
|  |           // double byte pair encoding a character. | ||||||
|  |           lastWasPossibleDoubleByteStart = true; | ||||||
|  |           if (i >= bytes.length - 1) { | ||||||
|  |             canBeShiftJIS = false; | ||||||
|  |           } else { | ||||||
|  |             int nextValue = bytes[i + 1] & 0xFF; | ||||||
|  |             if (nextValue < 0x40 || nextValue > 0xFC) { | ||||||
|  |               canBeShiftJIS = false; | ||||||
|  |             } else { | ||||||
|  |               maybeDoubleByteCount++; | ||||||
|  |             } | ||||||
|  |             // There is some conflicting information out there about which bytes can follow which in | ||||||
|  |             // double-byte Shift_JIS characters. The rule above seems to be the one that matches practice. | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         lastWasPossibleDoubleByteStart = false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (utf8BytesLeft > 0) { | ||||||
|  |       canBeUTF8 = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Easy -- if assuming Shift_JIS and no evidence it can't be, done | ||||||
|  |     if (canBeShiftJIS && ASSUME_SHIFT_JIS) { | ||||||
|  |       return SHIFT_JIS; | ||||||
|  |     } | ||||||
|  |     if (canBeUTF8 && sawUTF8Start) { | ||||||
|  |       return UTF8; | ||||||
|  |     } | ||||||
|  |     // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is: | ||||||
|  |     // - If we saw | ||||||
|  |     //   - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or | ||||||
|  |     //   - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1), | ||||||
|  |     // - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS | ||||||
|  |     if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) { | ||||||
|  |       return SHIFT_JIS; | ||||||
|  |     } | ||||||
|  |     // Otherwise, we default to ISO-8859-1 unless we know it can't be | ||||||
|  |     if (!sawLatin1Supplement && canBeISO88591) { | ||||||
|  |       return ISO88591; | ||||||
|  |     } | ||||||
|  |     // Otherwise, we take a wild guess with platform encoding | ||||||
|  |     return PLATFORM_DEFAULT_ENCODING; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,209 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.detector; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image. | ||||||
|  |  * It looks within a mostly white region of an image for a region of black and white, but mostly | ||||||
|  |  * black. It returns the four corners of the region, as best it can determine.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class MonochromeRectangleDetector { | ||||||
|  | 
 | ||||||
|  |   private static final int MAX_MODULES = 32; | ||||||
|  | 
 | ||||||
|  |   private final BitMatrix image; | ||||||
|  | 
 | ||||||
|  |   public MonochromeRectangleDetector(BitMatrix image) { | ||||||
|  |     this.image = image; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly | ||||||
|  |    * white, in an image.</p> | ||||||
|  |    * | ||||||
|  |    * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and | ||||||
|  |    *  last points are opposed on the diagonal, as are the second and third. The first point will be | ||||||
|  |    *  the topmost point and the last, the bottommost. The second point will be leftmost and the | ||||||
|  |    *  third, the rightmost | ||||||
|  |    * @throws NotFoundException if no Data Matrix Code can be found | ||||||
|  |    */ | ||||||
|  |   public ResultPoint[] detect() throws NotFoundException { | ||||||
|  |     int height = image.getHeight(); | ||||||
|  |     int width = image.getWidth(); | ||||||
|  |     int halfHeight = height >> 1; | ||||||
|  |     int halfWidth = width >> 1; | ||||||
|  |     int deltaY = Math.max(1, height / (MAX_MODULES << 3)); | ||||||
|  |     int deltaX = Math.max(1, width / (MAX_MODULES << 3)); | ||||||
|  | 
 | ||||||
|  |     int top = 0; | ||||||
|  |     int bottom = height; | ||||||
|  |     int left = 0; | ||||||
|  |     int right = width; | ||||||
|  |     ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, | ||||||
|  |         halfHeight, -deltaY, top, bottom, halfWidth >> 1); | ||||||
|  |     top = (int) pointA.getY() - 1; | ||||||
|  |     ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right, | ||||||
|  |         halfHeight, 0, top, bottom, halfHeight >> 1); | ||||||
|  |     left = (int) pointB.getX() - 1; | ||||||
|  |     ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, | ||||||
|  |         halfHeight, 0, top, bottom, halfHeight >> 1); | ||||||
|  |     right = (int) pointC.getX() + 1; | ||||||
|  |     ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, | ||||||
|  |         halfHeight, deltaY, top, bottom, halfWidth >> 1); | ||||||
|  |     bottom = (int) pointD.getY() + 1; | ||||||
|  | 
 | ||||||
|  |     // Go try to find point A again with better information -- might have been off at first. | ||||||
|  |     pointA = findCornerFromCenter(halfWidth, 0, left, right, | ||||||
|  |         halfHeight, -deltaY, top, bottom, halfWidth >> 2); | ||||||
|  | 
 | ||||||
|  |     return new ResultPoint[] { pointA, pointB, pointC, pointD }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center | ||||||
|  |    * point which should be within the barcode. | ||||||
|  |    * | ||||||
|  |    * @param centerX center's x component (horizontal) | ||||||
|  |    * @param deltaX same as deltaY but change in x per step instead | ||||||
|  |    * @param left minimum value of x | ||||||
|  |    * @param right maximum value of x | ||||||
|  |    * @param centerY center's y component (vertical) | ||||||
|  |    * @param deltaY change in y per step. If scanning up this is negative; down, positive; | ||||||
|  |    *  left or right, 0 | ||||||
|  |    * @param top minimum value of y to search through (meaningless when di == 0) | ||||||
|  |    * @param bottom maximum value of y | ||||||
|  |    * @param maxWhiteRun maximum run of white pixels that can still be considered to be within | ||||||
|  |    *  the barcode | ||||||
|  |    * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found | ||||||
|  |    * @throws NotFoundException if such a point cannot be found | ||||||
|  |    */ | ||||||
|  |   private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right, | ||||||
|  |       int centerY, int deltaY, int top, int bottom, int maxWhiteRun) throws NotFoundException { | ||||||
|  |     int[] lastRange = null; | ||||||
|  |     for (int y = centerY, x = centerX; | ||||||
|  |          y < bottom && y >= top && x < right && x >= left; | ||||||
|  |          y += deltaY, x += deltaX) { | ||||||
|  |       int[] range; | ||||||
|  |       if (deltaX == 0) { | ||||||
|  |         // horizontal slices, up and down | ||||||
|  |         range = blackWhiteRange(y, maxWhiteRun, left, right, true); | ||||||
|  |       } else { | ||||||
|  |         // vertical slices, left and right | ||||||
|  |         range = blackWhiteRange(x, maxWhiteRun, top, bottom, false); | ||||||
|  |       } | ||||||
|  |       if (range == null) { | ||||||
|  |         if (lastRange == null) { | ||||||
|  |           throw NotFoundException.getNotFoundInstance(); | ||||||
|  |         } | ||||||
|  |         // lastRange was found | ||||||
|  |         if (deltaX == 0) { | ||||||
|  |           int lastY = y - deltaY; | ||||||
|  |           if (lastRange[0] < centerX) { | ||||||
|  |             if (lastRange[1] > centerX) { | ||||||
|  |               // straddle, choose one or the other based on direction | ||||||
|  |               return new ResultPoint(deltaY > 0 ? lastRange[0] : lastRange[1], lastY); | ||||||
|  |             } | ||||||
|  |             return new ResultPoint(lastRange[0], lastY); | ||||||
|  |           } else { | ||||||
|  |             return new ResultPoint(lastRange[1], lastY); | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           int lastX = x - deltaX; | ||||||
|  |           if (lastRange[0] < centerY) { | ||||||
|  |             if (lastRange[1] > centerY) { | ||||||
|  |               return new ResultPoint(lastX, deltaX < 0 ? lastRange[0] : lastRange[1]); | ||||||
|  |             } | ||||||
|  |             return new ResultPoint(lastX, lastRange[0]); | ||||||
|  |           } else { | ||||||
|  |             return new ResultPoint(lastX, lastRange[1]); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       lastRange = range; | ||||||
|  |     } | ||||||
|  |     throw NotFoundException.getNotFoundInstance(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Computes the start and end of a region of pixels, either horizontally or vertically, that could | ||||||
|  |    * be part of a Data Matrix barcode. | ||||||
|  |    * | ||||||
|  |    * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) | ||||||
|  |    *  where we are scanning. If scanning vertically it's the column, the fixed horizontal location | ||||||
|  |    * @param maxWhiteRun largest run of white pixels that can still be considered part of the | ||||||
|  |    *  barcode region | ||||||
|  |    * @param minDim minimum pixel location, horizontally or vertically, to consider | ||||||
|  |    * @param maxDim maximum pixel location, horizontally or vertically, to consider | ||||||
|  |    * @param horizontal if true, we're scanning left-right, instead of up-down | ||||||
|  |    * @return int[] with start and end of found range, or null if no such range is found | ||||||
|  |    *  (e.g. only white was found) | ||||||
|  |    */ | ||||||
|  |   private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, | ||||||
|  |       boolean horizontal) { | ||||||
|  | 
 | ||||||
|  |     int center = (minDim + maxDim) >> 1; | ||||||
|  | 
 | ||||||
|  |     // Scan left/up first | ||||||
|  |     int start = center; | ||||||
|  |     while (start >= minDim) { | ||||||
|  |       if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) { | ||||||
|  |         start--; | ||||||
|  |       } else { | ||||||
|  |         int whiteRunStart = start; | ||||||
|  |         do { | ||||||
|  |           start--; | ||||||
|  |         } while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) : | ||||||
|  |             image.get(fixedDimension, start))); | ||||||
|  |         int whiteRunSize = whiteRunStart - start; | ||||||
|  |         if (start < minDim || whiteRunSize > maxWhiteRun) { | ||||||
|  |           start = whiteRunStart; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     start++; | ||||||
|  | 
 | ||||||
|  |     // Then try right/down | ||||||
|  |     int end = center; | ||||||
|  |     while (end < maxDim) { | ||||||
|  |       if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) { | ||||||
|  |         end++; | ||||||
|  |       } else { | ||||||
|  |         int whiteRunStart = end; | ||||||
|  |         do { | ||||||
|  |           end++; | ||||||
|  |         } while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) : | ||||||
|  |             image.get(fixedDimension, end))); | ||||||
|  |         int whiteRunSize = end - whiteRunStart; | ||||||
|  |         if (end >= maxDim || whiteRunSize > maxWhiteRun) { | ||||||
|  |           end = whiteRunStart; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     end--; | ||||||
|  | 
 | ||||||
|  |     return end > start ? new int[]{start, end} : null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										316
									
								
								src/com/google/zxing/common/detector/WhiteRectangleDetector.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								src/com/google/zxing/common/detector/WhiteRectangleDetector.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,316 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2010 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.detector; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p> | ||||||
|  |  * Detects a candidate barcode-like rectangular region within an image. It | ||||||
|  |  * starts around the center of the image, increases the size of the candidate | ||||||
|  |  * region until it finds a white rectangular region. By keeping track of the | ||||||
|  |  * last black points it encountered, it determines the corners of the barcode. | ||||||
|  |  * </p> | ||||||
|  |  * | ||||||
|  |  * @author David Olivier | ||||||
|  |  */ | ||||||
|  | public final class WhiteRectangleDetector { | ||||||
|  | 
 | ||||||
|  |   private static final int INIT_SIZE = 40; | ||||||
|  |   private static final int CORR = 1; | ||||||
|  | 
 | ||||||
|  |   private final BitMatrix image; | ||||||
|  |   private final int height; | ||||||
|  |   private final int width; | ||||||
|  | 
 | ||||||
|  |   public WhiteRectangleDetector(BitMatrix image) { | ||||||
|  |     this.image = image; | ||||||
|  |     height = image.getHeight(); | ||||||
|  |     width = image.getWidth(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p> | ||||||
|  |    * Detects a candidate barcode-like rectangular region within an image. It | ||||||
|  |    * starts around the center of the image, increases the size of the candidate | ||||||
|  |    * region until it finds a white rectangular region. | ||||||
|  |    * </p> | ||||||
|  |    * | ||||||
|  |    * @return {@link ResultPoint}[] describing the corners of the rectangular | ||||||
|  |    *         region. The first and last points are opposed on the diagonal, as | ||||||
|  |    *         are the second and third. The first point will be the topmost | ||||||
|  |    *         point and the last, the bottommost. The second point will be | ||||||
|  |    *         leftmost and the third, the rightmost | ||||||
|  |    * @throws NotFoundException if no Data Matrix Code can be found | ||||||
|  |    */ | ||||||
|  |   public ResultPoint[] detect() throws NotFoundException { | ||||||
|  | 
 | ||||||
|  |     int left = (width - INIT_SIZE) >> 1; | ||||||
|  |     int right = (width + INIT_SIZE) >> 1; | ||||||
|  |     int up = (height - INIT_SIZE) >> 1; | ||||||
|  |     int down = (height + INIT_SIZE) >> 1; | ||||||
|  |     boolean sizeExceeded = false; | ||||||
|  |     boolean aBlackPointFoundOnBorder = true; | ||||||
|  |     boolean atLeastOneBlackPointFoundOnBorder = false; | ||||||
|  | 
 | ||||||
|  |     while (aBlackPointFoundOnBorder) { | ||||||
|  | 
 | ||||||
|  |       aBlackPointFoundOnBorder = false; | ||||||
|  | 
 | ||||||
|  |       // ..... | ||||||
|  |       // .   | | ||||||
|  |       // ..... | ||||||
|  |       boolean rightBorderNotWhite = true; | ||||||
|  |       while (rightBorderNotWhite && right < width) { | ||||||
|  |         rightBorderNotWhite = containsBlackPoint(up, down, right, false); | ||||||
|  |         if (rightBorderNotWhite) { | ||||||
|  |           right++; | ||||||
|  |           aBlackPointFoundOnBorder = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (right >= width) { | ||||||
|  |         sizeExceeded = true; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // ..... | ||||||
|  |       // .   . | ||||||
|  |       // .___. | ||||||
|  |       boolean bottomBorderNotWhite = true; | ||||||
|  |       while (bottomBorderNotWhite && down < height) { | ||||||
|  |         bottomBorderNotWhite = containsBlackPoint(left, right, down, true); | ||||||
|  |         if (bottomBorderNotWhite) { | ||||||
|  |           down++; | ||||||
|  |           aBlackPointFoundOnBorder = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (down >= height) { | ||||||
|  |         sizeExceeded = true; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // ..... | ||||||
|  |       // |   . | ||||||
|  |       // ..... | ||||||
|  |       boolean leftBorderNotWhite = true; | ||||||
|  |       while (leftBorderNotWhite && left >= 0) { | ||||||
|  |         leftBorderNotWhite = containsBlackPoint(up, down, left, false); | ||||||
|  |         if (leftBorderNotWhite) { | ||||||
|  |           left--; | ||||||
|  |           aBlackPointFoundOnBorder = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (left < 0) { | ||||||
|  |         sizeExceeded = true; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // .___. | ||||||
|  |       // .   . | ||||||
|  |       // ..... | ||||||
|  |       boolean topBorderNotWhite = true; | ||||||
|  |       while (topBorderNotWhite && up >= 0) { | ||||||
|  |         topBorderNotWhite = containsBlackPoint(left, right, up, true); | ||||||
|  |         if (topBorderNotWhite) { | ||||||
|  |           up--; | ||||||
|  |           aBlackPointFoundOnBorder = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (up < 0) { | ||||||
|  |         sizeExceeded = true; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (aBlackPointFoundOnBorder) { | ||||||
|  |         atLeastOneBlackPointFoundOnBorder = true; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) { | ||||||
|  | 
 | ||||||
|  |       int maxSize = right - left; | ||||||
|  | 
 | ||||||
|  |       ResultPoint z = null; | ||||||
|  |       for (int i = 1; i < maxSize; i++) { | ||||||
|  |         z = getBlackPointOnSegment(left, down - i, left + i, down); | ||||||
|  |         if (z != null) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (z == null) { | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       ResultPoint t = null; | ||||||
|  |       //go down right | ||||||
|  |       for (int i = 1; i < maxSize; i++) { | ||||||
|  |         t = getBlackPointOnSegment(left, up + i, left + i, up); | ||||||
|  |         if (t != null) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (t == null) { | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       ResultPoint x = null; | ||||||
|  |       //go down left | ||||||
|  |       for (int i = 1; i < maxSize; i++) { | ||||||
|  |         x = getBlackPointOnSegment(right, up + i, right - i, up); | ||||||
|  |         if (x != null) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (x == null) { | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       ResultPoint y = null; | ||||||
|  |       //go up left | ||||||
|  |       for (int i = 1; i < maxSize; i++) { | ||||||
|  |         y = getBlackPointOnSegment(right, down - i, right - i, down); | ||||||
|  |         if (y != null) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (y == null) { | ||||||
|  |         throw NotFoundException.getNotFoundInstance(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return centerEdges(y, z, x, t); | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Ends up being a bit faster than Math.round(). This merely rounds its | ||||||
|  |    * argument to the nearest int, where x.5 rounds up. | ||||||
|  |    */ | ||||||
|  |   private static int round(float d) { | ||||||
|  |     return (int) (d + 0.5f); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) { | ||||||
|  |     int dist = distanceL2(aX, aY, bX, bY); | ||||||
|  |     float xStep = (bX - aX) / dist; | ||||||
|  |     float yStep = (bY - aY) / dist; | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < dist; i++) { | ||||||
|  |       int x = round(aX + i * xStep); | ||||||
|  |       int y = round(aY + i * yStep); | ||||||
|  |       if (image.get(x, y)) { | ||||||
|  |         return new ResultPoint(x, y); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static int distanceL2(float aX, float aY, float bX, float bY) { | ||||||
|  |     float xDiff = aX - bX; | ||||||
|  |     float yDiff = aY - bY; | ||||||
|  |     return round((float) Math.sqrt(xDiff * xDiff + yDiff * yDiff)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * recenters the points of a constant distance towards the center | ||||||
|  |    * | ||||||
|  |    * @param y bottom most point | ||||||
|  |    * @param z left most point | ||||||
|  |    * @param x right most point | ||||||
|  |    * @param t top most point | ||||||
|  |    * @return {@link ResultPoint}[] describing the corners of the rectangular | ||||||
|  |    *         region. The first and last points are opposed on the diagonal, as | ||||||
|  |    *         are the second and third. The first point will be the topmost | ||||||
|  |    *         point and the last, the bottommost. The second point will be | ||||||
|  |    *         leftmost and the third, the rightmost | ||||||
|  |    */ | ||||||
|  |   private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z, | ||||||
|  |                                     ResultPoint x, ResultPoint t) { | ||||||
|  | 
 | ||||||
|  |     // | ||||||
|  |     //       t            t | ||||||
|  |     //  z                      x | ||||||
|  |     //        x    OR    z | ||||||
|  |     //   y                    y | ||||||
|  |     // | ||||||
|  | 
 | ||||||
|  |     float yi = y.getX(); | ||||||
|  |     float yj = y.getY(); | ||||||
|  |     float zi = z.getX(); | ||||||
|  |     float zj = z.getY(); | ||||||
|  |     float xi = x.getX(); | ||||||
|  |     float xj = x.getY(); | ||||||
|  |     float ti = t.getX(); | ||||||
|  |     float tj = t.getY(); | ||||||
|  | 
 | ||||||
|  |     if (yi < width / 2) { | ||||||
|  |       return new ResultPoint[]{ | ||||||
|  |           new ResultPoint(ti - CORR, tj + CORR), | ||||||
|  |           new ResultPoint(zi + CORR, zj + CORR), | ||||||
|  |           new ResultPoint(xi - CORR, xj - CORR), | ||||||
|  |           new ResultPoint(yi + CORR, yj - CORR)}; | ||||||
|  |     } else { | ||||||
|  |       return new ResultPoint[]{ | ||||||
|  |           new ResultPoint(ti + CORR, tj + CORR), | ||||||
|  |           new ResultPoint(zi + CORR, zj - CORR), | ||||||
|  |           new ResultPoint(xi - CORR, xj + CORR), | ||||||
|  |           new ResultPoint(yi - CORR, yj - CORR)}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Determines whether a segment contains a black point | ||||||
|  |    * | ||||||
|  |    * @param a          min value of the scanned coordinate | ||||||
|  |    * @param b          max value of the scanned coordinate | ||||||
|  |    * @param fixed      value of fixed coordinate | ||||||
|  |    * @param horizontal set to true if scan must be horizontal, false if vertical | ||||||
|  |    * @return true if a black point has been found, else false. | ||||||
|  |    */ | ||||||
|  |   private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) { | ||||||
|  | 
 | ||||||
|  |     if (horizontal) { | ||||||
|  |       for (int x = a; x <= b; x++) { | ||||||
|  |         if (image.get(x, fixed)) { | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       for (int y = a; y <= b; y++) { | ||||||
|  |         if (image.get(fixed, y)) { | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										139
									
								
								src/com/google/zxing/common/reedsolomon/GF256.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/com/google/zxing/common/reedsolomon/GF256.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.reedsolomon; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>This class contains utility methods for performing mathematical operations over | ||||||
|  |  * the Galois Field GF(256). Operations use a given primitive polynomial in calculations.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Throughout this package, elements of GF(256) are represented as an <code>int</code> | ||||||
|  |  * for convenience and speed (but at the cost of memory). | ||||||
|  |  * Only the bottom 8 bits are really used.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class GF256 { | ||||||
|  | 
 | ||||||
|  |   public static final GF256 QR_CODE_FIELD = new GF256(0x011D); // x^8 + x^4 + x^3 + x^2 + 1 | ||||||
|  |   public static final GF256 DATA_MATRIX_FIELD = new GF256(0x012D); // x^8 + x^5 + x^3 + x^2 + 1 | ||||||
|  | 
 | ||||||
|  |   private final int[] expTable; | ||||||
|  |   private final int[] logTable; | ||||||
|  |   private final GF256Poly zero; | ||||||
|  |   private final GF256Poly one; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Create a representation of GF(256) using the given primitive polynomial. | ||||||
|  |    * | ||||||
|  |    * @param primitive irreducible polynomial whose coefficients are represented by | ||||||
|  |    *  the bits of an int, where the least-significant bit represents the constant | ||||||
|  |    *  coefficient | ||||||
|  |    */ | ||||||
|  |   private GF256(int primitive) { | ||||||
|  |     expTable = new int[256]; | ||||||
|  |     logTable = new int[256]; | ||||||
|  |     int x = 1; | ||||||
|  |     for (int i = 0; i < 256; i++) { | ||||||
|  |       expTable[i] = x; | ||||||
|  |       x <<= 1; // x = x * 2; we're assuming the generator alpha is 2 | ||||||
|  |       if (x >= 0x100) { | ||||||
|  |         x ^= primitive; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     for (int i = 0; i < 255; i++) { | ||||||
|  |       logTable[expTable[i]] = i; | ||||||
|  |     } | ||||||
|  |     // logTable[0] == 0 but this should never be used | ||||||
|  |     zero = new GF256Poly(this, new int[]{0}); | ||||||
|  |     one = new GF256Poly(this, new int[]{1}); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly getZero() { | ||||||
|  |     return zero; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly getOne() { | ||||||
|  |     return one; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return the monomial representing coefficient * x^degree | ||||||
|  |    */ | ||||||
|  |   GF256Poly buildMonomial(int degree, int coefficient) { | ||||||
|  |     if (degree < 0) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     if (coefficient == 0) { | ||||||
|  |       return zero; | ||||||
|  |     } | ||||||
|  |     int[] coefficients = new int[degree + 1]; | ||||||
|  |     coefficients[0] = coefficient; | ||||||
|  |     return new GF256Poly(this, coefficients); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Implements both addition and subtraction -- they are the same in GF(256). | ||||||
|  |    * | ||||||
|  |    * @return sum/difference of a and b | ||||||
|  |    */ | ||||||
|  |   static int addOrSubtract(int a, int b) { | ||||||
|  |     return a ^ b; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return 2 to the power of a in GF(256) | ||||||
|  |    */ | ||||||
|  |   int exp(int a) { | ||||||
|  |     return expTable[a]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return base 2 log of a in GF(256) | ||||||
|  |    */ | ||||||
|  |   int log(int a) { | ||||||
|  |     if (a == 0) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     return logTable[a]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return multiplicative inverse of a | ||||||
|  |    */ | ||||||
|  |   int inverse(int a) { | ||||||
|  |     if (a == 0) { | ||||||
|  |       throw new ArithmeticException(); | ||||||
|  |     } | ||||||
|  |     return expTable[255 - logTable[a]]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param a | ||||||
|  |    * @param b | ||||||
|  |    * @return product of a and b in GF(256) | ||||||
|  |    */ | ||||||
|  |   int multiply(int a, int b) { | ||||||
|  |     if (a == 0 || b == 0) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     int logSum = logTable[a] + logTable[b]; | ||||||
|  |     // index is a sped-up alternative to logSum % 255 since sum | ||||||
|  |     // is in [0,510]. Thanks to jmsachs for the idea | ||||||
|  |     return expTable[(logSum & 0xFF) + (logSum >>> 8)]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										263
									
								
								src/com/google/zxing/common/reedsolomon/GF256Poly.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/com/google/zxing/common/reedsolomon/GF256Poly.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,263 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.reedsolomon; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Represents a polynomial whose coefficients are elements of GF(256). | ||||||
|  |  * Instances of this class are immutable.</p> | ||||||
|  |  * | ||||||
|  |  * <p>Much credit is due to William Rucklidge since portions of this code are an indirect | ||||||
|  |  * port of his C++ Reed-Solomon implementation.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class GF256Poly { | ||||||
|  | 
 | ||||||
|  |   private final GF256 field; | ||||||
|  |   private final int[] coefficients; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param field the {@link GF256} instance representing the field to use | ||||||
|  |    * to perform computations | ||||||
|  |    * @param coefficients coefficients as ints representing elements of GF(256), arranged | ||||||
|  |    * from most significant (highest-power term) coefficient to least significant | ||||||
|  |    * @throws IllegalArgumentException if argument is null or empty, | ||||||
|  |    * or if leading coefficient is 0 and this is not a | ||||||
|  |    * constant polynomial (that is, it is not the monomial "0") | ||||||
|  |    */ | ||||||
|  |   GF256Poly(GF256 field, int[] coefficients) { | ||||||
|  |     if (coefficients == null || coefficients.length == 0) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     this.field = field; | ||||||
|  |     int coefficientsLength = coefficients.length; | ||||||
|  |     if (coefficientsLength > 1 && coefficients[0] == 0) { | ||||||
|  |       // Leading term must be non-zero for anything except the constant polynomial "0" | ||||||
|  |       int firstNonZero = 1; | ||||||
|  |       while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { | ||||||
|  |         firstNonZero++; | ||||||
|  |       } | ||||||
|  |       if (firstNonZero == coefficientsLength) { | ||||||
|  |         this.coefficients = field.getZero().coefficients; | ||||||
|  |       } else { | ||||||
|  |         this.coefficients = new int[coefficientsLength - firstNonZero]; | ||||||
|  |         System.arraycopy(coefficients, | ||||||
|  |             firstNonZero, | ||||||
|  |             this.coefficients, | ||||||
|  |             0, | ||||||
|  |             this.coefficients.length); | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       this.coefficients = coefficients; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int[] getCoefficients() { | ||||||
|  |     return coefficients; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return degree of this polynomial | ||||||
|  |    */ | ||||||
|  |   int getDegree() { | ||||||
|  |     return coefficients.length - 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return true iff this polynomial is the monomial "0" | ||||||
|  |    */ | ||||||
|  |   boolean isZero() { | ||||||
|  |     return coefficients[0] == 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return coefficient of x^degree term in this polynomial | ||||||
|  |    */ | ||||||
|  |   int getCoefficient(int degree) { | ||||||
|  |     return coefficients[coefficients.length - 1 - degree]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @return evaluation of this polynomial at a given point | ||||||
|  |    */ | ||||||
|  |   int evaluateAt(int a) { | ||||||
|  |     if (a == 0) { | ||||||
|  |       // Just return the x^0 coefficient | ||||||
|  |       return getCoefficient(0); | ||||||
|  |     } | ||||||
|  |     int size = coefficients.length; | ||||||
|  |     if (a == 1) { | ||||||
|  |       // Just the sum of the coefficients | ||||||
|  |       int result = 0; | ||||||
|  |       for (int i = 0; i < size; i++) { | ||||||
|  |         result = GF256.addOrSubtract(result, coefficients[i]); | ||||||
|  |       } | ||||||
|  |       return result; | ||||||
|  |     } | ||||||
|  |     int result = coefficients[0]; | ||||||
|  |     for (int i = 1; i < size; i++) { | ||||||
|  |       result = GF256.addOrSubtract(field.multiply(a, result), coefficients[i]); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly addOrSubtract(GF256Poly other) { | ||||||
|  |     if (!field.equals(other.field)) { | ||||||
|  |       throw new IllegalArgumentException("GF256Polys do not have same GF256 field"); | ||||||
|  |     } | ||||||
|  |     if (isZero()) { | ||||||
|  |       return other; | ||||||
|  |     } | ||||||
|  |     if (other.isZero()) { | ||||||
|  |       return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int[] smallerCoefficients = this.coefficients; | ||||||
|  |     int[] largerCoefficients = other.coefficients; | ||||||
|  |     if (smallerCoefficients.length > largerCoefficients.length) { | ||||||
|  |       int[] temp = smallerCoefficients; | ||||||
|  |       smallerCoefficients = largerCoefficients; | ||||||
|  |       largerCoefficients = temp; | ||||||
|  |     } | ||||||
|  |     int[] sumDiff = new int[largerCoefficients.length]; | ||||||
|  |     int lengthDiff = largerCoefficients.length - smallerCoefficients.length; | ||||||
|  |     // Copy high-order terms only found in higher-degree polynomial's coefficients | ||||||
|  |     System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); | ||||||
|  | 
 | ||||||
|  |     for (int i = lengthDiff; i < largerCoefficients.length; i++) { | ||||||
|  |       sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new GF256Poly(field, sumDiff); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly multiply(GF256Poly other) { | ||||||
|  |     if (!field.equals(other.field)) { | ||||||
|  |       throw new IllegalArgumentException("GF256Polys do not have same GF256 field"); | ||||||
|  |     } | ||||||
|  |     if (isZero() || other.isZero()) { | ||||||
|  |       return field.getZero(); | ||||||
|  |     } | ||||||
|  |     int[] aCoefficients = this.coefficients; | ||||||
|  |     int aLength = aCoefficients.length; | ||||||
|  |     int[] bCoefficients = other.coefficients; | ||||||
|  |     int bLength = bCoefficients.length; | ||||||
|  |     int[] product = new int[aLength + bLength - 1]; | ||||||
|  |     for (int i = 0; i < aLength; i++) { | ||||||
|  |       int aCoeff = aCoefficients[i]; | ||||||
|  |       for (int j = 0; j < bLength; j++) { | ||||||
|  |         product[i + j] = GF256.addOrSubtract(product[i + j], | ||||||
|  |             field.multiply(aCoeff, bCoefficients[j])); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return new GF256Poly(field, product); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly multiply(int scalar) { | ||||||
|  |     if (scalar == 0) { | ||||||
|  |       return field.getZero(); | ||||||
|  |     } | ||||||
|  |     if (scalar == 1) { | ||||||
|  |       return this; | ||||||
|  |     } | ||||||
|  |     int size = coefficients.length; | ||||||
|  |     int[] product = new int[size]; | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       product[i] = field.multiply(coefficients[i], scalar); | ||||||
|  |     } | ||||||
|  |     return new GF256Poly(field, product); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly multiplyByMonomial(int degree, int coefficient) { | ||||||
|  |     if (degree < 0) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  |     if (coefficient == 0) { | ||||||
|  |       return field.getZero(); | ||||||
|  |     } | ||||||
|  |     int size = coefficients.length; | ||||||
|  |     int[] product = new int[size + degree]; | ||||||
|  |     for (int i = 0; i < size; i++) { | ||||||
|  |       product[i] = field.multiply(coefficients[i], coefficient); | ||||||
|  |     } | ||||||
|  |     return new GF256Poly(field, product); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   GF256Poly[] divide(GF256Poly other) { | ||||||
|  |     if (!field.equals(other.field)) { | ||||||
|  |       throw new IllegalArgumentException("GF256Polys do not have same GF256 field"); | ||||||
|  |     } | ||||||
|  |     if (other.isZero()) { | ||||||
|  |       throw new IllegalArgumentException("Divide by 0"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     GF256Poly quotient = field.getZero(); | ||||||
|  |     GF256Poly remainder = this; | ||||||
|  | 
 | ||||||
|  |     int denominatorLeadingTerm = other.getCoefficient(other.getDegree()); | ||||||
|  |     int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); | ||||||
|  | 
 | ||||||
|  |     while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { | ||||||
|  |       int degreeDifference = remainder.getDegree() - other.getDegree(); | ||||||
|  |       int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); | ||||||
|  |       GF256Poly term = other.multiplyByMonomial(degreeDifference, scale); | ||||||
|  |       GF256Poly iterationQuotient = field.buildMonomial(degreeDifference, scale); | ||||||
|  |       quotient = quotient.addOrSubtract(iterationQuotient); | ||||||
|  |       remainder = remainder.addOrSubtract(term); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new GF256Poly[] { quotient, remainder }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     StringBuffer result = new StringBuffer(8 * getDegree()); | ||||||
|  |     for (int degree = getDegree(); degree >= 0; degree--) { | ||||||
|  |       int coefficient = getCoefficient(degree); | ||||||
|  |       if (coefficient != 0) { | ||||||
|  |         if (coefficient < 0) { | ||||||
|  |           result.append(" - "); | ||||||
|  |           coefficient = -coefficient; | ||||||
|  |         } else { | ||||||
|  |           if (result.length() > 0) { | ||||||
|  |             result.append(" + "); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         if (degree == 0 || coefficient != 1) { | ||||||
|  |           int alphaPower = field.log(coefficient); | ||||||
|  |           if (alphaPower == 0) { | ||||||
|  |             result.append('1'); | ||||||
|  |           } else if (alphaPower == 1) { | ||||||
|  |             result.append('a'); | ||||||
|  |           } else { | ||||||
|  |             result.append("a^"); | ||||||
|  |             result.append(alphaPower); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         if (degree != 0) { | ||||||
|  |           if (degree == 1) { | ||||||
|  |             result.append('x'); | ||||||
|  |           } else { | ||||||
|  |             result.append("x^"); | ||||||
|  |             result.append(degree); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										194
									
								
								src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,194 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.reedsolomon; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Implements Reed-Solomon decoding, as the name implies.</p> | ||||||
|  |  * | ||||||
|  |  * <p>The algorithm will not be explained here, but the following references were helpful | ||||||
|  |  * in creating this implementation:</p> | ||||||
|  |  * | ||||||
|  |  * <ul> | ||||||
|  |  * <li>Bruce Maggs. | ||||||
|  |  * <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps"> | ||||||
|  |  * "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li> | ||||||
|  |  * <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf"> | ||||||
|  |  * "Chapter 5. Generalized Reed-Solomon Codes"</a> | ||||||
|  |  * (see discussion of Euclidean algorithm)</li> | ||||||
|  |  * </ul> | ||||||
|  |  * | ||||||
|  |  * <p>Much credit is due to William Rucklidge since portions of this code are an indirect | ||||||
|  |  * port of his C++ Reed-Solomon implementation.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author William Rucklidge | ||||||
|  |  * @author sanfordsquires | ||||||
|  |  */ | ||||||
|  | public final class ReedSolomonDecoder { | ||||||
|  | 
 | ||||||
|  |   private final GF256 field; | ||||||
|  | 
 | ||||||
|  |   public ReedSolomonDecoder(GF256 field) { | ||||||
|  |     this.field = field; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Decodes given set of received codewords, which include both data and error-correction | ||||||
|  |    * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, | ||||||
|  |    * in the input.</p> | ||||||
|  |    * | ||||||
|  |    * @param received data and error-correction codewords | ||||||
|  |    * @param twoS number of error-correction codewords available | ||||||
|  |    * @throws ReedSolomonException if decoding fails for any reason | ||||||
|  |    */ | ||||||
|  |   public void decode(int[] received, int twoS) throws ReedSolomonException { | ||||||
|  |     GF256Poly poly = new GF256Poly(field, received); | ||||||
|  |     int[] syndromeCoefficients = new int[twoS]; | ||||||
|  |     boolean dataMatrix = field.equals(GF256.DATA_MATRIX_FIELD); | ||||||
|  |     boolean noError = true; | ||||||
|  |     for (int i = 0; i < twoS; i++) { | ||||||
|  |       // Thanks to sanfordsquires for this fix: | ||||||
|  |       int eval = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i)); | ||||||
|  |       syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval; | ||||||
|  |       if (eval != 0) { | ||||||
|  |         noError = false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (noError) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     GF256Poly syndrome = new GF256Poly(field, syndromeCoefficients); | ||||||
|  |     GF256Poly[] sigmaOmega = | ||||||
|  |         runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); | ||||||
|  |     GF256Poly sigma = sigmaOmega[0]; | ||||||
|  |     GF256Poly omega = sigmaOmega[1]; | ||||||
|  |     int[] errorLocations = findErrorLocations(sigma); | ||||||
|  |     int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix); | ||||||
|  |     for (int i = 0; i < errorLocations.length; i++) { | ||||||
|  |       int position = received.length - 1 - field.log(errorLocations[i]); | ||||||
|  |       if (position < 0) { | ||||||
|  |         throw new ReedSolomonException("Bad error location"); | ||||||
|  |       } | ||||||
|  |       received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R) | ||||||
|  |       throws ReedSolomonException { | ||||||
|  |     // Assume a's degree is >= b's | ||||||
|  |     if (a.getDegree() < b.getDegree()) { | ||||||
|  |       GF256Poly temp = a; | ||||||
|  |       a = b; | ||||||
|  |       b = temp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     GF256Poly rLast = a; | ||||||
|  |     GF256Poly r = b; | ||||||
|  |     GF256Poly sLast = field.getOne(); | ||||||
|  |     GF256Poly s = field.getZero(); | ||||||
|  |     GF256Poly tLast = field.getZero(); | ||||||
|  |     GF256Poly t = field.getOne(); | ||||||
|  | 
 | ||||||
|  |     // Run Euclidean algorithm until r's degree is less than R/2 | ||||||
|  |     while (r.getDegree() >= R / 2) { | ||||||
|  |       GF256Poly rLastLast = rLast; | ||||||
|  |       GF256Poly sLastLast = sLast; | ||||||
|  |       GF256Poly tLastLast = tLast; | ||||||
|  |       rLast = r; | ||||||
|  |       sLast = s; | ||||||
|  |       tLast = t; | ||||||
|  | 
 | ||||||
|  |       // Divide rLastLast by rLast, with quotient in q and remainder in r | ||||||
|  |       if (rLast.isZero()) { | ||||||
|  |         // Oops, Euclidean algorithm already terminated? | ||||||
|  |         throw new ReedSolomonException("r_{i-1} was zero"); | ||||||
|  |       } | ||||||
|  |       r = rLastLast; | ||||||
|  |       GF256Poly q = field.getZero(); | ||||||
|  |       int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); | ||||||
|  |       int dltInverse = field.inverse(denominatorLeadingTerm); | ||||||
|  |       while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { | ||||||
|  |         int degreeDiff = r.getDegree() - rLast.getDegree(); | ||||||
|  |         int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); | ||||||
|  |         q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); | ||||||
|  |         r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       s = q.multiply(sLast).addOrSubtract(sLastLast); | ||||||
|  |       t = q.multiply(tLast).addOrSubtract(tLastLast); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int sigmaTildeAtZero = t.getCoefficient(0); | ||||||
|  |     if (sigmaTildeAtZero == 0) { | ||||||
|  |       throw new ReedSolomonException("sigmaTilde(0) was zero"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int inverse = field.inverse(sigmaTildeAtZero); | ||||||
|  |     GF256Poly sigma = t.multiply(inverse); | ||||||
|  |     GF256Poly omega = r.multiply(inverse); | ||||||
|  |     return new GF256Poly[]{sigma, omega}; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private int[] findErrorLocations(GF256Poly errorLocator) throws ReedSolomonException { | ||||||
|  |     // This is a direct application of Chien's search | ||||||
|  |     int numErrors = errorLocator.getDegree(); | ||||||
|  |     if (numErrors == 1) { // shortcut | ||||||
|  |       return new int[] { errorLocator.getCoefficient(1) }; | ||||||
|  |     } | ||||||
|  |     int[] result = new int[numErrors]; | ||||||
|  |     int e = 0; | ||||||
|  |     for (int i = 1; i < 256 && e < numErrors; i++) { | ||||||
|  |       if (errorLocator.evaluateAt(i) == 0) { | ||||||
|  |         result[e] = field.inverse(i); | ||||||
|  |         e++; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (e != numErrors) { | ||||||
|  |       throw new ReedSolomonException("Error locator degree does not match number of roots"); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private int[] findErrorMagnitudes(GF256Poly errorEvaluator, int[] errorLocations, boolean dataMatrix) { | ||||||
|  |     // This is directly applying Forney's Formula | ||||||
|  |     int s = errorLocations.length; | ||||||
|  |     int[] result = new int[s]; | ||||||
|  |     for (int i = 0; i < s; i++) { | ||||||
|  |       int xiInverse = field.inverse(errorLocations[i]); | ||||||
|  |       int denominator = 1; | ||||||
|  |       for (int j = 0; j < s; j++) { | ||||||
|  |         if (i != j) { | ||||||
|  |           //denominator = field.multiply(denominator, | ||||||
|  |           //    GF256.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))); | ||||||
|  |           // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug. | ||||||
|  |           // Below is a funny-looking workaround from Steven Parkes | ||||||
|  |           int term = field.multiply(errorLocations[j], xiInverse); | ||||||
|  |           int termPlus1 = ((term & 0x1) == 0) ? (term | 1) : (term & ~1); | ||||||
|  |           denominator = field.multiply(denominator, termPlus1); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), | ||||||
|  |           field.inverse(denominator)); | ||||||
|  |       // Thanks to sanfordsquires for this fix: | ||||||
|  |       if (dataMatrix) { | ||||||
|  |         result[i] = field.multiply(result[i], xiInverse); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,75 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.reedsolomon; | ||||||
|  | 
 | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Implements Reed-Solomon enbcoding, as the name implies.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  * @author William Rucklidge | ||||||
|  |  */ | ||||||
|  | public final class ReedSolomonEncoder { | ||||||
|  | 
 | ||||||
|  |   private final GF256 field; | ||||||
|  |   private final Vector cachedGenerators; | ||||||
|  | 
 | ||||||
|  |   public ReedSolomonEncoder(GF256 field) { | ||||||
|  |     if (!GF256.QR_CODE_FIELD.equals(field)) { | ||||||
|  |       throw new IllegalArgumentException("Only QR Code is supported at this time"); | ||||||
|  |     } | ||||||
|  |     this.field = field; | ||||||
|  |     this.cachedGenerators = new Vector(); | ||||||
|  |     cachedGenerators.addElement(new GF256Poly(field, new int[] { 1 })); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private GF256Poly buildGenerator(int degree) { | ||||||
|  |     if (degree >= cachedGenerators.size()) { | ||||||
|  |       GF256Poly lastGenerator = (GF256Poly) cachedGenerators.elementAt(cachedGenerators.size() - 1); | ||||||
|  |       for (int d = cachedGenerators.size(); d <= degree; d++) { | ||||||
|  |         GF256Poly nextGenerator = lastGenerator.multiply(new GF256Poly(field, new int[] { 1, field.exp(d - 1) })); | ||||||
|  |         cachedGenerators.addElement(nextGenerator); | ||||||
|  |         lastGenerator = nextGenerator; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return (GF256Poly) cachedGenerators.elementAt(degree);     | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void encode(int[] toEncode, int ecBytes) { | ||||||
|  |     if (ecBytes == 0) { | ||||||
|  |       throw new IllegalArgumentException("No error correction bytes"); | ||||||
|  |     } | ||||||
|  |     int dataBytes = toEncode.length - ecBytes; | ||||||
|  |     if (dataBytes <= 0) { | ||||||
|  |       throw new IllegalArgumentException("No data bytes provided"); | ||||||
|  |     } | ||||||
|  |     GF256Poly generator = buildGenerator(ecBytes); | ||||||
|  |     int[] infoCoefficients = new int[dataBytes]; | ||||||
|  |     System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); | ||||||
|  |     GF256Poly info = new GF256Poly(field, infoCoefficients); | ||||||
|  |     info = info.multiplyByMonomial(ecBytes, 1); | ||||||
|  |     GF256Poly remainder = info.divide(generator)[1]; | ||||||
|  |     int[] coefficients = remainder.getCoefficients(); | ||||||
|  |     int numZeroCoefficients = ecBytes - coefficients.length; | ||||||
|  |     for (int i = 0; i < numZeroCoefficients; i++) { | ||||||
|  |       toEncode[dataBytes + i] = 0; | ||||||
|  |     } | ||||||
|  |     System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,31 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.common.reedsolomon; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when | ||||||
|  |  * there are too many errors to correct.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class ReedSolomonException extends Exception { | ||||||
|  | 
 | ||||||
|  |   public ReedSolomonException(String message) { | ||||||
|  |     super(message); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										161
									
								
								src/com/google/zxing/datamatrix/DataMatrixReader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/com/google/zxing/datamatrix/DataMatrixReader.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,161 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BarcodeFormat; | ||||||
|  | import com.google.zxing.BinaryBitmap; | ||||||
|  | import com.google.zxing.ChecksumException; | ||||||
|  | import com.google.zxing.DecodeHintType; | ||||||
|  | import com.google.zxing.FormatException; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.Reader; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.ResultMetadataType; | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.common.DecoderResult; | ||||||
|  | import com.google.zxing.common.DetectorResult; | ||||||
|  | import com.google.zxing.datamatrix.decoder.Decoder; | ||||||
|  | import com.google.zxing.datamatrix.detector.Detector; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This implementation can detect and decode Data Matrix codes in an image. | ||||||
|  |  * | ||||||
|  |  * @author bbrown@google.com (Brian Brown) | ||||||
|  |  */ | ||||||
|  | public final class DataMatrixReader implements Reader { | ||||||
|  | 
 | ||||||
|  |   private static final ResultPoint[] NO_POINTS = new ResultPoint[0]; | ||||||
|  | 
 | ||||||
|  |   private final Decoder decoder = new Decoder(); | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Locates and decodes a Data Matrix code in an image. | ||||||
|  |    * | ||||||
|  |    * @return a String representing the content encoded by the Data Matrix code | ||||||
|  |    * @throws NotFoundException if a Data Matrix code cannot be found | ||||||
|  |    * @throws FormatException if a Data Matrix code cannot be decoded | ||||||
|  |    * @throws ChecksumException if error correction fails | ||||||
|  |    */ | ||||||
|  |   public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException { | ||||||
|  |     return decode(image, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Result decode(BinaryBitmap image, Hashtable hints) | ||||||
|  |       throws NotFoundException, ChecksumException, FormatException { | ||||||
|  |     DecoderResult decoderResult; | ||||||
|  |     ResultPoint[] points; | ||||||
|  |     if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) { | ||||||
|  |       BitMatrix bits = extractPureBits(image.getBlackMatrix()); | ||||||
|  |       decoderResult = decoder.decode(bits); | ||||||
|  |       points = NO_POINTS; | ||||||
|  |     } else { | ||||||
|  |       DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(); | ||||||
|  |       decoderResult = decoder.decode(detectorResult.getBits()); | ||||||
|  |       points = detectorResult.getPoints(); | ||||||
|  |     } | ||||||
|  |     Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, | ||||||
|  |         BarcodeFormat.DATA_MATRIX); | ||||||
|  |     if (decoderResult.getByteSegments() != null) { | ||||||
|  |       result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.getByteSegments()); | ||||||
|  |     } | ||||||
|  |     if (decoderResult.getECLevel() != null) { | ||||||
|  |       result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.getECLevel().toString()); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void reset() { | ||||||
|  |     // do nothing | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image | ||||||
|  |    * which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border | ||||||
|  |    * around it. This is a specialized method that works exceptionally fast in this special | ||||||
|  |    * case. | ||||||
|  |    * | ||||||
|  |    * @see com.google.zxing.qrcode.QRCodeReader#extractPureBits(BitMatrix) | ||||||
|  |    */ | ||||||
|  |   private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException { | ||||||
|  | 
 | ||||||
|  |     int height = image.getHeight(); | ||||||
|  |     int width = image.getWidth(); | ||||||
|  |     int minDimension = Math.min(height, width); | ||||||
|  | 
 | ||||||
|  |     // And then keep tracking across the top-left black module to determine module size | ||||||
|  |     //int moduleEnd = borderWidth; | ||||||
|  |     int[] leftTopBlack = image.getTopLeftOnBit(); | ||||||
|  |     if (leftTopBlack == null) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  |     int x = leftTopBlack[0]; | ||||||
|  |     int y = leftTopBlack[1]; | ||||||
|  |     while (x < minDimension && y < minDimension && image.get(x, y)) { | ||||||
|  |       x++; | ||||||
|  |     } | ||||||
|  |     if (x == minDimension) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int moduleSize = x - leftTopBlack[0]; | ||||||
|  | 
 | ||||||
|  |     // And now find where the rightmost black module on the first row ends | ||||||
|  |     int rowEndOfSymbol = width - 1; | ||||||
|  |     while (rowEndOfSymbol >= 0 && !image.get(rowEndOfSymbol, y)) { | ||||||
|  |       rowEndOfSymbol--; | ||||||
|  |     } | ||||||
|  |     if (rowEndOfSymbol < 0) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  |     rowEndOfSymbol++; | ||||||
|  | 
 | ||||||
|  |     // Make sure width of barcode is a multiple of module size | ||||||
|  |     if ((rowEndOfSymbol - x) % moduleSize != 0) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  |     int dimension = 2 + ((rowEndOfSymbol - x) / moduleSize); | ||||||
|  | 
 | ||||||
|  |     y += moduleSize; | ||||||
|  | 
 | ||||||
|  |     // Push in the "border" by half the module width so that we start | ||||||
|  |     // sampling in the middle of the module. Just in case the image is a | ||||||
|  |     // little off, this will help recover. | ||||||
|  |     x -= moduleSize >> 1; | ||||||
|  |     y -= moduleSize >> 1; | ||||||
|  | 
 | ||||||
|  |     if ((x + (dimension - 1) * moduleSize) >= width || | ||||||
|  |         (y + (dimension - 1) * moduleSize) >= height) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Now just read off the bits | ||||||
|  |     BitMatrix bits = new BitMatrix(dimension); | ||||||
|  |     for (int i = 0; i < dimension; i++) { | ||||||
|  |       int iOffset = y + i * moduleSize; | ||||||
|  |       for (int j = 0; j < dimension; j++) { | ||||||
|  |         if (image.get(x + j * moduleSize, iOffset)) { | ||||||
|  |           bits.set(j, i); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return bits; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										446
									
								
								src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,446 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix.decoder; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.FormatException; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author bbrown@google.com (Brian Brown) | ||||||
|  |  */ | ||||||
|  | final class BitMatrixParser { | ||||||
|  | 
 | ||||||
|  |   private final BitMatrix mappingBitMatrix; | ||||||
|  |   private final BitMatrix readMappingMatrix; | ||||||
|  |   private final Version version; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * @param bitMatrix {@link BitMatrix} to parse | ||||||
|  |    * @throws FormatException if dimension is < 10 or > 144 or not 0 mod 2 | ||||||
|  |    */ | ||||||
|  |   BitMatrixParser(BitMatrix bitMatrix) throws FormatException { | ||||||
|  |     int dimension = bitMatrix.getHeight(); | ||||||
|  |     if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) { | ||||||
|  |       throw FormatException.getFormatInstance(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     version = readVersion(bitMatrix); | ||||||
|  |     this.mappingBitMatrix = extractDataRegion(bitMatrix); | ||||||
|  |     // TODO(bbrown): Make this work for rectangular symbols | ||||||
|  |     this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getHeight()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Creates the version object based on the dimension of the original bit matrix from  | ||||||
|  |    * the datamatrix code.</p> | ||||||
|  |    * | ||||||
|  |    * <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p> | ||||||
|  |    *  | ||||||
|  |    * @param bitMatrix Original {@link BitMatrix} including alignment patterns | ||||||
|  |    * @return {@link Version} encapsulating the Data Matrix Code's "version" | ||||||
|  |    * @throws FormatException if the dimensions of the mapping matrix are not valid | ||||||
|  |    * Data Matrix dimensions. | ||||||
|  |    */ | ||||||
|  |   Version readVersion(BitMatrix bitMatrix) throws FormatException { | ||||||
|  | 
 | ||||||
|  |     if (version != null) { | ||||||
|  |       return version; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO(bbrown): make this work for rectangular dimensions as well. | ||||||
|  |     int numRows = bitMatrix.getHeight(); | ||||||
|  |     int numColumns = numRows; | ||||||
|  |      | ||||||
|  |     return Version.getVersionForDimensions(numRows, numColumns); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns) | ||||||
|  |    * in the correct order in order to reconstitute the codewords bytes contained within the | ||||||
|  |    * Data Matrix Code.</p> | ||||||
|  |    * | ||||||
|  |    * @return bytes encoded within the Data Matrix Code | ||||||
|  |    * @throws FormatException if the exact number of bytes expected is not read | ||||||
|  |    */ | ||||||
|  |   byte[] readCodewords() throws FormatException { | ||||||
|  | 
 | ||||||
|  |     byte[] result = new byte[version.getTotalCodewords()]; | ||||||
|  |     int resultOffset = 0; | ||||||
|  |      | ||||||
|  |     int row = 4; | ||||||
|  |     int column = 0; | ||||||
|  |     // TODO(bbrown): Data Matrix can be rectangular, assuming square for now | ||||||
|  |     int numRows = mappingBitMatrix.getHeight(); | ||||||
|  |     int numColumns = numRows; | ||||||
|  |      | ||||||
|  |     boolean corner1Read = false; | ||||||
|  |     boolean corner2Read = false; | ||||||
|  |     boolean corner3Read = false; | ||||||
|  |     boolean corner4Read = false; | ||||||
|  |      | ||||||
|  |     // Read all of the codewords | ||||||
|  |     do { | ||||||
|  |       // Check the four corner cases | ||||||
|  |       if ((row == numRows) && (column == 0) && !corner1Read) { | ||||||
|  |         result[resultOffset++] = (byte) readCorner1(numRows, numColumns); | ||||||
|  |         row -= 2; | ||||||
|  |         column +=2; | ||||||
|  |         corner1Read = true; | ||||||
|  |       } else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) { | ||||||
|  |         result[resultOffset++] = (byte) readCorner2(numRows, numColumns); | ||||||
|  |         row -= 2; | ||||||
|  |         column +=2; | ||||||
|  |         corner2Read = true; | ||||||
|  |       } else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) { | ||||||
|  |         result[resultOffset++] = (byte) readCorner3(numRows, numColumns); | ||||||
|  |         row -= 2; | ||||||
|  |         column +=2; | ||||||
|  |         corner3Read = true; | ||||||
|  |       } else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) { | ||||||
|  |         result[resultOffset++] = (byte) readCorner4(numRows, numColumns); | ||||||
|  |         row -= 2; | ||||||
|  |         column +=2; | ||||||
|  |         corner4Read = true; | ||||||
|  |       } else { | ||||||
|  |         // Sweep upward diagonally to the right | ||||||
|  |         do { | ||||||
|  |           if ((row < numRows) && (column >= 0) && !readMappingMatrix.get(column, row)) { | ||||||
|  |             result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns); | ||||||
|  |           } | ||||||
|  |           row -= 2; | ||||||
|  |           column +=2; | ||||||
|  |         } while ((row >= 0) && (column < numColumns)); | ||||||
|  |         row += 1; | ||||||
|  |         column +=3; | ||||||
|  |          | ||||||
|  |         // Sweep downward diagonally to the left | ||||||
|  |         do { | ||||||
|  |           if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(column, row)) { | ||||||
|  |              result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns); | ||||||
|  |           } | ||||||
|  |           row += 2; | ||||||
|  |           column -=2; | ||||||
|  |         } while ((row < numRows) && (column >= 0)); | ||||||
|  |         row += 3; | ||||||
|  |         column +=1; | ||||||
|  |       } | ||||||
|  |     } while ((row < numRows) || (column < numColumns)); | ||||||
|  | 
 | ||||||
|  |     if (resultOffset != version.getTotalCodewords()) { | ||||||
|  |       throw FormatException.getFormatInstance(); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p> | ||||||
|  |    *  | ||||||
|  |    * @param row Row to read in the mapping matrix | ||||||
|  |    * @param column Column to read in the mapping matrix | ||||||
|  |    * @param numRows Number of rows in the mapping matrix | ||||||
|  |    * @param numColumns Number of columns in the mapping matrix | ||||||
|  |    * @return value of the given bit in the mapping matrix | ||||||
|  |    */ | ||||||
|  |   boolean readModule(int row, int column, int numRows, int numColumns) { | ||||||
|  |     // Adjust the row and column indices based on boundary wrapping | ||||||
|  |     if (row < 0) { | ||||||
|  |       row += numRows; | ||||||
|  |       column += 4 - ((numRows + 4) & 0x07); | ||||||
|  |     } | ||||||
|  |     if (column < 0) { | ||||||
|  |       column += numColumns; | ||||||
|  |       row += 4 - ((numColumns + 4) & 0x07); | ||||||
|  |     } | ||||||
|  |     readMappingMatrix.set(column, row); | ||||||
|  |     return mappingBitMatrix.get(column, row); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Reads the 8 bits of the standard Utah-shaped pattern.</p> | ||||||
|  |    *  | ||||||
|  |    * <p>See ISO 16022:2006, 5.8.1 Figure 6</p> | ||||||
|  |    *  | ||||||
|  |    * @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern | ||||||
|  |    * @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern | ||||||
|  |    * @param numRows Number of rows in the mapping matrix | ||||||
|  |    * @param numColumns Number of columns in the mapping matrix | ||||||
|  |    * @return byte from the utah shape | ||||||
|  |    */ | ||||||
|  |   int readUtah(int row, int column, int numRows, int numColumns) { | ||||||
|  |     int currentByte = 0; | ||||||
|  |     if (readModule(row - 2, column - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row - 2, column - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row - 1, column - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row - 1, column - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row - 1, column, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row, column - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row, column - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(row, column, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     return currentByte; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Reads the 8 bits of the special corner condition 1.</p> | ||||||
|  |    *  | ||||||
|  |    * <p>See ISO 16022:2006, Figure F.3</p> | ||||||
|  |    *  | ||||||
|  |    * @param numRows Number of rows in the mapping matrix | ||||||
|  |    * @param numColumns Number of columns in the mapping matrix | ||||||
|  |    * @return byte from the Corner condition 1 | ||||||
|  |    */ | ||||||
|  |   int readCorner1(int numRows, int numColumns) { | ||||||
|  |     int currentByte = 0; | ||||||
|  |     if (readModule(numRows - 1, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 1, 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 1, 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(1, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(2, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(3, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     return currentByte; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Reads the 8 bits of the special corner condition 2.</p> | ||||||
|  |    *  | ||||||
|  |    * <p>See ISO 16022:2006, Figure F.4</p> | ||||||
|  |    *  | ||||||
|  |    * @param numRows Number of rows in the mapping matrix | ||||||
|  |    * @param numColumns Number of columns in the mapping matrix | ||||||
|  |    * @return byte from the Corner condition 2 | ||||||
|  |    */ | ||||||
|  |   int readCorner2(int numRows, int numColumns) { | ||||||
|  |     int currentByte = 0; | ||||||
|  |     if (readModule(numRows - 3, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 2, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 1, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 4, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 3, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(1, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     return currentByte; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Reads the 8 bits of the special corner condition 3.</p> | ||||||
|  |    *  | ||||||
|  |    * <p>See ISO 16022:2006, Figure F.5</p> | ||||||
|  |    *  | ||||||
|  |    * @param numRows Number of rows in the mapping matrix | ||||||
|  |    * @param numColumns Number of columns in the mapping matrix | ||||||
|  |    * @return byte from the Corner condition 3 | ||||||
|  |    */ | ||||||
|  |   int readCorner3(int numRows, int numColumns) { | ||||||
|  |     int currentByte = 0; | ||||||
|  |     if (readModule(numRows - 1, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 3, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(1, numColumns - 3, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(1, numColumns - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(1, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     return currentByte; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Reads the 8 bits of the special corner condition 4.</p> | ||||||
|  |    *  | ||||||
|  |    * <p>See ISO 16022:2006, Figure F.6</p> | ||||||
|  |    *  | ||||||
|  |    * @param numRows Number of rows in the mapping matrix | ||||||
|  |    * @param numColumns Number of columns in the mapping matrix | ||||||
|  |    * @return byte from the Corner condition 4 | ||||||
|  |    */ | ||||||
|  |   int readCorner4(int numRows, int numColumns) { | ||||||
|  |     int currentByte = 0; | ||||||
|  |     if (readModule(numRows - 3, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 2, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(numRows - 1, 0, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 2, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(0, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(1, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(2, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     currentByte <<= 1; | ||||||
|  |     if (readModule(3, numColumns - 1, numRows, numColumns)) { | ||||||
|  |       currentByte |= 1; | ||||||
|  |     } | ||||||
|  |     return currentByte; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * <p>Extracts the data region from a {@link BitMatrix} that contains | ||||||
|  |    * alignment patterns.</p> | ||||||
|  |    *  | ||||||
|  |    * @param bitMatrix Original {@link BitMatrix} with alignment patterns | ||||||
|  |    * @return BitMatrix that has the alignment patterns removed | ||||||
|  |    */ | ||||||
|  |   BitMatrix extractDataRegion(BitMatrix bitMatrix) { | ||||||
|  |     int symbolSizeRows = version.getSymbolSizeRows(); | ||||||
|  |     int symbolSizeColumns = version.getSymbolSizeColumns(); | ||||||
|  |      | ||||||
|  |     // TODO(bbrown): Make this work with rectangular codes | ||||||
|  |     if (bitMatrix.getHeight() != symbolSizeRows) { | ||||||
|  |       throw new IllegalArgumentException("Dimension of bitMarix must match the version size"); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     int dataRegionSizeRows = version.getDataRegionSizeRows(); | ||||||
|  |     int dataRegionSizeColumns = version.getDataRegionSizeColumns(); | ||||||
|  |      | ||||||
|  |     int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows; | ||||||
|  |     int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; | ||||||
|  |      | ||||||
|  |     int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; | ||||||
|  |     //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; | ||||||
|  |      | ||||||
|  |     // TODO(bbrown): Make this work with rectangular codes | ||||||
|  |     BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow); | ||||||
|  |     for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { | ||||||
|  |       int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; | ||||||
|  |       for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { | ||||||
|  |         int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; | ||||||
|  |         for (int i = 0; i < dataRegionSizeRows; ++i) { | ||||||
|  |           int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; | ||||||
|  |           int writeRowOffset = dataRegionRowOffset + i; | ||||||
|  |           for (int j = 0; j < dataRegionSizeColumns; ++j) { | ||||||
|  |             int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; | ||||||
|  |             if (bitMatrix.get(readColumnOffset, readRowOffset)) { | ||||||
|  |               int writeColumnOffset = dataRegionColumnOffset + j; | ||||||
|  |               bitMatrixWithoutAlignment.set(writeColumnOffset, writeRowOffset); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return bitMatrixWithoutAlignment; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										118
									
								
								src/com/google/zxing/datamatrix/decoder/DataBlock.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/com/google/zxing/datamatrix/decoder/DataBlock.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix.decoder; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into | ||||||
|  |  * multiple blocks, each of which is a unit of data and error-correction codewords. Each | ||||||
|  |  * is represented by an instance of this class.</p> | ||||||
|  |  * | ||||||
|  |  * @author bbrown@google.com (Brian Brown) | ||||||
|  |  */ | ||||||
|  | final class DataBlock { | ||||||
|  | 
 | ||||||
|  |   private final int numDataCodewords; | ||||||
|  |   private final byte[] codewords; | ||||||
|  | 
 | ||||||
|  |   private DataBlock(int numDataCodewords, byte[] codewords) { | ||||||
|  |     this.numDataCodewords = numDataCodewords; | ||||||
|  |     this.codewords = codewords; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them. | ||||||
|  |    * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This | ||||||
|  |    * method will separate the data into original blocks.</p> | ||||||
|  |    * | ||||||
|  |    * @param rawCodewords bytes as read directly from the Data Matrix Code | ||||||
|  |    * @param version version of the Data Matrix Code | ||||||
|  |    * @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the | ||||||
|  |    *         Data Matrix Code | ||||||
|  |    */ | ||||||
|  |   static DataBlock[] getDataBlocks(byte[] rawCodewords, | ||||||
|  |                                    Version version) { | ||||||
|  |     // Figure out the number and size of data blocks used by this version | ||||||
|  |     Version.ECBlocks ecBlocks = version.getECBlocks(); | ||||||
|  | 
 | ||||||
|  |     // First count the total number of data blocks | ||||||
|  |     int totalBlocks = 0; | ||||||
|  |     Version.ECB[] ecBlockArray = ecBlocks.getECBlocks(); | ||||||
|  |     for (int i = 0; i < ecBlockArray.length; i++) { | ||||||
|  |       totalBlocks += ecBlockArray[i].getCount(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Now establish DataBlocks of the appropriate size and number of data codewords | ||||||
|  |     DataBlock[] result = new DataBlock[totalBlocks]; | ||||||
|  |     int numResultBlocks = 0; | ||||||
|  |     for (int j = 0; j < ecBlockArray.length; j++) { | ||||||
|  |       Version.ECB ecBlock = ecBlockArray[j]; | ||||||
|  |       for (int i = 0; i < ecBlock.getCount(); i++) { | ||||||
|  |         int numDataCodewords = ecBlock.getDataCodewords(); | ||||||
|  |         int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords; | ||||||
|  |         result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // All blocks have the same amount of data, except that the last n | ||||||
|  |     // (where n may be 0) have 1 less byte. Figure out where these start. | ||||||
|  |     // TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144 | ||||||
|  |     int longerBlocksTotalCodewords = result[0].codewords.length; | ||||||
|  |     //int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1; | ||||||
|  | 
 | ||||||
|  |     int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords(); | ||||||
|  |     int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1; | ||||||
|  |     // The last elements of result may be 1 element shorter for 144 matrix | ||||||
|  |     // first fill out as many elements as all of them have minus 1 | ||||||
|  |     int rawCodewordsOffset = 0; | ||||||
|  |     for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { | ||||||
|  |       for (int j = 0; j < numResultBlocks; j++) { | ||||||
|  |         result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Fill out the last data block in the longer ones | ||||||
|  |     boolean specialVersion = version.getVersionNumber() == 24; | ||||||
|  |     int numLongerBlocks = specialVersion ? 8 : numResultBlocks; | ||||||
|  |     for (int j = 0; j < numLongerBlocks; j++) { | ||||||
|  |       result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++]; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Now add in error correction blocks | ||||||
|  |     int max = result[0].codewords.length; | ||||||
|  |     for (int i = longerBlocksNumDataCodewords; i < max; i++) { | ||||||
|  |       for (int j = 0; j < numResultBlocks; j++) { | ||||||
|  |         int iOffset = (specialVersion && j > 7) ? i - 1 : i; | ||||||
|  |         result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (rawCodewordsOffset != rawCodewords.length) { | ||||||
|  |       throw new IllegalArgumentException(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int getNumDataCodewords() { | ||||||
|  |     return numDataCodewords; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   byte[] getCodewords() { | ||||||
|  |     return codewords; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,462 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix.decoder; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.FormatException; | ||||||
|  | import com.google.zxing.common.BitSource; | ||||||
|  | import com.google.zxing.common.DecoderResult; | ||||||
|  | 
 | ||||||
|  | import java.io.UnsupportedEncodingException; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes | ||||||
|  |  * in one Data Matrix Code. This class decodes the bits back into text.</p> | ||||||
|  |  * | ||||||
|  |  * <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p> | ||||||
|  |  * | ||||||
|  |  * @author bbrown@google.com (Brian Brown) | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | final class DecodedBitStreamParser { | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, Annex C Table C.1 | ||||||
|  |    * The C40 Basic Character Set (*'s used for placeholders for the shift values) | ||||||
|  |    */ | ||||||
|  |   private static final char[] C40_BASIC_SET_CHARS = { | ||||||
|  |       '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | ||||||
|  |       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', | ||||||
|  |       'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' | ||||||
|  |   }; | ||||||
|  |    | ||||||
|  |   private static final char[] C40_SHIFT2_SET_CHARS = { | ||||||
|  |     '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', | ||||||
|  |     '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' | ||||||
|  |   }; | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, Annex C Table C.2 | ||||||
|  |    * The Text Basic Character Set (*'s used for placeholders for the shift values) | ||||||
|  |    */ | ||||||
|  |   private static final char[] TEXT_BASIC_SET_CHARS = { | ||||||
|  |     '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | ||||||
|  |     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', | ||||||
|  |     'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' | ||||||
|  |   }; | ||||||
|  |    | ||||||
|  |   private static final char[] TEXT_SHIFT3_SET_CHARS = { | ||||||
|  |     '\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', | ||||||
|  |     'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127 | ||||||
|  |   }; | ||||||
|  |    | ||||||
|  |   private static final int PAD_ENCODE = 0;  // Not really an encoding | ||||||
|  |   private static final int ASCII_ENCODE = 1; | ||||||
|  |   private static final int C40_ENCODE = 2; | ||||||
|  |   private static final int TEXT_ENCODE = 3; | ||||||
|  |   private static final int ANSIX12_ENCODE = 4; | ||||||
|  |   private static final int EDIFACT_ENCODE = 5; | ||||||
|  |   private static final int BASE256_ENCODE = 6; | ||||||
|  | 
 | ||||||
|  |   private DecodedBitStreamParser() { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static DecoderResult decode(byte[] bytes) throws FormatException { | ||||||
|  |     BitSource bits = new BitSource(bytes); | ||||||
|  |     StringBuffer result = new StringBuffer(100); | ||||||
|  |     StringBuffer resultTrailer = new StringBuffer(0); | ||||||
|  |     Vector byteSegments = new Vector(1); | ||||||
|  |     int mode = ASCII_ENCODE; | ||||||
|  |     do { | ||||||
|  |       if (mode == ASCII_ENCODE) { | ||||||
|  |         mode = decodeAsciiSegment(bits, result, resultTrailer); | ||||||
|  |       } else { | ||||||
|  |         switch (mode) { | ||||||
|  |           case C40_ENCODE: | ||||||
|  |             decodeC40Segment(bits, result); | ||||||
|  |             break; | ||||||
|  |           case TEXT_ENCODE: | ||||||
|  |             decodeTextSegment(bits, result); | ||||||
|  |             break; | ||||||
|  |           case ANSIX12_ENCODE: | ||||||
|  |             decodeAnsiX12Segment(bits, result); | ||||||
|  |             break; | ||||||
|  |           case EDIFACT_ENCODE: | ||||||
|  |             decodeEdifactSegment(bits, result); | ||||||
|  |             break; | ||||||
|  |           case BASE256_ENCODE: | ||||||
|  |             decodeBase256Segment(bits, result, byteSegments); | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             throw FormatException.getFormatInstance(); | ||||||
|  |         } | ||||||
|  |         mode = ASCII_ENCODE; | ||||||
|  |       } | ||||||
|  |     } while (mode != PAD_ENCODE && bits.available() > 0); | ||||||
|  |     if (resultTrailer.length() > 0) { | ||||||
|  |       result.append(resultTrailer.toString()); | ||||||
|  |     } | ||||||
|  |     return new DecoderResult(bytes, result.toString(), byteSegments.isEmpty() ? null : byteSegments, null); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 | ||||||
|  |    */ | ||||||
|  |   private static int decodeAsciiSegment(BitSource bits, StringBuffer result, StringBuffer resultTrailer) | ||||||
|  |       throws FormatException { | ||||||
|  |     boolean upperShift = false; | ||||||
|  |     do { | ||||||
|  |       int oneByte = bits.readBits(8); | ||||||
|  |       if (oneByte == 0) { | ||||||
|  |         throw FormatException.getFormatInstance(); | ||||||
|  |       } else if (oneByte <= 128) {  // ASCII data (ASCII value + 1) | ||||||
|  |         oneByte = upperShift ? (oneByte + 128) : oneByte; | ||||||
|  |         upperShift = false; | ||||||
|  |         result.append((char) (oneByte - 1)); | ||||||
|  |         return ASCII_ENCODE; | ||||||
|  |       } else if (oneByte == 129) {  // Pad | ||||||
|  |         return PAD_ENCODE; | ||||||
|  |       } else if (oneByte <= 229) {  // 2-digit data 00-99 (Numeric Value + 130) | ||||||
|  |         int value = oneByte - 130; | ||||||
|  |         if (value < 10) { // padd with '0' for single digit values | ||||||
|  |           result.append('0'); | ||||||
|  |         } | ||||||
|  |         result.append(value); | ||||||
|  |       } else if (oneByte == 230) {  // Latch to C40 encodation | ||||||
|  |         return C40_ENCODE; | ||||||
|  |       } else if (oneByte == 231) {  // Latch to Base 256 encodation | ||||||
|  |         return BASE256_ENCODE; | ||||||
|  |       } else if (oneByte == 232) {  // FNC1 | ||||||
|  |         //throw ReaderException.getInstance(); | ||||||
|  |         // Ignore this symbol for now | ||||||
|  |       } else if (oneByte == 233) {  // Structured Append | ||||||
|  |         //throw ReaderException.getInstance(); | ||||||
|  |         // Ignore this symbol for now | ||||||
|  |       } else if (oneByte == 234) {  // Reader Programming | ||||||
|  |         //throw ReaderException.getInstance(); | ||||||
|  |         // Ignore this symbol for now | ||||||
|  |       } else if (oneByte == 235) {  // Upper Shift (shift to Extended ASCII) | ||||||
|  |         upperShift = true; | ||||||
|  |       } else if (oneByte == 236) {  // 05 Macro | ||||||
|  |         result.append("[)>\u001E05\u001D"); | ||||||
|  |         resultTrailer.insert(0, "\u001E\u0004"); | ||||||
|  |       } else if (oneByte == 237) {  // 06 Macro | ||||||
|  |         result.append("[)>\u001E06\u001D"); | ||||||
|  |         resultTrailer.insert(0, "\u001E\u0004"); | ||||||
|  |       } else if (oneByte == 238) {  // Latch to ANSI X12 encodation | ||||||
|  |         return ANSIX12_ENCODE; | ||||||
|  |       } else if (oneByte == 239) {  // Latch to Text encodation | ||||||
|  |         return TEXT_ENCODE; | ||||||
|  |       } else if (oneByte == 240) {  // Latch to EDIFACT encodation | ||||||
|  |         return EDIFACT_ENCODE; | ||||||
|  |       } else if (oneByte == 241) {  // ECI Character | ||||||
|  |         // TODO(bbrown): I think we need to support ECI | ||||||
|  |         //throw ReaderException.getInstance(); | ||||||
|  |         // Ignore this symbol for now | ||||||
|  |       } else if (oneByte >= 242) {  // Not to be used in ASCII encodation | ||||||
|  |         throw FormatException.getFormatInstance(); | ||||||
|  |       } | ||||||
|  |     } while (bits.available() > 0); | ||||||
|  |     return ASCII_ENCODE; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 | ||||||
|  |    */ | ||||||
|  |   private static void decodeC40Segment(BitSource bits, StringBuffer result) throws FormatException { | ||||||
|  |     // Three C40 values are encoded in a 16-bit value as | ||||||
|  |     // (1600 * C1) + (40 * C2) + C3 + 1 | ||||||
|  |     // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time | ||||||
|  |     boolean upperShift = false; | ||||||
|  | 
 | ||||||
|  |     int[] cValues = new int[3]; | ||||||
|  |     do { | ||||||
|  |       // If there is only one byte left then it will be encoded as ASCII | ||||||
|  |       if (bits.available() == 8) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       int firstByte = bits.readBits(8); | ||||||
|  |       if (firstByte == 254) {  // Unlatch codeword | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       parseTwoBytes(firstByte, bits.readBits(8), cValues); | ||||||
|  | 
 | ||||||
|  |       int shift = 0; | ||||||
|  |       for (int i = 0; i < 3; i++) { | ||||||
|  |         int cValue = cValues[i]; | ||||||
|  |         switch (shift) { | ||||||
|  |           case 0: | ||||||
|  |             if (cValue < 3) { | ||||||
|  |               shift = cValue + 1; | ||||||
|  |             } else { | ||||||
|  |               if (upperShift) { | ||||||
|  |                 result.append((char) (C40_BASIC_SET_CHARS[cValue] + 128)); | ||||||
|  |                 upperShift = false; | ||||||
|  |               } else { | ||||||
|  |                 result.append(C40_BASIC_SET_CHARS[cValue]); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |           case 1: | ||||||
|  |             if (upperShift) { | ||||||
|  |               result.append((char) (cValue + 128)); | ||||||
|  |               upperShift = false; | ||||||
|  |             } else { | ||||||
|  |               result.append(cValue); | ||||||
|  |             } | ||||||
|  |             shift = 0; | ||||||
|  |             break; | ||||||
|  |           case 2: | ||||||
|  |             if (cValue < 27) { | ||||||
|  |               if (upperShift) { | ||||||
|  |                 result.append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128)); | ||||||
|  |                 upperShift = false; | ||||||
|  |               } else { | ||||||
|  |                 result.append(C40_SHIFT2_SET_CHARS[cValue]); | ||||||
|  |               } | ||||||
|  |             } else if (cValue == 27) {  // FNC1 | ||||||
|  |               throw FormatException.getFormatInstance(); | ||||||
|  |             } else if (cValue == 30) {  // Upper Shift | ||||||
|  |               upperShift = true; | ||||||
|  |             } else { | ||||||
|  |               throw FormatException.getFormatInstance(); | ||||||
|  |             } | ||||||
|  |             shift = 0; | ||||||
|  |             break; | ||||||
|  |           case 3: | ||||||
|  |             if (upperShift) { | ||||||
|  |               result.append((char) (cValue + 224)); | ||||||
|  |               upperShift = false; | ||||||
|  |             } else { | ||||||
|  |               result.append((char) (cValue + 96)); | ||||||
|  |             } | ||||||
|  |             shift = 0; | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             throw FormatException.getFormatInstance(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (bits.available() > 0); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 | ||||||
|  |    */ | ||||||
|  |   private static void decodeTextSegment(BitSource bits, StringBuffer result) throws FormatException { | ||||||
|  |     // Three Text values are encoded in a 16-bit value as | ||||||
|  |     // (1600 * C1) + (40 * C2) + C3 + 1 | ||||||
|  |     // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time | ||||||
|  |     boolean upperShift = false; | ||||||
|  | 
 | ||||||
|  |     int[] cValues = new int[3]; | ||||||
|  |     do { | ||||||
|  |       // If there is only one byte left then it will be encoded as ASCII | ||||||
|  |       if (bits.available() == 8) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       int firstByte = bits.readBits(8); | ||||||
|  |       if (firstByte == 254) {  // Unlatch codeword | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       parseTwoBytes(firstByte, bits.readBits(8), cValues); | ||||||
|  | 
 | ||||||
|  |       int shift = 0; | ||||||
|  |       for (int i = 0; i < 3; i++) { | ||||||
|  |         int cValue = cValues[i]; | ||||||
|  |         switch (shift) { | ||||||
|  |           case 0: | ||||||
|  |             if (cValue < 3) { | ||||||
|  |               shift = cValue + 1; | ||||||
|  |             } else { | ||||||
|  |               if (upperShift) { | ||||||
|  |                 result.append((char) (TEXT_BASIC_SET_CHARS[cValue] + 128)); | ||||||
|  |                 upperShift = false; | ||||||
|  |               } else { | ||||||
|  |                 result.append(TEXT_BASIC_SET_CHARS[cValue]); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |           case 1: | ||||||
|  |             if (upperShift) { | ||||||
|  |               result.append((char) (cValue + 128)); | ||||||
|  |               upperShift = false; | ||||||
|  |             } else { | ||||||
|  |               result.append(cValue); | ||||||
|  |             } | ||||||
|  |             shift = 0; | ||||||
|  |             break; | ||||||
|  |           case 2: | ||||||
|  |             // Shift 2 for Text is the same encoding as C40 | ||||||
|  |             if (cValue < 27) { | ||||||
|  |               if (upperShift) { | ||||||
|  |                 result.append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128)); | ||||||
|  |                 upperShift = false; | ||||||
|  |               } else { | ||||||
|  |                 result.append(C40_SHIFT2_SET_CHARS[cValue]); | ||||||
|  |               } | ||||||
|  |             } else if (cValue == 27) {  // FNC1 | ||||||
|  |               throw FormatException.getFormatInstance(); | ||||||
|  |             } else if (cValue == 30) {  // Upper Shift | ||||||
|  |               upperShift = true; | ||||||
|  |             } else { | ||||||
|  |               throw FormatException.getFormatInstance(); | ||||||
|  |             } | ||||||
|  |             shift = 0; | ||||||
|  |             break; | ||||||
|  |           case 3: | ||||||
|  |             if (upperShift) { | ||||||
|  |               result.append((char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128)); | ||||||
|  |               upperShift = false; | ||||||
|  |             } else { | ||||||
|  |               result.append(TEXT_SHIFT3_SET_CHARS[cValue]); | ||||||
|  |             } | ||||||
|  |             shift = 0; | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             throw FormatException.getFormatInstance(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (bits.available() > 0); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, 5.2.7 | ||||||
|  |    */ | ||||||
|  |   private static void decodeAnsiX12Segment(BitSource bits, StringBuffer result) throws FormatException { | ||||||
|  |     // Three ANSI X12 values are encoded in a 16-bit value as | ||||||
|  |     // (1600 * C1) + (40 * C2) + C3 + 1 | ||||||
|  | 
 | ||||||
|  |     int[] cValues = new int[3]; | ||||||
|  |     do { | ||||||
|  |       // If there is only one byte left then it will be encoded as ASCII | ||||||
|  |       if (bits.available() == 8) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       int firstByte = bits.readBits(8); | ||||||
|  |       if (firstByte == 254) {  // Unlatch codeword | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       parseTwoBytes(firstByte, bits.readBits(8), cValues); | ||||||
|  | 
 | ||||||
|  |       for (int i = 0; i < 3; i++) { | ||||||
|  |         int cValue = cValues[i]; | ||||||
|  |         if (cValue == 0) {  // X12 segment terminator <CR> | ||||||
|  |           result.append('\r'); | ||||||
|  |         } else if (cValue == 1) {  // X12 segment separator * | ||||||
|  |           result.append('*'); | ||||||
|  |         } else if (cValue == 2) {  // X12 sub-element separator > | ||||||
|  |           result.append('>'); | ||||||
|  |         } else if (cValue == 3) {  // space | ||||||
|  |           result.append(' '); | ||||||
|  |         } else if (cValue < 14) {  // 0 - 9 | ||||||
|  |           result.append((char) (cValue + 44)); | ||||||
|  |         } else if (cValue < 40) {  // A - Z | ||||||
|  |           result.append((char) (cValue + 51)); | ||||||
|  |         } else { | ||||||
|  |           throw FormatException.getFormatInstance(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (bits.available() > 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static void parseTwoBytes(int firstByte, int secondByte, int[] result) { | ||||||
|  |     int fullBitValue = (firstByte << 8) + secondByte - 1; | ||||||
|  |     int temp = fullBitValue / 1600; | ||||||
|  |     result[0] = temp; | ||||||
|  |     fullBitValue -= temp * 1600; | ||||||
|  |     temp = fullBitValue / 40; | ||||||
|  |     result[1] = temp; | ||||||
|  |     result[2] = fullBitValue - temp * 40; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, 5.2.8 and Annex C Table C.3 | ||||||
|  |    */ | ||||||
|  |   private static void decodeEdifactSegment(BitSource bits, StringBuffer result) { | ||||||
|  |     boolean unlatch = false; | ||||||
|  |     do { | ||||||
|  |       // If there is only two or less bytes left then it will be encoded as ASCII | ||||||
|  |       if (bits.available() <= 16) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       for (int i = 0; i < 4; i++) { | ||||||
|  |         int edifactValue = bits.readBits(6); | ||||||
|  | 
 | ||||||
|  |         // Check for the unlatch character | ||||||
|  |         if (edifactValue == 0x2B67) {  // 011111 | ||||||
|  |           unlatch = true; | ||||||
|  |           // If we encounter the unlatch code then continue reading because the Codeword triple | ||||||
|  |           // is padded with 0's | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (!unlatch) { | ||||||
|  |           if ((edifactValue & 32) == 0) {  // no 1 in the leading (6th) bit | ||||||
|  |             edifactValue |= 64;  // Add a leading 01 to the 6 bit binary value | ||||||
|  |           } | ||||||
|  |           result.append(edifactValue); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (!unlatch && bits.available() > 0); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, 5.2.9 and Annex B, B.2 | ||||||
|  |    */ | ||||||
|  |   private static void decodeBase256Segment(BitSource bits, StringBuffer result, Vector byteSegments) | ||||||
|  |       throws FormatException { | ||||||
|  |     // Figure out how long the Base 256 Segment is. | ||||||
|  |     int d1 = bits.readBits(8); | ||||||
|  |     int count; | ||||||
|  |     if (d1 == 0) {  // Read the remainder of the symbol | ||||||
|  |       count = bits.available() / 8; | ||||||
|  |     } else if (d1 < 250) { | ||||||
|  |       count = d1; | ||||||
|  |     } else { | ||||||
|  |       count = 250 * (d1 - 249) + bits.readBits(8); | ||||||
|  |     } | ||||||
|  |     byte[] bytes = new byte[count]; | ||||||
|  |     for (int i = 0; i < count; i++) { | ||||||
|  |       // Have seen this particular error in the wild, such as at | ||||||
|  |       // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2 | ||||||
|  |       if (bits.available() < 8) { | ||||||
|  |         throw FormatException.getFormatInstance(); | ||||||
|  |       } | ||||||
|  |       bytes[i] = unrandomize255State(bits.readBits(8), i); | ||||||
|  |     } | ||||||
|  |     byteSegments.addElement(bytes); | ||||||
|  |     try { | ||||||
|  |       result.append(new String(bytes, "ISO8859_1")); | ||||||
|  |     } catch (UnsupportedEncodingException uee) { | ||||||
|  |       throw new RuntimeException("Platform does not support required encoding: " + uee); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006, Annex B, B.2 | ||||||
|  |    */ | ||||||
|  |   private static byte unrandomize255State(int randomizedBase256Codeword, | ||||||
|  |                                           int base256CodewordPosition) { | ||||||
|  |     int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1; | ||||||
|  |     int tempVariable = randomizedBase256Codeword - pseudoRandomNumber; | ||||||
|  |     return (byte) (tempVariable >= 0 ? tempVariable : (tempVariable + 256)); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  | } | ||||||
							
								
								
									
										134
									
								
								src/com/google/zxing/datamatrix/decoder/Decoder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/com/google/zxing/datamatrix/decoder/Decoder.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix.decoder; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.ChecksumException; | ||||||
|  | import com.google.zxing.FormatException; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.common.DecoderResult; | ||||||
|  | import com.google.zxing.common.reedsolomon.GF256; | ||||||
|  | import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; | ||||||
|  | import com.google.zxing.common.reedsolomon.ReedSolomonException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting | ||||||
|  |  * the Data Matrix Code from an image.</p> | ||||||
|  |  * | ||||||
|  |  * @author bbrown@google.com (Brian Brown) | ||||||
|  |  */ | ||||||
|  | public final class Decoder { | ||||||
|  | 
 | ||||||
|  |   private final ReedSolomonDecoder rsDecoder; | ||||||
|  | 
 | ||||||
|  |   public Decoder() { | ||||||
|  |     rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans. | ||||||
|  |    * "true" is taken to mean a black module.</p> | ||||||
|  |    * | ||||||
|  |    * @param image booleans representing white/black Data Matrix Code modules | ||||||
|  |    * @return text and bytes encoded within the Data Matrix Code | ||||||
|  |    * @throws FormatException if the Data Matrix Code cannot be decoded | ||||||
|  |    * @throws ChecksumException if error correction fails | ||||||
|  |    */ | ||||||
|  |   public DecoderResult decode(boolean[][] image) throws FormatException, ChecksumException { | ||||||
|  |     int dimension = image.length; | ||||||
|  |     BitMatrix bits = new BitMatrix(dimension); | ||||||
|  |     for (int i = 0; i < dimension; i++) { | ||||||
|  |       for (int j = 0; j < dimension; j++) { | ||||||
|  |         if (image[i][j]) { | ||||||
|  |           bits.set(j, i); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return decode(bits); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken | ||||||
|  |    * to mean a black module.</p> | ||||||
|  |    * | ||||||
|  |    * @param bits booleans representing white/black Data Matrix Code modules | ||||||
|  |    * @return text and bytes encoded within the Data Matrix Code | ||||||
|  |    * @throws FormatException if the Data Matrix Code cannot be decoded | ||||||
|  |    * @throws ChecksumException if error correction fails | ||||||
|  |    */ | ||||||
|  |   public DecoderResult decode(BitMatrix bits) throws FormatException, ChecksumException { | ||||||
|  | 
 | ||||||
|  |     // Construct a parser and read version, error-correction level | ||||||
|  |     BitMatrixParser parser = new BitMatrixParser(bits); | ||||||
|  |     Version version = parser.readVersion(bits); | ||||||
|  | 
 | ||||||
|  |     // Read codewords | ||||||
|  |     byte[] codewords = parser.readCodewords(); | ||||||
|  |     // Separate into data blocks | ||||||
|  |     DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version); | ||||||
|  | 
 | ||||||
|  |     // Count total number of data bytes | ||||||
|  |     int totalBytes = 0; | ||||||
|  |     for (int i = 0; i < dataBlocks.length; i++) { | ||||||
|  |       totalBytes += dataBlocks[i].getNumDataCodewords(); | ||||||
|  |     } | ||||||
|  |     byte[] resultBytes = new byte[totalBytes]; | ||||||
|  |     int resultOffset = 0; | ||||||
|  | 
 | ||||||
|  |     // Error-correct and copy data blocks together into a stream of bytes | ||||||
|  |     for (int j = 0; j < dataBlocks.length; j++) { | ||||||
|  |       DataBlock dataBlock = dataBlocks[j]; | ||||||
|  |       byte[] codewordBytes = dataBlock.getCodewords(); | ||||||
|  |       int numDataCodewords = dataBlock.getNumDataCodewords(); | ||||||
|  |       correctErrors(codewordBytes, numDataCodewords); | ||||||
|  |       for (int i = 0; i < numDataCodewords; i++) { | ||||||
|  |         resultBytes[resultOffset++] = codewordBytes[i]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Decode the contents of that stream of bytes | ||||||
|  |     return DecodedBitStreamParser.decode(resultBytes); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to | ||||||
|  |    * correct the errors in-place using Reed-Solomon error correction.</p> | ||||||
|  |    * | ||||||
|  |    * @param codewordBytes data and error correction codewords | ||||||
|  |    * @param numDataCodewords number of codewords that are data bytes | ||||||
|  |    * @throws ChecksumException if error correction fails | ||||||
|  |    */ | ||||||
|  |   private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException { | ||||||
|  |     int numCodewords = codewordBytes.length; | ||||||
|  |     // First read into an array of ints | ||||||
|  |     int[] codewordsInts = new int[numCodewords]; | ||||||
|  |     for (int i = 0; i < numCodewords; i++) { | ||||||
|  |       codewordsInts[i] = codewordBytes[i] & 0xFF; | ||||||
|  |     } | ||||||
|  |     int numECCodewords = codewordBytes.length - numDataCodewords; | ||||||
|  |     try { | ||||||
|  |       rsDecoder.decode(codewordsInts, numECCodewords); | ||||||
|  |     } catch (ReedSolomonException rse) { | ||||||
|  |       throw ChecksumException.getChecksumInstance(); | ||||||
|  |     } | ||||||
|  |     // Copy back into array of bytes -- only need to worry about the bytes that were data | ||||||
|  |     // We don't care about errors in the error-correction codewords | ||||||
|  |     for (int i = 0; i < numDataCodewords; i++) { | ||||||
|  |       codewordBytes[i] = (byte) codewordsInts[i]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										242
									
								
								src/com/google/zxing/datamatrix/decoder/Version.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								src/com/google/zxing/datamatrix/decoder/Version.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,242 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix.decoder; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.FormatException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * The Version object encapsulates attributes about a particular | ||||||
|  |  * size Data Matrix Code. | ||||||
|  |  * | ||||||
|  |  * @author bbrown@google.com (Brian Brown) | ||||||
|  |  */ | ||||||
|  | public final class Version { | ||||||
|  | 
 | ||||||
|  |   private static final Version[] VERSIONS = buildVersions(); | ||||||
|  | 
 | ||||||
|  |   private final int versionNumber; | ||||||
|  |   private final int symbolSizeRows; | ||||||
|  |   private final int symbolSizeColumns; | ||||||
|  |   private final int dataRegionSizeRows; | ||||||
|  |   private final int dataRegionSizeColumns; | ||||||
|  |   private final ECBlocks ecBlocks; | ||||||
|  |   private final int totalCodewords; | ||||||
|  | 
 | ||||||
|  |   private Version(int versionNumber, | ||||||
|  |                   int symbolSizeRows, | ||||||
|  |                   int symbolSizeColumns, | ||||||
|  |                   int dataRegionSizeRows, | ||||||
|  |                   int dataRegionSizeColumns, | ||||||
|  |                   ECBlocks ecBlocks) { | ||||||
|  |     this.versionNumber = versionNumber; | ||||||
|  |     this.symbolSizeRows = symbolSizeRows; | ||||||
|  |     this.symbolSizeColumns = symbolSizeColumns; | ||||||
|  |     this.dataRegionSizeRows = dataRegionSizeRows; | ||||||
|  |     this.dataRegionSizeColumns = dataRegionSizeColumns; | ||||||
|  |     this.ecBlocks = ecBlocks; | ||||||
|  |      | ||||||
|  |     // Calculate the total number of codewords | ||||||
|  |     int total = 0; | ||||||
|  |     int ecCodewords = ecBlocks.getECCodewords(); | ||||||
|  |     ECB[] ecbArray = ecBlocks.getECBlocks(); | ||||||
|  |     for (int i = 0; i < ecbArray.length; i++) { | ||||||
|  |       ECB ecBlock = ecbArray[i]; | ||||||
|  |       total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); | ||||||
|  |     } | ||||||
|  |     this.totalCodewords = total; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int getVersionNumber() { | ||||||
|  |     return versionNumber; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public int getSymbolSizeRows() { | ||||||
|  |     return symbolSizeRows; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   public int getSymbolSizeColumns() { | ||||||
|  |     return symbolSizeColumns; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   public int getDataRegionSizeRows() { | ||||||
|  |     return dataRegionSizeRows; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   public int getDataRegionSizeColumns() { | ||||||
|  |     return dataRegionSizeColumns; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   public int getTotalCodewords() { | ||||||
|  |     return totalCodewords; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   ECBlocks getECBlocks() { | ||||||
|  |     return ecBlocks; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Deduces version information from Data Matrix dimensions.</p> | ||||||
|  |    * | ||||||
|  |    * @param numRows Number of rows in modules | ||||||
|  |    * @param numColumns Number of columns in modules | ||||||
|  |    * @return {@link Version} for a Data Matrix Code of those dimensions | ||||||
|  |    * @throws FormatException if dimensions do correspond to a valid Data Matrix size | ||||||
|  |    */ | ||||||
|  |   public static Version getVersionForDimensions(int numRows, int numColumns) throws FormatException { | ||||||
|  |     if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) { | ||||||
|  |       throw FormatException.getFormatInstance(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // TODO(bbrown): This is doing a linear search through the array of versions. | ||||||
|  |     // If we interleave the rectangular versions with the square versions we could | ||||||
|  |     // do a binary search. | ||||||
|  |     int numVersions = VERSIONS.length; | ||||||
|  |     for (int i = 0; i < numVersions; ++i){ | ||||||
|  |       Version version = VERSIONS[i]; | ||||||
|  |       if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) { | ||||||
|  |         return version; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     throw FormatException.getFormatInstance(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will | ||||||
|  |    * use blocks of differing sizes within one version, so, this encapsulates the parameters for | ||||||
|  |    * each set of blocks. It also holds the number of error-correction codewords per block since it | ||||||
|  |    * will be the same across all blocks within one version.</p> | ||||||
|  |    */ | ||||||
|  |   static final class ECBlocks { | ||||||
|  |     private final int ecCodewords; | ||||||
|  |     private final ECB[] ecBlocks; | ||||||
|  | 
 | ||||||
|  |     private ECBlocks(int ecCodewords, ECB ecBlocks) { | ||||||
|  |       this.ecCodewords = ecCodewords; | ||||||
|  |       this.ecBlocks = new ECB[] { ecBlocks }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) { | ||||||
|  |       this.ecCodewords = ecCodewords; | ||||||
|  |       this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int getECCodewords() { | ||||||
|  |       return ecCodewords; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ECB[] getECBlocks() { | ||||||
|  |       return ecBlocks; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Encapsualtes the parameters for one error-correction block in one symbol version. | ||||||
|  |    * This includes the number of data codewords, and the number of times a block with these | ||||||
|  |    * parameters is used consecutively in the Data Matrix code version's format.</p> | ||||||
|  |    */ | ||||||
|  |   static final class ECB { | ||||||
|  |     private final int count; | ||||||
|  |     private final int dataCodewords; | ||||||
|  | 
 | ||||||
|  |     private ECB(int count, int dataCodewords) { | ||||||
|  |       this.count = count; | ||||||
|  |       this.dataCodewords = dataCodewords; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int getCount() { | ||||||
|  |       return count; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int getDataCodewords() { | ||||||
|  |       return dataCodewords; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public String toString() { | ||||||
|  |     return String.valueOf(versionNumber); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * See ISO 16022:2006 5.5.1 Table 7 | ||||||
|  |    */ | ||||||
|  |   private static Version[] buildVersions() { | ||||||
|  |     return new Version[]{ | ||||||
|  |         new Version(1, 10, 10, 8, 8, | ||||||
|  |             new ECBlocks(5, new ECB(1, 3))), | ||||||
|  |         new Version(2, 12, 12, 10, 10, | ||||||
|  |             new ECBlocks(7, new ECB(1, 5))), | ||||||
|  |         new Version(3, 14, 14, 12, 12, | ||||||
|  |             new ECBlocks(10, new ECB(1, 8))), | ||||||
|  |         new Version(4, 16, 16, 14, 14, | ||||||
|  |             new ECBlocks(12, new ECB(1, 12))), | ||||||
|  |         new Version(5, 18, 18, 16, 16, | ||||||
|  |             new ECBlocks(14, new ECB(1, 18))), | ||||||
|  |         new Version(6, 20, 20, 18, 18, | ||||||
|  |             new ECBlocks(18, new ECB(1, 22))), | ||||||
|  |         new Version(7, 22, 22, 20, 20, | ||||||
|  |             new ECBlocks(20, new ECB(1, 30))), | ||||||
|  |         new Version(8, 24, 24, 22, 22, | ||||||
|  |             new ECBlocks(24, new ECB(1, 36))), | ||||||
|  |         new Version(9, 26, 26, 24, 24, | ||||||
|  |             new ECBlocks(28, new ECB(1, 44))), | ||||||
|  |         new Version(10, 32, 32, 14, 14, | ||||||
|  |             new ECBlocks(36, new ECB(1, 62))), | ||||||
|  |         new Version(11, 36, 36, 16, 16, | ||||||
|  |             new ECBlocks(42, new ECB(1, 86))), | ||||||
|  |         new Version(12, 40, 40, 18, 18, | ||||||
|  |             new ECBlocks(48, new ECB(1, 114))), | ||||||
|  |         new Version(13, 44, 44, 20, 20, | ||||||
|  |             new ECBlocks(56, new ECB(1, 144))), | ||||||
|  |         new Version(14, 48, 48, 22, 22, | ||||||
|  |             new ECBlocks(68, new ECB(1, 174))), | ||||||
|  |         new Version(15, 52, 52, 24, 24, | ||||||
|  |             new ECBlocks(42, new ECB(2, 102))), | ||||||
|  |         new Version(16, 64, 64, 14, 14, | ||||||
|  |             new ECBlocks(56, new ECB(2, 140))), | ||||||
|  |         new Version(17, 72, 72, 16, 16, | ||||||
|  |             new ECBlocks(36, new ECB(4, 92))), | ||||||
|  |         new Version(18, 80, 80, 18, 18, | ||||||
|  |             new ECBlocks(48, new ECB(4, 114))), | ||||||
|  |         new Version(19, 88, 88, 20, 20, | ||||||
|  |             new ECBlocks(56, new ECB(4, 144))), | ||||||
|  |         new Version(20, 96, 96, 22, 22, | ||||||
|  |             new ECBlocks(68, new ECB(4, 174))), | ||||||
|  |         new Version(21, 104, 104, 24, 24, | ||||||
|  |             new ECBlocks(56, new ECB(6, 136))), | ||||||
|  |         new Version(22, 120, 120, 18, 18, | ||||||
|  |             new ECBlocks(68, new ECB(6, 175))), | ||||||
|  |         new Version(23, 132, 132, 20, 20, | ||||||
|  |             new ECBlocks(62, new ECB(8, 163))), | ||||||
|  |         new Version(24, 144, 144, 22, 22, | ||||||
|  |             new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))), | ||||||
|  |         new Version(25, 8, 18, 6, 16, | ||||||
|  |             new ECBlocks(7, new ECB(1, 5))), | ||||||
|  |         new Version(26, 8, 32, 6, 14, | ||||||
|  |             new ECBlocks(11, new ECB(1, 10))), | ||||||
|  |         new Version(27, 12, 26, 10, 24, | ||||||
|  |             new ECBlocks(14, new ECB(1, 16))), | ||||||
|  |         new Version(28, 12, 36, 10, 16, | ||||||
|  |             new ECBlocks(18, new ECB(1, 22))), | ||||||
|  |         new Version(29, 16, 36, 10, 16, | ||||||
|  |             new ECBlocks(24, new ECB(1, 32))), | ||||||
|  |         new Version(30, 16, 48, 14, 22, | ||||||
|  |             new ECBlocks(28, new ECB(1, 49))) | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										348
									
								
								src/com/google/zxing/datamatrix/detector/Detector.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								src/com/google/zxing/datamatrix/detector/Detector.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,348 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2008 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.datamatrix.detector; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | import com.google.zxing.common.BitMatrix; | ||||||
|  | import com.google.zxing.common.Collections; | ||||||
|  | import com.google.zxing.common.Comparator; | ||||||
|  | import com.google.zxing.common.DetectorResult; | ||||||
|  | import com.google.zxing.common.GridSampler; | ||||||
|  | import com.google.zxing.common.detector.WhiteRectangleDetector; | ||||||
|  | 
 | ||||||
|  | import java.util.Enumeration; | ||||||
|  | import java.util.Hashtable; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code | ||||||
|  |  * is rotated or skewed, or partially obscured.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class Detector { | ||||||
|  | 
 | ||||||
|  |   // Trick to avoid creating new Integer objects below -- a sort of crude copy of | ||||||
|  |   // the Integer.valueOf(int) optimization added in Java 5, not in J2ME | ||||||
|  |   private static final Integer[] INTEGERS = | ||||||
|  |       { new Integer(0), new Integer(1), new Integer(2), new Integer(3), new Integer(4) }; | ||||||
|  |   // No, can't use valueOf() | ||||||
|  | 
 | ||||||
|  |   private final BitMatrix image; | ||||||
|  |   private final WhiteRectangleDetector rectangleDetector; | ||||||
|  | 
 | ||||||
|  |   public Detector(BitMatrix image) { | ||||||
|  |     this.image = image; | ||||||
|  |     rectangleDetector = new WhiteRectangleDetector(image); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * <p>Detects a Data Matrix Code in an image.</p> | ||||||
|  |    * | ||||||
|  |    * @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code | ||||||
|  |    * @throws NotFoundException if no Data Matrix Code can be found | ||||||
|  |    */ | ||||||
|  |   public DetectorResult detect() throws NotFoundException { | ||||||
|  | 
 | ||||||
|  |     ResultPoint[] cornerPoints = rectangleDetector.detect(); | ||||||
|  |     ResultPoint pointA = cornerPoints[0]; | ||||||
|  |     ResultPoint pointB = cornerPoints[1]; | ||||||
|  |     ResultPoint pointC = cornerPoints[2]; | ||||||
|  |     ResultPoint pointD = cornerPoints[3]; | ||||||
|  | 
 | ||||||
|  |     // Point A and D are across the diagonal from one another, | ||||||
|  |     // as are B and C. Figure out which are the solid black lines | ||||||
|  |     // by counting transitions | ||||||
|  |     Vector transitions = new Vector(4); | ||||||
|  |     transitions.addElement(transitionsBetween(pointA, pointB)); | ||||||
|  |     transitions.addElement(transitionsBetween(pointA, pointC)); | ||||||
|  |     transitions.addElement(transitionsBetween(pointB, pointD)); | ||||||
|  |     transitions.addElement(transitionsBetween(pointC, pointD)); | ||||||
|  |     Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator()); | ||||||
|  | 
 | ||||||
|  |     // Sort by number of transitions. First two will be the two solid sides; last two | ||||||
|  |     // will be the two alternating black/white sides | ||||||
|  |     ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions) transitions.elementAt(0); | ||||||
|  |     ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions) transitions.elementAt(1); | ||||||
|  | 
 | ||||||
|  |     // Figure out which point is their intersection by tallying up the number of times we see the | ||||||
|  |     // endpoints in the four endpoints. One will show up twice. | ||||||
|  |     Hashtable pointCount = new Hashtable(); | ||||||
|  |     increment(pointCount, lSideOne.getFrom()); | ||||||
|  |     increment(pointCount, lSideOne.getTo()); | ||||||
|  |     increment(pointCount, lSideTwo.getFrom()); | ||||||
|  |     increment(pointCount, lSideTwo.getTo()); | ||||||
|  | 
 | ||||||
|  |     ResultPoint maybeTopLeft = null; | ||||||
|  |     ResultPoint bottomLeft = null; | ||||||
|  |     ResultPoint maybeBottomRight = null; | ||||||
|  |     Enumeration points = pointCount.keys(); | ||||||
|  |     while (points.hasMoreElements()) { | ||||||
|  |       ResultPoint point = (ResultPoint) points.nextElement(); | ||||||
|  |       Integer value = (Integer) pointCount.get(point); | ||||||
|  |       if (value.intValue() == 2) { | ||||||
|  |         bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides | ||||||
|  |       } else { | ||||||
|  |         // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now | ||||||
|  |         if (maybeTopLeft == null) { | ||||||
|  |           maybeTopLeft = point; | ||||||
|  |         } else { | ||||||
|  |           maybeBottomRight = point; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Bottom left is correct but top left and bottom right might be switched | ||||||
|  |     ResultPoint[] corners = { maybeTopLeft, bottomLeft, maybeBottomRight }; | ||||||
|  |     // Use the dot product trick to sort them out | ||||||
|  |     ResultPoint.orderBestPatterns(corners); | ||||||
|  | 
 | ||||||
|  |     // Now we know which is which: | ||||||
|  |     ResultPoint bottomRight = corners[0]; | ||||||
|  |     bottomLeft = corners[1]; | ||||||
|  |     ResultPoint topLeft = corners[2]; | ||||||
|  | 
 | ||||||
|  |     // Which point didn't we find in relation to the "L" sides? that's the top right corner | ||||||
|  |     ResultPoint topRight; | ||||||
|  |     if (!pointCount.containsKey(pointA)) { | ||||||
|  |       topRight = pointA; | ||||||
|  |     } else if (!pointCount.containsKey(pointB)) { | ||||||
|  |       topRight = pointB; | ||||||
|  |     } else if (!pointCount.containsKey(pointC)) { | ||||||
|  |       topRight = pointC; | ||||||
|  |     } else { | ||||||
|  |       topRight = pointD; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Next determine the dimension by tracing along the top or right side and counting black/white | ||||||
|  |     // transitions. Since we start inside a black module, we should see a number of transitions | ||||||
|  |     // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to | ||||||
|  |     // end on a black module: | ||||||
|  | 
 | ||||||
|  |     // The top right point is actually the corner of a module, which is one of the two black modules | ||||||
|  |     // adjacent to the white module at the top right. Tracing to that corner from either the top left | ||||||
|  |     // or bottom right should work here. | ||||||
|  |     int dimension = Math.min(transitionsBetween(topLeft, topRight).getTransitions(), | ||||||
|  |                              transitionsBetween(bottomRight, topRight).getTransitions()); | ||||||
|  |     if ((dimension & 0x01) == 1) { | ||||||
|  |       // it can't be odd, so, round... up? | ||||||
|  |       dimension++; | ||||||
|  |     } | ||||||
|  |     dimension += 2; | ||||||
|  | 
 | ||||||
|  |     //correct top right point to match the white module | ||||||
|  |     ResultPoint correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension); | ||||||
|  |     if (correctedTopRight == null){ | ||||||
|  |     	correctedTopRight = topRight; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     //We redetermine the dimension using the corrected top right point | ||||||
|  |     int dimension2 = Math.max(transitionsBetween(topLeft, correctedTopRight).getTransitions(), | ||||||
|  |                               transitionsBetween(bottomRight, correctedTopRight).getTransitions()); | ||||||
|  |     dimension2++; | ||||||
|  |     if ((dimension2 & 0x01) == 1) { | ||||||
|  |       dimension2++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimension2); | ||||||
|  | 
 | ||||||
|  |     return new DetectorResult(bits, new ResultPoint[]{topLeft, bottomLeft, bottomRight, correctedTopRight}); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Calculates the position of the white top right module using the output of the rectangle detector | ||||||
|  |    */ | ||||||
|  |   private ResultPoint correctTopRight(ResultPoint bottomLeft, | ||||||
|  |                                       ResultPoint bottomRight, | ||||||
|  |                                       ResultPoint topLeft, | ||||||
|  |                                       ResultPoint topRight, | ||||||
|  |                                       int dimension) { | ||||||
|  | 		 | ||||||
|  | 		float corr = distance(bottomLeft, bottomRight) / (float)dimension; | ||||||
|  | 		int norm = distance(topLeft, topRight); | ||||||
|  | 		float cos = (topRight.getX() - topLeft.getX()) / norm; | ||||||
|  | 		float sin = (topRight.getY() - topLeft.getY()) / norm; | ||||||
|  | 		 | ||||||
|  | 		ResultPoint c1 = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin); | ||||||
|  | 	 | ||||||
|  | 		corr = distance(bottomLeft, bottomRight) / (float)dimension; | ||||||
|  | 		norm = distance(bottomRight, topRight); | ||||||
|  | 		cos = (topRight.getX() - bottomRight.getX()) / norm; | ||||||
|  | 		sin = (topRight.getY() - bottomRight.getY()) / norm; | ||||||
|  | 		 | ||||||
|  | 		ResultPoint c2 = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin); | ||||||
|  | 
 | ||||||
|  | 		if (!isValid(c1)){ | ||||||
|  | 			if (isValid(c2)){ | ||||||
|  | 				return c2; | ||||||
|  | 			} | ||||||
|  | 			return null; | ||||||
|  | 		} else if (!isValid(c2)){ | ||||||
|  | 			return c1; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		int l1 = Math.abs(transitionsBetween(topLeft, c1).getTransitions() - transitionsBetween(bottomRight, c1).getTransitions()); | ||||||
|  | 		int l2 = Math.abs(transitionsBetween(topLeft, c2).getTransitions() - transitionsBetween(bottomRight, c2).getTransitions()); | ||||||
|  | 		 | ||||||
|  | 		if (l1 <= l2){ | ||||||
|  | 			return c1; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return c2; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private boolean isValid(ResultPoint p) { | ||||||
|  | 	  return (p.getX() >= 0 && p.getX() < image.width && p.getY() > 0 && p.getY() < image.height); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Ends up being a bit faster than Math.round(). This merely rounds its | ||||||
|  |    * argument to the nearest int, where x.5 rounds up. | ||||||
|  |    */ | ||||||
|  |   private static int round(float d) { | ||||||
|  |     return (int) (d + 0.5f); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | // L2 distance | ||||||
|  |   private static int distance(ResultPoint a, ResultPoint b) { | ||||||
|  |     return round((float) Math.sqrt((a.getX() - b.getX()) | ||||||
|  |         * (a.getX() - b.getX()) + (a.getY() - b.getY()) | ||||||
|  |         * (a.getY() - b.getY()))); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Increments the Integer associated with a key by one. | ||||||
|  |    */ | ||||||
|  |   private static void increment(Hashtable table, ResultPoint key) { | ||||||
|  |     Integer value = (Integer) table.get(key); | ||||||
|  |     table.put(key, value == null ? INTEGERS[1] : INTEGERS[value.intValue() + 1]); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static BitMatrix sampleGrid(BitMatrix image, | ||||||
|  |                                       ResultPoint topLeft, | ||||||
|  |                                       ResultPoint bottomLeft, | ||||||
|  |                                       ResultPoint bottomRight, | ||||||
|  |                                       ResultPoint topRight, | ||||||
|  |                                       int dimension) throws NotFoundException { | ||||||
|  | 
 | ||||||
|  |     GridSampler sampler = GridSampler.getInstance(); | ||||||
|  | 
 | ||||||
|  |     return sampler.sampleGrid(image, | ||||||
|  |                               dimension, | ||||||
|  |                               0.5f, | ||||||
|  |                               0.5f, | ||||||
|  |                               dimension - 0.5f, | ||||||
|  |                               0.5f, | ||||||
|  |                               dimension - 0.5f, | ||||||
|  |                               dimension - 0.5f, | ||||||
|  |                               0.5f, | ||||||
|  |                               dimension - 0.5f, | ||||||
|  |                               topLeft.getX(), | ||||||
|  |                               topLeft.getY(), | ||||||
|  |                               topRight.getX(), | ||||||
|  |                               topRight.getY(), | ||||||
|  |                               bottomRight.getX(), | ||||||
|  |                               bottomRight.getY(), | ||||||
|  |                               bottomLeft.getX(), | ||||||
|  |                               bottomLeft.getY()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm. | ||||||
|  |    */ | ||||||
|  |   private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to) { | ||||||
|  |     // See QR Code Detector, sizeOfBlackWhiteBlackRun() | ||||||
|  |     int fromX = (int) from.getX(); | ||||||
|  |     int fromY = (int) from.getY(); | ||||||
|  |     int toX = (int) to.getX(); | ||||||
|  |     int toY = (int) to.getY(); | ||||||
|  |     boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX); | ||||||
|  |     if (steep) { | ||||||
|  |       int temp = fromX; | ||||||
|  |       fromX = fromY; | ||||||
|  |       fromY = temp; | ||||||
|  |       temp = toX; | ||||||
|  |       toX = toY; | ||||||
|  |       toY = temp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int dx = Math.abs(toX - fromX); | ||||||
|  |     int dy = Math.abs(toY - fromY); | ||||||
|  |     int error = -dx >> 1; | ||||||
|  |     int ystep = fromY < toY ? 1 : -1; | ||||||
|  |     int xstep = fromX < toX ? 1 : -1; | ||||||
|  |     int transitions = 0; | ||||||
|  |     boolean inBlack = image.get(steep ? fromY : fromX, steep ? fromX : fromY); | ||||||
|  |     for (int x = fromX, y = fromY; x != toX; x += xstep) { | ||||||
|  |       boolean isBlack = image.get(steep ? y : x, steep ? x : y); | ||||||
|  |       if (isBlack != inBlack) { | ||||||
|  |         transitions++; | ||||||
|  |         inBlack = isBlack; | ||||||
|  |       } | ||||||
|  |       error += dy; | ||||||
|  |       if (error > 0) { | ||||||
|  |         if (y == toY) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         y += ystep; | ||||||
|  |         error -= dx; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return new ResultPointsAndTransitions(from, to, transitions); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Simply encapsulates two points and a number of transitions between them. | ||||||
|  |    */ | ||||||
|  |   private static class ResultPointsAndTransitions { | ||||||
|  |     private final ResultPoint from; | ||||||
|  |     private final ResultPoint to; | ||||||
|  |     private final int transitions; | ||||||
|  |     private ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions) { | ||||||
|  |       this.from = from; | ||||||
|  |       this.to = to; | ||||||
|  |       this.transitions = transitions; | ||||||
|  |     } | ||||||
|  |     public ResultPoint getFrom() { | ||||||
|  |       return from; | ||||||
|  |     } | ||||||
|  |     public ResultPoint getTo() { | ||||||
|  |       return to; | ||||||
|  |     } | ||||||
|  |     public int getTransitions() { | ||||||
|  |       return transitions; | ||||||
|  |     } | ||||||
|  |     public String toString() { | ||||||
|  |       return from + "/" + to + '/' + transitions; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Orders ResultPointsAndTransitions by number of transitions, ascending. | ||||||
|  |    */ | ||||||
|  |   private static class ResultPointsAndTransitionsComparator implements Comparator { | ||||||
|  |     public int compare(Object o1, Object o2) { | ||||||
|  |       return ((ResultPointsAndTransitions) o1).getTransitions() - ((ResultPointsAndTransitions) o2).getTransitions(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										96
									
								
								src/com/google/zxing/multi/ByQuadrantReader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/com/google/zxing/multi/ByQuadrantReader.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.multi; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BinaryBitmap; | ||||||
|  | import com.google.zxing.ChecksumException; | ||||||
|  | import com.google.zxing.FormatException; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.Reader; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This class attempts to decode a barcode from an image, not by scanning the whole image, | ||||||
|  |  * but by scanning subsets of the image. This is important when there may be multiple barcodes in | ||||||
|  |  * an image, and detecting a barcode may find parts of multiple barcode and fail to decode | ||||||
|  |  * (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center | ||||||
|  |  * 'quadrant' to cover the case where a barcode is found in the center. | ||||||
|  |  * | ||||||
|  |  * @see GenericMultipleBarcodeReader | ||||||
|  |  */ | ||||||
|  | public final class ByQuadrantReader implements Reader { | ||||||
|  | 
 | ||||||
|  |   private final Reader delegate; | ||||||
|  | 
 | ||||||
|  |   public ByQuadrantReader(Reader delegate) { | ||||||
|  |     this.delegate = delegate; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Result decode(BinaryBitmap image) | ||||||
|  |       throws NotFoundException, ChecksumException, FormatException { | ||||||
|  |     return decode(image, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Result decode(BinaryBitmap image, Hashtable hints) | ||||||
|  |       throws NotFoundException, ChecksumException, FormatException { | ||||||
|  | 
 | ||||||
|  |     int width = image.getWidth(); | ||||||
|  |     int height = image.getHeight(); | ||||||
|  |     int halfWidth = width / 2; | ||||||
|  |     int halfHeight = height / 2; | ||||||
|  | 
 | ||||||
|  |     BinaryBitmap topLeft = image.crop(0, 0, halfWidth, halfHeight); | ||||||
|  |     try { | ||||||
|  |       return delegate.decode(topLeft, hints); | ||||||
|  |     } catch (NotFoundException re) { | ||||||
|  |       // continue | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     BinaryBitmap topRight = image.crop(halfWidth, 0, halfWidth, halfHeight); | ||||||
|  |     try { | ||||||
|  |       return delegate.decode(topRight, hints); | ||||||
|  |     } catch (NotFoundException re) { | ||||||
|  |       // continue | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     BinaryBitmap bottomLeft = image.crop(0, halfHeight, halfWidth, halfHeight); | ||||||
|  |     try { | ||||||
|  |       return delegate.decode(bottomLeft, hints); | ||||||
|  |     } catch (NotFoundException re) { | ||||||
|  |       // continue | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     BinaryBitmap bottomRight = image.crop(halfWidth, halfHeight, halfWidth, halfHeight); | ||||||
|  |     try { | ||||||
|  |       return delegate.decode(bottomRight, hints); | ||||||
|  |     } catch (NotFoundException re) { | ||||||
|  |       // continue | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int quarterWidth = halfWidth / 2; | ||||||
|  |     int quarterHeight = halfHeight / 2; | ||||||
|  |     BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight); | ||||||
|  |     return delegate.decode(center, hints); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public void reset() { | ||||||
|  |     delegate.reset(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										156
									
								
								src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2009 ZXing authors | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.google.zxing.multi; | ||||||
|  | 
 | ||||||
|  | import com.google.zxing.BinaryBitmap; | ||||||
|  | import com.google.zxing.NotFoundException; | ||||||
|  | import com.google.zxing.Reader; | ||||||
|  | import com.google.zxing.ReaderException; | ||||||
|  | import com.google.zxing.Result; | ||||||
|  | import com.google.zxing.ResultPoint; | ||||||
|  | 
 | ||||||
|  | import java.util.Hashtable; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image. | ||||||
|  |  * After one barcode is found, the areas left, above, right and below the barcode's | ||||||
|  |  * {@link com.google.zxing.ResultPoint}s are scanned, recursively.</p> | ||||||
|  |  * | ||||||
|  |  * <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple | ||||||
|  |  * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent | ||||||
|  |  * detecting any one of them.</p> | ||||||
|  |  * | ||||||
|  |  * <p>That is, instead of passing a {@link Reader} a caller might pass | ||||||
|  |  * <code>new ByQuadrantReader(reader)</code>.</p> | ||||||
|  |  * | ||||||
|  |  * @author Sean Owen | ||||||
|  |  */ | ||||||
|  | public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader { | ||||||
|  | 
 | ||||||
|  |   private static final int MIN_DIMENSION_TO_RECUR = 100; | ||||||
|  | 
 | ||||||
|  |   private final Reader delegate; | ||||||
|  | 
 | ||||||
|  |   public GenericMultipleBarcodeReader(Reader delegate) { | ||||||
|  |     this.delegate = delegate; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException { | ||||||
|  |     return decodeMultiple(image, null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public Result[] decodeMultiple(BinaryBitmap image, Hashtable hints) | ||||||
|  |       throws NotFoundException { | ||||||
|  |     Vector results = new Vector(); | ||||||
|  |     doDecodeMultiple(image, hints, results, 0, 0); | ||||||
|  |     if (results.isEmpty()) { | ||||||
|  |       throw NotFoundException.getNotFoundInstance(); | ||||||
|  |     } | ||||||
|  |     int numResults = results.size(); | ||||||
|  |     Result[] resultArray = new Result[numResults]; | ||||||
|  |     for (int i = 0; i < numResults; i++) { | ||||||
|  |       resultArray[i] = (Result) results.elementAt(i); | ||||||
|  |     } | ||||||
|  |     return resultArray; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private void doDecodeMultiple(BinaryBitmap image, | ||||||
|  |                                 Hashtable hints, | ||||||
|  |                                 Vector results, | ||||||
|  |                                 int xOffset, | ||||||
|  |                                 int yOffset) { | ||||||
|  |     Result result; | ||||||
|  |     try { | ||||||
|  |       result = delegate.decode(image, hints); | ||||||
|  |     } catch (ReaderException re) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     boolean alreadyFound = false; | ||||||
|  |     for (int i = 0; i < results.size(); i++) { | ||||||
|  |       Result existingResult = (Result) results.elementAt(i); | ||||||
|  |       if (existingResult.getText().equals(result.getText())) { | ||||||
|  |         alreadyFound = true; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (alreadyFound) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     results.addElement(translateResultPoints(result, xOffset, yOffset)); | ||||||
|  |     ResultPoint[] resultPoints = result.getResultPoints(); | ||||||
|  |     if (resultPoints == null || resultPoints.length == 0) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     int width = image.getWidth(); | ||||||
|  |     int height = image.getHeight(); | ||||||
|  |     float minX = width; | ||||||
|  |     float minY = height; | ||||||
|  |     float maxX = 0.0f; | ||||||
|  |     float maxY = 0.0f; | ||||||
|  |     for (int i = 0; i < resultPoints.length; i++) { | ||||||
|  |       ResultPoint point = resultPoints[i]; | ||||||
|  |       float x = point.getX(); | ||||||
|  |       float y = point.getY(); | ||||||
|  |       if (x < minX) { | ||||||
|  |         minX = x; | ||||||
|  |       } | ||||||
|  |       if (y < minY) { | ||||||
|  |         minY = y; | ||||||
|  |       } | ||||||
|  |       if (x > maxX) { | ||||||
|  |         maxX = x; | ||||||
|  |       } | ||||||
|  |       if (y > maxY) { | ||||||
|  |         maxY = y; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Decode left of barcode | ||||||
|  |     if (minX > MIN_DIMENSION_TO_RECUR) { | ||||||
|  |       doDecodeMultiple(image.crop(0, 0, (int) minX, height), | ||||||
|  |                        hints, results, xOffset, yOffset); | ||||||
|  |     } | ||||||
|  |     // Decode above barcode | ||||||
|  |     if (minY > MIN_DIMENSION_TO_RECUR) { | ||||||
|  |       doDecodeMultiple(image.crop(0, 0, width, (int) minY), | ||||||
|  |                        hints, results, xOffset, yOffset); | ||||||
|  |     } | ||||||
|  |     // Decode right of barcode | ||||||
|  |     if (maxX < width - MIN_DIMENSION_TO_RECUR) { | ||||||
|  |       doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height), | ||||||
|  |                        hints, results, xOffset + (int) maxX, yOffset); | ||||||
|  |     } | ||||||
|  |     // Decode below barcode | ||||||
|  |     if (maxY < height - MIN_DIMENSION_TO_RECUR) { | ||||||
|  |       doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY), | ||||||
|  |                        hints, results, xOffset, yOffset + (int) maxY); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static Result translateResultPoints(Result result, int xOffset, int yOffset) { | ||||||
|  |     ResultPoint[] oldResultPoints = result.getResultPoints(); | ||||||
|  |     ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.length]; | ||||||
|  |     for (int i = 0; i < oldResultPoints.length; i++) { | ||||||
|  |       ResultPoint oldPoint = oldResultPoints[i]; | ||||||
|  |       newResultPoints[i] = new ResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset); | ||||||
|  |     } | ||||||
|  |     return new Result(result.getText(), result.getRawBytes(), newResultPoints, | ||||||
|  |         result.getBarcodeFormat()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user