Replace zxing submodule by checked in code
This commit is contained in:
parent
67407e1095
commit
dd1853cab3
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -38,7 +38,3 @@
|
||||
path = extern/android-support-v4-preferencefragment
|
||||
url = https://github.com/kolavar/android-support-v4-preferencefragment.git
|
||||
ignore = dirty
|
||||
[submodule "extern/zxing-core"]
|
||||
path = extern/zxing-core
|
||||
url = https://gitlab.com/fdroid/zxing-core.git
|
||||
ignore = dirty
|
||||
|
1
extern/zxing-core
vendored
1
extern/zxing-core
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 6890fe32b4e55e936f693b217fcc03492b7325a5
|
9
extern/zxing-core/.classpath
vendored
Normal file
9
extern/zxing-core/.classpath
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
33
extern/zxing-core/.project
vendored
Normal file
33
extern/zxing-core/.project
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>zxing-core</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
5
extern/zxing-core/AndroidManifest.xml
vendored
Normal file
5
extern/zxing-core/AndroidManifest.xml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.zxing" >
|
||||
|
||||
</manifest>
|
3
extern/zxing-core/build.gradle
vendored
Normal file
3
extern/zxing-core/build.gradle
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
sourceCompatibility = 1.7
|
7
extern/zxing-core/project.properties
vendored
Normal file
7
extern/zxing-core/project.properties
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Dummy value
|
||||
target=android-19
|
||||
android.library=true
|
||||
|
||||
source.dir=src/main/java
|
||||
java.target=1.7
|
||||
java.source=1.7
|
77
extern/zxing-core/src/main/java/com/google/zxing/BarcodeFormat.java
vendored
Normal file
77
extern/zxing-core/src/main/java/com/google/zxing/BarcodeFormat.java
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Enumerates barcode formats known to this package. Please keep alphabetized.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum BarcodeFormat {
|
||||
|
||||
/** Aztec 2D barcode format. */
|
||||
AZTEC,
|
||||
|
||||
/** CODABAR 1D format. */
|
||||
CODABAR,
|
||||
|
||||
/** Code 39 1D format. */
|
||||
CODE_39,
|
||||
|
||||
/** Code 93 1D format. */
|
||||
CODE_93,
|
||||
|
||||
/** Code 128 1D format. */
|
||||
CODE_128,
|
||||
|
||||
/** Data Matrix 2D barcode format. */
|
||||
DATA_MATRIX,
|
||||
|
||||
/** EAN-8 1D format. */
|
||||
EAN_8,
|
||||
|
||||
/** EAN-13 1D format. */
|
||||
EAN_13,
|
||||
|
||||
/** ITF (Interleaved Two of Five) 1D format. */
|
||||
ITF,
|
||||
|
||||
/** MaxiCode 2D barcode format. */
|
||||
MAXICODE,
|
||||
|
||||
/** PDF417 format. */
|
||||
PDF_417,
|
||||
|
||||
/** QR Code 2D barcode format. */
|
||||
QR_CODE,
|
||||
|
||||
/** RSS 14 */
|
||||
RSS_14,
|
||||
|
||||
/** RSS EXPANDED */
|
||||
RSS_EXPANDED,
|
||||
|
||||
/** UPC-A 1D format. */
|
||||
UPC_A,
|
||||
|
||||
/** UPC-E 1D format. */
|
||||
UPC_E,
|
||||
|
||||
/** UPC/EAN extension format. Not a stand-alone format. */
|
||||
UPC_EAN_EXTENSION
|
||||
|
||||
}
|
87
extern/zxing-core/src/main/java/com/google/zxing/Binarizer.java
vendored
Normal file
87
extern/zxing-core/src/main/java/com/google/zxing/Binarizer.java
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public final 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, which must be in [0, 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).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
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).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
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);
|
||||
|
||||
public final int getWidth() {
|
||||
return source.getWidth();
|
||||
}
|
||||
|
||||
public final int getHeight() {
|
||||
return source.getHeight();
|
||||
}
|
||||
|
||||
}
|
150
extern/zxing-core/src/main/java/com/google/zxing/BinaryBitmap.java
vendored
Normal file
150
extern/zxing-core/src/main/java/com/google/zxing/BinaryBitmap.java
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return binarizer.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return binarizer.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, which must be in [0, 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).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
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).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
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, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,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 by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public BinaryBitmap rotateCounterClockwise() {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public BinaryBitmap rotateCounterClockwise45() {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise45();
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return getBlackMatrix().toString();
|
||||
} catch (NotFoundException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
37
extern/zxing-core/src/main/java/com/google/zxing/ChecksumException.java
vendored
Normal file
37
extern/zxing-core/src/main/java/com/google/zxing/ChecksumException.java
vendored
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;
|
||||
}
|
||||
|
||||
}
|
122
extern/zxing-core/src/main/java/com/google/zxing/DecodeHintType.java
vendored
Normal file
122
extern/zxing-core/src/main/java/com/google/zxing/DecodeHintType.java
vendored
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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.Map)
|
||||
*/
|
||||
public enum DecodeHintType {
|
||||
|
||||
/**
|
||||
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
OTHER(Object.class),
|
||||
|
||||
/**
|
||||
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
PURE_BARCODE(Void.class),
|
||||
|
||||
/**
|
||||
* Image is known to be of one of a few possible formats.
|
||||
* Maps to a {@link List} of {@link BarcodeFormat}s.
|
||||
*/
|
||||
POSSIBLE_FORMATS(List.class),
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
TRY_HARDER(Void.class),
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use when decoding, where applicable (type String)
|
||||
*/
|
||||
CHARACTER_SET(String.class),
|
||||
|
||||
/**
|
||||
* Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
|
||||
*/
|
||||
ALLOWED_LENGTHS(int[].class),
|
||||
|
||||
/**
|
||||
* Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ASSUME_CODE_39_CHECK_DIGIT(Void.class),
|
||||
|
||||
/**
|
||||
* Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
|
||||
* For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ASSUME_GS1(Void.class),
|
||||
|
||||
/**
|
||||
* If true, return the start and end digits in a Codabar barcode instead of stripping them. They
|
||||
* are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
|
||||
* to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
RETURN_CODABAR_START_END(Void.class),
|
||||
|
||||
/**
|
||||
* The caller needs to be notified via callback when a possible {@link ResultPoint}
|
||||
* is found. Maps to a {@link ResultPointCallback}.
|
||||
*/
|
||||
NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class),
|
||||
|
||||
|
||||
/**
|
||||
* Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
|
||||
* Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].
|
||||
* If it is optional to have an extension, do not set this hint. If this is set,
|
||||
* and a UPC or EAN barcode is found but an extension is not, then no result will be returned
|
||||
* at all.
|
||||
*/
|
||||
ALLOWED_EAN_EXTENSIONS(int[].class),
|
||||
|
||||
// End of enumeration values.
|
||||
;
|
||||
|
||||
/**
|
||||
* Data type the hint is expecting.
|
||||
* Among the possible values the {@link Void} stands out as being used for
|
||||
* hints that do not expect a value to be supplied (flag hints). Such hints
|
||||
* will possibly have their value ignored, or replaced by a
|
||||
* {@link Boolean#TRUE}. Hint suppliers should probably use
|
||||
* {@link Boolean#TRUE} as directed by the actual hint documentation.
|
||||
*/
|
||||
private final Class<?> valueType;
|
||||
|
||||
DecodeHintType(Class<?> valueType) {
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public Class<?> getValueType() {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
}
|
62
extern/zxing-core/src/main/java/com/google/zxing/Dimension.java
vendored
Normal file
62
extern/zxing-core/src/main/java/com/google/zxing/Dimension.java
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012 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;
|
||||
|
||||
/**
|
||||
* Simply encapsulates a width and height.
|
||||
*/
|
||||
public final class Dimension {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public Dimension(int width, int height) {
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Dimension) {
|
||||
Dimension d = (Dimension) other;
|
||||
return width == d.width && height == d.height;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return width * 32713 + height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return width + "x" + height;
|
||||
}
|
||||
|
||||
}
|
86
extern/zxing-core/src/main/java/com/google/zxing/EncodeHintType.java
vendored
Normal file
86
extern/zxing-core/src/main/java/com/google/zxing/EncodeHintType.java
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 enum EncodeHintType {
|
||||
|
||||
/**
|
||||
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||
* Type depends on the encoder. For example for QR codes it's type
|
||||
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
|
||||
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
|
||||
* Note: an Aztec symbol should have a minimum of 25% EC words.
|
||||
*/
|
||||
ERROR_CORRECTION,
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use where applicable (type {@link String})
|
||||
*/
|
||||
CHARACTER_SET,
|
||||
|
||||
/**
|
||||
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||
*/
|
||||
DATA_MATRIX_SHAPE,
|
||||
|
||||
/**
|
||||
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*/
|
||||
MIN_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*/
|
||||
MAX_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
|
||||
* by format; for example it controls margin before and after the barcode horizontally for
|
||||
* most 1D formats. (Type {@link Integer}).
|
||||
*/
|
||||
MARGIN,
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}).
|
||||
*/
|
||||
PDF417_COMPACT,
|
||||
|
||||
/**
|
||||
* Specifies what compaction mode to use for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction}).
|
||||
*/
|
||||
PDF417_COMPACTION,
|
||||
|
||||
/**
|
||||
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
|
||||
*/
|
||||
PDF417_DIMENSIONS,
|
||||
|
||||
/**
|
||||
* Specifies the required number of layers for an Aztec code:
|
||||
* a negative number (-1, -2, -3, -4) specifies a compact Aztec code
|
||||
* 0 indicates to use the minimum number of layers (the default)
|
||||
* a positive number (1, 2, .. 32) specifies a normaol (non-compact) Aztec code
|
||||
*/
|
||||
AZTEC_LAYERS,
|
||||
}
|
38
extern/zxing-core/src/main/java/com/google/zxing/FormatException.java
vendored
Normal file
38
extern/zxing-core/src/main/java/com/google/zxing/FormatException.java
vendored
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;
|
||||
}
|
||||
|
||||
}
|
88
extern/zxing-core/src/main/java/com/google/zxing/InvertedLuminanceSource.java
vendored
Normal file
88
extern/zxing-core/src/main/java/com/google/zxing/InvertedLuminanceSource.java
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2013 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 wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class InvertedLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final LuminanceSource delegate;
|
||||
|
||||
public InvertedLuminanceSource(LuminanceSource delegate) {
|
||||
super(delegate.getWidth(), delegate.getHeight());
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
row = delegate.getRow(y, row);
|
||||
int width = getWidth();
|
||||
for (int i = 0; i < width; i++) {
|
||||
row[i] = (byte) (255 - (row[i] & 0xFF));
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
byte[] matrix = delegate.getMatrix();
|
||||
int length = getWidth() * getHeight();
|
||||
byte[] invertedMatrix = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
invertedMatrix[i] = (byte) (255 - (matrix[i] & 0xFF));
|
||||
}
|
||||
return invertedMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return delegate.isCropSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new InvertedLuminanceSource(delegate.crop(left, top, width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotateSupported() {
|
||||
return delegate.isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return original delegate {@link LuminanceSource} since invert undoes itself
|
||||
*/
|
||||
@Override
|
||||
public LuminanceSource invert() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
return new InvertedLuminanceSource(delegate.rotateCounterClockwise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise45() {
|
||||
return new InvertedLuminanceSource(delegate.rotateCounterClockwise45());
|
||||
}
|
||||
|
||||
}
|
157
extern/zxing-core/src/main/java/com/google/zxing/LuminanceSource.java
vendored
Normal file
157
extern/zxing-core/src/main/java/com/google/zxing/LuminanceSource.java
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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, which must be in [0,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:
|
||||
* {@code 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, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,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 UnsupportedOperationException("This luminance source does not support cropping.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports counter-clockwise rotation.
|
||||
*/
|
||||
public boolean isRotateSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*/
|
||||
public LuminanceSource invert() {
|
||||
return new InvertedLuminanceSource(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
throw new UnsupportedOperationException("This luminance source does not support rotation by 90 degrees.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public LuminanceSource rotateCounterClockwise45() {
|
||||
throw new UnsupportedOperationException("This luminance source does not support rotation by 45 degrees.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
byte[] row = new byte[width];
|
||||
StringBuilder result = new StringBuilder(height * (width + 1));
|
||||
for (int y = 0; y < height; y++) {
|
||||
row = getRow(y, row);
|
||||
for (int x = 0; x < width; x++) {
|
||||
int luminance = row[x] & 0xFF;
|
||||
char c;
|
||||
if (luminance < 0x40) {
|
||||
c = '#';
|
||||
} else if (luminance < 0x80) {
|
||||
c = '+';
|
||||
} else if (luminance < 0xC0) {
|
||||
c = '.';
|
||||
} else {
|
||||
c = ' ';
|
||||
}
|
||||
result.append(c);
|
||||
}
|
||||
result.append('\n');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
180
extern/zxing-core/src/main/java/com/google/zxing/MultiFormatReader.java
vendored
Normal file
180
extern/zxing-core/src/main/java/com/google/zxing/MultiFormatReader.java
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.aztec.AztecReader;
|
||||
import com.google.zxing.datamatrix.DataMatrixReader;
|
||||
import com.google.zxing.maxicode.MaxiCodeReader;
|
||||
import com.google.zxing.oned.MultiFormatOneDReader;
|
||||
import com.google.zxing.pdf417.PDF417Reader;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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 Map<DecodeHintType,?> hints;
|
||||
private Reader[] 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
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> 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(Map<DecodeHintType,?> hints) {
|
||||
this.hints = hints;
|
||||
|
||||
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<BarcodeFormat> formats =
|
||||
hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);
|
||||
Collection<Reader> readers = new ArrayList<>();
|
||||
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.RSS_14) ||
|
||||
formats.contains(BarcodeFormat.RSS_EXPANDED);
|
||||
// Put 1D readers upfront in "normal" mode
|
||||
if (addOneDReader && !tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.QR_CODE)) {
|
||||
readers.add(new QRCodeReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.DATA_MATRIX)) {
|
||||
readers.add(new DataMatrixReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.AZTEC)) {
|
||||
readers.add(new AztecReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.PDF_417)) {
|
||||
readers.add(new PDF417Reader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.MAXICODE)) {
|
||||
readers.add(new MaxiCodeReader());
|
||||
}
|
||||
// At end in "try harder" mode
|
||||
if (addOneDReader && tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
}
|
||||
if (readers.isEmpty()) {
|
||||
if (!tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
|
||||
readers.add(new QRCodeReader());
|
||||
readers.add(new DataMatrixReader());
|
||||
readers.add(new AztecReader());
|
||||
readers.add(new PDF417Reader());
|
||||
readers.add(new MaxiCodeReader());
|
||||
|
||||
if (tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
}
|
||||
this.readers = readers.toArray(new Reader[readers.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (readers != null) {
|
||||
for (Reader reader : readers) {
|
||||
reader.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Result decodeInternal(BinaryBitmap image) throws NotFoundException {
|
||||
if (readers != null) {
|
||||
for (Reader reader : readers) {
|
||||
try {
|
||||
return reader.decode(image, hints);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
}
|
97
extern/zxing-core/src/main/java/com/google/zxing/MultiFormatWriter.java
vendored
Normal file
97
extern/zxing-core/src/main/java/com/google/zxing/MultiFormatWriter.java
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.aztec.AztecWriter;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.datamatrix.DataMatrixWriter;
|
||||
import com.google.zxing.oned.CodaBarWriter;
|
||||
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.oned.UPCAWriter;
|
||||
import com.google.zxing.pdf417.PDF417Writer;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height) throws WriterException {
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width, int height,
|
||||
Map<EncodeHintType,?> hints) throws WriterException {
|
||||
|
||||
Writer writer;
|
||||
switch (format) {
|
||||
case EAN_8:
|
||||
writer = new EAN8Writer();
|
||||
break;
|
||||
case EAN_13:
|
||||
writer = new EAN13Writer();
|
||||
break;
|
||||
case UPC_A:
|
||||
writer = new UPCAWriter();
|
||||
break;
|
||||
case QR_CODE:
|
||||
writer = new QRCodeWriter();
|
||||
break;
|
||||
case CODE_39:
|
||||
writer = new Code39Writer();
|
||||
break;
|
||||
case CODE_128:
|
||||
writer = new Code128Writer();
|
||||
break;
|
||||
case ITF:
|
||||
writer = new ITFWriter();
|
||||
break;
|
||||
case PDF_417:
|
||||
writer = new PDF417Writer();
|
||||
break;
|
||||
case CODABAR:
|
||||
writer = new CodaBarWriter();
|
||||
break;
|
||||
case DATA_MATRIX:
|
||||
writer = new DataMatrixWriter();
|
||||
break;
|
||||
case AZTEC:
|
||||
writer = new AztecWriter();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("No encoder available for format " + format);
|
||||
}
|
||||
return writer.encode(contents, format, width, height, hints);
|
||||
}
|
||||
|
||||
}
|
37
extern/zxing-core/src/main/java/com/google/zxing/NotFoundException.java
vendored
Normal file
37
extern/zxing-core/src/main/java/com/google/zxing/NotFoundException.java
vendored
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;
|
||||
}
|
||||
|
||||
}
|
169
extern/zxing-core/src/main/java/com/google/zxing/PlanarYUVLuminanceSource.java
vendored
Normal file
169
extern/zxing-core/src/main/java/com/google/zxing/PlanarYUVLuminanceSource.java
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This object extends LuminanceSource around an array of YUV data returned from the camera driver,
|
||||
* with the option to crop to a rectangle within the full data. This can be used to exclude
|
||||
* superfluous pixels around the perimeter and speed up decoding.
|
||||
*
|
||||
* It works for any pixel format where the Y channel is planar and appears first, including
|
||||
* YCbCr_420_SP and YCbCr_422_SP.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class PlanarYUVLuminanceSource extends LuminanceSource {
|
||||
|
||||
private static final int THUMBNAIL_SCALE_FACTOR = 2;
|
||||
|
||||
private final byte[] yuvData;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public PlanarYUVLuminanceSource(byte[] yuvData,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height,
|
||||
boolean reverseHorizontal) {
|
||||
super(width, height);
|
||||
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
|
||||
this.yuvData = yuvData;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
if (reverseHorizontal) {
|
||||
reverseHorizontal(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@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];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(yuvData, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return yuvData;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(yuvData, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
byte[] yuv = yuvData;
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new PlanarYUVLuminanceSource(yuvData,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height,
|
||||
false);
|
||||
}
|
||||
|
||||
public int[] renderThumbnail() {
|
||||
int width = getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
int height = getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
int[] pixels = new int[width * height];
|
||||
byte[] yuv = yuvData;
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
|
||||
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
|
||||
}
|
||||
inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailWidth() {
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailHeight() {
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
private void reverseHorizontal(int width, int height) {
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
142
extern/zxing-core/src/main/java/com/google/zxing/RGBLuminanceSource.java
vendored
Normal file
142
extern/zxing-core/src/main/java/com/google/zxing/RGBLuminanceSource.java
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This class is used to help decode images from files which arrive as RGB data from
|
||||
* an ARGB pixel array. It does not support rotation.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Betaminos
|
||||
*/
|
||||
public final class RGBLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final byte[] luminances;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public RGBLuminanceSource(int width, int height, int[] pixels) {
|
||||
super(width, height);
|
||||
|
||||
dataWidth = width;
|
||||
dataHeight = height;
|
||||
left = 0;
|
||||
top = 0;
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
luminances = new byte[width * height];
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = pixels[offset + x];
|
||||
int r = (pixel >> 16) & 0xff;
|
||||
int g = (pixel >> 8) & 0xff;
|
||||
int b = pixel & 0xff;
|
||||
if (r == g && g == b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
luminances[offset + x] = (byte) r;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RGBLuminanceSource(byte[] pixels,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height) {
|
||||
super(width, height);
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
this.luminances = pixels;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
}
|
||||
|
||||
@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];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(luminances, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return luminances;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(luminances, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
byte[] rgb = luminances;
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(rgb, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new RGBLuminanceSource(luminances,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
}
|
69
extern/zxing-core/src/main/java/com/google/zxing/Reader.java
vendored
Normal file
69
extern/zxing-core/src/main/java/com/google/zxing/Reader.java
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
/**
|
||||
* 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 no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
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.Map} 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 no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, ChecksumException, FormatException;
|
||||
|
||||
/**
|
||||
* Resets any internal state the implementation has after a decode, to prepare it
|
||||
* for reuse.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
}
|
40
extern/zxing-core/src/main/java/com/google/zxing/ReaderException.java
vendored
Normal file
40
extern/zxing-core/src/main/java/com/google/zxing/ReaderException.java
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
ReaderException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// 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.
|
||||
@Override
|
||||
public final Throwable fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
133
extern/zxing-core/src/main/java/com/google/zxing/Result.java
vendored
Normal file
133
extern/zxing-core/src/main/java/com/google/zxing/Result.java
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <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 Map<ResultMetadataType,Object> 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) {
|
||||
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
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
|
||||
*/
|
||||
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 Map} mapping {@link ResultMetadataType} keys to values. May be
|
||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||
* like orientation.
|
||||
*/
|
||||
public Map<ResultMetadataType,Object> getResultMetadata() {
|
||||
return resultMetadata;
|
||||
}
|
||||
|
||||
public void putMetadata(ResultMetadataType type, Object value) {
|
||||
if (resultMetadata == null) {
|
||||
resultMetadata = new EnumMap<>(ResultMetadataType.class);
|
||||
}
|
||||
resultMetadata.put(type, value);
|
||||
}
|
||||
|
||||
public void putAllMetadata(Map<ResultMetadataType,Object> metadata) {
|
||||
if (metadata != null) {
|
||||
if (resultMetadata == null) {
|
||||
resultMetadata = metadata;
|
||||
} else {
|
||||
resultMetadata.putAll(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addResultPoints(ResultPoint[] newPoints) {
|
||||
ResultPoint[] oldPoints = resultPoints;
|
||||
if (oldPoints == null) {
|
||||
resultPoints = newPoints;
|
||||
} else if (newPoints != null && newPoints.length > 0) {
|
||||
ResultPoint[] allPoints = new ResultPoint[oldPoints.length + newPoints.length];
|
||||
System.arraycopy(oldPoints, 0, allPoints, 0, oldPoints.length);
|
||||
System.arraycopy(newPoints, 0, allPoints, oldPoints.length, newPoints.length);
|
||||
resultPoints = allPoints;
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
97
extern/zxing-core/src/main/java/com/google/zxing/ResultMetadataType.java
vendored
Normal file
97
extern/zxing-core/src/main/java/com/google/zxing/ResultMetadataType.java
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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 enum ResultMetadataType {
|
||||
|
||||
/**
|
||||
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
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.List} of byte arrays corresponding to the
|
||||
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||
*/
|
||||
BYTE_SEGMENTS,
|
||||
|
||||
/**
|
||||
* Error correction level used, if applicable. The value type depends on the
|
||||
* format, but is typically a String.
|
||||
*/
|
||||
ERROR_CORRECTION_LEVEL,
|
||||
|
||||
/**
|
||||
* For some periodicals, indicates the issue number as an {@link Integer}.
|
||||
*/
|
||||
ISSUE_NUMBER,
|
||||
|
||||
/**
|
||||
* For some products, indicates the suggested retail price in the barcode as a
|
||||
* formatted {@link String}.
|
||||
*/
|
||||
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".
|
||||
*/
|
||||
POSSIBLE_COUNTRY,
|
||||
|
||||
/**
|
||||
* For some products, the extension text
|
||||
*/
|
||||
UPC_EAN_EXTENSION,
|
||||
|
||||
/**
|
||||
* PDF417-specific metadata
|
||||
*/
|
||||
PDF417_EXTRA_METADATA,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* sequence number is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_SEQUENCE,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* parity is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_PARITY,
|
||||
|
||||
}
|
138
extern/zxing-core/src/main/java/com/google/zxing/ResultPoint.java
vendored
Normal file
138
extern/zxing-core/src/main/java/com/google/zxing/ResultPoint.java
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.common.detector.MathUtils;
|
||||
|
||||
/**
|
||||
* <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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object other) {
|
||||
if (other instanceof ResultPoint) {
|
||||
ResultPoint otherPoint = (ResultPoint) other;
|
||||
return x == otherPoint.x && y == otherPoint.y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
StringBuilder result = new StringBuilder(25);
|
||||
result.append('(');
|
||||
result.append(x);
|
||||
result.append(',');
|
||||
result.append(y);
|
||||
result.append(')');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
|
||||
* and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
||||
*
|
||||
* @param patterns array of three {@link ResultPoint} to order
|
||||
*/
|
||||
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;
|
||||
ResultPoint pointB;
|
||||
ResultPoint 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param pattern1 first pattern
|
||||
* @param pattern2 second pattern
|
||||
* @return distance between two points
|
||||
*/
|
||||
public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
|
||||
return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
extern/zxing-core/src/main/java/com/google/zxing/ResultPointCallback.java
vendored
Normal file
29
extern/zxing-core/src/main/java/com/google/zxing/ResultPointCallback.java
vendored
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);
|
||||
|
||||
}
|
59
extern/zxing-core/src/main/java/com/google/zxing/Writer.java
vendored
Normal file
59
extern/zxing-core/src/main/java/com/google/zxing/Writer.java
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
/**
|
||||
* 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 {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
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 {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height,
|
||||
Map<EncodeHintType,?> hints)
|
||||
throws WriterException;
|
||||
|
||||
}
|
38
extern/zxing-core/src/main/java/com/google/zxing/WriterException.java
vendored
Normal file
38
extern/zxing-core/src/main/java/com/google/zxing/WriterException.java
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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() {
|
||||
}
|
||||
|
||||
public WriterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WriterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
52
extern/zxing-core/src/main/java/com/google/zxing/aztec/AztecDetectorResult.java
vendored
Normal file
52
extern/zxing-core/src/main/java/com/google/zxing/aztec/AztecDetectorResult.java
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.aztec;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
|
||||
public final class AztecDetectorResult extends DetectorResult {
|
||||
|
||||
private final boolean compact;
|
||||
private final int nbDatablocks;
|
||||
private final int nbLayers;
|
||||
|
||||
public AztecDetectorResult(BitMatrix bits,
|
||||
ResultPoint[] points,
|
||||
boolean compact,
|
||||
int nbDatablocks,
|
||||
int nbLayers) {
|
||||
super(bits, points);
|
||||
this.compact = compact;
|
||||
this.nbDatablocks = nbDatablocks;
|
||||
this.nbLayers = nbLayers;
|
||||
}
|
||||
|
||||
public int getNbLayers() {
|
||||
return nbLayers;
|
||||
}
|
||||
|
||||
public int getNbDatablocks() {
|
||||
return nbDatablocks;
|
||||
}
|
||||
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
}
|
117
extern/zxing-core/src/main/java/com/google/zxing/aztec/AztecReader.java
vendored
Normal file
117
extern/zxing-core/src/main/java/com/google/zxing/aztec/AztecReader.java
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
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.ResultPointCallback;
|
||||
import com.google.zxing.aztec.decoder.Decoder;
|
||||
import com.google.zxing.aztec.detector.Detector;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode Aztec codes in an image.
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class AztecReader implements Reader {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, FormatException {
|
||||
|
||||
NotFoundException notFoundException = null;
|
||||
FormatException formatException = null;
|
||||
Detector detector = new Detector(image.getBlackMatrix());
|
||||
ResultPoint[] points = null;
|
||||
DecoderResult decoderResult = null;
|
||||
try {
|
||||
AztecDetectorResult detectorResult = detector.detect(false);
|
||||
points = detectorResult.getPoints();
|
||||
decoderResult = new Decoder().decode(detectorResult);
|
||||
} catch (NotFoundException e) {
|
||||
notFoundException = e;
|
||||
} catch (FormatException e) {
|
||||
formatException = e;
|
||||
}
|
||||
if (decoderResult == null) {
|
||||
try {
|
||||
AztecDetectorResult detectorResult = detector.detect(true);
|
||||
points = detectorResult.getPoints();
|
||||
decoderResult = new Decoder().decode(detectorResult);
|
||||
} catch (NotFoundException | FormatException e) {
|
||||
if (notFoundException != null) {
|
||||
throw notFoundException;
|
||||
}
|
||||
if (formatException != null) {
|
||||
throw formatException;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (hints != null) {
|
||||
ResultPointCallback rpcb = (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
|
||||
if (rpcb != null) {
|
||||
for (ResultPoint point : points) {
|
||||
rpcb.foundPossibleResultPoint(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.AZTEC);
|
||||
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
if (byteSegments != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
|
||||
}
|
||||
String ecLevel = decoderResult.getECLevel();
|
||||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
88
extern/zxing-core/src/main/java/com/google/zxing/aztec/AztecWriter.java
vendored
Normal file
88
extern/zxing-core/src/main/java/com/google/zxing/aztec/AztecWriter.java
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.Writer;
|
||||
import com.google.zxing.aztec.encoder.AztecCode;
|
||||
import com.google.zxing.aztec.encoder.Encoder;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
public final class AztecWriter implements Writer {
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {
|
||||
String charset = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
|
||||
Number eccPercent = hints == null ? null : (Number) hints.get(EncodeHintType.ERROR_CORRECTION);
|
||||
Number layers = hints == null ? null : (Number) hints.get(EncodeHintType.AZTEC_LAYERS);
|
||||
return encode(contents,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
charset == null ? DEFAULT_CHARSET : Charset.forName(charset),
|
||||
eccPercent == null ? Encoder.DEFAULT_EC_PERCENT : eccPercent.intValue(),
|
||||
layers == null ? Encoder.DEFAULT_AZTEC_LAYERS : layers.intValue());
|
||||
}
|
||||
|
||||
private static BitMatrix encode(String contents, BarcodeFormat format,
|
||||
int width, int height,
|
||||
Charset charset, int eccPercent, int layers) {
|
||||
if (format != BarcodeFormat.AZTEC) {
|
||||
throw new IllegalArgumentException("Can only encode AZTEC, but got " + format);
|
||||
}
|
||||
AztecCode aztec = Encoder.encode(contents.getBytes(charset), eccPercent, layers);
|
||||
return renderResult(aztec, width, height);
|
||||
}
|
||||
|
||||
private static BitMatrix renderResult(AztecCode code, int width, int height) {
|
||||
BitMatrix input = code.getMatrix();
|
||||
if (input == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int inputWidth = input.getWidth();
|
||||
int inputHeight = input.getHeight();
|
||||
int outputWidth = Math.max(width, inputWidth);
|
||||
int outputHeight = Math.max(height, inputHeight);
|
||||
|
||||
int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
|
||||
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
|
||||
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
|
||||
|
||||
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
|
||||
|
||||
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
|
||||
// Write the contents of this row of the barcode
|
||||
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
|
||||
if (input.get(inputX, inputY)) {
|
||||
output.setRegion(outputX, outputY, multiple, multiple);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
335
extern/zxing-core/src/main/java/com/google/zxing/aztec/decoder/Decoder.java
vendored
Normal file
335
extern/zxing-core/src/main/java/com/google/zxing/aztec/decoder/Decoder.java
vendored
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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.aztec.decoder;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.aztec.AztecDetectorResult;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements Aztec Code decoding -- as opposed to locating and extracting
|
||||
* the Aztec Code from an image.</p>
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class Decoder {
|
||||
|
||||
private enum Table {
|
||||
UPPER,
|
||||
LOWER,
|
||||
MIXED,
|
||||
DIGIT,
|
||||
PUNCT,
|
||||
BINARY
|
||||
}
|
||||
|
||||
private static final String[] UPPER_TABLE = {
|
||||
"CTRL_PS", " ", "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", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] LOWER_TABLE = {
|
||||
"CTRL_PS", " ", "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", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] MIXED_TABLE = {
|
||||
"CTRL_PS", " ", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\b", "\t", "\n",
|
||||
"\13", "\f", "\r", "\33", "\34", "\35", "\36", "\37", "@", "\\", "^", "_",
|
||||
"`", "|", "~", "\177", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] PUNCT_TABLE = {
|
||||
"", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")",
|
||||
"*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL"
|
||||
};
|
||||
|
||||
private static final String[] DIGIT_TABLE = {
|
||||
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", "CTRL_US"
|
||||
};
|
||||
|
||||
private AztecDetectorResult ddata;
|
||||
|
||||
public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatException {
|
||||
ddata = detectorResult;
|
||||
BitMatrix matrix = detectorResult.getBits();
|
||||
boolean[] rawbits = extractBits(matrix);
|
||||
boolean[] correctedBits = correctBits(rawbits);
|
||||
String result = getEncodedData(correctedBits);
|
||||
return new DecoderResult(null, result, null, null);
|
||||
}
|
||||
|
||||
// This method is used for testing the high-level encoder
|
||||
public static String highLevelDecode(boolean[] correctedBits) {
|
||||
return getEncodedData(correctedBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string encoded in the aztec code bits
|
||||
*
|
||||
* @return the decoded string
|
||||
*/
|
||||
private static String getEncodedData(boolean[] correctedBits) {
|
||||
int endIndex = correctedBits.length;
|
||||
Table latchTable = Table.UPPER; // table most recently latched to
|
||||
Table shiftTable = Table.UPPER; // table to use for the next read
|
||||
StringBuilder result = new StringBuilder(20);
|
||||
int index = 0;
|
||||
while (index < endIndex) {
|
||||
if (shiftTable == Table.BINARY) {
|
||||
if (endIndex - index < 5) {
|
||||
break;
|
||||
}
|
||||
int length = readCode(correctedBits, index, 5);
|
||||
index += 5;
|
||||
if (length == 0) {
|
||||
if (endIndex - index < 11) {
|
||||
break;
|
||||
}
|
||||
length = readCode(correctedBits, index, 11) + 31;
|
||||
index += 11;
|
||||
}
|
||||
for (int charCount = 0; charCount < length; charCount++) {
|
||||
if (endIndex - index < 8) {
|
||||
index = endIndex; // Force outer loop to exit
|
||||
break;
|
||||
}
|
||||
int code = readCode(correctedBits, index, 8);
|
||||
result.append((char) code);
|
||||
index += 8;
|
||||
}
|
||||
// Go back to whatever mode we had been in
|
||||
shiftTable = latchTable;
|
||||
} else {
|
||||
int size = shiftTable == Table.DIGIT ? 4 : 5;
|
||||
if (endIndex - index < size) {
|
||||
break;
|
||||
}
|
||||
int code = readCode(correctedBits, index, size);
|
||||
index += size;
|
||||
String str = getCharacter(shiftTable, code);
|
||||
if (str.startsWith("CTRL_")) {
|
||||
// Table changes
|
||||
shiftTable = getTable(str.charAt(5));
|
||||
if (str.charAt(6) == 'L') {
|
||||
latchTable = shiftTable;
|
||||
}
|
||||
} else {
|
||||
result.append(str);
|
||||
// Go back to whatever mode we had been in
|
||||
shiftTable = latchTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the table corresponding to the char passed
|
||||
*/
|
||||
private static Table getTable(char t) {
|
||||
switch (t) {
|
||||
case 'L':
|
||||
return Table.LOWER;
|
||||
case 'P':
|
||||
return Table.PUNCT;
|
||||
case 'M':
|
||||
return Table.MIXED;
|
||||
case 'D':
|
||||
return Table.DIGIT;
|
||||
case 'B':
|
||||
return Table.BINARY;
|
||||
case 'U':
|
||||
default:
|
||||
return Table.UPPER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character (or string) corresponding to the passed code in the given table
|
||||
*
|
||||
* @param table the table used
|
||||
* @param code the code of the character
|
||||
*/
|
||||
private static String getCharacter(Table table, int code) {
|
||||
switch (table) {
|
||||
case UPPER:
|
||||
return UPPER_TABLE[code];
|
||||
case LOWER:
|
||||
return LOWER_TABLE[code];
|
||||
case MIXED:
|
||||
return MIXED_TABLE[code];
|
||||
case PUNCT:
|
||||
return PUNCT_TABLE[code];
|
||||
case DIGIT:
|
||||
return DIGIT_TABLE[code];
|
||||
default:
|
||||
// Should not reach here.
|
||||
throw new IllegalStateException("Bad table");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Performs RS error correction on an array of bits.</p>
|
||||
*
|
||||
* @return the corrected array
|
||||
* @throws FormatException if the input contains too many errors
|
||||
*/
|
||||
private boolean[] correctBits(boolean[] rawbits) throws FormatException {
|
||||
GenericGF gf;
|
||||
int codewordSize;
|
||||
|
||||
if (ddata.getNbLayers() <= 2) {
|
||||
codewordSize = 6;
|
||||
gf = GenericGF.AZTEC_DATA_6;
|
||||
} else if (ddata.getNbLayers() <= 8) {
|
||||
codewordSize = 8;
|
||||
gf = GenericGF.AZTEC_DATA_8;
|
||||
} else if (ddata.getNbLayers() <= 22) {
|
||||
codewordSize = 10;
|
||||
gf = GenericGF.AZTEC_DATA_10;
|
||||
} else {
|
||||
codewordSize = 12;
|
||||
gf = GenericGF.AZTEC_DATA_12;
|
||||
}
|
||||
|
||||
int numDataCodewords = ddata.getNbDatablocks();
|
||||
int numCodewords = rawbits.length / codewordSize;
|
||||
int offset = rawbits.length % codewordSize;
|
||||
int numECCodewords = numCodewords - numDataCodewords;
|
||||
|
||||
int[] dataWords = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++, offset += codewordSize) {
|
||||
dataWords[i] = readCode(rawbits, offset, codewordSize);
|
||||
}
|
||||
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(gf);
|
||||
rsDecoder.decode(dataWords, numECCodewords);
|
||||
} catch (ReedSolomonException ignored) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
// Now perform the unstuffing operation.
|
||||
// First, count how many bits are going to be thrown out as stuffing
|
||||
int mask = (1 << codewordSize) - 1;
|
||||
int stuffedBits = 0;
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
int dataWord = dataWords[i];
|
||||
if (dataWord == 0 || dataWord == mask) {
|
||||
throw FormatException.getFormatInstance();
|
||||
} else if (dataWord == 1 || dataWord == mask - 1) {
|
||||
stuffedBits++;
|
||||
}
|
||||
}
|
||||
// Now, actually unpack the bits and remove the stuffing
|
||||
boolean[] correctedBits = new boolean[numDataCodewords * codewordSize - stuffedBits];
|
||||
int index = 0;
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
int dataWord = dataWords[i];
|
||||
if (dataWord == 1 || dataWord == mask - 1) {
|
||||
// next codewordSize-1 bits are all zeros or all ones
|
||||
Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1);
|
||||
index += codewordSize - 1;
|
||||
} else {
|
||||
for (int bit = codewordSize - 1; bit >= 0; --bit) {
|
||||
correctedBits[index++] = (dataWord & (1 << bit)) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return correctedBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of bits from an Aztec Code matrix
|
||||
*
|
||||
* @return the array of bits
|
||||
*/
|
||||
boolean[] extractBits(BitMatrix matrix) {
|
||||
boolean compact = ddata.isCompact();
|
||||
int layers = ddata.getNbLayers();
|
||||
int baseMatrixSize = compact ? 11 + layers * 4 : 14 + layers * 4; // not including alignment lines
|
||||
int[] alignmentMap = new int[baseMatrixSize];
|
||||
boolean[] rawbits = new boolean[totalBitsInLayer(layers, compact)];
|
||||
|
||||
if (compact) {
|
||||
for (int i = 0; i < alignmentMap.length; i++) {
|
||||
alignmentMap[i] = i;
|
||||
}
|
||||
} else {
|
||||
int matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
|
||||
int origCenter = baseMatrixSize / 2;
|
||||
int center = matrixSize / 2;
|
||||
for (int i = 0; i < origCenter; i++) {
|
||||
int newOffset = i + i / 15;
|
||||
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
|
||||
alignmentMap[origCenter + i] = center + newOffset + 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0, rowOffset = 0; i < layers; i++) {
|
||||
int rowSize = compact ? (layers - i) * 4 + 9 : (layers - i) * 4 + 12;
|
||||
// The top-left most point of this layer is <low, low> (not including alignment lines)
|
||||
int low = i * 2;
|
||||
// The bottom-right most point of this layer is <high, high> (not including alignment lines)
|
||||
int high = baseMatrixSize - 1 - low;
|
||||
// We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows
|
||||
for (int j = 0; j < rowSize; j++) {
|
||||
int columnOffset = j * 2;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
// left column
|
||||
rawbits[rowOffset + columnOffset + k] =
|
||||
matrix.get(alignmentMap[low + k], alignmentMap[low + j]);
|
||||
// bottom row
|
||||
rawbits[rowOffset + 2 * rowSize + columnOffset + k] =
|
||||
matrix.get(alignmentMap[low + j], alignmentMap[high - k]);
|
||||
// right column
|
||||
rawbits[rowOffset + 4 * rowSize + columnOffset + k] =
|
||||
matrix.get(alignmentMap[high - k], alignmentMap[high - j]);
|
||||
// top row
|
||||
rawbits[rowOffset + 6 * rowSize + columnOffset + k] =
|
||||
matrix.get(alignmentMap[high - j], alignmentMap[low + k]);
|
||||
}
|
||||
}
|
||||
rowOffset += rowSize * 8;
|
||||
}
|
||||
return rawbits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a code of given length and at given index in an array of bits
|
||||
*/
|
||||
private static int readCode(boolean[] rawbits, int startIndex, int length) {
|
||||
int res = 0;
|
||||
for (int i = startIndex; i < startIndex + length; i++) {
|
||||
res <<= 1;
|
||||
if (rawbits[i]) {
|
||||
res++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int totalBitsInLayer(int layers, boolean compact) {
|
||||
return ((compact ? 88 : 112) + 16 * layers) * layers;
|
||||
}
|
||||
}
|
600
extern/zxing-core/src/main/java/com/google/zxing/aztec/detector/Detector.java
vendored
Normal file
600
extern/zxing-core/src/main/java/com/google/zxing/aztec/detector/Detector.java
vendored
Normal file
@ -0,0 +1,600 @@
|
||||
/*
|
||||
* 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.aztec.detector;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.aztec.AztecDetectorResult;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.GridSampler;
|
||||
import com.google.zxing.common.detector.MathUtils;
|
||||
import com.google.zxing.common.detector.WhiteRectangleDetector;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
/**
|
||||
* Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code
|
||||
* is rotated or skewed, or partially obscured.
|
||||
*
|
||||
* @author David Olivier
|
||||
* @author Frank Yellin
|
||||
*/
|
||||
public final class Detector {
|
||||
|
||||
private final BitMatrix image;
|
||||
|
||||
private boolean compact;
|
||||
private int nbLayers;
|
||||
private int nbDataBlocks;
|
||||
private int nbCenterLayers;
|
||||
private int shift;
|
||||
|
||||
public Detector(BitMatrix image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public AztecDetectorResult detect() throws NotFoundException {
|
||||
return detect(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects an Aztec Code in an image.
|
||||
*
|
||||
* @param isMirror if true, image is a mirror-image of original
|
||||
* @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code
|
||||
* @throws NotFoundException if no Aztec Code can be found
|
||||
*/
|
||||
public AztecDetectorResult detect(boolean isMirror) throws NotFoundException {
|
||||
|
||||
// 1. Get the center of the aztec matrix
|
||||
Point pCenter = getMatrixCenter();
|
||||
|
||||
// 2. Get the center points of the four diagonal points just outside the bull's eye
|
||||
// [topRight, bottomRight, bottomLeft, topLeft]
|
||||
ResultPoint[] bullsEyeCorners = getBullsEyeCorners(pCenter);
|
||||
|
||||
if (isMirror) {
|
||||
ResultPoint temp = bullsEyeCorners[0];
|
||||
bullsEyeCorners[0] = bullsEyeCorners[2];
|
||||
bullsEyeCorners[2] = temp;
|
||||
}
|
||||
|
||||
// 3. Get the size of the matrix and other parameters from the bull's eye
|
||||
extractParameters(bullsEyeCorners);
|
||||
|
||||
// 4. Sample the grid
|
||||
BitMatrix bits = sampleGrid(image,
|
||||
bullsEyeCorners[shift % 4],
|
||||
bullsEyeCorners[(shift + 1) % 4],
|
||||
bullsEyeCorners[(shift + 2) % 4],
|
||||
bullsEyeCorners[(shift + 3) % 4]);
|
||||
|
||||
// 5. Get the corners of the matrix.
|
||||
ResultPoint[] corners = getMatrixCornerPoints(bullsEyeCorners);
|
||||
|
||||
return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the number of data layers and data blocks from the layer around the bull's eye.
|
||||
*
|
||||
* @param bullsEyeCorners the array of bull's eye corners
|
||||
* @throws NotFoundException in case of too many errors or invalid parameters
|
||||
*/
|
||||
private void extractParameters(ResultPoint[] bullsEyeCorners) throws NotFoundException {
|
||||
if (!isValid(bullsEyeCorners[0]) || !isValid(bullsEyeCorners[1]) ||
|
||||
!isValid(bullsEyeCorners[2]) || !isValid(bullsEyeCorners[3])) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
int length = 2 * nbCenterLayers;
|
||||
// Get the bits around the bull's eye
|
||||
int[] sides = {
|
||||
sampleLine(bullsEyeCorners[0], bullsEyeCorners[1], length), // Right side
|
||||
sampleLine(bullsEyeCorners[1], bullsEyeCorners[2], length), // Bottom
|
||||
sampleLine(bullsEyeCorners[2], bullsEyeCorners[3], length), // Left side
|
||||
sampleLine(bullsEyeCorners[3], bullsEyeCorners[0], length) // Top
|
||||
};
|
||||
|
||||
// bullsEyeCorners[shift] is the corner of the bulls'eye that has three
|
||||
// orientation marks.
|
||||
// sides[shift] is the row/column that goes from the corner with three
|
||||
// orientation marks to the corner with two.
|
||||
shift = getRotation(sides, length);
|
||||
|
||||
// Flatten the parameter bits into a single 28- or 40-bit long
|
||||
long parameterData = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int side = sides[(shift + i) % 4];
|
||||
if (compact) {
|
||||
// Each side of the form ..XXXXXXX. where Xs are parameter data
|
||||
parameterData <<= 7;
|
||||
parameterData += (side >> 1) & 0x7F;
|
||||
} else {
|
||||
// Each side of the form ..XXXXX.XXXXX. where Xs are parameter data
|
||||
parameterData <<= 10;
|
||||
parameterData += ((side >> 2) & (0x1f << 5)) + ((side >> 1) & 0x1F);
|
||||
}
|
||||
}
|
||||
|
||||
// Corrects parameter data using RS. Returns just the data portion
|
||||
// without the error correction.
|
||||
int correctedData = getCorrectedParameterData(parameterData, compact);
|
||||
|
||||
if (compact) {
|
||||
// 8 bits: 2 bits layers and 6 bits data blocks
|
||||
nbLayers = (correctedData >> 6) + 1;
|
||||
nbDataBlocks = (correctedData & 0x3F) + 1;
|
||||
} else {
|
||||
// 16 bits: 5 bits layers and 11 bits data blocks
|
||||
nbLayers = (correctedData >> 11) + 1;
|
||||
nbDataBlocks = (correctedData & 0x7FF) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int[] EXPECTED_CORNER_BITS = {
|
||||
0xee0, // 07340 XXX .XX X.. ...
|
||||
0x1dc, // 00734 ... XXX .XX X..
|
||||
0x83b, // 04073 X.. ... XXX .XX
|
||||
0x707, // 03407 .XX X.. ... XXX
|
||||
};
|
||||
|
||||
private static int getRotation(int[] sides, int length) throws NotFoundException {
|
||||
// In a normal pattern, we expect to See
|
||||
// ** .* D A
|
||||
// * *
|
||||
//
|
||||
// . *
|
||||
// .. .. C B
|
||||
//
|
||||
// Grab the 3 bits from each of the sides the form the locator pattern and concatenate
|
||||
// into a 12-bit integer. Start with the bit at A
|
||||
int cornerBits = 0;
|
||||
for (int side : sides) {
|
||||
// XX......X where X's are orientation marks
|
||||
int t = ((side >> (length - 2)) << 1) + (side & 1);
|
||||
cornerBits = (cornerBits << 3) + t;
|
||||
}
|
||||
// Mov the bottom bit to the top, so that the three bits of the locator pattern at A are
|
||||
// together. cornerBits is now:
|
||||
// 3 orientation bits at A || 3 orientation bits at B || ... || 3 orientation bits at D
|
||||
cornerBits = ((cornerBits & 1) << 11) + (cornerBits >> 1);
|
||||
// The result shift indicates which element of BullsEyeCorners[] goes into the top-left
|
||||
// corner. Since the four rotation values have a Hamming distance of 8, we
|
||||
// can easily tolerate two errors.
|
||||
for (int shift = 0; shift < 4; shift++) {
|
||||
if (Integer.bitCount(cornerBits ^ EXPECTED_CORNER_BITS[shift]) <= 2) {
|
||||
return shift;
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrects the parameter bits using Reed-Solomon algorithm.
|
||||
*
|
||||
* @param parameterData parameter bits
|
||||
* @param compact true if this is a compact Aztec code
|
||||
* @throws NotFoundException if the array contains too many errors
|
||||
*/
|
||||
private static int getCorrectedParameterData(long parameterData, boolean compact) throws NotFoundException {
|
||||
int numCodewords;
|
||||
int numDataCodewords;
|
||||
|
||||
if (compact) {
|
||||
numCodewords = 7;
|
||||
numDataCodewords = 2;
|
||||
} else {
|
||||
numCodewords = 10;
|
||||
numDataCodewords = 4;
|
||||
}
|
||||
|
||||
int numECCodewords = numCodewords - numDataCodewords;
|
||||
int[] parameterWords = new int[numCodewords];
|
||||
for (int i = numCodewords - 1; i >= 0; --i) {
|
||||
parameterWords[i] = (int) parameterData & 0xF;
|
||||
parameterData >>= 4;
|
||||
}
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(GenericGF.AZTEC_PARAM);
|
||||
rsDecoder.decode(parameterWords, numECCodewords);
|
||||
} catch (ReedSolomonException ignored) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
// Toss the error correction. Just return the data as an integer
|
||||
int result = 0;
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
result = (result << 4) + parameterWords[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the corners of a bull-eye centered on the passed point.
|
||||
* This returns the centers of the diagonal points just outside the bull's eye
|
||||
* Returns [topRight, bottomRight, bottomLeft, topLeft]
|
||||
*
|
||||
* @param pCenter Center point
|
||||
* @return The corners of the bull-eye
|
||||
* @throws NotFoundException If no valid bull-eye can be found
|
||||
*/
|
||||
private ResultPoint[] getBullsEyeCorners(Point pCenter) throws NotFoundException {
|
||||
|
||||
Point pina = pCenter;
|
||||
Point pinb = pCenter;
|
||||
Point pinc = pCenter;
|
||||
Point pind = pCenter;
|
||||
|
||||
boolean color = true;
|
||||
|
||||
for (nbCenterLayers = 1; nbCenterLayers < 9; nbCenterLayers++) {
|
||||
Point pouta = getFirstDifferent(pina, color, 1, -1);
|
||||
Point poutb = getFirstDifferent(pinb, color, 1, 1);
|
||||
Point poutc = getFirstDifferent(pinc, color, -1, 1);
|
||||
Point poutd = getFirstDifferent(pind, color, -1, -1);
|
||||
|
||||
//d a
|
||||
//
|
||||
//c b
|
||||
|
||||
if (nbCenterLayers > 2) {
|
||||
float q = distance(poutd, pouta) * nbCenterLayers / (distance(pind, pina) * (nbCenterLayers + 2));
|
||||
if (q < 0.75 || q > 1.25 || !isWhiteOrBlackRectangle(pouta, poutb, poutc, poutd)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pina = pouta;
|
||||
pinb = poutb;
|
||||
pinc = poutc;
|
||||
pind = poutd;
|
||||
|
||||
color = !color;
|
||||
}
|
||||
|
||||
if (nbCenterLayers != 5 && nbCenterLayers != 7) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
compact = nbCenterLayers == 5;
|
||||
|
||||
// Expand the square by .5 pixel in each direction so that we're on the border
|
||||
// between the white square and the black square
|
||||
ResultPoint pinax = new ResultPoint(pina.getX() + 0.5f, pina.getY() - 0.5f);
|
||||
ResultPoint pinbx = new ResultPoint(pinb.getX() + 0.5f, pinb.getY() + 0.5f);
|
||||
ResultPoint pincx = new ResultPoint(pinc.getX() - 0.5f, pinc.getY() + 0.5f);
|
||||
ResultPoint pindx = new ResultPoint(pind.getX() - 0.5f, pind.getY() - 0.5f);
|
||||
|
||||
// Expand the square so that its corners are the centers of the points
|
||||
// just outside the bull's eye.
|
||||
return expandSquare(new ResultPoint[]{pinax, pinbx, pincx, pindx},
|
||||
2 * nbCenterLayers - 3,
|
||||
2 * nbCenterLayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a candidate center point of an Aztec code from an image
|
||||
*
|
||||
* @return the center point
|
||||
*/
|
||||
private Point getMatrixCenter() {
|
||||
|
||||
ResultPoint pointA;
|
||||
ResultPoint pointB;
|
||||
ResultPoint pointC;
|
||||
ResultPoint pointD;
|
||||
|
||||
//Get a white rectangle that can be the border of the matrix in center bull's eye or
|
||||
try {
|
||||
|
||||
ResultPoint[] cornerPoints = new WhiteRectangleDetector(image).detect();
|
||||
pointA = cornerPoints[0];
|
||||
pointB = cornerPoints[1];
|
||||
pointC = cornerPoints[2];
|
||||
pointD = cornerPoints[3];
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
|
||||
// This exception can be in case the initial rectangle is white
|
||||
// In that case, surely in the bull's eye, we try to expand the rectangle.
|
||||
int cx = image.getWidth() / 2;
|
||||
int cy = image.getHeight() / 2;
|
||||
pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();
|
||||
pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();
|
||||
pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();
|
||||
pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();
|
||||
|
||||
}
|
||||
|
||||
//Compute the center of the rectangle
|
||||
int cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0f);
|
||||
int cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0f);
|
||||
|
||||
// Redetermine the white rectangle starting from previously computed center.
|
||||
// This will ensure that we end up with a white rectangle in center bull's eye
|
||||
// in order to compute a more accurate center.
|
||||
try {
|
||||
ResultPoint[] cornerPoints = new WhiteRectangleDetector(image, 15, cx, cy).detect();
|
||||
pointA = cornerPoints[0];
|
||||
pointB = cornerPoints[1];
|
||||
pointC = cornerPoints[2];
|
||||
pointD = cornerPoints[3];
|
||||
} catch (NotFoundException e) {
|
||||
// This exception can be in case the initial rectangle is white
|
||||
// In that case we try to expand the rectangle.
|
||||
pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();
|
||||
pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();
|
||||
pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();
|
||||
pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();
|
||||
}
|
||||
|
||||
// Recompute the center of the rectangle
|
||||
cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0f);
|
||||
cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0f);
|
||||
|
||||
return new Point(cx, cy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Aztec code corners from the bull's eye corners and the parameters.
|
||||
*
|
||||
* @param bullsEyeCorners the array of bull's eye corners
|
||||
* @return the array of aztec code corners
|
||||
*/
|
||||
private ResultPoint[] getMatrixCornerPoints(ResultPoint[] bullsEyeCorners) {
|
||||
return expandSquare(bullsEyeCorners, 2 * nbCenterLayers, getDimension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BitMatrix by sampling the provided image.
|
||||
* topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the
|
||||
* diagonal just outside the bull's eye.
|
||||
*/
|
||||
private BitMatrix sampleGrid(BitMatrix image,
|
||||
ResultPoint topLeft,
|
||||
ResultPoint topRight,
|
||||
ResultPoint bottomRight,
|
||||
ResultPoint bottomLeft) throws NotFoundException {
|
||||
|
||||
GridSampler sampler = GridSampler.getInstance();
|
||||
int dimension = getDimension();
|
||||
|
||||
float low = dimension / 2.0f - nbCenterLayers;
|
||||
float high = dimension / 2.0f + nbCenterLayers;
|
||||
|
||||
return sampler.sampleGrid(image,
|
||||
dimension,
|
||||
dimension,
|
||||
low, low, // topleft
|
||||
high, low, // topright
|
||||
high, high, // bottomright
|
||||
low, high, // bottomleft
|
||||
topLeft.getX(), topLeft.getY(),
|
||||
topRight.getX(), topRight.getY(),
|
||||
bottomRight.getX(), bottomRight.getY(),
|
||||
bottomLeft.getX(), bottomLeft.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples a line.
|
||||
*
|
||||
* @param p1 start point (inclusive)
|
||||
* @param p2 end point (exclusive)
|
||||
* @param size number of bits
|
||||
* @return the array of bits as an int (first bit is high-order bit of result)
|
||||
*/
|
||||
private int sampleLine(ResultPoint p1, ResultPoint p2, int size) {
|
||||
int result = 0;
|
||||
|
||||
float d = distance(p1, p2);
|
||||
float moduleSize = d / size;
|
||||
float px = p1.getX();
|
||||
float py = p1.getY();
|
||||
float dx = moduleSize * (p2.getX() - p1.getX()) / d;
|
||||
float dy = moduleSize * (p2.getY() - p1.getY()) / d;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (image.get(MathUtils.round(px + i * dx), MathUtils.round(py + i * dy))) {
|
||||
result |= 1 << (size - i - 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the border of the rectangle passed in parameter is compound of white points only
|
||||
* or black points only
|
||||
*/
|
||||
private boolean isWhiteOrBlackRectangle(Point p1,
|
||||
Point p2,
|
||||
Point p3,
|
||||
Point p4) {
|
||||
|
||||
int corr = 3;
|
||||
|
||||
p1 = new Point(p1.getX() - corr, p1.getY() + corr);
|
||||
p2 = new Point(p2.getX() - corr, p2.getY() - corr);
|
||||
p3 = new Point(p3.getX() + corr, p3.getY() - corr);
|
||||
p4 = new Point(p4.getX() + corr, p4.getY() + corr);
|
||||
|
||||
int cInit = getColor(p4, p1);
|
||||
|
||||
if (cInit == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int c = getColor(p1, p2);
|
||||
|
||||
if (c != cInit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
c = getColor(p2, p3);
|
||||
|
||||
if (c != cInit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
c = getColor(p3, p4);
|
||||
|
||||
return c == cInit;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of a segment
|
||||
*
|
||||
* @return 1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else
|
||||
*/
|
||||
private int getColor(Point p1, Point p2) {
|
||||
float d = distance(p1, p2);
|
||||
float dx = (p2.getX() - p1.getX()) / d;
|
||||
float dy = (p2.getY() - p1.getY()) / d;
|
||||
int error = 0;
|
||||
|
||||
float px = p1.getX();
|
||||
float py = p1.getY();
|
||||
|
||||
boolean colorModel = image.get(p1.getX(), p1.getY());
|
||||
|
||||
for (int i = 0; i < d; i++) {
|
||||
px += dx;
|
||||
py += dy;
|
||||
if (image.get(MathUtils.round(px), MathUtils.round(py)) != colorModel) {
|
||||
error++;
|
||||
}
|
||||
}
|
||||
|
||||
float errRatio = error / d;
|
||||
|
||||
if (errRatio > 0.1f && errRatio < 0.9f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (errRatio <= 0.1f) == colorModel ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the coordinate of the first point with a different color in the given direction
|
||||
*/
|
||||
private Point getFirstDifferent(Point init, boolean color, int dx, int dy) {
|
||||
int x = init.getX() + dx;
|
||||
int y = init.getY() + dy;
|
||||
|
||||
while (isValid(x, y) && image.get(x, y) == color) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
|
||||
x -= dx;
|
||||
y -= dy;
|
||||
|
||||
while (isValid(x, y) && image.get(x, y) == color) {
|
||||
x += dx;
|
||||
}
|
||||
x -= dx;
|
||||
|
||||
while (isValid(x, y) && image.get(x, y) == color) {
|
||||
y += dy;
|
||||
}
|
||||
y -= dy;
|
||||
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the square represented by the corner points by pushing out equally in all directions
|
||||
*
|
||||
* @param cornerPoints the corners of the square, which has the bull's eye at its center
|
||||
* @param oldSide the original length of the side of the square in the target bit matrix
|
||||
* @param newSide the new length of the size of the square in the target bit matrix
|
||||
* @return the corners of the expanded square
|
||||
*/
|
||||
private static ResultPoint[] expandSquare(ResultPoint[] cornerPoints, float oldSide, float newSide) {
|
||||
float ratio = newSide / (2 * oldSide);
|
||||
float dx = cornerPoints[0].getX() - cornerPoints[2].getX();
|
||||
float dy = cornerPoints[0].getY() - cornerPoints[2].getY();
|
||||
float centerx = (cornerPoints[0].getX() + cornerPoints[2].getX()) / 2.0f;
|
||||
float centery = (cornerPoints[0].getY() + cornerPoints[2].getY()) / 2.0f;
|
||||
|
||||
ResultPoint result0 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
|
||||
ResultPoint result2 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
|
||||
|
||||
dx = cornerPoints[1].getX() - cornerPoints[3].getX();
|
||||
dy = cornerPoints[1].getY() - cornerPoints[3].getY();
|
||||
centerx = (cornerPoints[1].getX() + cornerPoints[3].getX()) / 2.0f;
|
||||
centery = (cornerPoints[1].getY() + cornerPoints[3].getY()) / 2.0f;
|
||||
ResultPoint result1 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
|
||||
ResultPoint result3 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
|
||||
|
||||
return new ResultPoint[]{result0, result1, result2, result3};
|
||||
}
|
||||
|
||||
private boolean isValid(int x, int y) {
|
||||
return x >= 0 && x < image.getWidth() && y > 0 && y < image.getHeight();
|
||||
}
|
||||
|
||||
private boolean isValid(ResultPoint point) {
|
||||
int x = MathUtils.round(point.getX());
|
||||
int y = MathUtils.round(point.getY());
|
||||
return isValid(x, y);
|
||||
}
|
||||
|
||||
private static float distance(Point a, Point b) {
|
||||
return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());
|
||||
}
|
||||
|
||||
private static float distance(ResultPoint a, ResultPoint b) {
|
||||
return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());
|
||||
}
|
||||
|
||||
private int getDimension() {
|
||||
if (compact) {
|
||||
return 4 * nbLayers + 11;
|
||||
}
|
||||
if (nbLayers <= 4) {
|
||||
return 4 * nbLayers + 15;
|
||||
}
|
||||
return 4 * nbLayers + 2 * ((nbLayers - 4) / 8 + 1) + 15;
|
||||
}
|
||||
|
||||
static final class Point {
|
||||
private final int x;
|
||||
private final int y;
|
||||
|
||||
ResultPoint toResultPoint() {
|
||||
return new ResultPoint(getX(), getY());
|
||||
}
|
||||
|
||||
Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<" + x + ' ' + y + '>';
|
||||
}
|
||||
}
|
||||
}
|
89
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/AztecCode.java
vendored
Normal file
89
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/AztecCode.java
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* Aztec 2D code representation
|
||||
*
|
||||
* @author Rustam Abdullaev
|
||||
*/
|
||||
public final class AztecCode {
|
||||
|
||||
private boolean compact;
|
||||
private int size;
|
||||
private int layers;
|
||||
private int codeWords;
|
||||
private BitMatrix matrix;
|
||||
|
||||
/**
|
||||
* @return {@code true} if compact instead of full mode
|
||||
*/
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
public void setCompact(boolean compact) {
|
||||
this.compact = compact;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return size in pixels (width and height)
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of levels
|
||||
*/
|
||||
public int getLayers() {
|
||||
return layers;
|
||||
}
|
||||
|
||||
public void setLayers(int layers) {
|
||||
this.layers = layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of data codewords
|
||||
*/
|
||||
public int getCodeWords() {
|
||||
return codeWords;
|
||||
}
|
||||
|
||||
public void setCodeWords(int codeWords) {
|
||||
this.codeWords = codeWords;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the symbol image
|
||||
*/
|
||||
public BitMatrix getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public void setMatrix(BitMatrix matrix) {
|
||||
this.matrix = matrix;
|
||||
}
|
||||
|
||||
}
|
60
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/BinaryShiftToken.java
vendored
Normal file
60
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/BinaryShiftToken.java
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
final class BinaryShiftToken extends Token {
|
||||
|
||||
private final short binaryShiftStart;
|
||||
private final short binaryShiftByteCount;
|
||||
|
||||
BinaryShiftToken(Token previous,
|
||||
int binaryShiftStart,
|
||||
int binaryShiftByteCount) {
|
||||
super(previous);
|
||||
this.binaryShiftStart = (short) binaryShiftStart;
|
||||
this.binaryShiftByteCount = (short) binaryShiftByteCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(BitArray bitArray, byte[] text) {
|
||||
for (int i = 0; i < binaryShiftByteCount; i++) {
|
||||
if (i == 0 || (i == 31 && binaryShiftByteCount <= 62)) {
|
||||
// We need a header before the first character, and before
|
||||
// character 31 when the total byte code is <= 62
|
||||
bitArray.appendBits(31, 5); // BINARY_SHIFT
|
||||
if (binaryShiftByteCount > 62) {
|
||||
bitArray.appendBits(binaryShiftByteCount - 31, 16);
|
||||
} else if (i == 0) {
|
||||
// 1 <= binaryShiftByteCode <= 62
|
||||
bitArray.appendBits(Math.min(binaryShiftByteCount, 31), 5);
|
||||
} else {
|
||||
// 32 <= binaryShiftCount <= 62 and i == 31
|
||||
bitArray.appendBits(binaryShiftByteCount - 31, 5);
|
||||
}
|
||||
}
|
||||
bitArray.appendBits(text[binaryShiftStart + i], 8);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<" + binaryShiftStart + "::" + (binaryShiftStart + binaryShiftByteCount - 1) + '>';
|
||||
}
|
||||
|
||||
}
|
346
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/Encoder.java
vendored
Normal file
346
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/Encoder.java
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
|
||||
|
||||
/**
|
||||
* Generates Aztec 2D barcodes.
|
||||
*
|
||||
* @author Rustam Abdullaev
|
||||
*/
|
||||
public final class Encoder {
|
||||
|
||||
public static final int DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words
|
||||
public static final int DEFAULT_AZTEC_LAYERS = 0;
|
||||
private static final int MAX_NB_BITS = 32;
|
||||
private static final int MAX_NB_BITS_COMPACT = 4;
|
||||
|
||||
private static final int[] WORD_SIZE = {
|
||||
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12
|
||||
};
|
||||
|
||||
private Encoder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given binary content as an Aztec symbol
|
||||
*
|
||||
* @param data input data string
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(byte[] data) {
|
||||
return encode(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given binary content as an Aztec symbol
|
||||
*
|
||||
* @param data input data string
|
||||
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
|
||||
* a minimum of 23% + 3 words is recommended)
|
||||
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers) {
|
||||
// High-level encode
|
||||
BitArray bits = new HighLevelEncoder(data).encode();
|
||||
|
||||
// stuff bits and choose symbol size
|
||||
int eccBits = bits.getSize() * minECCPercent / 100 + 11;
|
||||
int totalSizeBits = bits.getSize() + eccBits;
|
||||
boolean compact;
|
||||
int layers;
|
||||
int totalBitsInLayer;
|
||||
int wordSize;
|
||||
BitArray stuffedBits;
|
||||
if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) {
|
||||
compact = userSpecifiedLayers < 0;
|
||||
layers = Math.abs(userSpecifiedLayers);
|
||||
if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Illegal value %s for layers", userSpecifiedLayers));
|
||||
}
|
||||
totalBitsInLayer = totalBitsInLayer(layers, compact);
|
||||
wordSize = WORD_SIZE[layers];
|
||||
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
|
||||
stuffedBits = stuffBits(bits, wordSize);
|
||||
if (stuffedBits.getSize() + eccBits > usableBitsInLayers) {
|
||||
throw new IllegalArgumentException("Data to large for user specified layer");
|
||||
}
|
||||
if (compact && stuffedBits.getSize() > wordSize * 64) {
|
||||
// Compact format only allows 64 data words, though C4 can hold more words than that
|
||||
throw new IllegalArgumentException("Data to large for user specified layer");
|
||||
}
|
||||
} else {
|
||||
wordSize = 0;
|
||||
stuffedBits = null;
|
||||
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
|
||||
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
|
||||
// is the same size, but has more data.
|
||||
for (int i = 0; ; i++) {
|
||||
if (i > MAX_NB_BITS) {
|
||||
throw new IllegalArgumentException("Data too large for an Aztec code");
|
||||
}
|
||||
compact = i <= 3;
|
||||
layers = compact ? i + 1 : i;
|
||||
totalBitsInLayer = totalBitsInLayer(layers, compact);
|
||||
if (totalSizeBits > totalBitsInLayer) {
|
||||
continue;
|
||||
}
|
||||
// [Re]stuff the bits if this is the first opportunity, or if the
|
||||
// wordSize has changed
|
||||
if (wordSize != WORD_SIZE[layers]) {
|
||||
wordSize = WORD_SIZE[layers];
|
||||
stuffedBits = stuffBits(bits, wordSize);
|
||||
}
|
||||
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
|
||||
if (compact && stuffedBits.getSize() > wordSize * 64) {
|
||||
// Compact format only allows 64 data words, though C4 can hold more words than that
|
||||
continue;
|
||||
}
|
||||
if (stuffedBits.getSize() + eccBits <= usableBitsInLayers) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize);
|
||||
|
||||
// generate mode message
|
||||
int messageSizeInWords = stuffedBits.getSize() / wordSize;
|
||||
BitArray modeMessage = generateModeMessage(compact, layers, messageSizeInWords);
|
||||
|
||||
// allocate symbol
|
||||
int baseMatrixSize = compact ? 11 + layers * 4 : 14 + layers * 4; // not including alignment lines
|
||||
int[] alignmentMap = new int[baseMatrixSize];
|
||||
int matrixSize;
|
||||
if (compact) {
|
||||
// no alignment marks in compact mode, alignmentMap is a no-op
|
||||
matrixSize = baseMatrixSize;
|
||||
for (int i = 0; i < alignmentMap.length; i++) {
|
||||
alignmentMap[i] = i;
|
||||
}
|
||||
} else {
|
||||
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
|
||||
int origCenter = baseMatrixSize / 2;
|
||||
int center = matrixSize / 2;
|
||||
for (int i = 0; i < origCenter; i++) {
|
||||
int newOffset = i + i / 15;
|
||||
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
|
||||
alignmentMap[origCenter + i] = center + newOffset + 1;
|
||||
}
|
||||
}
|
||||
BitMatrix matrix = new BitMatrix(matrixSize);
|
||||
|
||||
// draw data bits
|
||||
for (int i = 0, rowOffset = 0; i < layers; i++) {
|
||||
int rowSize = compact ? (layers - i) * 4 + 9 : (layers - i) * 4 + 12;
|
||||
for (int j = 0; j < rowSize; j++) {
|
||||
int columnOffset = j * 2;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (messageBits.get(rowOffset + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);
|
||||
}
|
||||
if (messageBits.get(rowOffset + rowSize * 2 + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]);
|
||||
}
|
||||
if (messageBits.get(rowOffset + rowSize * 4 + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]);
|
||||
}
|
||||
if (messageBits.get(rowOffset + rowSize * 6 + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
rowOffset += rowSize * 8;
|
||||
}
|
||||
|
||||
// draw mode message
|
||||
drawModeMessage(matrix, compact, matrixSize, modeMessage);
|
||||
|
||||
// draw alignment marks
|
||||
if (compact) {
|
||||
drawBullsEye(matrix, matrixSize / 2, 5);
|
||||
} else {
|
||||
drawBullsEye(matrix, matrixSize / 2, 7);
|
||||
for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
|
||||
for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
|
||||
matrix.set(matrixSize / 2 - j, k);
|
||||
matrix.set(matrixSize / 2 + j, k);
|
||||
matrix.set(k, matrixSize / 2 - j);
|
||||
matrix.set(k, matrixSize / 2 + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AztecCode aztec = new AztecCode();
|
||||
aztec.setCompact(compact);
|
||||
aztec.setSize(matrixSize);
|
||||
aztec.setLayers(layers);
|
||||
aztec.setCodeWords(messageSizeInWords);
|
||||
aztec.setMatrix(matrix);
|
||||
return aztec;
|
||||
}
|
||||
|
||||
private static void drawBullsEye(BitMatrix matrix, int center, int size) {
|
||||
for (int i = 0; i < size; i += 2) {
|
||||
for (int j = center - i; j <= center + i; j++) {
|
||||
matrix.set(j, center - i);
|
||||
matrix.set(j, center + i);
|
||||
matrix.set(center - i, j);
|
||||
matrix.set(center + i, j);
|
||||
}
|
||||
}
|
||||
matrix.set(center - size, center - size);
|
||||
matrix.set(center - size + 1, center - size);
|
||||
matrix.set(center - size, center - size + 1);
|
||||
matrix.set(center + size, center - size);
|
||||
matrix.set(center + size, center - size + 1);
|
||||
matrix.set(center + size, center + size - 1);
|
||||
}
|
||||
|
||||
static BitArray generateModeMessage(boolean compact, int layers, int messageSizeInWords) {
|
||||
BitArray modeMessage = new BitArray();
|
||||
if (compact) {
|
||||
modeMessage.appendBits(layers - 1, 2);
|
||||
modeMessage.appendBits(messageSizeInWords - 1, 6);
|
||||
modeMessage = generateCheckWords(modeMessage, 28, 4);
|
||||
} else {
|
||||
modeMessage.appendBits(layers - 1, 5);
|
||||
modeMessage.appendBits(messageSizeInWords - 1, 11);
|
||||
modeMessage = generateCheckWords(modeMessage, 40, 4);
|
||||
}
|
||||
return modeMessage;
|
||||
}
|
||||
|
||||
private static void drawModeMessage(BitMatrix matrix, boolean compact, int matrixSize, BitArray modeMessage) {
|
||||
int center = matrixSize / 2;
|
||||
if (compact) {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
int offset = center - 3 + i;
|
||||
if (modeMessage.get(i)) {
|
||||
matrix.set(offset, center - 5);
|
||||
}
|
||||
if (modeMessage.get(i + 7)) {
|
||||
matrix.set(center + 5, offset);
|
||||
}
|
||||
if (modeMessage.get(20 - i)) {
|
||||
matrix.set(offset, center + 5);
|
||||
}
|
||||
if (modeMessage.get(27 - i)) {
|
||||
matrix.set(center - 5, offset);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int offset = center - 5 + i + i / 5;
|
||||
if (modeMessage.get(i)) {
|
||||
matrix.set(offset, center - 7);
|
||||
}
|
||||
if (modeMessage.get(i + 10)) {
|
||||
matrix.set(center + 7, offset);
|
||||
}
|
||||
if (modeMessage.get(29 - i)) {
|
||||
matrix.set(offset, center + 7);
|
||||
}
|
||||
if (modeMessage.get(39 - i)) {
|
||||
matrix.set(center - 7, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize) {
|
||||
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
|
||||
int messageSizeInWords = bitArray.getSize() / wordSize;
|
||||
ReedSolomonEncoder rs = new ReedSolomonEncoder(getGF(wordSize));
|
||||
int totalWords = totalBits / wordSize;
|
||||
int[] messageWords = bitsToWords(bitArray, wordSize, totalWords);
|
||||
rs.encode(messageWords, totalWords - messageSizeInWords);
|
||||
int startPad = totalBits % wordSize;
|
||||
BitArray messageBits = new BitArray();
|
||||
messageBits.appendBits(0, startPad);
|
||||
for (int messageWord : messageWords) {
|
||||
messageBits.appendBits(messageWord, wordSize);
|
||||
}
|
||||
return messageBits;
|
||||
}
|
||||
|
||||
private static int[] bitsToWords(BitArray stuffedBits, int wordSize, int totalWords) {
|
||||
int[] message = new int[totalWords];
|
||||
int i;
|
||||
int n;
|
||||
for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {
|
||||
int value = 0;
|
||||
for (int j = 0; j < wordSize; j++) {
|
||||
value |= stuffedBits.get(i * wordSize + j) ? (1 << wordSize - j - 1) : 0;
|
||||
}
|
||||
message[i] = value;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static GenericGF getGF(int wordSize) {
|
||||
switch (wordSize) {
|
||||
case 4:
|
||||
return GenericGF.AZTEC_PARAM;
|
||||
case 6:
|
||||
return GenericGF.AZTEC_DATA_6;
|
||||
case 8:
|
||||
return GenericGF.AZTEC_DATA_8;
|
||||
case 10:
|
||||
return GenericGF.AZTEC_DATA_10;
|
||||
case 12:
|
||||
return GenericGF.AZTEC_DATA_12;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static BitArray stuffBits(BitArray bits, int wordSize) {
|
||||
BitArray out = new BitArray();
|
||||
|
||||
int n = bits.getSize();
|
||||
int mask = (1 << wordSize) - 2;
|
||||
for (int i = 0; i < n; i += wordSize) {
|
||||
int word = 0;
|
||||
for (int j = 0; j < wordSize; j++) {
|
||||
if (i + j >= n || bits.get(i + j)) {
|
||||
word |= 1 << (wordSize - 1 - j);
|
||||
}
|
||||
}
|
||||
if ((word & mask) == mask) {
|
||||
out.appendBits(word & mask, wordSize);
|
||||
i--;
|
||||
} else if ((word & mask) == 0) {
|
||||
out.appendBits(word | 1, wordSize);
|
||||
i--;
|
||||
} else {
|
||||
out.appendBits(word, wordSize);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int totalBitsInLayer(int layers, boolean compact) {
|
||||
return ((compact ? 88 : 112) + 16 * layers) * layers;
|
||||
}
|
||||
}
|
307
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java
vendored
Normal file
307
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This produces nearly optimal encodings of text into the first-level of
|
||||
* encoding used by Aztec code.
|
||||
*
|
||||
* It uses a dynamic algorithm. For each prefix of the string, it determines
|
||||
* a set of encodings that could lead to this prefix. We repeatedly add a
|
||||
* character and generate a new set of optimal encodings until we have read
|
||||
* through the entire input.
|
||||
*
|
||||
* @author Frank Yellin
|
||||
* @author Rustam Abdullaev
|
||||
*/
|
||||
public final class HighLevelEncoder {
|
||||
|
||||
static final String[] MODE_NAMES = {"UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"};
|
||||
|
||||
static final int MODE_UPPER = 0; // 5 bits
|
||||
static final int MODE_LOWER = 1; // 5 bits
|
||||
static final int MODE_DIGIT = 2; // 4 bits
|
||||
static final int MODE_MIXED = 3; // 5 bits
|
||||
static final int MODE_PUNCT = 4; // 5 bits
|
||||
|
||||
// The Latch Table shows, for each pair of Modes, the optimal method for
|
||||
// getting from one mode to another. In the worst possible case, this can
|
||||
// be up to 14 bits. In the best possible case, we are already there!
|
||||
// The high half-word of each entry gives the number of bits.
|
||||
// The low half-word of each entry are the actual bits necessary to change
|
||||
static final int[][] LATCH_TABLE = {
|
||||
{
|
||||
0,
|
||||
(5 << 16) + 28, // UPPER -> LOWER
|
||||
(5 << 16) + 30, // UPPER -> DIGIT
|
||||
(5 << 16) + 29, // UPPER -> MIXED
|
||||
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
|
||||
0,
|
||||
(5 << 16) + 30, // LOWER -> DIGIT
|
||||
(5 << 16) + 29, // LOWER -> MIXED
|
||||
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(4 << 16) + 14, // DIGIT -> UPPER
|
||||
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
|
||||
0,
|
||||
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
|
||||
(14 << 16) + (14 << 10) + (29 << 5) + 30,
|
||||
// DIGIT -> UPPER -> MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(5 << 16) + 29, // MIXED -> UPPER
|
||||
(5 << 16) + 28, // MIXED -> LOWER
|
||||
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
|
||||
0,
|
||||
(5 << 16) + 30, // MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(5 << 16) + 31, // PUNCT -> UPPER
|
||||
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
|
||||
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
|
||||
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
|
||||
0,
|
||||
},
|
||||
};
|
||||
|
||||
// A reverse mapping from [mode][char] to the encoding for that character
|
||||
// in that mode. An entry of 0 indicates no mapping exists.
|
||||
private static final int[][] CHAR_MAP = new int[5][256];
|
||||
static {
|
||||
CHAR_MAP[MODE_UPPER][' '] = 1;
|
||||
for (int c = 'A'; c <= 'Z'; c++) {
|
||||
CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
|
||||
}
|
||||
CHAR_MAP[MODE_LOWER][' '] = 1;
|
||||
for (int c = 'a'; c <= 'z'; c++) {
|
||||
CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
|
||||
}
|
||||
CHAR_MAP[MODE_DIGIT][' '] = 1;
|
||||
for (int c = '0'; c <= '9'; c++) {
|
||||
CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
|
||||
}
|
||||
CHAR_MAP[MODE_DIGIT][','] = 12;
|
||||
CHAR_MAP[MODE_DIGIT]['.'] = 13;
|
||||
int[] mixedTable = {
|
||||
'\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
|
||||
'\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
|
||||
'_', '`', '|', '~', '\177'
|
||||
};
|
||||
for (int i = 0; i < mixedTable.length; i++) {
|
||||
CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
|
||||
}
|
||||
int[] punctTable = {
|
||||
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
|
||||
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
|
||||
'[', ']', '{', '}'
|
||||
};
|
||||
for (int i = 0; i < punctTable.length; i++) {
|
||||
if (punctTable[i] > 0) {
|
||||
CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A map showing the available shift codes. (The shifts to BINARY are not
|
||||
// shown
|
||||
static final int[][] SHIFT_TABLE = new int[6][6]; // mode shift codes, per table
|
||||
static {
|
||||
for (int[] table : SHIFT_TABLE) {
|
||||
Arrays.fill(table, -1);
|
||||
}
|
||||
SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
|
||||
|
||||
SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
|
||||
SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
|
||||
|
||||
SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
|
||||
|
||||
SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
|
||||
SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
|
||||
}
|
||||
|
||||
private final byte[] text;
|
||||
|
||||
public HighLevelEncoder(byte[] text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return text represented by this encoder encoded as a {@link BitArray}
|
||||
*/
|
||||
public BitArray encode() {
|
||||
Collection<State> states = Collections.singletonList(State.INITIAL_STATE);
|
||||
for (int index = 0; index < text.length; index++) {
|
||||
int pairCode;
|
||||
int nextChar = index + 1 < text.length ? text[index + 1] : 0;
|
||||
switch (text[index]) {
|
||||
case '\r':
|
||||
pairCode = nextChar == '\n' ? 2 : 0;
|
||||
break;
|
||||
case '.' :
|
||||
pairCode = nextChar == ' ' ? 3 : 0;
|
||||
break;
|
||||
case ',' :
|
||||
pairCode = nextChar == ' ' ? 4 : 0;
|
||||
break;
|
||||
case ':' :
|
||||
pairCode = nextChar == ' ' ? 5 : 0;
|
||||
break;
|
||||
default:
|
||||
pairCode = 0;
|
||||
}
|
||||
if (pairCode > 0) {
|
||||
// We have one of the four special PUNCT pairs. Treat them specially.
|
||||
// Get a new set of states for the two new characters.
|
||||
states = updateStateListForPair(states, index, pairCode);
|
||||
index++;
|
||||
} else {
|
||||
// Get a new set of states for the new character.
|
||||
states = updateStateListForChar(states, index);
|
||||
}
|
||||
}
|
||||
// We are left with a set of states. Find the shortest one.
|
||||
State minState = Collections.min(states, new Comparator<State>() {
|
||||
@Override
|
||||
public int compare(State a, State b) {
|
||||
return a.getBitCount() - b.getBitCount();
|
||||
}
|
||||
});
|
||||
// Convert it to a bit array, and return.
|
||||
return minState.toBitArray(text);
|
||||
}
|
||||
|
||||
// We update a set of states for a new character by updating each state
|
||||
// for the new character, merging the results, and then removing the
|
||||
// non-optimal states.
|
||||
private Collection<State> updateStateListForChar(Iterable<State> states, int index) {
|
||||
Collection<State> result = new LinkedList<>();
|
||||
for (State state : states) {
|
||||
updateStateForChar(state, index, result);
|
||||
}
|
||||
return simplifyStates(result);
|
||||
}
|
||||
|
||||
// Return a set of states that represent the possible ways of updating this
|
||||
// state for the next character. The resulting set of states are added to
|
||||
// the "result" list.
|
||||
private void updateStateForChar(State state, int index, Collection<State> result) {
|
||||
char ch = (char) (text[index] & 0xFF);
|
||||
boolean charInCurrentTable = CHAR_MAP[state.getMode()][ch] > 0;
|
||||
State stateNoBinary = null;
|
||||
for (int mode = 0; mode <= MODE_PUNCT; mode++) {
|
||||
int charInMode = CHAR_MAP[mode][ch];
|
||||
if (charInMode > 0) {
|
||||
if (stateNoBinary == null) {
|
||||
// Only create stateNoBinary the first time it's required.
|
||||
stateNoBinary = state.endBinaryShift(index);
|
||||
}
|
||||
// Try generating the character by latching to its mode
|
||||
if (!charInCurrentTable || mode == state.getMode() || mode == MODE_DIGIT) {
|
||||
// If the character is in the current table, we don't want to latch to
|
||||
// any other mode except possibly digit (which uses only 4 bits). Any
|
||||
// other latch would be equally successful *after* this character, and
|
||||
// so wouldn't save any bits.
|
||||
State latch_state = stateNoBinary.latchAndAppend(mode, charInMode);
|
||||
result.add(latch_state);
|
||||
}
|
||||
// Try generating the character by switching to its mode.
|
||||
if (!charInCurrentTable && SHIFT_TABLE[state.getMode()][mode] >= 0) {
|
||||
// It never makes sense to temporarily shift to another mode if the
|
||||
// character exists in the current mode. That can never save bits.
|
||||
State shift_state = stateNoBinary.shiftAndAppend(mode, charInMode);
|
||||
result.add(shift_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.getBinaryShiftByteCount() > 0 || CHAR_MAP[state.getMode()][ch] == 0) {
|
||||
// It's never worthwhile to go into binary shift mode if you're not already
|
||||
// in binary shift mode, and the character exists in your current mode.
|
||||
// That can never save bits over just outputting the char in the current mode.
|
||||
State binaryState = state.addBinaryShiftChar(index);
|
||||
result.add(binaryState);
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<State> updateStateListForPair(Iterable<State> states, int index, int pairCode) {
|
||||
Collection<State> result = new LinkedList<>();
|
||||
for (State state : states) {
|
||||
updateStateForPair(state, index, pairCode, result);
|
||||
}
|
||||
return simplifyStates(result);
|
||||
}
|
||||
|
||||
private static void updateStateForPair(State state, int index, int pairCode, Collection<State> result) {
|
||||
State stateNoBinary = state.endBinaryShift(index);
|
||||
// Possibility 1. Latch to MODE_PUNCT, and then append this code
|
||||
result.add(stateNoBinary.latchAndAppend(MODE_PUNCT, pairCode));
|
||||
if (state.getMode() != MODE_PUNCT) {
|
||||
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
|
||||
// Every state except MODE_PUNCT (handled above) can shift
|
||||
result.add(stateNoBinary.shiftAndAppend(MODE_PUNCT, pairCode));
|
||||
}
|
||||
if (pairCode == 3 || pairCode == 4) {
|
||||
// both characters are in DIGITS. Sometimes better to just add two digits
|
||||
State digit_state = stateNoBinary
|
||||
.latchAndAppend(MODE_DIGIT, 16 - pairCode) // period or comma in DIGIT
|
||||
.latchAndAppend(MODE_DIGIT, 1); // space in DIGIT
|
||||
result.add(digit_state);
|
||||
}
|
||||
if (state.getBinaryShiftByteCount() > 0) {
|
||||
// It only makes sense to do the characters as binary if we're already
|
||||
// in binary mode.
|
||||
State binaryState = state.addBinaryShiftChar(index).addBinaryShiftChar(index + 1);
|
||||
result.add(binaryState);
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<State> simplifyStates(Iterable<State> states) {
|
||||
List<State> result = new LinkedList<>();
|
||||
for (State newState : states) {
|
||||
boolean add = true;
|
||||
for (Iterator<State> iterator = result.iterator(); iterator.hasNext(); ) {
|
||||
State oldState = iterator.next();
|
||||
if (oldState.isBetterThanOrEqualTo(newState)) {
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
if (newState.isBetterThanOrEqualTo(oldState)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
result.add(newState);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
45
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/SimpleToken.java
vendored
Normal file
45
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/SimpleToken.java
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
final class SimpleToken extends Token {
|
||||
|
||||
// For normal words, indicates value and bitCount
|
||||
private final short value;
|
||||
private final short bitCount;
|
||||
|
||||
SimpleToken(Token previous, int value, int bitCount) {
|
||||
super(previous);
|
||||
this.value = (short) value;
|
||||
this.bitCount = (short) bitCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
void appendTo(BitArray bitArray, byte[] text) {
|
||||
bitArray.appendBits(value, bitCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
int value = this.value & ((1 << bitCount) - 1);
|
||||
value |= 1 << bitCount;
|
||||
return '<' + Integer.toBinaryString(value | (1 << bitCount)).substring(1) + '>';
|
||||
}
|
||||
|
||||
}
|
169
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/State.java
vendored
Normal file
169
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/State.java
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
/**
|
||||
* State represents all information about a sequence necessary to generate the current output.
|
||||
* Note that a state is immutable.
|
||||
*/
|
||||
final class State {
|
||||
|
||||
static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
|
||||
|
||||
// The current mode of the encoding (or the mode to which we'll return if
|
||||
// we're in Binary Shift mode.
|
||||
private final int mode;
|
||||
// The list of tokens that we output. If we are in Binary Shift mode, this
|
||||
// token list does *not* yet included the token for those bytes
|
||||
private final Token token;
|
||||
// If non-zero, the number of most recent bytes that should be output
|
||||
// in Binary Shift mode.
|
||||
private final int binaryShiftByteCount;
|
||||
// The total number of bits generated (including Binary Shift).
|
||||
private final int bitCount;
|
||||
|
||||
private State(Token token, int mode, int binaryBytes, int bitCount) {
|
||||
this.token = token;
|
||||
this.mode = mode;
|
||||
this.binaryShiftByteCount = binaryBytes;
|
||||
this.bitCount = bitCount;
|
||||
// Make sure we match the token
|
||||
//int binaryShiftBitCount = (binaryShiftByteCount * 8) +
|
||||
// (binaryShiftByteCount == 0 ? 0 :
|
||||
// binaryShiftByteCount <= 31 ? 10 :
|
||||
// binaryShiftByteCount <= 62 ? 20 : 21);
|
||||
//assert this.bitCount == token.getTotalBitCount() + binaryShiftBitCount;
|
||||
}
|
||||
|
||||
int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
Token getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
int getBinaryShiftByteCount() {
|
||||
return binaryShiftByteCount;
|
||||
}
|
||||
|
||||
int getBitCount() {
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
// Create a new state representing this state with a latch to a (not
|
||||
// necessary different) mode, and then a code.
|
||||
State latchAndAppend(int mode, int value) {
|
||||
//assert binaryShiftByteCount == 0;
|
||||
int bitCount = this.bitCount;
|
||||
Token token = this.token;
|
||||
if (mode != this.mode) {
|
||||
int latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode];
|
||||
token = token.add(latch & 0xFFFF, latch >> 16);
|
||||
bitCount += latch >> 16;
|
||||
}
|
||||
int latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
|
||||
token = token.add(value, latchModeBitCount);
|
||||
return new State(token, mode, 0, bitCount + latchModeBitCount);
|
||||
}
|
||||
|
||||
// Create a new state representing this state, with a temporary shift
|
||||
// to a different mode to output a single value.
|
||||
State shiftAndAppend(int mode, int value) {
|
||||
//assert binaryShiftByteCount == 0 && this.mode != mode;
|
||||
Token token = this.token;
|
||||
int thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
|
||||
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
|
||||
token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount);
|
||||
token = token.add(value, 5);
|
||||
return new State(token, this.mode, 0, this.bitCount + thisModeBitCount + 5);
|
||||
}
|
||||
|
||||
// Create a new state representing this state, but an additional character
|
||||
// output in Binary Shift mode.
|
||||
State addBinaryShiftChar(int index) {
|
||||
Token token = this.token;
|
||||
int mode = this.mode;
|
||||
int bitCount = this.bitCount;
|
||||
if (this.mode == HighLevelEncoder.MODE_PUNCT || this.mode == HighLevelEncoder.MODE_DIGIT) {
|
||||
//assert binaryShiftByteCount == 0;
|
||||
int latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER];
|
||||
token = token.add(latch & 0xFFFF, latch >> 16);
|
||||
bitCount += latch >> 16;
|
||||
mode = HighLevelEncoder.MODE_UPPER;
|
||||
}
|
||||
int deltaBitCount =
|
||||
(binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 :
|
||||
(binaryShiftByteCount == 62) ? 9 : 8;
|
||||
State result = new State(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount);
|
||||
if (result.binaryShiftByteCount == 2047 + 31) {
|
||||
// The string is as long as it's allowed to be. We should end it.
|
||||
result = result.endBinaryShift(index + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create the state identical to this one, but we are no longer in
|
||||
// Binary Shift mode.
|
||||
State endBinaryShift(int index) {
|
||||
if (binaryShiftByteCount == 0) {
|
||||
return this;
|
||||
}
|
||||
Token token = this.token;
|
||||
token = token.addBinaryShift(index - binaryShiftByteCount, binaryShiftByteCount);
|
||||
//assert token.getTotalBitCount() == this.bitCount;
|
||||
return new State(token, mode, 0, this.bitCount);
|
||||
}
|
||||
|
||||
// Returns true if "this" state is better (or equal) to be in than "that"
|
||||
// state under all possible circumstances.
|
||||
boolean isBetterThanOrEqualTo(State other) {
|
||||
int mySize = this.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);
|
||||
if (other.binaryShiftByteCount > 0 &&
|
||||
(this.binaryShiftByteCount == 0 || this.binaryShiftByteCount > other.binaryShiftByteCount)) {
|
||||
mySize += 10; // Cost of entering Binary Shift mode.
|
||||
}
|
||||
return mySize <= other.bitCount;
|
||||
}
|
||||
|
||||
BitArray toBitArray(byte[] text) {
|
||||
// Reverse the tokens, so that they are in the order that they should
|
||||
// be output
|
||||
Deque<Token> symbols = new LinkedList<>();
|
||||
for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
|
||||
symbols.addFirst(token);
|
||||
}
|
||||
BitArray bitArray = new BitArray();
|
||||
// Add each token to the result.
|
||||
for (Token symbol : symbols) {
|
||||
symbol.appendTo(bitArray, text);
|
||||
}
|
||||
//assert bitArray.getSize() == this.bitCount;
|
||||
return bitArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
|
||||
}
|
||||
|
||||
}
|
46
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/Token.java
vendored
Normal file
46
extern/zxing-core/src/main/java/com/google/zxing/aztec/encoder/Token.java
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2013 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.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
abstract class Token {
|
||||
|
||||
static final Token EMPTY = new SimpleToken(null, 0, 0);
|
||||
|
||||
private final Token previous;
|
||||
|
||||
Token(Token previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
final Token getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
final Token add(int value, int bitCount) {
|
||||
return new SimpleToken(this, value, bitCount);
|
||||
}
|
||||
|
||||
final Token addBinaryShift(int start, int byteCount) {
|
||||
//int bitCount = (byteCount * 8) + (byteCount <= 31 ? 10 : byteCount <= 62 ? 20 : 21);
|
||||
return new BinaryShiftToken(this, start, byteCount);
|
||||
}
|
||||
|
||||
abstract void appendTo(BitArray bitArray, byte[] text);
|
||||
|
||||
}
|
39
extern/zxing-core/src/main/java/com/google/zxing/client/result/AbstractDoCoMoResultParser.java
vendored
Normal file
39
extern/zxing-core/src/main/java/com/google/zxing/client/result/AbstractDoCoMoResultParser.java
vendored
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
91
extern/zxing-core/src/main/java/com/google/zxing/client/result/AddressBookAUResultParser.java
vendored
Normal file
91
extern/zxing-core/src/main/java/com/google/zxing/client/result/AddressBookAUResultParser.java
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public final class AddressBookAUResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public AddressBookParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
// MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF
|
||||
if (!rawText.contains("MEMORY") || !rawText.contains("\r\n")) {
|
||||
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),
|
||||
null,
|
||||
pronunciation,
|
||||
phoneNumbers,
|
||||
null,
|
||||
emails,
|
||||
null,
|
||||
null,
|
||||
note,
|
||||
addresses,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
private static String[] matchMultipleValuePrefix(String prefix,
|
||||
int max,
|
||||
String rawText,
|
||||
boolean trim) {
|
||||
List<String> 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 ArrayList<>(max); // lazy init
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
return values.toArray(new String[values.size()]);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
public final class AddressBookDoCoMoResultParser extends AbstractDoCoMoResultParser {
|
||||
|
||||
@Override
|
||||
public AddressBookParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!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 (!isStringOfDigits(birthday, 8)) {
|
||||
// No reason to throw out the whole card because the birthday is formatted wrong.
|
||||
birthday = null;
|
||||
}
|
||||
String[] urls = matchDoCoMoPrefixedField("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),
|
||||
null,
|
||||
pronunciation,
|
||||
phoneNumbers,
|
||||
null,
|
||||
emails,
|
||||
null,
|
||||
null,
|
||||
note,
|
||||
addresses,
|
||||
null,
|
||||
org,
|
||||
birthday,
|
||||
null,
|
||||
urls,
|
||||
null);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
208
extern/zxing-core/src/main/java/com/google/zxing/client/result/AddressBookParsedResult.java
vendored
Normal file
208
extern/zxing-core/src/main/java/com/google/zxing/client/result/AddressBookParsedResult.java
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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[] nicknames;
|
||||
private final String pronunciation;
|
||||
private final String[] phoneNumbers;
|
||||
private final String[] phoneTypes;
|
||||
private final String[] emails;
|
||||
private final String[] emailTypes;
|
||||
private final String instantMessenger;
|
||||
private final String note;
|
||||
private final String[] addresses;
|
||||
private final String[] addressTypes;
|
||||
private final String org;
|
||||
private final String birthday;
|
||||
private final String title;
|
||||
private final String[] urls;
|
||||
private final String[] geo;
|
||||
|
||||
public AddressBookParsedResult(String[] names,
|
||||
String[] phoneNumbers,
|
||||
String[] phoneTypes,
|
||||
String[] emails,
|
||||
String[] emailTypes,
|
||||
String[] addresses,
|
||||
String[] addressTypes) {
|
||||
this(names,
|
||||
null,
|
||||
null,
|
||||
phoneNumbers,
|
||||
phoneTypes,
|
||||
emails,
|
||||
emailTypes,
|
||||
null,
|
||||
null,
|
||||
addresses,
|
||||
addressTypes,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
public AddressBookParsedResult(String[] names,
|
||||
String[] nicknames,
|
||||
String pronunciation,
|
||||
String[] phoneNumbers,
|
||||
String[] phoneTypes,
|
||||
String[] emails,
|
||||
String[] emailTypes,
|
||||
String instantMessenger,
|
||||
String note,
|
||||
String[] addresses,
|
||||
String[] addressTypes,
|
||||
String org,
|
||||
String birthday,
|
||||
String title,
|
||||
String[] urls,
|
||||
String[] geo) {
|
||||
super(ParsedResultType.ADDRESSBOOK);
|
||||
this.names = names;
|
||||
this.nicknames = nicknames;
|
||||
this.pronunciation = pronunciation;
|
||||
this.phoneNumbers = phoneNumbers;
|
||||
this.phoneTypes = phoneTypes;
|
||||
this.emails = emails;
|
||||
this.emailTypes = emailTypes;
|
||||
this.instantMessenger = instantMessenger;
|
||||
this.note = note;
|
||||
this.addresses = addresses;
|
||||
this.addressTypes = addressTypes;
|
||||
this.org = org;
|
||||
this.birthday = birthday;
|
||||
this.title = title;
|
||||
this.urls = urls;
|
||||
this.geo = geo;
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public String[] getNicknames() {
|
||||
return nicknames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional descriptions of the type of each phone number. It could be like "HOME", but,
|
||||
* there is no guaranteed or standard format.
|
||||
*/
|
||||
public String[] getPhoneTypes() {
|
||||
return phoneTypes;
|
||||
}
|
||||
|
||||
public String[] getEmails() {
|
||||
return emails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional descriptions of the type of each e-mail. It could be like "WORK", but,
|
||||
* there is no guaranteed or standard format.
|
||||
*/
|
||||
public String[] getEmailTypes() {
|
||||
return emailTypes;
|
||||
}
|
||||
|
||||
public String getInstantMessenger() {
|
||||
return instantMessenger;
|
||||
}
|
||||
|
||||
public String getNote() {
|
||||
return note;
|
||||
}
|
||||
|
||||
public String[] getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional descriptions of the type of each e-mail. It could be like "WORK", but,
|
||||
* there is no guaranteed or standard format.
|
||||
*/
|
||||
public String[] getAddressTypes() {
|
||||
return addressTypes;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getOrg() {
|
||||
return org;
|
||||
}
|
||||
|
||||
public String[] getURLs() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return birthday formatted as yyyyMMdd (e.g. 19780917)
|
||||
*/
|
||||
public String getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a location as a latitude/longitude pair
|
||||
*/
|
||||
public String[] getGeo() {
|
||||
return geo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(100);
|
||||
maybeAppend(names, result);
|
||||
maybeAppend(nicknames, result);
|
||||
maybeAppend(pronunciation, result);
|
||||
maybeAppend(title, result);
|
||||
maybeAppend(org, result);
|
||||
maybeAppend(addresses, result);
|
||||
maybeAppend(phoneNumbers, result);
|
||||
maybeAppend(emails, result);
|
||||
maybeAppend(instantMessenger, result);
|
||||
maybeAppend(urls, result);
|
||||
maybeAppend(birthday, result);
|
||||
maybeAppend(geo, result);
|
||||
maybeAppend(note, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
100
extern/zxing-core/src/main/java/com/google/zxing/client/result/BizcardResultParser.java
vendored
Normal file
100
extern/zxing-core/src/main/java/com/google/zxing/client/result/BizcardResultParser.java
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public 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
|
||||
|
||||
@Override
|
||||
public AddressBookParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!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,
|
||||
null,
|
||||
buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3),
|
||||
null,
|
||||
maybeWrap(email),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
addresses,
|
||||
null,
|
||||
org,
|
||||
null,
|
||||
title,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
private static String[] buildPhoneNumbers(String number1,
|
||||
String number2,
|
||||
String number3) {
|
||||
List<String> numbers = new ArrayList<>(3);
|
||||
if (number1 != null) {
|
||||
numbers.add(number1);
|
||||
}
|
||||
if (number2 != null) {
|
||||
numbers.add(number2);
|
||||
}
|
||||
if (number3 != null) {
|
||||
numbers.add(number3);
|
||||
}
|
||||
int size = numbers.size();
|
||||
if (size == 0) {
|
||||
return null;
|
||||
}
|
||||
return numbers.toArray(new String[size]);
|
||||
}
|
||||
|
||||
private static String buildName(String firstName, String lastName) {
|
||||
if (firstName == null) {
|
||||
return lastName;
|
||||
} else {
|
||||
return lastName == null ? firstName : firstName + ' ' + lastName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
41
extern/zxing-core/src/main/java/com/google/zxing/client/result/BookmarkDoCoMoResultParser.java
vendored
Normal file
41
extern/zxing-core/src/main/java/com/google/zxing/client/result/BookmarkDoCoMoResultParser.java
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
public final class BookmarkDoCoMoResultParser extends AbstractDoCoMoResultParser {
|
||||
|
||||
@Override
|
||||
public URIParsedResult parse(Result result) {
|
||||
String rawText = result.getText();
|
||||
if (!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];
|
||||
return URIResultParser.isBasicallyValidURI(uri) ? new URIParsedResult(uri, title) : null;
|
||||
}
|
||||
|
||||
}
|
246
extern/zxing-core/src/main/java/com/google/zxing/client/result/CalendarParsedResult.java
vendored
Normal file
246
extern/zxing-core/src/main/java/com/google/zxing/client/result/CalendarParsedResult.java
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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 java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class CalendarParsedResult extends ParsedResult {
|
||||
|
||||
private static final Pattern RFC2445_DURATION =
|
||||
Pattern.compile("P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?");
|
||||
private static final long[] RFC2445_DURATION_FIELD_UNITS = {
|
||||
7 * 24 * 60 * 60 * 1000L, // 1 week
|
||||
24 * 60 * 60 * 1000L, // 1 day
|
||||
60 * 60 * 1000L, // 1 hour
|
||||
60 * 1000L, // 1 minute
|
||||
1000L, // 1 second
|
||||
};
|
||||
|
||||
private static final Pattern DATE_TIME = Pattern.compile("[0-9]{8}(T[0-9]{6}Z?)?");
|
||||
|
||||
private final String summary;
|
||||
private final Date start;
|
||||
private final boolean startAllDay;
|
||||
private final Date end;
|
||||
private final boolean endAllDay;
|
||||
private final String location;
|
||||
private final String organizer;
|
||||
private final String[] attendees;
|
||||
private final String description;
|
||||
private final double latitude;
|
||||
private final double longitude;
|
||||
|
||||
public CalendarParsedResult(String summary,
|
||||
String startString,
|
||||
String endString,
|
||||
String durationString,
|
||||
String location,
|
||||
String organizer,
|
||||
String[] attendees,
|
||||
String description,
|
||||
double latitude,
|
||||
double longitude) {
|
||||
super(ParsedResultType.CALENDAR);
|
||||
this.summary = summary;
|
||||
|
||||
try {
|
||||
this.start = parseDate(startString);
|
||||
} catch (ParseException pe) {
|
||||
throw new IllegalArgumentException(pe.toString());
|
||||
}
|
||||
|
||||
if (endString == null) {
|
||||
long durationMS = parseDurationMS(durationString);
|
||||
end = durationMS < 0L ? null : new Date(start.getTime() + durationMS);
|
||||
} else {
|
||||
try {
|
||||
this.end = parseDate(endString);
|
||||
} catch (ParseException pe) {
|
||||
throw new IllegalArgumentException(pe.toString());
|
||||
}
|
||||
}
|
||||
|
||||
this.startAllDay = startString.length() == 8;
|
||||
this.endAllDay = endString != null && endString.length() == 8;
|
||||
|
||||
this.location = location;
|
||||
this.organizer = organizer;
|
||||
this.attendees = attendees;
|
||||
this.description = description;
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return start time
|
||||
*/
|
||||
public Date getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if start time was specified as a whole day
|
||||
*/
|
||||
public boolean isStartAllDay() {
|
||||
return startAllDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return event end {@link Date}, or {@code null} if event has no duration
|
||||
* @see #getStart()
|
||||
*/
|
||||
public Date getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if end time was specified as a whole day
|
||||
*/
|
||||
public boolean isEndAllDay() {
|
||||
return endAllDay;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public String getOrganizer() {
|
||||
return organizer;
|
||||
}
|
||||
|
||||
public String[] getAttendees() {
|
||||
return attendees;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(100);
|
||||
maybeAppend(summary, result);
|
||||
maybeAppend(format(startAllDay, start), result);
|
||||
maybeAppend(format(endAllDay, end), result);
|
||||
maybeAppend(location, result);
|
||||
maybeAppend(organizer, result);
|
||||
maybeAppend(attendees, result);
|
||||
maybeAppend(description, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string as a date. 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 when The string to parse
|
||||
* @throws ParseException if not able to parse as a date
|
||||
*/
|
||||
private static Date parseDate(String when) throws ParseException {
|
||||
if (!DATE_TIME.matcher(when).matches()) {
|
||||
throw new ParseException(when, 0);
|
||||
}
|
||||
if (when.length() == 8) {
|
||||
// Show only year/month/day
|
||||
return buildDateFormat().parse(when);
|
||||
} else {
|
||||
// The when string can be local time, or UTC if it ends with a Z
|
||||
Date date;
|
||||
if (when.length() == 16 && when.charAt(15) == 'Z') {
|
||||
date = buildDateTimeFormat().parse(when.substring(0, 15));
|
||||
Calendar calendar = new GregorianCalendar();
|
||||
long milliseconds = date.getTime();
|
||||
// Account for time zone difference
|
||||
milliseconds += calendar.get(Calendar.ZONE_OFFSET);
|
||||
// Might need to correct for daylight savings time, but use target time since
|
||||
// now might be in DST but not then, or vice versa
|
||||
calendar.setTime(new Date(milliseconds));
|
||||
milliseconds += calendar.get(Calendar.DST_OFFSET);
|
||||
date = new Date(milliseconds);
|
||||
} else {
|
||||
date = buildDateTimeFormat().parse(when);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
private static String format(boolean allDay, Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
DateFormat format = allDay
|
||||
? DateFormat.getDateInstance(DateFormat.MEDIUM)
|
||||
: DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
private static long parseDurationMS(CharSequence durationString) {
|
||||
if (durationString == null) {
|
||||
return -1L;
|
||||
}
|
||||
Matcher m = RFC2445_DURATION.matcher(durationString);
|
||||
if (!m.matches()) {
|
||||
return -1L;
|
||||
}
|
||||
long durationMS = 0L;
|
||||
for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.length; i++) {
|
||||
String fieldValue = m.group(i + 1);
|
||||
if (fieldValue != null) {
|
||||
durationMS += RFC2445_DURATION_FIELD_UNITS[i] * Integer.parseInt(fieldValue);
|
||||
}
|
||||
}
|
||||
return durationMS;
|
||||
}
|
||||
|
||||
private static DateFormat buildDateFormat() {
|
||||
DateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);
|
||||
// For dates without a time, for purposes of interacting with Android, the resulting timestamp
|
||||
// needs to be midnight of that day in GMT. See:
|
||||
// http://code.google.com/p/android/issues/detail?id=8330
|
||||
format.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
return format;
|
||||
}
|
||||
|
||||
private static DateFormat buildDateTimeFormat() {
|
||||
return new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH);
|
||||
}
|
||||
|
||||
}
|
65
extern/zxing-core/src/main/java/com/google/zxing/client/result/EmailAddressParsedResult.java
vendored
Normal file
65
extern/zxing-core/src/main/java/com/google/zxing/client/result/EmailAddressParsedResult.java
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(30);
|
||||
maybeAppend(emailAddress, result);
|
||||
maybeAppend(subject, result);
|
||||
maybeAppend(body, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
63
extern/zxing-core/src/main/java/com/google/zxing/client/result/EmailAddressResultParser.java
vendored
Normal file
63
extern/zxing-core/src/main/java/com/google/zxing/client/result/EmailAddressResultParser.java
vendored
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.client.result;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public final class EmailAddressResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public EmailAddressParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
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);
|
||||
}
|
||||
emailAddress = urlDecode(emailAddress);
|
||||
Map<String,String> nameValues = parseNameValuePairs(rawText);
|
||||
String subject = null;
|
||||
String body = null;
|
||||
if (nameValues != null) {
|
||||
if (emailAddress.isEmpty()) {
|
||||
emailAddress = nameValues.get("to");
|
||||
}
|
||||
subject = nameValues.get("subject");
|
||||
body = 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
63
extern/zxing-core/src/main/java/com/google/zxing/client/result/EmailDoCoMoResultParser.java
vendored
Normal file
63
extern/zxing-core/src/main/java/com/google/zxing/client/result/EmailDoCoMoResultParser.java
vendored
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.client.result;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Implements the "MATMSG" email message entry format.
|
||||
*
|
||||
* Supported keys: TO, SUB, BODY
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class EmailDoCoMoResultParser extends AbstractDoCoMoResultParser {
|
||||
|
||||
private static final Pattern ATEXT_ALPHANUMERIC = Pattern.compile("[a-zA-Z0-9@.!#$%&'*+\\-/=?^_`{|}~]+");
|
||||
|
||||
@Override
|
||||
public EmailAddressParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!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 '@' and 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) {
|
||||
return email != null && ATEXT_ALPHANUMERIC.matcher(email).matches() && email.indexOf('@') >= 0;
|
||||
}
|
||||
|
||||
}
|
204
extern/zxing-core/src/main/java/com/google/zxing/client/result/ExpandedProductParsedResult.java
vendored
Normal file
204
extern/zxing-core/src/main/java/com/google/zxing/client/result/ExpandedProductParsedResult.java
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
/**
|
||||
* @author Antonio Manuel Benjumea Conde, Servinform, S.A.
|
||||
* @author Agustín Delgado, Servinform, S.A.
|
||||
*/
|
||||
public final class ExpandedProductParsedResult extends ParsedResult {
|
||||
|
||||
public static final String KILOGRAM = "KG";
|
||||
public static final String POUND = "LB";
|
||||
|
||||
private final String rawText;
|
||||
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 Map<String,String> uncommonAIs;
|
||||
|
||||
public ExpandedProductParsedResult(String rawText,
|
||||
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,
|
||||
Map<String,String> uncommonAIs) {
|
||||
super(ParsedResultType.PRODUCT);
|
||||
this.rawText = rawText;
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if (!(o instanceof ExpandedProductParsedResult)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpandedProductParsedResult other = (ExpandedProductParsedResult)o;
|
||||
|
||||
return equalsOrNull(productID, other.productID)
|
||||
&& equalsOrNull(sscc, other.sscc)
|
||||
&& equalsOrNull(lotNumber, other.lotNumber)
|
||||
&& equalsOrNull(productionDate, other.productionDate)
|
||||
&& equalsOrNull(bestBeforeDate, other.bestBeforeDate)
|
||||
&& equalsOrNull(expirationDate, other.expirationDate)
|
||||
&& equalsOrNull(weight, other.weight)
|
||||
&& equalsOrNull(weightType, other.weightType)
|
||||
&& equalsOrNull(weightIncrement, other.weightIncrement)
|
||||
&& equalsOrNull(price, other.price)
|
||||
&& equalsOrNull(priceIncrement, other.priceIncrement)
|
||||
&& equalsOrNull(priceCurrency, other.priceCurrency)
|
||||
&& equalsOrNull(uncommonAIs, other.uncommonAIs);
|
||||
}
|
||||
|
||||
private static boolean equalsOrNull(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
int hash = 0;
|
||||
hash ^= hashNotNull(productID);
|
||||
hash ^= hashNotNull(sscc);
|
||||
hash ^= hashNotNull(lotNumber);
|
||||
hash ^= hashNotNull(productionDate);
|
||||
hash ^= hashNotNull(bestBeforeDate);
|
||||
hash ^= hashNotNull(expirationDate);
|
||||
hash ^= hashNotNull(weight);
|
||||
hash ^= hashNotNull(weightType);
|
||||
hash ^= hashNotNull(weightIncrement);
|
||||
hash ^= hashNotNull(price);
|
||||
hash ^= hashNotNull(priceIncrement);
|
||||
hash ^= hashNotNull(priceCurrency);
|
||||
hash ^= hashNotNull(uncommonAIs);
|
||||
return hash;
|
||||
}
|
||||
|
||||
private static int hashNotNull(Object o) {
|
||||
return o == null ? 0 : o.hashCode();
|
||||
}
|
||||
|
||||
public String getRawText() {
|
||||
return rawText;
|
||||
}
|
||||
|
||||
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 Map<String,String> getUncommonAIs() {
|
||||
return uncommonAIs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
return String.valueOf(rawText);
|
||||
}
|
||||
}
|
218
extern/zxing-core/src/main/java/com/google/zxing/client/result/ExpandedProductResultParser.java
vendored
Normal file
218
extern/zxing-core/src/main/java/com/google/zxing/client/result/ExpandedProductResultParser.java
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.
|
||||
*/
|
||||
public final class ExpandedProductResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public ExpandedProductParsedResult parse(Result result) {
|
||||
BarcodeFormat format = result.getBarcodeFormat();
|
||||
if (format != BarcodeFormat.RSS_EXPANDED) {
|
||||
// ExtendedProductParsedResult NOT created. Not a RSS Expanded barcode
|
||||
return null;
|
||||
}
|
||||
String rawText = getMassagedText(result);
|
||||
|
||||
String productID = null;
|
||||
String sscc = null;
|
||||
String lotNumber = null;
|
||||
String productionDate = null;
|
||||
String packagingDate = null;
|
||||
String bestBeforeDate = null;
|
||||
String expirationDate = null;
|
||||
String weight = null;
|
||||
String weightType = null;
|
||||
String weightIncrement = null;
|
||||
String price = null;
|
||||
String priceIncrement = null;
|
||||
String priceCurrency = null;
|
||||
Map<String,String> uncommonAIs = new HashMap<>();
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (i < rawText.length()) {
|
||||
String ai = findAIvalue(i, rawText);
|
||||
if (ai == null) {
|
||||
// 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();
|
||||
|
||||
switch (ai) {
|
||||
case "00":
|
||||
sscc = value;
|
||||
break;
|
||||
case "01":
|
||||
productID = value;
|
||||
break;
|
||||
case "10":
|
||||
lotNumber = value;
|
||||
break;
|
||||
case "11":
|
||||
productionDate = value;
|
||||
break;
|
||||
case "13":
|
||||
packagingDate = value;
|
||||
break;
|
||||
case "15":
|
||||
bestBeforeDate = value;
|
||||
break;
|
||||
case "17":
|
||||
expirationDate = value;
|
||||
break;
|
||||
case "3100":
|
||||
case "3101":
|
||||
case "3102":
|
||||
case "3103":
|
||||
case "3104":
|
||||
case "3105":
|
||||
case "3106":
|
||||
case "3107":
|
||||
case "3108":
|
||||
case "3109":
|
||||
weight = value;
|
||||
weightType = ExpandedProductParsedResult.KILOGRAM;
|
||||
weightIncrement = ai.substring(3);
|
||||
break;
|
||||
case "3200":
|
||||
case "3201":
|
||||
case "3202":
|
||||
case "3203":
|
||||
case "3204":
|
||||
case "3205":
|
||||
case "3206":
|
||||
case "3207":
|
||||
case "3208":
|
||||
case "3209":
|
||||
weight = value;
|
||||
weightType = ExpandedProductParsedResult.POUND;
|
||||
weightIncrement = ai.substring(3);
|
||||
break;
|
||||
case "3920":
|
||||
case "3921":
|
||||
case "3922":
|
||||
case "3923":
|
||||
price = value;
|
||||
priceIncrement = ai.substring(3);
|
||||
break;
|
||||
case "3930":
|
||||
case "3931":
|
||||
case "3932":
|
||||
case "3933":
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
// No match with common AIs
|
||||
uncommonAIs.put(ai, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new ExpandedProductParsedResult(rawText,
|
||||
productID,
|
||||
sscc,
|
||||
lotNumber,
|
||||
productionDate,
|
||||
packagingDate,
|
||||
bestBeforeDate,
|
||||
expirationDate,
|
||||
weight,
|
||||
weightType,
|
||||
weightIncrement,
|
||||
price,
|
||||
priceIncrement,
|
||||
priceCurrency,
|
||||
uncommonAIs);
|
||||
}
|
||||
|
||||
private static String findAIvalue(int i, String rawText) {
|
||||
char c = rawText.charAt(i);
|
||||
// First character must be a open parenthesis.If not, ERROR
|
||||
if (c != '(') {
|
||||
return null;
|
||||
}
|
||||
|
||||
CharSequence rawTextAux = rawText.substring(i + 1);
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int index = 0; index < rawTextAux.length(); index++) {
|
||||
char currentChar = rawTextAux.charAt(index);
|
||||
if (currentChar == ')') {
|
||||
return buf.toString();
|
||||
} else if (currentChar >= '0' && currentChar <= '9') {
|
||||
buf.append(currentChar);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String findValue(int i, String rawText) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
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 (findAIvalue(index, rawTextAux) == null) {
|
||||
buf.append('(');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
buf.append(c);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
101
extern/zxing-core/src/main/java/com/google/zxing/client/result/GeoParsedResult.java
vendored
Normal file
101
extern/zxing-core/src/main/java/com/google/zxing/client/result/GeoParsedResult.java
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(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();
|
||||
}
|
||||
|
||||
}
|
73
extern/zxing-core/src/main/java/com/google/zxing/client/result/GeoResultParser.java
vendored
Normal file
73
extern/zxing-core/src/main/java/com/google/zxing/client/result/GeoResultParser.java
vendored
Normal file
@ -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.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public final class GeoResultParser extends ResultParser {
|
||||
|
||||
private static final Pattern GEO_URL_PATTERN =
|
||||
Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@Override
|
||||
public GeoParsedResult parse(Result result) {
|
||||
CharSequence rawText = getMassagedText(result);
|
||||
Matcher matcher = GEO_URL_PATTERN.matcher(rawText);
|
||||
if (!matcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String query = matcher.group(4);
|
||||
|
||||
double latitude;
|
||||
double longitude;
|
||||
double altitude;
|
||||
try {
|
||||
latitude = Double.parseDouble(matcher.group(1));
|
||||
if (latitude > 90.0 || latitude < -90.0) {
|
||||
return null;
|
||||
}
|
||||
longitude = Double.parseDouble(matcher.group(2));
|
||||
if (longitude > 180.0 || longitude < -180.0) {
|
||||
return null;
|
||||
}
|
||||
if (matcher.group(3) == null) {
|
||||
altitude = 0.0;
|
||||
} else {
|
||||
altitude = Double.parseDouble(matcher.group(3));
|
||||
if (altitude < 0.0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
return new GeoParsedResult(latitude, longitude, altitude, query);
|
||||
}
|
||||
|
||||
}
|
40
extern/zxing-core/src/main/java/com/google/zxing/client/result/ISBNParsedResult.java
vendored
Normal file
40
extern/zxing-core/src/main/java/com/google/zxing/client/result/ISBNParsedResult.java
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
return isbn;
|
||||
}
|
||||
|
||||
}
|
50
extern/zxing-core/src/main/java/com/google/zxing/client/result/ISBNResultParser.java
vendored
Normal file
50
extern/zxing-core/src/main/java/com/google/zxing/client/result/ISBNResultParser.java
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 final class ISBNResultParser extends ResultParser {
|
||||
|
||||
/**
|
||||
* See <a href="http://www.bisg.org/isbn-13/for.dummies.html">ISBN-13 For Dummies</a>
|
||||
*/
|
||||
@Override
|
||||
public ISBNParsedResult parse(Result result) {
|
||||
BarcodeFormat format = result.getBarcodeFormat();
|
||||
if (format != BarcodeFormat.EAN_13) {
|
||||
return null;
|
||||
}
|
||||
String rawText = getMassagedText(result);
|
||||
int length = rawText.length();
|
||||
if (length != 13) {
|
||||
return null;
|
||||
}
|
||||
if (!rawText.startsWith("978") && !rawText.startsWith("979")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ISBNParsedResult(rawText);
|
||||
}
|
||||
|
||||
}
|
67
extern/zxing-core/src/main/java/com/google/zxing/client/result/ParsedResult.java
vendored
Normal file
67
extern/zxing-core/src/main/java/com/google/zxing/client/result/ParsedResult.java
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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>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(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 ParsedResult {
|
||||
|
||||
private final ParsedResultType type;
|
||||
|
||||
protected ParsedResult(ParsedResultType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public final ParsedResultType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public abstract String getDisplayResult();
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return getDisplayResult();
|
||||
}
|
||||
|
||||
public static void maybeAppend(String value, StringBuilder result) {
|
||||
if (value != null && !value.isEmpty()) {
|
||||
// Don't add a newline before the first value
|
||||
if (result.length() > 0) {
|
||||
result.append('\n');
|
||||
}
|
||||
result.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void maybeAppend(String[] values, StringBuilder result) {
|
||||
if (values != null) {
|
||||
for (String value : values) {
|
||||
maybeAppend(value, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
40
extern/zxing-core/src/main/java/com/google/zxing/client/result/ParsedResultType.java
vendored
Normal file
40
extern/zxing-core/src/main/java/com/google/zxing/client/result/ParsedResultType.java
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 enum ParsedResultType {
|
||||
|
||||
ADDRESSBOOK,
|
||||
EMAIL_ADDRESS,
|
||||
PRODUCT,
|
||||
URI,
|
||||
TEXT,
|
||||
GEO,
|
||||
TEL,
|
||||
SMS,
|
||||
CALENDAR,
|
||||
WIFI,
|
||||
ISBN,
|
||||
VIN,
|
||||
|
||||
}
|
50
extern/zxing-core/src/main/java/com/google/zxing/client/result/ProductParsedResult.java
vendored
Normal file
50
extern/zxing-core/src/main/java/com/google/zxing/client/result/ProductParsedResult.java
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
return productID;
|
||||
}
|
||||
|
||||
}
|
55
extern/zxing-core/src/main/java/com/google/zxing/client/result/ProductResultParser.java
vendored
Normal file
55
extern/zxing-core/src/main/java/com/google/zxing/client/result/ProductResultParser.java
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
public final class ProductResultParser extends ResultParser {
|
||||
|
||||
// Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes.
|
||||
@Override
|
||||
public ProductParsedResult parse(Result result) {
|
||||
BarcodeFormat format = result.getBarcodeFormat();
|
||||
if (!(format == BarcodeFormat.UPC_A || format == BarcodeFormat.UPC_E ||
|
||||
format == BarcodeFormat.EAN_8 || format == BarcodeFormat.EAN_13)) {
|
||||
return null;
|
||||
}
|
||||
String rawText = getMassagedText(result);
|
||||
if (!isStringOfDigits(rawText, rawText.length())) {
|
||||
return null;
|
||||
}
|
||||
// Not actually checking the checksum again here
|
||||
|
||||
String normalizedProductID;
|
||||
// Expand UPC-E for purposes of searching
|
||||
if (format == BarcodeFormat.UPC_E && rawText.length() == 8) {
|
||||
normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText);
|
||||
} else {
|
||||
normalizedProductID = rawText;
|
||||
}
|
||||
|
||||
return new ProductParsedResult(rawText, normalizedProductID);
|
||||
}
|
||||
|
||||
}
|
247
extern/zxing-core/src/main/java/com/google/zxing/client/result/ResultParser.java
vendored
Normal file
247
extern/zxing-core/src/main/java/com/google/zxing/client/result/ResultParser.java
vendored
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.client.result;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <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(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 {
|
||||
|
||||
private static final ResultParser[] PARSERS = {
|
||||
new BookmarkDoCoMoResultParser(),
|
||||
new AddressBookDoCoMoResultParser(),
|
||||
new EmailDoCoMoResultParser(),
|
||||
new AddressBookAUResultParser(),
|
||||
new VCardResultParser(),
|
||||
new BizcardResultParser(),
|
||||
new VEventResultParser(),
|
||||
new EmailAddressResultParser(),
|
||||
new SMTPResultParser(),
|
||||
new TelResultParser(),
|
||||
new SMSMMSResultParser(),
|
||||
new SMSTOMMSTOResultParser(),
|
||||
new GeoResultParser(),
|
||||
new WifiResultParser(),
|
||||
new URLTOResultParser(),
|
||||
new URIResultParser(),
|
||||
new ISBNResultParser(),
|
||||
new ProductResultParser(),
|
||||
new ExpandedProductResultParser(),
|
||||
new VINResultParser(),
|
||||
};
|
||||
|
||||
private static final Pattern DIGITS = Pattern.compile("\\d+");
|
||||
private static final Pattern AMPERSAND = Pattern.compile("&");
|
||||
private static final Pattern EQUALS = Pattern.compile("=");
|
||||
private static final String BYTE_ORDER_MARK = "\ufeff";
|
||||
|
||||
/**
|
||||
* Attempts to parse the raw {@link Result}'s contents as a particular type
|
||||
* of information (email, URL, etc.) and return a {@link ParsedResult} encapsulating
|
||||
* the result of parsing.
|
||||
*
|
||||
* @param theResult the raw {@link Result} to parse
|
||||
* @return {@link ParsedResult} encapsulating the parsing result
|
||||
*/
|
||||
public abstract ParsedResult parse(Result theResult);
|
||||
|
||||
protected static String getMassagedText(Result result) {
|
||||
String text = result.getText();
|
||||
if (text.startsWith(BYTE_ORDER_MARK)) {
|
||||
text = text.substring(1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static ParsedResult parseResult(Result theResult) {
|
||||
for (ResultParser parser : PARSERS) {
|
||||
ParsedResult result = parser.parse(theResult);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return new TextParsedResult(theResult.getText(), null);
|
||||
}
|
||||
|
||||
protected static void maybeAppend(String value, StringBuilder result) {
|
||||
if (value != null) {
|
||||
result.append('\n');
|
||||
result.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void maybeAppend(String[] value, StringBuilder result) {
|
||||
if (value != null) {
|
||||
for (String s : value) {
|
||||
result.append('\n');
|
||||
result.append(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static String[] maybeWrap(String value) {
|
||||
return value == null ? null : new String[] { value };
|
||||
}
|
||||
|
||||
protected static String unescapeBackslash(String escaped) {
|
||||
int backslash = escaped.indexOf((int) '\\');
|
||||
if (backslash < 0) {
|
||||
return escaped;
|
||||
}
|
||||
int max = escaped.length();
|
||||
StringBuilder unescaped = new StringBuilder(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();
|
||||
}
|
||||
|
||||
protected static int parseHexDigit(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return 10 + (c - 'a');
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return 10 + (c - 'A');
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected static boolean isStringOfDigits(CharSequence value, int length) {
|
||||
return value != null && length > 0 && length == value.length() && DIGITS.matcher(value).matches();
|
||||
}
|
||||
|
||||
protected static boolean isSubstringOfDigits(CharSequence value, int offset, int length) {
|
||||
if (value == null || length <= 0) {
|
||||
return false;
|
||||
}
|
||||
int max = offset + length;
|
||||
return value.length() >= max && DIGITS.matcher(value.subSequence(offset, max)).matches();
|
||||
}
|
||||
|
||||
static Map<String,String> parseNameValuePairs(String uri) {
|
||||
int paramStart = uri.indexOf('?');
|
||||
if (paramStart < 0) {
|
||||
return null;
|
||||
}
|
||||
Map<String,String> result = new HashMap<>(3);
|
||||
for (String keyValue : AMPERSAND.split(uri.substring(paramStart + 1))) {
|
||||
appendKeyValue(keyValue, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void appendKeyValue(CharSequence keyValue, Map<String,String> result) {
|
||||
String[] keyValueTokens = EQUALS.split(keyValue, 2);
|
||||
if (keyValueTokens.length == 2) {
|
||||
String key = keyValueTokens[0];
|
||||
String value = keyValueTokens[1];
|
||||
try {
|
||||
value = urlDecode(value);
|
||||
result.put(key, value);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// continue; invalid data such as an escape like %0t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String urlDecode(String encoded) {
|
||||
try {
|
||||
return URLDecoder.decode(encoded, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new IllegalStateException(uee); // can't happen
|
||||
}
|
||||
}
|
||||
|
||||
static String[] matchPrefixedField(String prefix, String rawText, char endChar, boolean trim) {
|
||||
List<String> 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 more = true;
|
||||
while (more) {
|
||||
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();
|
||||
more = false;
|
||||
} else if (rawText.charAt(i - 1) == '\\') {
|
||||
// semicolon was escaped so continue
|
||||
i++;
|
||||
} else {
|
||||
// found a match
|
||||
if (matches == null) {
|
||||
matches = new ArrayList<>(3); // lazy init
|
||||
}
|
||||
String element = unescapeBackslash(rawText.substring(start, i));
|
||||
if (trim) {
|
||||
element = element.trim();
|
||||
}
|
||||
if (!element.isEmpty()) {
|
||||
matches.add(element);
|
||||
}
|
||||
i++;
|
||||
more = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matches == null || matches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return matches.toArray(new String[matches.size()]);
|
||||
}
|
||||
|
||||
static String matchSinglePrefixedField(String prefix, String rawText, char endChar, boolean trim) {
|
||||
String[] matches = matchPrefixedField(prefix, rawText, endChar, trim);
|
||||
return matches == null ? null : matches[0];
|
||||
}
|
||||
|
||||
}
|
109
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMSMMSResultParser.java
vendored
Normal file
109
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMSMMSResultParser.java
vendored
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.client.result;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <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
|
||||
*/
|
||||
public final class SMSMMSResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public SMSParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
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
|
||||
Map<String,String> nameValuePairs = parseNameValuePairs(rawText);
|
||||
String subject = null;
|
||||
String body = null;
|
||||
boolean querySyntax = false;
|
||||
if (nameValuePairs != null && !nameValuePairs.isEmpty()) {
|
||||
subject = nameValuePairs.get("subject");
|
||||
body = 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;
|
||||
List<String> numbers = new ArrayList<>(1);
|
||||
List<String> vias = new ArrayList<>(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(numbers.toArray(new String[numbers.size()]),
|
||||
vias.toArray(new String[vias.size()]),
|
||||
subject,
|
||||
body);
|
||||
}
|
||||
|
||||
private static void addNumberVia(Collection<String> numbers,
|
||||
Collection<String> vias,
|
||||
String numberPart) {
|
||||
int numberEnd = numberPart.indexOf(';');
|
||||
if (numberEnd < 0) {
|
||||
numbers.add(numberPart);
|
||||
vias.add(null);
|
||||
} else {
|
||||
numbers.add(numberPart.substring(0, numberEnd));
|
||||
String maybeVia = numberPart.substring(numberEnd + 1);
|
||||
String via;
|
||||
if (maybeVia.startsWith("via=")) {
|
||||
via = maybeVia.substring(4);
|
||||
} else {
|
||||
via = null;
|
||||
}
|
||||
vias.add(via);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
111
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMSParsedResult.java
vendored
Normal file
111
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMSParsedResult.java
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
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 != null && 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(100);
|
||||
maybeAppend(numbers, result);
|
||||
maybeAppend(subject, result);
|
||||
maybeAppend(body, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
52
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMSTOMMSTOResultParser.java
vendored
Normal file
52
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMSTOMMSTOResultParser.java
vendored
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.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)}.</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
|
||||
*/
|
||||
public final class SMSTOMMSTOResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public SMSParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
51
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMTPResultParser.java
vendored
Normal file
51
extern/zxing-core/src/main/java/com/google/zxing/client/result/SMTPResultParser.java
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <p>Parses an "smtp:" URI result, whose format is not standardized but appears to be like:
|
||||
* {@code smtp[:subject[:body]]}.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class SMTPResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public EmailAddressParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!(rawText.startsWith("smtp:") || rawText.startsWith("SMTP:"))) {
|
||||
return null;
|
||||
}
|
||||
String emailAddress = rawText.substring(5);
|
||||
String subject = null;
|
||||
String body = null;
|
||||
int colon = emailAddress.indexOf(':');
|
||||
if (colon >= 0) {
|
||||
subject = emailAddress.substring(colon + 1);
|
||||
emailAddress = emailAddress.substring(0, colon);
|
||||
colon = subject.indexOf(':');
|
||||
if (colon >= 0) {
|
||||
body = subject.substring(colon + 1);
|
||||
subject = subject.substring(0, colon);
|
||||
}
|
||||
}
|
||||
String mailtoURI = "mailto:" + emailAddress;
|
||||
return new EmailAddressParsedResult(emailAddress, subject, body, mailtoURI);
|
||||
}
|
||||
}
|
55
extern/zxing-core/src/main/java/com/google/zxing/client/result/TelParsedResult.java
vendored
Normal file
55
extern/zxing-core/src/main/java/com/google/zxing/client/result/TelParsedResult.java
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(20);
|
||||
maybeAppend(number, result);
|
||||
maybeAppend(title, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
42
extern/zxing-core/src/main/java/com/google/zxing/client/result/TelResultParser.java
vendored
Normal file
42
extern/zxing-core/src/main/java/com/google/zxing/client/result/TelResultParser.java
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
public final class TelResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public TelParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!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);
|
||||
}
|
||||
|
||||
}
|
49
extern/zxing-core/src/main/java/com/google/zxing/client/result/TextParsedResult.java
vendored
Normal file
49
extern/zxing-core/src/main/java/com/google/zxing/client/result/TextParsedResult.java
vendored
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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
92
extern/zxing-core/src/main/java/com/google/zxing/client/result/URIParsedResult.java
vendored
Normal file
92
extern/zxing-core/src/main/java/com/google/zxing/client/result/URIParsedResult.java
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class URIParsedResult extends ParsedResult {
|
||||
|
||||
private static final Pattern USER_IN_HOST = Pattern.compile(":/*([^/@]+)@[^/]+");
|
||||
|
||||
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 USER_IN_HOST.matcher(uri).find();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(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) {
|
||||
uri = uri.trim();
|
||||
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;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
private static boolean isColonFollowedByPortNumber(String uri, int protocolEnd) {
|
||||
int start = protocolEnd + 1;
|
||||
int nextSlash = uri.indexOf('/', start);
|
||||
if (nextSlash < 0) {
|
||||
nextSlash = uri.length();
|
||||
}
|
||||
return ResultParser.isSubstringOfDigits(uri, start, nextSlash - start);
|
||||
}
|
||||
|
||||
|
||||
}
|
62
extern/zxing-core/src/main/java/com/google/zxing/client/result/URIResultParser.java
vendored
Normal file
62
extern/zxing-core/src/main/java/com/google/zxing/client/result/URIResultParser.java
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Tries to parse results that are a URI of some kind.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class URIResultParser extends ResultParser {
|
||||
|
||||
private static final Pattern URL_WITH_PROTOCOL_PATTERN = Pattern.compile("[a-zA-Z0-9]{2,}:");
|
||||
private static final Pattern URL_WITHOUT_PROTOCOL_PATTERN = Pattern.compile(
|
||||
"([a-zA-Z0-9\\-]+\\.)+[a-zA-Z]{2,}" + // host name elements
|
||||
"(:\\d{1,5})?" + // maybe port
|
||||
"(/|\\?|$)"); // query, path or nothing
|
||||
|
||||
@Override
|
||||
public URIParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
// We specifically handle the odd "URL" scheme here for simplicity and add "URI" for fun
|
||||
// Assume anything starting this way really means to be a URI
|
||||
if (rawText.startsWith("URL:") || rawText.startsWith("URI:")) {
|
||||
return new URIParsedResult(rawText.substring(4).trim(), null);
|
||||
}
|
||||
rawText = rawText.trim();
|
||||
return isBasicallyValidURI(rawText) ? new URIParsedResult(rawText, null) : null;
|
||||
}
|
||||
|
||||
static boolean isBasicallyValidURI(String uri) {
|
||||
if (uri.contains(" ")) {
|
||||
// Quick hack check for a common case
|
||||
return false;
|
||||
}
|
||||
Matcher m = URL_WITH_PROTOCOL_PATTERN.matcher(uri);
|
||||
if (m.find() && m.start() == 0) { // match at start only
|
||||
return true;
|
||||
}
|
||||
m = URL_WITHOUT_PROTOCOL_PATTERN.matcher(uri);
|
||||
return m.find() && m.start() == 0;
|
||||
}
|
||||
|
||||
}
|
45
extern/zxing-core/src/main/java/com/google/zxing/client/result/URLTOResultParser.java
vendored
Normal file
45
extern/zxing-core/src/main/java/com/google/zxing/client/result/URLTOResultParser.java
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
public final class URLTOResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public URIParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!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);
|
||||
}
|
||||
|
||||
}
|
357
extern/zxing-core/src/main/java/com/google/zxing/client/result/VCardResultParser.java
vendored
Normal file
357
extern/zxing-core/src/main/java/com/google/zxing/client/result/VCardResultParser.java
vendored
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* 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.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public final class VCardResultParser extends ResultParser {
|
||||
|
||||
private static final Pattern BEGIN_VCARD = Pattern.compile("BEGIN:VCARD", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern VCARD_LIKE_DATE = Pattern.compile("\\d{4}-?\\d{2}-?\\d{2}");
|
||||
private static final Pattern CR_LF_SPACE_TAB = Pattern.compile("\r\n[ \t]");
|
||||
private static final Pattern NEWLINE_ESCAPE = Pattern.compile("\\\\[nN]");
|
||||
private static final Pattern VCARD_ESCAPES = Pattern.compile("\\\\([,;\\\\])");
|
||||
private static final Pattern EQUALS = Pattern.compile("=");
|
||||
private static final Pattern SEMICOLON = Pattern.compile(";");
|
||||
private static final Pattern UNESCAPED_SEMICOLONS = Pattern.compile("(?<!\\\\);+");
|
||||
private static final Pattern COMMA = Pattern.compile(",");
|
||||
private static final Pattern SEMICOLON_OR_COMMA = Pattern.compile("[;,]");
|
||||
|
||||
@Override
|
||||
public 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 = getMassagedText(result);
|
||||
Matcher m = BEGIN_VCARD.matcher(rawText);
|
||||
if (!m.find() || m.start() != 0) {
|
||||
return null;
|
||||
}
|
||||
List<List<String>> names = matchVCardPrefixedField("FN", rawText, true, false);
|
||||
if (names == null) {
|
||||
// If no display names found, look for regular name fields and format them
|
||||
names = matchVCardPrefixedField("N", rawText, true, false);
|
||||
formatNames(names);
|
||||
}
|
||||
List<String> nicknameString = matchSingleVCardPrefixedField("NICKNAME", rawText, true, false);
|
||||
String[] nicknames = nicknameString == null ? null : COMMA.split(nicknameString.get(0));
|
||||
List<List<String>> phoneNumbers = matchVCardPrefixedField("TEL", rawText, true, false);
|
||||
List<List<String>> emails = matchVCardPrefixedField("EMAIL", rawText, true, false);
|
||||
List<String> note = matchSingleVCardPrefixedField("NOTE", rawText, false, false);
|
||||
List<List<String>> addresses = matchVCardPrefixedField("ADR", rawText, true, true);
|
||||
List<String> org = matchSingleVCardPrefixedField("ORG", rawText, true, true);
|
||||
List<String> birthday = matchSingleVCardPrefixedField("BDAY", rawText, true, false);
|
||||
if (birthday != null && !isLikeVCardDate(birthday.get(0))) {
|
||||
birthday = null;
|
||||
}
|
||||
List<String> title = matchSingleVCardPrefixedField("TITLE", rawText, true, false);
|
||||
List<List<String>> urls = matchVCardPrefixedField("URL", rawText, true, false);
|
||||
List<String> instantMessenger = matchSingleVCardPrefixedField("IMPP", rawText, true, false);
|
||||
List<String> geoString = matchSingleVCardPrefixedField("GEO", rawText, true, false);
|
||||
String[] geo = geoString == null ? null : SEMICOLON_OR_COMMA.split(geoString.get(0));
|
||||
if (geo != null && geo.length != 2) {
|
||||
geo = null;
|
||||
}
|
||||
return new AddressBookParsedResult(toPrimaryValues(names),
|
||||
nicknames,
|
||||
null,
|
||||
toPrimaryValues(phoneNumbers),
|
||||
toTypes(phoneNumbers),
|
||||
toPrimaryValues(emails),
|
||||
toTypes(emails),
|
||||
toPrimaryValue(instantMessenger),
|
||||
toPrimaryValue(note),
|
||||
toPrimaryValues(addresses),
|
||||
toTypes(addresses),
|
||||
toPrimaryValue(org),
|
||||
toPrimaryValue(birthday),
|
||||
toPrimaryValue(title),
|
||||
toPrimaryValues(urls),
|
||||
geo);
|
||||
}
|
||||
|
||||
static List<List<String>> matchVCardPrefixedField(CharSequence prefix,
|
||||
String rawText,
|
||||
boolean trim,
|
||||
boolean parseFieldDivider) {
|
||||
List<List<String>> matches = null;
|
||||
int i = 0;
|
||||
int max = rawText.length();
|
||||
|
||||
while (i < max) {
|
||||
|
||||
// At start or after newline, match prefix, followed by optional metadata
|
||||
// (led by ;) ultimately ending in colon
|
||||
Matcher matcher = Pattern.compile("(?:^|\n)" + prefix + "(?:;([^:]*))?:",
|
||||
Pattern.CASE_INSENSITIVE).matcher(rawText);
|
||||
if (i > 0) {
|
||||
i--; // Find from i-1 not i since looking at the preceding character
|
||||
}
|
||||
if (!matcher.find(i)) {
|
||||
break;
|
||||
}
|
||||
i = matcher.end(0); // group 0 = whole pattern; end(0) is past final colon
|
||||
|
||||
String metadataString = matcher.group(1); // group 1 = metadata substring
|
||||
List<String> metadata = null;
|
||||
boolean quotedPrintable = false;
|
||||
String quotedPrintableCharset = null;
|
||||
if (metadataString != null) {
|
||||
for (String metadatum : SEMICOLON.split(metadataString)) {
|
||||
if (metadata == null) {
|
||||
metadata = new ArrayList<>(1);
|
||||
}
|
||||
metadata.add(metadatum);
|
||||
String[] metadatumTokens = EQUALS.split(metadatum, 2);
|
||||
if (metadatumTokens.length > 1) {
|
||||
String key = metadatumTokens[0];
|
||||
String value = metadatumTokens[1];
|
||||
if ("ENCODING".equalsIgnoreCase(key) && "QUOTED-PRINTABLE".equalsIgnoreCase(value)) {
|
||||
quotedPrintable = true;
|
||||
} else if ("CHARSET".equalsIgnoreCase(key)) {
|
||||
quotedPrintableCharset = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
((i >= 1 && rawText.charAt(i-1) == '=') || // this is a continuation
|
||||
(i >= 2 && 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 ArrayList<>(1); // lazy init
|
||||
}
|
||||
if (i >= 1 && 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);
|
||||
if (parseFieldDivider) {
|
||||
element = UNESCAPED_SEMICOLONS.matcher(element).replaceAll("\n").trim();
|
||||
}
|
||||
} else {
|
||||
if (parseFieldDivider) {
|
||||
element = UNESCAPED_SEMICOLONS.matcher(element).replaceAll("\n").trim();
|
||||
}
|
||||
element = CR_LF_SPACE_TAB.matcher(element).replaceAll("");
|
||||
element = NEWLINE_ESCAPE.matcher(element).replaceAll("\n");
|
||||
element = VCARD_ESCAPES.matcher(element).replaceAll("$1");
|
||||
}
|
||||
if (metadata == null) {
|
||||
List<String> match = new ArrayList<>(1);
|
||||
match.add(element);
|
||||
matches.add(match);
|
||||
} else {
|
||||
metadata.add(0, element);
|
||||
matches.add(metadata);
|
||||
}
|
||||
i++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private static String decodeQuotedPrintable(CharSequence value, String charset) {
|
||||
int length = value.length();
|
||||
StringBuilder result = new StringBuilder(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') {
|
||||
char nextNextChar = value.charAt(i+2);
|
||||
int firstDigit = parseHexDigit(nextChar);
|
||||
int secondDigit = parseHexDigit(nextNextChar);
|
||||
if (firstDigit >= 0 && secondDigit >= 0) {
|
||||
fragmentBuffer.write((firstDigit << 4) + secondDigit);
|
||||
} // else ignore it, 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 void maybeAppendFragment(ByteArrayOutputStream fragmentBuffer,
|
||||
String charset,
|
||||
StringBuilder result) {
|
||||
if (fragmentBuffer.size() > 0) {
|
||||
byte[] fragmentBytes = fragmentBuffer.toByteArray();
|
||||
String fragment;
|
||||
if (charset == null) {
|
||||
fragment = new String(fragmentBytes, Charset.forName("UTF-8"));
|
||||
} else {
|
||||
try {
|
||||
fragment = new String(fragmentBytes, charset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
fragment = new String(fragmentBytes, Charset.forName("UTF-8"));
|
||||
}
|
||||
}
|
||||
fragmentBuffer.reset();
|
||||
result.append(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> matchSingleVCardPrefixedField(CharSequence prefix,
|
||||
String rawText,
|
||||
boolean trim,
|
||||
boolean parseFieldDivider) {
|
||||
List<List<String>> values = matchVCardPrefixedField(prefix, rawText, trim, parseFieldDivider);
|
||||
return values == null || values.isEmpty() ? null : values.get(0);
|
||||
}
|
||||
|
||||
private static String toPrimaryValue(List<String> list) {
|
||||
return list == null || list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
private static String[] toPrimaryValues(Collection<List<String>> lists) {
|
||||
if (lists == null || lists.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<String> result = new ArrayList<>(lists.size());
|
||||
for (List<String> list : lists) {
|
||||
String value = list.get(0);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
return result.toArray(new String[lists.size()]);
|
||||
}
|
||||
|
||||
private static String[] toTypes(Collection<List<String>> lists) {
|
||||
if (lists == null || lists.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<String> result = new ArrayList<>(lists.size());
|
||||
for (List<String> list : lists) {
|
||||
String type = null;
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
String metadatum = list.get(i);
|
||||
int equals = metadatum.indexOf('=');
|
||||
if (equals < 0) {
|
||||
// take the whole thing as a usable label
|
||||
type = metadatum;
|
||||
break;
|
||||
}
|
||||
if ("TYPE".equalsIgnoreCase(metadatum.substring(0, equals))) {
|
||||
type = metadatum.substring(equals + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.add(type);
|
||||
}
|
||||
return result.toArray(new String[lists.size()]);
|
||||
}
|
||||
|
||||
private static boolean isLikeVCardDate(CharSequence value) {
|
||||
return value == null || VCARD_LIKE_DATE.matcher(value).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Iterable<List<String>> names) {
|
||||
if (names != null) {
|
||||
for (List<String> list : names) {
|
||||
String name = list.get(0);
|
||||
String[] components = new String[5];
|
||||
int start = 0;
|
||||
int end;
|
||||
int componentIndex = 0;
|
||||
while (componentIndex < components.length - 1 && (end = name.indexOf(';', start)) >= 0) {
|
||||
components[componentIndex] = name.substring(start, end);
|
||||
componentIndex++;
|
||||
start = end + 1;
|
||||
}
|
||||
components[componentIndex] = name.substring(start);
|
||||
StringBuilder newName = new StringBuilder(100);
|
||||
maybeAppendComponent(components, 3, newName);
|
||||
maybeAppendComponent(components, 1, newName);
|
||||
maybeAppendComponent(components, 2, newName);
|
||||
maybeAppendComponent(components, 0, newName);
|
||||
maybeAppendComponent(components, 4, newName);
|
||||
list.set(0, newName.toString().trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void maybeAppendComponent(String[] components, int i, StringBuilder newName) {
|
||||
if (components[i] != null && !components[i].isEmpty()) {
|
||||
if (newName.length() > 0) {
|
||||
newName.append(' ');
|
||||
}
|
||||
newName.append(components[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
119
extern/zxing-core/src/main/java/com/google/zxing/client/result/VEventResultParser.java
vendored
Normal file
119
extern/zxing-core/src/main/java/com/google/zxing/client/result/VEventResultParser.java
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.List;
|
||||
|
||||
/**
|
||||
* Partially implements the iCalendar format's "VEVENT" format for specifying a
|
||||
* calendar event. See RFC 2445. This supports SUMMARY, LOCATION, GEO, DTSTART and DTEND fields.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class VEventResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public CalendarParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
int vEventStart = rawText.indexOf("BEGIN:VEVENT");
|
||||
if (vEventStart < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String summary = matchSingleVCardPrefixedField("SUMMARY", rawText, true);
|
||||
String start = matchSingleVCardPrefixedField("DTSTART", rawText, true);
|
||||
if (start == null) {
|
||||
return null;
|
||||
}
|
||||
String end = matchSingleVCardPrefixedField("DTEND", rawText, true);
|
||||
String duration = matchSingleVCardPrefixedField("DURATION", rawText, true);
|
||||
String location = matchSingleVCardPrefixedField("LOCATION", rawText, true);
|
||||
String organizer = stripMailto(matchSingleVCardPrefixedField("ORGANIZER", rawText, true));
|
||||
|
||||
String[] attendees = matchVCardPrefixedField("ATTENDEE", rawText, true);
|
||||
if (attendees != null) {
|
||||
for (int i = 0; i < attendees.length; i++) {
|
||||
attendees[i] = stripMailto(attendees[i]);
|
||||
}
|
||||
}
|
||||
String description = matchSingleVCardPrefixedField("DESCRIPTION", rawText, true);
|
||||
|
||||
String geoString = matchSingleVCardPrefixedField("GEO", rawText, true);
|
||||
double latitude;
|
||||
double longitude;
|
||||
if (geoString == null) {
|
||||
latitude = Double.NaN;
|
||||
longitude = Double.NaN;
|
||||
} else {
|
||||
int semicolon = geoString.indexOf(';');
|
||||
if (semicolon < 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
latitude = Double.parseDouble(geoString.substring(0, semicolon));
|
||||
longitude = Double.parseDouble(geoString.substring(semicolon + 1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return new CalendarParsedResult(summary,
|
||||
start,
|
||||
end,
|
||||
duration,
|
||||
location,
|
||||
organizer,
|
||||
attendees,
|
||||
description,
|
||||
latitude,
|
||||
longitude);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String matchSingleVCardPrefixedField(CharSequence prefix,
|
||||
String rawText,
|
||||
boolean trim) {
|
||||
List<String> values = VCardResultParser.matchSingleVCardPrefixedField(prefix, rawText, trim, false);
|
||||
return values == null || values.isEmpty() ? null : values.get(0);
|
||||
}
|
||||
|
||||
private static String[] matchVCardPrefixedField(CharSequence prefix, String rawText, boolean trim) {
|
||||
List<List<String>> values = VCardResultParser.matchVCardPrefixedField(prefix, rawText, trim, false);
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int size = values.size();
|
||||
String[] result = new String[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
result[i] = values.get(i).get(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String stripMailto(String s) {
|
||||
if (s != null && (s.startsWith("mailto:") || s.startsWith("MAILTO:"))) {
|
||||
s = s.substring(7);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
104
extern/zxing-core/src/main/java/com/google/zxing/client/result/VINParsedResult.java
vendored
Normal file
104
extern/zxing-core/src/main/java/com/google/zxing/client/result/VINParsedResult.java
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2014 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;
|
||||
|
||||
|
||||
public final class VINParsedResult extends ParsedResult {
|
||||
|
||||
private final String vin;
|
||||
private final String worldManufacturerID;
|
||||
private final String vehicleDescriptorSection;
|
||||
private final String vehicleIdentifierSection;
|
||||
private final String countryCode;
|
||||
private final String vehicleAttributes;
|
||||
private final int modelYear;
|
||||
private final char plantCode;
|
||||
private final String sequentialNumber;
|
||||
|
||||
public VINParsedResult(String vin,
|
||||
String worldManufacturerID,
|
||||
String vehicleDescriptorSection,
|
||||
String vehicleIdentifierSection,
|
||||
String countryCode,
|
||||
String vehicleAttributes,
|
||||
int modelYear,
|
||||
char plantCode,
|
||||
String sequentialNumber) {
|
||||
super(ParsedResultType.VIN);
|
||||
this.vin = vin;
|
||||
this.worldManufacturerID = worldManufacturerID;
|
||||
this.vehicleDescriptorSection = vehicleDescriptorSection;
|
||||
this.vehicleIdentifierSection = vehicleIdentifierSection;
|
||||
this.countryCode = countryCode;
|
||||
this.vehicleAttributes = vehicleAttributes;
|
||||
this.modelYear = modelYear;
|
||||
this.plantCode = plantCode;
|
||||
this.sequentialNumber = sequentialNumber;
|
||||
}
|
||||
|
||||
public String getVIN() {
|
||||
return vin;
|
||||
}
|
||||
|
||||
public String getWorldManufacturerID() {
|
||||
return worldManufacturerID;
|
||||
}
|
||||
|
||||
public String getVehicleDescriptorSection() {
|
||||
return vehicleDescriptorSection;
|
||||
}
|
||||
|
||||
public String getVehicleIdentifierSection() {
|
||||
return vehicleIdentifierSection;
|
||||
}
|
||||
|
||||
public String getCountryCode() {
|
||||
return countryCode;
|
||||
}
|
||||
|
||||
public String getVehicleAttributes() {
|
||||
return vehicleAttributes;
|
||||
}
|
||||
|
||||
public int getModelYear() {
|
||||
return modelYear;
|
||||
}
|
||||
|
||||
public char getPlantCode() {
|
||||
return plantCode;
|
||||
}
|
||||
|
||||
public String getSequentialNumber() {
|
||||
return sequentialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(50);
|
||||
result.append(worldManufacturerID).append(' ');
|
||||
result.append(vehicleDescriptorSection).append(' ');
|
||||
result.append(vehicleIdentifierSection).append('\n');
|
||||
if (countryCode != null) {
|
||||
result.append(countryCode).append(' ');
|
||||
}
|
||||
result.append(modelYear).append(' ');
|
||||
result.append(plantCode).append(' ');
|
||||
result.append(sequentialNumber).append('\n');
|
||||
return result.toString();
|
||||
}
|
||||
}
|
209
extern/zxing-core/src/main/java/com/google/zxing/client/result/VINResultParser.java
vendored
Normal file
209
extern/zxing-core/src/main/java/com/google/zxing/client/result/VINResultParser.java
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Detects a result that is likely a vehicle identification number.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class VINResultParser extends ResultParser {
|
||||
|
||||
private static final Pattern IOQ = Pattern.compile("[IOQ]");
|
||||
private static final Pattern AZ09 = Pattern.compile("[A-Z0-9]{17}");
|
||||
|
||||
@Override
|
||||
public VINParsedResult parse(Result result) {
|
||||
if (result.getBarcodeFormat() != BarcodeFormat.CODE_39) {
|
||||
return null;
|
||||
}
|
||||
String rawText = result.getText();
|
||||
rawText = IOQ.matcher(rawText).replaceAll("").trim();
|
||||
if (!AZ09.matcher(rawText).matches()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (!checkChecksum(rawText)) {
|
||||
return null;
|
||||
}
|
||||
String wmi = rawText.substring(0, 3);
|
||||
return new VINParsedResult(rawText,
|
||||
wmi,
|
||||
rawText.substring(3, 9),
|
||||
rawText.substring(9, 17),
|
||||
countryCode(wmi),
|
||||
rawText.substring(3, 8),
|
||||
modelYear(rawText.charAt(9)),
|
||||
rawText.charAt(10),
|
||||
rawText.substring(11));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkChecksum(CharSequence vin) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < vin.length(); i++) {
|
||||
sum += vinPositionWeight(i + 1) * vinCharValue(vin.charAt(i));
|
||||
}
|
||||
char checkChar = vin.charAt(8);
|
||||
char expectedCheckChar = checkChar(sum % 11);
|
||||
return checkChar == expectedCheckChar;
|
||||
}
|
||||
|
||||
private static int vinCharValue(char c) {
|
||||
if (c >= 'A' && c <= 'I') {
|
||||
return (c - 'A') + 1;
|
||||
}
|
||||
if (c >= 'J' && c <= 'R') {
|
||||
return (c - 'J') + 1;
|
||||
}
|
||||
if (c >= 'S' && c <= 'Z') {
|
||||
return (c - 'S') + 2;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private static int vinPositionWeight(int position) {
|
||||
if (position >= 1 && position <= 7) {
|
||||
return 9 - position;
|
||||
}
|
||||
if (position == 8) {
|
||||
return 10;
|
||||
}
|
||||
if (position == 9) {
|
||||
return 0;
|
||||
}
|
||||
if (position >= 10 && position <= 17) {
|
||||
return 19 - position;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private static char checkChar(int remainder) {
|
||||
if (remainder < 10) {
|
||||
return (char) ('0' + remainder);
|
||||
}
|
||||
if (remainder == 10) {
|
||||
return 'X';
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private static int modelYear(char c) {
|
||||
if (c >= 'E' && c <= 'H') {
|
||||
return (c - 'E') + 1984;
|
||||
}
|
||||
if (c >= 'J' && c <= 'N') {
|
||||
return (c - 'J') + 1988;
|
||||
}
|
||||
if (c == 'P') {
|
||||
return 1993;
|
||||
}
|
||||
if (c >= 'R' && c <= 'T') {
|
||||
return (c - 'R') + 1994;
|
||||
}
|
||||
if (c >= 'V' && c <= 'Y') {
|
||||
return (c - 'V') + 1997;
|
||||
}
|
||||
if (c >= '1' && c <= '9') {
|
||||
return (c - '1') + 2001;
|
||||
}
|
||||
if (c >= 'A' && c <= 'D') {
|
||||
return (c - 'A') + 2010;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private static String countryCode(CharSequence wmi) {
|
||||
char c1 = wmi.charAt(0);
|
||||
char c2 = wmi.charAt(1);
|
||||
switch (c1) {
|
||||
case '1':
|
||||
case '4':
|
||||
case '5':
|
||||
return "US";
|
||||
case '2':
|
||||
return "CA";
|
||||
case '3':
|
||||
if (c2 >= 'A' && c2 <= 'W') {
|
||||
return "MX";
|
||||
}
|
||||
break;
|
||||
case '9':
|
||||
if ((c2 >= 'A' && c2 <= 'E') || (c2 >= '3' && c2 <= '9')) {
|
||||
return "BR";
|
||||
}
|
||||
break;
|
||||
case 'J':
|
||||
if (c2 >= 'A' && c2 <= 'T') {
|
||||
return "JP";
|
||||
}
|
||||
break;
|
||||
case 'K':
|
||||
if (c2 >= 'L' && c2 <= 'R') {
|
||||
return "KO";
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
return "CN";
|
||||
case 'M':
|
||||
if (c2 >= 'A' && c2 <= 'E') {
|
||||
return "IN";
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (c2 >= 'A' && c2 <= 'M') {
|
||||
return "UK";
|
||||
}
|
||||
if (c2 >= 'N' && c2 <= 'T') {
|
||||
return "DE";
|
||||
}
|
||||
break;
|
||||
case 'V':
|
||||
if (c2 >= 'F' && c2 <= 'R') {
|
||||
return "FR";
|
||||
}
|
||||
if (c2 >= 'S' && c2 <= 'W') {
|
||||
return "ES";
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
return "DE";
|
||||
case 'X':
|
||||
if (c2 == '0' || (c2 >= '3' && c2 <= '9')) {
|
||||
return "RU";
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
if (c2 >= 'A' && c2 <= 'R') {
|
||||
return "IT";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
67
extern/zxing-core/src/main/java/com/google/zxing/client/result/WifiParsedResult.java
vendored
Normal file
67
extern/zxing-core/src/main/java/com/google/zxing/client/result/WifiParsedResult.java
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
private final boolean hidden;
|
||||
|
||||
public WifiParsedResult(String networkEncryption, String ssid, String password) {
|
||||
this(networkEncryption, ssid, password, false);
|
||||
}
|
||||
|
||||
public WifiParsedResult(String networkEncryption, String ssid, String password, boolean hidden) {
|
||||
super(ParsedResultType.WIFI);
|
||||
this.ssid = ssid;
|
||||
this.networkEncryption = networkEncryption;
|
||||
this.password = password;
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
public String getSsid() {
|
||||
return ssid;
|
||||
}
|
||||
|
||||
public String getNetworkEncryption() {
|
||||
return networkEncryption;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayResult() {
|
||||
StringBuilder result = new StringBuilder(80);
|
||||
maybeAppend(ssid, result);
|
||||
maybeAppend(networkEncryption, result);
|
||||
maybeAppend(password, result);
|
||||
maybeAppend(Boolean.toString(hidden), result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
51
extern/zxing-core/src/main/java/com/google/zxing/client/result/WifiResultParser.java
vendored
Normal file
51
extern/zxing-core/src/main/java/com/google/zxing/client/result/WifiResultParser.java
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <p>Parses a WIFI configuration string. Strings will be of the form:</p>
|
||||
*
|
||||
* <p>{@code WIFI:T:[network type];S:[network SSID];P:[network password];H:[hidden?];;}</p>
|
||||
*
|
||||
* <p>The fields can appear in any order. Only "S:" is required.</p>
|
||||
*
|
||||
* @author Vikram Aggarwal
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class WifiResultParser extends ResultParser {
|
||||
|
||||
@Override
|
||||
public WifiParsedResult parse(Result result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!rawText.startsWith("WIFI:")) {
|
||||
return null;
|
||||
}
|
||||
String ssid = matchSinglePrefixedField("S:", rawText, ';', false);
|
||||
if (ssid == null || ssid.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String pass = matchSinglePrefixedField("P:", rawText, ';', false);
|
||||
String type = matchSinglePrefixedField("T:", rawText, ';', false);
|
||||
if (type == null) {
|
||||
type = "nopass";
|
||||
}
|
||||
boolean hidden = Boolean.parseBoolean(matchSinglePrefixedField("H:", rawText, ';', false));
|
||||
return new WifiParsedResult(type, ssid, pass, hidden);
|
||||
}
|
||||
}
|
375
extern/zxing-core/src/main/java/com/google/zxing/common/BitArray.java
vendored
Normal file
375
extern/zxing-core/src/main/java/com/google/zxing/common/BitArray.java
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* 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.Arrays;
|
||||
|
||||
/**
|
||||
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class BitArray implements Cloneable {
|
||||
|
||||
private int[] bits;
|
||||
private int size;
|
||||
|
||||
public BitArray() {
|
||||
this.size = 0;
|
||||
this.bits = new int[1];
|
||||
}
|
||||
|
||||
public BitArray(int size) {
|
||||
this.size = size;
|
||||
this.bits = makeArray(size);
|
||||
}
|
||||
|
||||
// For testing only
|
||||
BitArray(int[] bits, int size) {
|
||||
this.bits = bits;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getSizeInBytes() {
|
||||
return (size + 7) / 8;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int size) {
|
||||
if (size > bits.length * 32) {
|
||||
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 / 32] & (1 << (i & 0x1F))) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public void set(int i) {
|
||||
bits[i / 32] |= 1 << (i & 0x1F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public void flip(int i) {
|
||||
bits[i / 32] ^= 1 << (i & 0x1F);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from first bit to check
|
||||
* @return index of first bit that is set, starting from the given index, or size if none are set
|
||||
* at or beyond this given index
|
||||
* @see #getNextUnset(int)
|
||||
*/
|
||||
public int getNextSet(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
int bitsOffset = from / 32;
|
||||
int currentBits = bits[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= ~((1 << (from & 0x1F)) - 1);
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == bits.length) {
|
||||
return size;
|
||||
}
|
||||
currentBits = bits[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
|
||||
return result > size ? size : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from index to start looking for unset bit
|
||||
* @return index of next unset bit, or {@code size} if none are unset until the end
|
||||
* @see #getNextSet(int)
|
||||
*/
|
||||
public int getNextUnset(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
int bitsOffset = from / 32;
|
||||
int currentBits = ~bits[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= ~((1 << (from & 0x1F)) - 1);
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == bits.length) {
|
||||
return size;
|
||||
}
|
||||
currentBits = ~bits[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
|
||||
return result > size ? size : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 / 32] = newBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a range of bits.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
*/
|
||||
public void setRange(int start, int end) {
|
||||
if (end < start) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (end == start) {
|
||||
return;
|
||||
}
|
||||
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
int firstInt = start / 32;
|
||||
int lastInt = end / 32;
|
||||
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;
|
||||
}
|
||||
}
|
||||
bits[i] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 / 32;
|
||||
int lastInt = end / 32;
|
||||
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 / 32] |= 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.
|
||||
*
|
||||
* @param value {@code int} containing bits to append
|
||||
* @param numBits bits from value to append
|
||||
*/
|
||||
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.size;
|
||||
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];
|
||||
// reverse all int's first
|
||||
int len = ((size-1) / 32);
|
||||
int oldBitsLen = len + 1;
|
||||
for (int i = 0; i < oldBitsLen; i++) {
|
||||
long x = (long) bits[i];
|
||||
x = ((x >> 1) & 0x55555555L) | ((x & 0x55555555L) << 1);
|
||||
x = ((x >> 2) & 0x33333333L) | ((x & 0x33333333L) << 2);
|
||||
x = ((x >> 4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) << 4);
|
||||
x = ((x >> 8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) << 8);
|
||||
x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16);
|
||||
newBits[len - i] = (int) x;
|
||||
}
|
||||
// now correct the int's if the bit size isn't a multiple of 32
|
||||
if (size != oldBitsLen * 32) {
|
||||
int leftOffset = oldBitsLen * 32 - size;
|
||||
int mask = 1;
|
||||
for (int i = 0; i < 31 - leftOffset; i++) {
|
||||
mask = (mask << 1) | 1;
|
||||
}
|
||||
int currentInt = (newBits[0] >> leftOffset) & mask;
|
||||
for (int i = 1; i < oldBitsLen; i++) {
|
||||
int nextInt = newBits[i];
|
||||
currentInt |= nextInt << (32 - leftOffset);
|
||||
newBits[i - 1] = currentInt;
|
||||
currentInt = (nextInt >> leftOffset) & mask;
|
||||
}
|
||||
newBits[oldBitsLen - 1] = currentInt;
|
||||
}
|
||||
bits = newBits;
|
||||
}
|
||||
|
||||
private static int[] makeArray(int size) {
|
||||
return new int[(size + 31) / 32];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BitArray)) {
|
||||
return false;
|
||||
}
|
||||
BitArray other = (BitArray) o;
|
||||
return size == other.size && Arrays.equals(bits, other.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * size + Arrays.hashCode(bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if ((i & 0x07) == 0) {
|
||||
result.append(' ');
|
||||
}
|
||||
result.append(get(i) ? 'X' : '.');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitArray clone() {
|
||||
return new BitArray(bits.clone(), size);
|
||||
}
|
||||
|
||||
}
|
335
extern/zxing-core/src/main/java/com/google/zxing/common/BitMatrix.java
vendored
Executable file
335
extern/zxing-core/src/main/java/com/google/zxing/common/BitMatrix.java
vendored
Executable file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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.Arrays;
|
||||
|
||||
/**
|
||||
* <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 implements Cloneable {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int rowSize;
|
||||
private 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];
|
||||
}
|
||||
|
||||
private BitMatrix(int width, int height, int rowSize, int[] bits) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.rowSize = rowSize;
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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);
|
||||
} else {
|
||||
row.clear();
|
||||
}
|
||||
int offset = y * rowSize;
|
||||
for (int x = 0; x < rowSize; x++) {
|
||||
row.setBulk(x << 5, bits[offset + x]);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param y row to set
|
||||
* @param row {@link BitArray} to copy from
|
||||
*/
|
||||
public void setRow(int y, BitArray row) {
|
||||
System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
|
||||
*/
|
||||
public void rotate180() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
BitArray topRow = new BitArray(width);
|
||||
BitArray bottomRow = new BitArray(width);
|
||||
for (int i = 0; i < (height+1) / 2; i++) {
|
||||
topRow = getRow(i, topRow);
|
||||
bottomRow = getRow(height - 1 - i, bottomRow);
|
||||
topRow.reverse();
|
||||
bottomRow.reverse();
|
||||
setRow(i, bottomRow);
|
||||
setRow(height - 1 - i, topRow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
|
||||
*
|
||||
* @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
|
||||
*/
|
||||
public int[] getEnclosingRectangle() {
|
||||
int left = width;
|
||||
int top = height;
|
||||
int right = -1;
|
||||
int bottom = -1;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x32 = 0; x32 < rowSize; x32++) {
|
||||
int theBits = bits[y * rowSize + x32];
|
||||
if (theBits != 0) {
|
||||
if (y < top) {
|
||||
top = y;
|
||||
}
|
||||
if (y > bottom) {
|
||||
bottom = y;
|
||||
}
|
||||
if (x32 * 32 < left) {
|
||||
int bit = 0;
|
||||
while ((theBits << (31 - bit)) == 0) {
|
||||
bit++;
|
||||
}
|
||||
if ((x32 * 32 + bit) < left) {
|
||||
left = x32 * 32 + bit;
|
||||
}
|
||||
}
|
||||
if (x32 * 32 + 31 > right) {
|
||||
int bit = 31;
|
||||
while ((theBits >>> bit) == 0) {
|
||||
bit--;
|
||||
}
|
||||
if ((x32 * 32 + bit) > right) {
|
||||
right = x32 * 32 + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width = right - left;
|
||||
int height = bottom - top;
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new int[] {left, top, width, height};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful in detecting a corner of a 'pure' barcode.
|
||||
*
|
||||
* @return {@code 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};
|
||||
}
|
||||
|
||||
public int[] getBottomRightOnBit() {
|
||||
int bitsOffset = bits.length - 1;
|
||||
while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
|
||||
bitsOffset--;
|
||||
}
|
||||
if (bitsOffset < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int y = bitsOffset / rowSize;
|
||||
int x = (bitsOffset % rowSize) << 5;
|
||||
|
||||
int theBits = bits[bitsOffset];
|
||||
int bit = 31;
|
||||
while ((theBits >>> 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BitMatrix)) {
|
||||
return false;
|
||||
}
|
||||
BitMatrix other = (BitMatrix) o;
|
||||
return width == other.width && height == other.height && rowSize == other.rowSize &&
|
||||
Arrays.equals(bits, other.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = width;
|
||||
hash = 31 * hash + width;
|
||||
hash = 31 * hash + height;
|
||||
hash = 31 * hash + rowSize;
|
||||
hash = 31 * hash + Arrays.hashCode(bits);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix clone() {
|
||||
return new BitMatrix(width, height, rowSize, bits.clone());
|
||||
}
|
||||
|
||||
}
|
111
extern/zxing-core/src/main/java/com/google/zxing/common/BitSource.java
vendored
Executable file
111
extern/zxing-core/src/main/java/com/google/zxing/common/BitSource.java
vendored
Executable file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public int getBitOffset() {
|
||||
return bitOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public int getByteOffset() {
|
||||
return byteOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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] or more than is available
|
||||
*/
|
||||
public int readBits(int numBits) {
|
||||
if (numBits < 1 || numBits > 32 || numBits > available()) {
|
||||
throw new IllegalArgumentException(String.valueOf(numBits));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
118
extern/zxing-core/src/main/java/com/google/zxing/common/CharacterSetECI.java
vendored
Normal file
118
extern/zxing-core/src/main/java/com/google/zxing/common/CharacterSetECI.java
vendored
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.common;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
|
||||
* of ISO 18004.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum CharacterSetECI {
|
||||
|
||||
// Enum name is a Java encoding valid for java.lang and java.io
|
||||
Cp437(new int[]{0,2}),
|
||||
ISO8859_1(new int[]{1,3}, "ISO-8859-1"),
|
||||
ISO8859_2(4, "ISO-8859-2"),
|
||||
ISO8859_3(5, "ISO-8859-3"),
|
||||
ISO8859_4(6, "ISO-8859-4"),
|
||||
ISO8859_5(7, "ISO-8859-5"),
|
||||
ISO8859_6(8, "ISO-8859-6"),
|
||||
ISO8859_7(9, "ISO-8859-7"),
|
||||
ISO8859_8(10, "ISO-8859-8"),
|
||||
ISO8859_9(11, "ISO-8859-9"),
|
||||
ISO8859_10(12, "ISO-8859-10"),
|
||||
ISO8859_11(13, "ISO-8859-11"),
|
||||
ISO8859_13(15, "ISO-8859-13"),
|
||||
ISO8859_14(16, "ISO-8859-14"),
|
||||
ISO8859_15(17, "ISO-8859-15"),
|
||||
ISO8859_16(18, "ISO-8859-16"),
|
||||
SJIS(20, "Shift_JIS"),
|
||||
Cp1250(21, "windows-1250"),
|
||||
Cp1251(22, "windows-1251"),
|
||||
Cp1252(23, "windows-1252"),
|
||||
Cp1256(24, "windows-1256"),
|
||||
UnicodeBigUnmarked(25, "UTF-16BE", "UnicodeBig"),
|
||||
UTF8(26, "UTF-8"),
|
||||
ASCII(new int[] {27, 170}, "US-ASCII"),
|
||||
Big5(28),
|
||||
GB18030(29, "GB2312", "EUC_CN", "GBK"),
|
||||
EUC_KR(30, "EUC-KR");
|
||||
|
||||
private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>();
|
||||
private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>();
|
||||
static {
|
||||
for (CharacterSetECI eci : values()) {
|
||||
for (int value : eci.values) {
|
||||
VALUE_TO_ECI.put(value, eci);
|
||||
}
|
||||
NAME_TO_ECI.put(eci.name(), eci);
|
||||
for (String name : eci.otherEncodingNames) {
|
||||
NAME_TO_ECI.put(name, eci);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int[] values;
|
||||
private final String[] otherEncodingNames;
|
||||
|
||||
CharacterSetECI(int value) {
|
||||
this(new int[] {value});
|
||||
}
|
||||
|
||||
CharacterSetECI(int value, String... otherEncodingNames) {
|
||||
this.values = new int[] {value};
|
||||
this.otherEncodingNames = otherEncodingNames;
|
||||
}
|
||||
|
||||
CharacterSetECI(int[] values, String... otherEncodingNames) {
|
||||
this.values = values;
|
||||
this.otherEncodingNames = otherEncodingNames;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value character set ECI value
|
||||
* @return {@link CharacterSetECI} representing ECI of given value, or null if it is legal but
|
||||
* unsupported
|
||||
* @throws FormatException if ECI value is invalid
|
||||
*/
|
||||
public static CharacterSetECI getCharacterSetECIByValue(int value) throws FormatException {
|
||||
if (value < 0 || value >= 900) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
return VALUE_TO_ECI.get(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name character set ECI encoding name
|
||||
* @return CharacterSetECI representing ECI for character encoding, or null if it is legal
|
||||
* but unsupported
|
||||
*/
|
||||
public static CharacterSetECI getCharacterSetECIByName(String name) {
|
||||
return NAME_TO_ECI.get(name);
|
||||
}
|
||||
|
||||
}
|
113
extern/zxing-core/src/main/java/com/google/zxing/common/DecoderResult.java
vendored
Normal file
113
extern/zxing-core/src/main/java/com/google/zxing/common/DecoderResult.java
vendored
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.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <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 List<byte[]> byteSegments;
|
||||
private final String ecLevel;
|
||||
private Integer errorsCorrected;
|
||||
private Integer erasures;
|
||||
private Object other;
|
||||
private final int structuredAppendParity;
|
||||
private final int structuredAppendSequenceNumber;
|
||||
|
||||
public DecoderResult(byte[] rawBytes,
|
||||
String text,
|
||||
List<byte[]> byteSegments,
|
||||
String ecLevel) {
|
||||
this(rawBytes, text, byteSegments, ecLevel, -1, -1);
|
||||
}
|
||||
|
||||
public DecoderResult(byte[] rawBytes,
|
||||
String text,
|
||||
List<byte[]> byteSegments,
|
||||
String ecLevel,
|
||||
int saSequence,
|
||||
int saParity) {
|
||||
this.rawBytes = rawBytes;
|
||||
this.text = text;
|
||||
this.byteSegments = byteSegments;
|
||||
this.ecLevel = ecLevel;
|
||||
this.structuredAppendParity = saParity;
|
||||
this.structuredAppendSequenceNumber = saSequence;
|
||||
}
|
||||
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public List<byte[]> getByteSegments() {
|
||||
return byteSegments;
|
||||
}
|
||||
|
||||
public String getECLevel() {
|
||||
return ecLevel;
|
||||
}
|
||||
|
||||
public Integer getErrorsCorrected() {
|
||||
return errorsCorrected;
|
||||
}
|
||||
|
||||
public void setErrorsCorrected(Integer errorsCorrected) {
|
||||
this.errorsCorrected = errorsCorrected;
|
||||
}
|
||||
|
||||
public Integer getErasures() {
|
||||
return erasures;
|
||||
}
|
||||
|
||||
public void setErasures(Integer erasures) {
|
||||
this.erasures = erasures;
|
||||
}
|
||||
|
||||
public Object getOther() {
|
||||
return other;
|
||||
}
|
||||
|
||||
public void setOther(Object other) {
|
||||
this.other = other;
|
||||
}
|
||||
|
||||
public boolean hasStructuredAppend() {
|
||||
return structuredAppendParity >= 0 && structuredAppendSequenceNumber >= 0;
|
||||
}
|
||||
|
||||
public int getStructuredAppendParity() {
|
||||
return structuredAppendParity;
|
||||
}
|
||||
|
||||
public int getStructuredAppendSequenceNumber() {
|
||||
return structuredAppendSequenceNumber;
|
||||
}
|
||||
|
||||
}
|
88
extern/zxing-core/src/main/java/com/google/zxing/common/DefaultGridSampler.java
vendored
Normal file
88
extern/zxing-core/src/main/java/com/google/zxing/common/DefaultGridSampler.java
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
@Override
|
||||
public BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
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, dimensionX, dimensionY, transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
PerspectiveTransform transform) throws NotFoundException {
|
||||
if (dimensionX <= 0 || dimensionY <= 0) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
BitMatrix bits = new BitMatrix(dimensionX, dimensionY);
|
||||
float[] points = new float[dimensionX << 1];
|
||||
for (int y = 0; y < dimensionY; 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
extern/zxing-core/src/main/java/com/google/zxing/common/DetectorResult.java
vendored
Normal file
46
extern/zxing-core/src/main/java/com/google/zxing/common/DetectorResult.java
vendored
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 class DetectorResult {
|
||||
|
||||
private final BitMatrix bits;
|
||||
private final ResultPoint[] points;
|
||||
|
||||
public DetectorResult(BitMatrix bits, ResultPoint[] points) {
|
||||
this.bits = bits;
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
public final BitMatrix getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
public final ResultPoint[] getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
}
|
196
extern/zxing-core/src/main/java/com/google/zxing/common/GlobalHistogramBinarizer.java
vendored
Normal file
196
extern/zxing-core/src/main/java/com/google/zxing/common/GlobalHistogramBinarizer.java
vendored
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 static final byte[] EMPTY = new byte[0];
|
||||
|
||||
private byte[] luminances;
|
||||
private final int[] buckets;
|
||||
|
||||
public GlobalHistogramBinarizer(LuminanceSource source) {
|
||||
super(source);
|
||||
luminances = EMPTY;
|
||||
buckets = new int[LUMINANCE_BUCKETS];
|
||||
}
|
||||
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
|
||||
@Override
|
||||
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.
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Binarizer createBinarizer(LuminanceSource source) {
|
||||
return new GlobalHistogramBinarizer(source);
|
||||
}
|
||||
|
||||
private void initArrays(int luminanceSize) {
|
||||
if (luminances.length < luminanceSize) {
|
||||
luminances = new byte[luminanceSize];
|
||||
}
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
173
extern/zxing-core/src/main/java/com/google/zxing/common/GridSampler.java
vendored
Normal file
173
extern/zxing-core/src/main/java/com/google/zxing/common/GridSampler.java
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 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) {
|
||||
gridSampler = newGridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current implementation of GridSampler
|
||||
*/
|
||||
public static GridSampler getInstance() {
|
||||
return gridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples an image for a rectangular matrix of bits of the given dimension. The sampling
|
||||
* transformation is determined by the coordinates of 4 points, in the original and transformed
|
||||
* image space.
|
||||
*
|
||||
* @param image image to sample
|
||||
* @param dimensionX width of {@link BitMatrix} to sample from image
|
||||
* @param dimensionY height of {@link BitMatrix} to sample from image
|
||||
* @param p1ToX point 1 preimage X
|
||||
* @param p1ToY point 1 preimage Y
|
||||
* @param p2ToX point 2 preimage X
|
||||
* @param p2ToY point 2 preimage Y
|
||||
* @param p3ToX point 3 preimage X
|
||||
* @param p3ToY point 3 preimage Y
|
||||
* @param p4ToX point 4 preimage X
|
||||
* @param p4ToY point 4 preimage Y
|
||||
* @param p1FromX point 1 image X
|
||||
* @param p1FromY point 1 image Y
|
||||
* @param p2FromX point 2 image X
|
||||
* @param p2FromY point 2 image Y
|
||||
* @param p3FromX point 3 image X
|
||||
* @param p3FromY point 3 image Y
|
||||
* @param p4FromX point 4 image X
|
||||
* @param p4FromY point 4 image Y
|
||||
* @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 dimensionX,
|
||||
int dimensionY,
|
||||
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 abstract BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
PerspectiveTransform transform) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
237
extern/zxing-core/src/main/java/com/google/zxing/common/HybridBinarizer.java
vendored
Normal file
237
extern/zxing-core/src/main/java/com/google/zxing/common/HybridBinarizer.java
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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 BLOCK_SIZE_POWER = 3;
|
||||
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00
|
||||
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11
|
||||
private static final int MINIMUM_DIMENSION = BLOCK_SIZE * 5;
|
||||
private static final int MIN_DYNAMIC_RANGE = 24;
|
||||
|
||||
private BitMatrix matrix;
|
||||
|
||||
public HybridBinarizer(LuminanceSource source) {
|
||||
super(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.
|
||||
*/
|
||||
@Override
|
||||
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||
if (matrix != null) {
|
||||
return matrix;
|
||||
}
|
||||
LuminanceSource source = getLuminanceSource();
|
||||
int width = source.getWidth();
|
||||
int height = source.getHeight();
|
||||
if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) {
|
||||
byte[] luminances = source.getMatrix();
|
||||
int subWidth = width >> BLOCK_SIZE_POWER;
|
||||
if ((width & BLOCK_SIZE_MASK) != 0) {
|
||||
subWidth++;
|
||||
}
|
||||
int subHeight = height >> BLOCK_SIZE_POWER;
|
||||
if ((height & BLOCK_SIZE_MASK) != 0) {
|
||||
subHeight++;
|
||||
}
|
||||
int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height);
|
||||
|
||||
BitMatrix newMatrix = new BitMatrix(width, height);
|
||||
calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix);
|
||||
matrix = newMatrix;
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram approach.
|
||||
matrix = super.getBlackMatrix();
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Binarizer createBinarizer(LuminanceSource source) {
|
||||
return new HybridBinarizer(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* For each 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 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 << BLOCK_SIZE_POWER;
|
||||
int maxYOffset = height - BLOCK_SIZE;
|
||||
if (yoffset > maxYOffset) {
|
||||
yoffset = maxYOffset;
|
||||
}
|
||||
for (int x = 0; x < subWidth; x++) {
|
||||
int xoffset = x << BLOCK_SIZE_POWER;
|
||||
int maxXOffset = width - BLOCK_SIZE;
|
||||
if (xoffset > maxXOffset) {
|
||||
xoffset = maxXOffset;
|
||||
}
|
||||
int left = cap(x, 2, subWidth - 3);
|
||||
int top = cap(y, 2, subHeight - 3);
|
||||
int sum = 0;
|
||||
for (int z = -2; z <= 2; z++) {
|
||||
int[] blackRow = blackPoints[top + z];
|
||||
sum += blackRow[left - 2] + blackRow[left - 1] + blackRow[left] + blackRow[left + 1] + blackRow[left + 2];
|
||||
}
|
||||
int average = sum / 25;
|
||||
thresholdBlock(luminances, xoffset, yoffset, average, width, matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int cap(int value, int min, int max) {
|
||||
return value < min ? min : value > max ? max : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a single threshold to a block of pixels.
|
||||
*/
|
||||
private static void thresholdBlock(byte[] luminances,
|
||||
int xoffset,
|
||||
int yoffset,
|
||||
int threshold,
|
||||
int stride,
|
||||
BitMatrix matrix) {
|
||||
for (int y = 0, offset = yoffset * stride + xoffset; y < BLOCK_SIZE; y++, offset += stride) {
|
||||
for (int x = 0; x < BLOCK_SIZE; x++) {
|
||||
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
|
||||
if ((luminances[offset + x] & 0xFF) <= threshold) {
|
||||
matrix.set(xoffset + x, yoffset + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a single black point for each block of pixels and saves it away.
|
||||
* See the following thread for a discussion of this algorithm:
|
||||
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
||||
*/
|
||||
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 << BLOCK_SIZE_POWER;
|
||||
int maxYOffset = height - BLOCK_SIZE;
|
||||
if (yoffset > maxYOffset) {
|
||||
yoffset = maxYOffset;
|
||||
}
|
||||
for (int x = 0; x < subWidth; x++) {
|
||||
int xoffset = x << BLOCK_SIZE_POWER;
|
||||
int maxXOffset = width - BLOCK_SIZE;
|
||||
if (xoffset > maxXOffset) {
|
||||
xoffset = maxXOffset;
|
||||
}
|
||||
int sum = 0;
|
||||
int min = 0xFF;
|
||||
int max = 0;
|
||||
for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE; yy++, offset += width) {
|
||||
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
|
||||
int pixel = luminances[offset + xx] & 0xFF;
|
||||
sum += pixel;
|
||||
// still looking for good contrast
|
||||
if (pixel < min) {
|
||||
min = pixel;
|
||||
}
|
||||
if (pixel > max) {
|
||||
max = pixel;
|
||||
}
|
||||
}
|
||||
// short-circuit min/max tests once dynamic range is met
|
||||
if (max - min > MIN_DYNAMIC_RANGE) {
|
||||
// finish the rest of the rows quickly
|
||||
for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) {
|
||||
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
|
||||
sum += luminances[offset + xx] & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default estimate is the average of the values in the block.
|
||||
int average = sum >> (BLOCK_SIZE_POWER * 2);
|
||||
if (max - min <= MIN_DYNAMIC_RANGE) {
|
||||
// If variation within the block is low, assume this is a block with only light or only
|
||||
// dark pixels. In that case we do not want to use the average, as it would divide this
|
||||
// low contrast area into black and white pixels, essentially creating data out of noise.
|
||||
//
|
||||
// The default assumption is that the block is light/background. Since no estimate for
|
||||
// the level of dark pixels exists locally, use half the min for the block.
|
||||
average = min >> 1;
|
||||
|
||||
if (y > 0 && x > 0) {
|
||||
// Correct the "white background" assumption for blocks that have neighbors by comparing
|
||||
// the pixels in this block to the previously calculated black points. This is based on
|
||||
// the fact that dark barcode symbology is always surrounded by some amount of light
|
||||
// background for which reasonable black point estimates were made. The bp estimated at
|
||||
// the boundaries is used for the interior.
|
||||
|
||||
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
|
||||
int averageNeighborBlackPoint = (blackPoints[y - 1][x] + (2 * blackPoints[y][x - 1]) +
|
||||
blackPoints[y - 1][x - 1]) >> 2;
|
||||
if (min < averageNeighborBlackPoint) {
|
||||
average = averageNeighborBlackPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
blackPoints[y][x] = average;
|
||||
}
|
||||
}
|
||||
return blackPoints;
|
||||
}
|
||||
|
||||
}
|
156
extern/zxing-core/src/main/java/com/google/zxing/common/PerspectiveTransform.java
vendored
Normal file
156
extern/zxing-core/src/main/java/com/google/zxing/common/PerspectiveTransform.java
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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;
|
||||
private final float a12;
|
||||
private final float a13;
|
||||
private final float a21;
|
||||
private final float a22;
|
||||
private final float a23;
|
||||
private final float a31;
|
||||
private final float a32;
|
||||
private final float 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 dx3 = x0 - x1 + x2 - x3;
|
||||
float dy3 = y0 - y1 + y2 - y3;
|
||||
if (dx3 == 0.0f && dy3 == 0.0f) {
|
||||
// Affine
|
||||
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 dy1 = y1 - y2;
|
||||
float dy2 = y3 - 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
213
extern/zxing-core/src/main/java/com/google/zxing/common/StringUtils.java
vendored
Normal file
213
extern/zxing-core/src/main/java/com/google/zxing/common/StringUtils.java
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.zxing.DecodeHintType;
|
||||
|
||||
/**
|
||||
* Common string-related functions.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Alex Dupre
|
||||
*/
|
||||
public final class StringUtils {
|
||||
|
||||
private static final String PLATFORM_DEFAULT_ENCODING = Charset.defaultCharset().name();
|
||||
public static final String SHIFT_JIS = "SJIS";
|
||||
public static final String GB2312 = "GB2312";
|
||||
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, Map<DecodeHintType,?> hints) {
|
||||
if (hints != null) {
|
||||
String characterSet = (String) hints.get(DecodeHintType.CHARACTER_SET);
|
||||
if (characterSet != null) {
|
||||
return characterSet;
|
||||
}
|
||||
}
|
||||
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
|
||||
// which should be by far the most common encodings.
|
||||
int length = bytes.length;
|
||||
boolean canBeISO88591 = true;
|
||||
boolean canBeShiftJIS = true;
|
||||
boolean canBeUTF8 = true;
|
||||
int utf8BytesLeft = 0;
|
||||
//int utf8LowChars = 0;
|
||||
int utf2BytesChars = 0;
|
||||
int utf3BytesChars = 0;
|
||||
int utf4BytesChars = 0;
|
||||
int sjisBytesLeft = 0;
|
||||
//int sjisLowChars = 0;
|
||||
int sjisKatakanaChars = 0;
|
||||
//int sjisDoubleBytesChars = 0;
|
||||
int sjisCurKatakanaWordLength = 0;
|
||||
int sjisCurDoubleBytesWordLength = 0;
|
||||
int sjisMaxKatakanaWordLength = 0;
|
||||
int sjisMaxDoubleBytesWordLength = 0;
|
||||
//int isoLowChars = 0;
|
||||
//int isoHighChars = 0;
|
||||
int isoHighOther = 0;
|
||||
|
||||
boolean utf8bom = bytes.length > 3 &&
|
||||
bytes[0] == (byte) 0xEF &&
|
||||
bytes[1] == (byte) 0xBB &&
|
||||
bytes[2] == (byte) 0xBF;
|
||||
|
||||
for (int i = 0;
|
||||
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
|
||||
i++) {
|
||||
|
||||
int value = bytes[i] & 0xFF;
|
||||
|
||||
// UTF-8 stuff
|
||||
if (canBeUTF8) {
|
||||
if (utf8BytesLeft > 0) {
|
||||
if ((value & 0x80) == 0) {
|
||||
canBeUTF8 = false;
|
||||
} else {
|
||||
utf8BytesLeft--;
|
||||
}
|
||||
} else if ((value & 0x80) != 0) {
|
||||
if ((value & 0x40) == 0) {
|
||||
canBeUTF8 = false;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x20) == 0) {
|
||||
utf2BytesChars++;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x10) == 0) {
|
||||
utf3BytesChars++;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x08) == 0) {
|
||||
utf4BytesChars++;
|
||||
} else {
|
||||
canBeUTF8 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} //else {
|
||||
//utf8LowChars++;
|
||||
//}
|
||||
}
|
||||
|
||||
// ISO-8859-1 stuff
|
||||
if (canBeISO88591) {
|
||||
if (value > 0x7F && value < 0xA0) {
|
||||
canBeISO88591 = false;
|
||||
} else if (value > 0x9F) {
|
||||
if (value < 0xC0 || value == 0xD7 || value == 0xF7) {
|
||||
isoHighOther++;
|
||||
} //else {
|
||||
//isoHighChars++;
|
||||
//}
|
||||
} //else {
|
||||
//isoLowChars++;
|
||||
//}
|
||||
}
|
||||
|
||||
// Shift_JIS stuff
|
||||
if (canBeShiftJIS) {
|
||||
if (sjisBytesLeft > 0) {
|
||||
if (value < 0x40 || value == 0x7F || value > 0xFC) {
|
||||
canBeShiftJIS = false;
|
||||
} else {
|
||||
sjisBytesLeft--;
|
||||
}
|
||||
} else if (value == 0x80 || value == 0xA0 || value > 0xEF) {
|
||||
canBeShiftJIS = false;
|
||||
} else if (value > 0xA0 && value < 0xE0) {
|
||||
sjisKatakanaChars++;
|
||||
sjisCurDoubleBytesWordLength = 0;
|
||||
sjisCurKatakanaWordLength++;
|
||||
if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) {
|
||||
sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength;
|
||||
}
|
||||
} else if (value > 0x7F) {
|
||||
sjisBytesLeft++;
|
||||
//sjisDoubleBytesChars++;
|
||||
sjisCurKatakanaWordLength = 0;
|
||||
sjisCurDoubleBytesWordLength++;
|
||||
if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) {
|
||||
sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength;
|
||||
}
|
||||
} else {
|
||||
//sjisLowChars++;
|
||||
sjisCurKatakanaWordLength = 0;
|
||||
sjisCurDoubleBytesWordLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canBeUTF8 && utf8BytesLeft > 0) {
|
||||
canBeUTF8 = false;
|
||||
}
|
||||
if (canBeShiftJIS && sjisBytesLeft > 0) {
|
||||
canBeShiftJIS = false;
|
||||
}
|
||||
|
||||
// Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done
|
||||
if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) {
|
||||
return UTF8;
|
||||
}
|
||||
// Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii characters (and no evidence it can't be), done
|
||||
if (canBeShiftJIS && (ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is:
|
||||
// - If we saw
|
||||
// - only two consecutive katakana chars in the whole text, or
|
||||
// - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
|
||||
// - then we conclude Shift_JIS, else ISO-8859-1
|
||||
if (canBeISO88591 && canBeShiftJIS) {
|
||||
return (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther * 10 >= length
|
||||
? SHIFT_JIS : ISO88591;
|
||||
}
|
||||
|
||||
// Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding
|
||||
if (canBeISO88591) {
|
||||
return ISO88591;
|
||||
}
|
||||
if (canBeShiftJIS) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
if (canBeUTF8) {
|
||||
return UTF8;
|
||||
}
|
||||
// Otherwise, we take a wild guess with platform encoding
|
||||
return PLATFORM_DEFAULT_ENCODING;
|
||||
}
|
||||
|
||||
}
|
47
extern/zxing-core/src/main/java/com/google/zxing/common/detector/MathUtils.java
vendored
Normal file
47
extern/zxing-core/src/main/java/com/google/zxing/common/detector/MathUtils.java
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012 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;
|
||||
|
||||
public final class MathUtils {
|
||||
|
||||
private MathUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up to x+1.
|
||||
*
|
||||
* @param d real value to round
|
||||
* @return nearest {@code int}
|
||||
*/
|
||||
public static int round(float d) {
|
||||
return (int) (d + 0.5f);
|
||||
}
|
||||
|
||||
public static float distance(float aX, float aY, float bX, float bY) {
|
||||
float xDiff = aX - bX;
|
||||
float yDiff = aY - bY;
|
||||
return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
}
|
||||
|
||||
public static float distance(int aX, int aY, int bX, int bY) {
|
||||
int xDiff = aX - bX;
|
||||
int yDiff = aY - bY;
|
||||
return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
}
|
||||
|
||||
}
|
215
extern/zxing-core/src/main/java/com/google/zxing/common/detector/MonochromeRectangleDetector.java
vendored
Normal file
215
extern/zxing-core/src/main/java/com/google/zxing/common/detector/MonochromeRectangleDetector.java
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
342
extern/zxing-core/src/main/java/com/google/zxing/common/detector/WhiteRectangleDetector.java
vendored
Normal file
342
extern/zxing-core/src/main/java/com/google/zxing/common/detector/WhiteRectangleDetector.java
vendored
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* 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 = 10;
|
||||
private static final int CORR = 1;
|
||||
|
||||
private final BitMatrix image;
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int leftInit;
|
||||
private final int rightInit;
|
||||
private final int downInit;
|
||||
private final int upInit;
|
||||
|
||||
public WhiteRectangleDetector(BitMatrix image) throws NotFoundException {
|
||||
this(image, INIT_SIZE, image.getWidth() / 2, image.getHeight() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param image barcode image to find a rectangle in
|
||||
* @param initSize initial size of search area around center
|
||||
* @param x x position of search center
|
||||
* @param y y position of search center
|
||||
* @throws NotFoundException if image is too small to accommodate {@code initSize}
|
||||
*/
|
||||
public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException {
|
||||
this.image = image;
|
||||
height = image.getHeight();
|
||||
width = image.getWidth();
|
||||
int halfsize = initSize / 2;
|
||||
leftInit = x - halfsize;
|
||||
rightInit = x + halfsize;
|
||||
upInit = y - halfsize;
|
||||
downInit = y + halfsize;
|
||||
if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <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 = leftInit;
|
||||
int right = rightInit;
|
||||
int up = upInit;
|
||||
int down = downInit;
|
||||
boolean sizeExceeded = false;
|
||||
boolean aBlackPointFoundOnBorder = true;
|
||||
boolean atLeastOneBlackPointFoundOnBorder = false;
|
||||
|
||||
boolean atLeastOneBlackPointFoundOnRight = false;
|
||||
boolean atLeastOneBlackPointFoundOnBottom = false;
|
||||
boolean atLeastOneBlackPointFoundOnLeft = false;
|
||||
boolean atLeastOneBlackPointFoundOnTop = false;
|
||||
|
||||
while (aBlackPointFoundOnBorder) {
|
||||
|
||||
aBlackPointFoundOnBorder = false;
|
||||
|
||||
// .....
|
||||
// . |
|
||||
// .....
|
||||
boolean rightBorderNotWhite = true;
|
||||
while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < width) {
|
||||
rightBorderNotWhite = containsBlackPoint(up, down, right, false);
|
||||
if (rightBorderNotWhite) {
|
||||
right++;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnRight = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnRight) {
|
||||
right++;
|
||||
}
|
||||
}
|
||||
|
||||
if (right >= width) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// .....
|
||||
// . .
|
||||
// .___.
|
||||
boolean bottomBorderNotWhite = true;
|
||||
while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < height) {
|
||||
bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
|
||||
if (bottomBorderNotWhite) {
|
||||
down++;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnBottom = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnBottom) {
|
||||
down++;
|
||||
}
|
||||
}
|
||||
|
||||
if (down >= height) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// .....
|
||||
// | .
|
||||
// .....
|
||||
boolean leftBorderNotWhite = true;
|
||||
while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) {
|
||||
leftBorderNotWhite = containsBlackPoint(up, down, left, false);
|
||||
if (leftBorderNotWhite) {
|
||||
left--;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnLeft = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnLeft) {
|
||||
left--;
|
||||
}
|
||||
}
|
||||
|
||||
if (left < 0) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// .___.
|
||||
// . .
|
||||
// .....
|
||||
boolean topBorderNotWhite = true;
|
||||
while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) {
|
||||
topBorderNotWhite = containsBlackPoint(left, right, up, true);
|
||||
if (topBorderNotWhite) {
|
||||
up--;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnTop = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnTop) {
|
||||
up--;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
|
||||
int dist = MathUtils.round(MathUtils.distance(aX, aY, bX, bY));
|
||||
float xStep = (bX - aX) / dist;
|
||||
float yStep = (bY - aY) / dist;
|
||||
|
||||
for (int i = 0; i < dist; i++) {
|
||||
int x = MathUtils.round(aX + i * xStep);
|
||||
int y = MathUtils.round(aY + i * yStep);
|
||||
if (image.get(x, y)) {
|
||||
return new ResultPoint(x, y);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.0f) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
196
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java
vendored
Normal file
196
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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 Fields. Operations use a given primitive polynomial in calculations.</p>
|
||||
*
|
||||
* <p>Throughout this package, elements of the GF are represented as an {@code int}
|
||||
* for convenience and speed (but at the cost of memory).
|
||||
* </p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class GenericGF {
|
||||
|
||||
public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||
public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||
public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
|
||||
public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
|
||||
public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||
public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||
public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;
|
||||
public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6;
|
||||
|
||||
private static final int INITIALIZATION_THRESHOLD = 0;
|
||||
|
||||
private int[] expTable;
|
||||
private int[] logTable;
|
||||
private GenericGFPoly zero;
|
||||
private GenericGFPoly one;
|
||||
private final int size;
|
||||
private final int primitive;
|
||||
private final int generatorBase;
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Create a representation of GF(size) 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
|
||||
* @param size the size of the field
|
||||
* @param b the factor b in the generator polynomial can be 0- or 1-based
|
||||
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
* In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
public GenericGF(int primitive, int size, int b) {
|
||||
this.primitive = primitive;
|
||||
this.size = size;
|
||||
this.generatorBase = b;
|
||||
|
||||
if (size <= INITIALIZATION_THRESHOLD) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
expTable = new int[size];
|
||||
logTable = new int[size];
|
||||
int x = 1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
expTable[i] = x;
|
||||
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
|
||||
if (x >= size) {
|
||||
x ^= primitive;
|
||||
x &= size-1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < size-1; i++) {
|
||||
logTable[expTable[i]] = i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
zero = new GenericGFPoly(this, new int[]{0});
|
||||
one = new GenericGFPoly(this, new int[]{1});
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void checkInit() {
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
GenericGFPoly getZero() {
|
||||
checkInit();
|
||||
|
||||
return zero;
|
||||
}
|
||||
|
||||
GenericGFPoly getOne() {
|
||||
checkInit();
|
||||
|
||||
return one;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monomial representing coefficient * x^degree
|
||||
*/
|
||||
GenericGFPoly buildMonomial(int degree, int coefficient) {
|
||||
checkInit();
|
||||
|
||||
if (degree < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (coefficient == 0) {
|
||||
return zero;
|
||||
}
|
||||
int[] coefficients = new int[degree + 1];
|
||||
coefficients[0] = coefficient;
|
||||
return new GenericGFPoly(this, coefficients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @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(size)
|
||||
*/
|
||||
int exp(int a) {
|
||||
checkInit();
|
||||
|
||||
return expTable[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
int log(int a) {
|
||||
checkInit();
|
||||
|
||||
if (a == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return logTable[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return multiplicative inverse of a
|
||||
*/
|
||||
int inverse(int a) {
|
||||
checkInit();
|
||||
|
||||
if (a == 0) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return expTable[size - logTable[a] - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return product of a and b in GF(size)
|
||||
*/
|
||||
int multiply(int a, int b) {
|
||||
checkInit();
|
||||
|
||||
if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return expTable[(logTable[a] + logTable[b]) % (size - 1)];
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getGeneratorBase() {
|
||||
return generatorBase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')';
|
||||
}
|
||||
|
||||
}
|
264
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java
vendored
Normal file
264
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 a GF.
|
||||
* 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 GenericGFPoly {
|
||||
|
||||
private final GenericGF field;
|
||||
private final int[] coefficients;
|
||||
|
||||
/**
|
||||
* @param field the {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param coefficients coefficients as ints representing elements of GF(size), 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")
|
||||
*/
|
||||
GenericGFPoly(GenericGF field, int[] coefficients) {
|
||||
if (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 coefficient : coefficients) {
|
||||
result = GenericGF.addOrSubtract(result, coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int result = coefficients[0];
|
||||
for (int i = 1; i < size; i++) {
|
||||
result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GenericGFPoly addOrSubtract(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF 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] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||
}
|
||||
|
||||
return new GenericGFPoly(field, sumDiff);
|
||||
}
|
||||
|
||||
GenericGFPoly multiply(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF 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] = GenericGF.addOrSubtract(product[i + j],
|
||||
field.multiply(aCoeff, bCoefficients[j]));
|
||||
}
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly 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 GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly 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 GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly[] divide(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if (other.isZero()) {
|
||||
throw new IllegalArgumentException("Divide by 0");
|
||||
}
|
||||
|
||||
GenericGFPoly quotient = field.getZero();
|
||||
GenericGFPoly 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);
|
||||
GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);
|
||||
GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);
|
||||
quotient = quotient.addOrSubtract(iterationQuotient);
|
||||
remainder = remainder.addOrSubtract(term);
|
||||
}
|
||||
|
||||
return new GenericGFPoly[] { quotient, remainder };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(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();
|
||||
}
|
||||
|
||||
}
|
190
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
vendored
Normal file
190
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 GenericGF field;
|
||||
|
||||
public ReedSolomonDecoder(GenericGF 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 {
|
||||
GenericGFPoly poly = new GenericGFPoly(field, received);
|
||||
int[] syndromeCoefficients = new int[twoS];
|
||||
boolean noError = true;
|
||||
for (int i = 0; i < twoS; i++) {
|
||||
int eval = poly.evaluateAt(field.exp(i + field.getGeneratorBase()));
|
||||
syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
|
||||
if (eval != 0) {
|
||||
noError = false;
|
||||
}
|
||||
}
|
||||
if (noError) {
|
||||
return;
|
||||
}
|
||||
GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients);
|
||||
GenericGFPoly[] sigmaOmega =
|
||||
runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
|
||||
GenericGFPoly sigma = sigmaOmega[0];
|
||||
GenericGFPoly omega = sigmaOmega[1];
|
||||
int[] errorLocations = findErrorLocations(sigma);
|
||||
int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations);
|
||||
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] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R)
|
||||
throws ReedSolomonException {
|
||||
// Assume a's degree is >= b's
|
||||
if (a.getDegree() < b.getDegree()) {
|
||||
GenericGFPoly temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
GenericGFPoly rLast = a;
|
||||
GenericGFPoly r = b;
|
||||
GenericGFPoly tLast = field.getZero();
|
||||
GenericGFPoly t = field.getOne();
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while (r.getDegree() >= R / 2) {
|
||||
GenericGFPoly rLastLast = rLast;
|
||||
GenericGFPoly tLastLast = tLast;
|
||||
rLast = r;
|
||||
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;
|
||||
GenericGFPoly 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));
|
||||
}
|
||||
|
||||
t = q.multiply(tLast).addOrSubtract(tLastLast);
|
||||
|
||||
if (r.getDegree() >= rLast.getDegree()) {
|
||||
throw new IllegalStateException("Division algorithm failed to reduce polynomial?");
|
||||
}
|
||||
}
|
||||
|
||||
int sigmaTildeAtZero = t.getCoefficient(0);
|
||||
if (sigmaTildeAtZero == 0) {
|
||||
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||
}
|
||||
|
||||
int inverse = field.inverse(sigmaTildeAtZero);
|
||||
GenericGFPoly sigma = t.multiply(inverse);
|
||||
GenericGFPoly omega = r.multiply(inverse);
|
||||
return new GenericGFPoly[]{sigma, omega};
|
||||
}
|
||||
|
||||
private int[] findErrorLocations(GenericGFPoly 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 < field.getSize() && 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(GenericGFPoly errorEvaluator, int[] errorLocations) {
|
||||
// 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,
|
||||
// GenericGF.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));
|
||||
if (field.getGeneratorBase() != 0) {
|
||||
result[i] = field.multiply(result[i], xiInverse);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
74
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
vendored
Normal file
74
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Implements Reed-Solomon enbcoding, as the name implies.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author William Rucklidge
|
||||
*/
|
||||
public final class ReedSolomonEncoder {
|
||||
|
||||
private final GenericGF field;
|
||||
private final List<GenericGFPoly> cachedGenerators;
|
||||
|
||||
public ReedSolomonEncoder(GenericGF field) {
|
||||
this.field = field;
|
||||
this.cachedGenerators = new ArrayList<>();
|
||||
cachedGenerators.add(new GenericGFPoly(field, new int[]{1}));
|
||||
}
|
||||
|
||||
private GenericGFPoly buildGenerator(int degree) {
|
||||
if (degree >= cachedGenerators.size()) {
|
||||
GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1);
|
||||
for (int d = cachedGenerators.size(); d <= degree; d++) {
|
||||
GenericGFPoly nextGenerator = lastGenerator.multiply(
|
||||
new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) }));
|
||||
cachedGenerators.add(nextGenerator);
|
||||
lastGenerator = nextGenerator;
|
||||
}
|
||||
}
|
||||
return cachedGenerators.get(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");
|
||||
}
|
||||
GenericGFPoly generator = buildGenerator(ecBytes);
|
||||
int[] infoCoefficients = new int[dataBytes];
|
||||
System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
|
||||
GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);
|
||||
info = info.multiplyByMonomial(ecBytes, 1);
|
||||
GenericGFPoly 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);
|
||||
}
|
||||
|
||||
}
|
31
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonException.java
vendored
Normal file
31
extern/zxing-core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonException.java
vendored
Normal file
@ -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
extern/zxing-core/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java
vendored
Normal file
161
extern/zxing-core/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java
vendored
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.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> 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);
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
if (byteSegments != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
|
||||
}
|
||||
String ecLevel = decoderResult.getECLevel();
|
||||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a 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[] leftTopBlack = image.getTopLeftOnBit();
|
||||
int[] rightBottomBlack = image.getBottomRightOnBit();
|
||||
if (leftTopBlack == null || rightBottomBlack == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
int moduleSize = moduleSize(leftTopBlack, image);
|
||||
|
||||
int top = leftTopBlack[1];
|
||||
int bottom = rightBottomBlack[1];
|
||||
int left = leftTopBlack[0];
|
||||
int right = rightBottomBlack[0];
|
||||
|
||||
int matrixWidth = (right - left + 1) / moduleSize;
|
||||
int matrixHeight = (bottom - top + 1) / moduleSize;
|
||||
if (matrixWidth <= 0 || matrixHeight <= 0) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
// 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.
|
||||
int nudge = moduleSize >> 1;
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
|
||||
// Now just read off the bits
|
||||
BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);
|
||||
for (int y = 0; y < matrixHeight; y++) {
|
||||
int iOffset = top + y * moduleSize;
|
||||
for (int x = 0; x < matrixWidth; x++) {
|
||||
if (image.get(left + x * moduleSize, iOffset)) {
|
||||
bits.set(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static int moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {
|
||||
int width = image.getWidth();
|
||||
int x = leftTopBlack[0];
|
||||
int y = leftTopBlack[1];
|
||||
while (x < width && image.get(x, y)) {
|
||||
x++;
|
||||
}
|
||||
if (x == width) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
int moduleSize = x - leftTopBlack[0];
|
||||
if (moduleSize == 0) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
return moduleSize;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user